Modifications for deployment

This commit is contained in:
Gregory Trullemans 2022-05-06 14:49:54 +02:00
parent 410f74802e
commit 86ad95a72e
15 changed files with 369 additions and 14 deletions

10
.gitignore vendored
View File

@ -63,9 +63,12 @@ coverage.xml
# Django stuff:
*.log
local_settings.py
#db.sqlite3
db.sqlite3-journal
*.pot
*.pyc
__pycache__
db.sqlite3
*.sql
media
# Flask stuff:
instance/
@ -117,6 +120,7 @@ venv/
ENV/
env.bak/
venv.bak/
data/
# Spyder project settings
.spyderproject

2
Procfile Normal file
View File

@ -0,0 +1,2 @@
release: python manage.py migrate
web: gunicorn config.wsgi

View File

@ -26,6 +26,7 @@ from .forms import (
from tools.pdf_generator import BillPaper
@login_required
@require_http_methods(["GET"])
def client_lookup(request):
""" Récupère la liste des clients à la volée suivant des caractères de recherches entrés.
@ -42,6 +43,7 @@ def client_lookup(request):
return HttpResponse(json, content_type="application/json")
@login_required
@require_http_methods(["GET"])
def client_listing(request):
""" Récupère la liste de tous les clients. """
@ -50,6 +52,7 @@ def client_listing(request):
return render(request, "billing/clients/listing.html", context)
@login_required
@require_http_methods(["GET"])
def client_details(request, client_id=None):
""" Récupère les informations d'un client.
@ -62,7 +65,7 @@ def client_details(request, client_id=None):
return render(request, "billing/clients/details.html", context)
# @login_required
@login_required
@require_http_methods(["GET", "POST"])
def client_create_or_update(request, client_id=None):
""" Création d'un client.
@ -90,6 +93,7 @@ def client_create_or_update(request, client_id=None):
return render(request, "billing/clients/create.html", context)
@login_required
@require_http_methods(["GET"])
def contract_listing(request):
""" Récupère la liste de tous les contrats. """
@ -98,6 +102,7 @@ def contract_listing(request):
return render(request, "billing/contracts/listing.html", context)
@login_required
@require_http_methods(["GET"])
def contract_detail(request, contract_id):
"""
@ -123,7 +128,7 @@ def contract_detail(request, contract_id):
return render(request, "billing/contracts/details.html", context)
# @login_required
@login_required
@require_http_methods(["GET", "POST"])
def contract_create_or_update(request, contract_id=None):
""" Création d'un contract.
@ -151,6 +156,7 @@ def contract_create_or_update(request, contract_id=None):
return render(request, "billing/contracts/create.html", context)
@login_required
@require_http_methods(["GET"])
def contract_export(request, contract_id):
""" Génere un fichier PDF pour fournir au client.
@ -173,6 +179,7 @@ def contract_export(request, contract_id):
return response
@login_required
@require_http_methods(["GET", "POST"])
def prestation_create_or_update(request, prestation_id=None):
""" Création d'un contract.
@ -202,6 +209,7 @@ def prestation_create_or_update(request, prestation_id=None):
return render(request, "billing/prestations/create.html", context)
@login_required
@require_http_methods(["GET"])
def contract_lookup(request):
""" Récupère la liste des contrat à la volée suivant des caractères de recherches entrés.

View File

@ -1,4 +1,23 @@
import os
import environ
# Initialise environment variables
env = environ.Env()
environ.Env.read_env()
# Sentry
SENTRY_DSN = env("SENTRY_DSN", default=None)
if SENTRY_DSN is not None:
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn=SENTRY_DSN,
integrations=[DjangoIntegration()],
traces_sample_rate=env("SENTRY_TRACES_SAMPLE_RATE", default=1.0),
send_default_pii=True,
debug=env("SENTRY_DEBUG", default=True)
)
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -8,12 +27,14 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "lf#a+mh&9gw&wwn32d!=7_a)g7s5ju2z0m)r(l3xhhe+eb$fp@"
# SECRET_KEY = "lf#a+mh&9gw&wwn32d!=7_a)g7s5ju2z0m)r(l3xhhe+eb$fp@"
SECRET_KEY = env('SECRET_KEY', default="Super Little Poney 2000")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# ALLOWED_HOSTS = []
ALLOWED_HOSTS = env('ALLOWED_HOSTS', default="localhost").split()
# Application definition
@ -68,10 +89,11 @@ TEMPLATES = [
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
# "default": {
# "ENGINE": "django.db.backends.sqlite3",
# "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
# }
'default': env.db_url("DATABASE_URL", default="sqlite:///db.sqlite3")
}
# CACHES = {

View File

@ -39,12 +39,12 @@ urlpatterns = [
),
# Billing
# TODO: une seule ligne !
path(r"billing/client/", include(billing.urls.client_urlpatterns)),
path(r"billing/contract/", include(billing.urls.contract_urlpatterns)),
path(r"billing/prestation/", include(billing.urls.prestation_urlpatterns)),
# Home
path(r"", year_listing),
path("", include("core.urls")),
path(r"admin/", admin.site.urls),
]

View File

@ -8,6 +8,7 @@ from django.urls import reverse
from collections import OrderedDict
from django.views.decorators.http import require_http_methods
from django.contrib.auth.decorators import login_required
from .models import (
Transaction,
@ -39,6 +40,7 @@ EXTENSES = 0
RECETTES = 1
@login_required
@require_http_methods(["GET"])
def comptability_export(request, accounting_year, export_type):
"""
@ -58,6 +60,7 @@ def comptability_export(request, accounting_year, export_type):
return get_transaction_list_for_accountingyear_ooo(request, accounting_year)
@login_required
@require_http_methods(["GET"])
def get_transaction_list_for_accountingyear_ooo(request, accounting_year):
"""
@ -82,6 +85,7 @@ def get_transaction_list_for_accountingyear_ooo(request, accounting_year):
return render(request, "comptability/year_transaction_export_ooo.html", context)
@login_required
@require_http_methods(["GET"])
def get_transaction_list_for_accountingyear_sxs(request, accounting_year):
"""
@ -112,6 +116,7 @@ def get_transaction_list_for_accountingyear_sxs(request, accounting_year):
return render(request, "comptability/year_transaction_export_sxs.html", context)
@login_required
@require_http_methods(["GET"])
def export_year_spf_finance(request, accounting_year):
"""
@ -188,6 +193,7 @@ def export_year_spf_finance(request, accounting_year):
return render(request, "comptability/year_transaction_export_spf.html", context)
@login_required
@require_http_methods(["GET"])
def generate_pdf_for_spf(request, accounting_year):
""" Génère un fichier PDF suivant les contraintes du SPF Finances
@ -237,6 +243,7 @@ def __get_transactions_for_accounting_year_and_kind(
return transactions_list, transaction_kind
@login_required
@require_http_methods(["GET"])
def transaction_by_year_listing(request, accounting_year, transaction_kind=None):
""" Liste toutes les lignes de comptabilité pour une année recue en paramètre
@ -323,6 +330,7 @@ def transaction_by_year_listing(request, accounting_year, transaction_kind=None)
return render(request, "comptability/transactions/listing.html", context)
@login_required
@require_http_methods(["GET"])
def year_listing(request):
"""
@ -338,6 +346,7 @@ def year_listing(request):
return render(request, "comptability/year_listing.html", context)
@login_required
@require_http_methods(["GET"])
def transaction_listing_for_year_and_type(
request, accounting_year, transaction_type_id
@ -384,6 +393,7 @@ def transaction_listing_for_year_and_type(
return render(request, "comptability/transactions/listing.html", context)
@login_required
@require_http_methods(["GET"])
def transaction_details(request, transaction_id):
"""
@ -395,6 +405,7 @@ def transaction_details(request, transaction_id):
return render(request, "comptability/transactions/details.html", context)
@login_required
@require_http_methods(["GET", "POST"])
def transaction_create_or_update(request, transaction_id=None):
""" Création ou modificatin d'une transaction.

0
src/core/__init__.py Normal file
View File

11
src/core/urls.py Normal file
View File

@ -0,0 +1,11 @@
from django.urls import path
from .views import login, logout #, home, search
urlpatterns = [
# path(r"search/", search, name="global_search"),
path(r"login/", login, name="login"),
path(r"logout/", logout, name="logout"),
# path(r"", home, name="home"),
]

41
src/core/views.py Normal file
View File

@ -0,0 +1,41 @@
from datetime import datetime, timedelta
from django.db.models import Q
from django.shortcuts import render
from django.utils import timezone
from django.contrib.auth import authenticate, login as auth_login, logout as auth_logout
from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods
from django.urls import reverse
def login(request):
"""Formulaire d'authentifictation."""
if request.method == "POST":
username = request.POST["login"]
password = request.POST["password"]
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
auth_login(request, user)
return HttpResponseRedirect(reverse("year_listing"))
context = {"message": "Account disabled."}
else:
context = {"message": "Wrong login/password."}
else:
context = {}
return render(request, "login.html", context)
@login_required
@require_http_methods(["GET"])
def logout(request):
"""Fonction de déconnexion."""
auth_logout(request)
return HttpResponseRedirect(reverse("login"))

View File

@ -2,6 +2,7 @@
from django.db.models import Sum
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from collections import OrderedDict
@ -88,6 +89,7 @@ def __compute_sum_amount(transaction_list, with_simulated=None):
return total
@login_required
@require_http_methods(["GET"])
def event_details(request, event_id):
"""
@ -162,6 +164,7 @@ def event_details(request, event_id):
return render(request, "event/details.html", context)
@login_required
@require_http_methods(["GET"])
def event_listing(request):
"""
@ -172,6 +175,7 @@ def event_listing(request):
return render(request, "event/listing.html", context)
@login_required
@require_http_methods(["GET", "POST"])
def event_create_or_update(request, event_id=None):
""" Création ou modificatin d'une event.

View File

@ -49,7 +49,10 @@
<li><a href="/billing/contract/">Liste contracts</a></li>
</ul>
</li>
<li><a href="/admin/" target="_blank">Administration</a></li>
{% if user.username == 'Sulley' %}
<li><a href="/admin/" target="_blank">Administration</a></li>
{% endif %}
<li><a href="{% url 'logout' %}">Déconnexion</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>

View File

@ -0,0 +1,99 @@
{% extends "base.html" %}
{% block content %}
<div id="page" class=" sidebar_right">
<div class="container">
<div id="frame2">
<div id="content">
<div class="row">
<div class="col-md-12 col-lg-12">
<h1>{% if prestation_id %}Modification{% else %}Nouvelle{% endif %} prestation</h1>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-10 col-lg-8 col-md-offset-1 col-lg-offset-2">
<div class="well well-small text-right">
<form action="{% if prestation_id %}{% url 'prestation_update' prestation_id %}{% else %}{% url 'prestation_create' %}{% endif %}" method="post" class="form-horizontal" id="formulaire" name="formulaire">
{% csrf_token %}
<div class="form-group row ">
<label for="id_contract" class="col-sm-4 col-md-4 col-lg-3 col-xl-2 control-label">{{ form.contract.label }}</label>
<div class="col-sm-6 col-md-6 col-lg-6 col-xl-6 {% if form.contract.errors %}has-danger{% endif %}">
{{ form.contract }}
{% if form.contract.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.contract.errors %}{{error}}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_date" class="col-sm-4 col-md-4 col-lg-3 col-xl-2 control-label">{{ form.date.label }}</label>
<div class="col-sm-3 col-md-3 col-lg-3 col-xl-3 {% if form.date.errors %}has-danger{% endif %}">
{{ form.date }}
{% if form.date.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.date.errors %}{{error}}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_label" class="col-sm-4 col-md-4 col-lg-3 col-xl-2 control-label">{{ form.label.label }}</label>
<div class="col-sm-6 col-md-6 col-lg-8 col-xl-8 {% if form.label.errors %}has-danger{% endif %}">
{{ form.label }}
{% if form.label.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.label.errors %}{{error}}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_unit" class="col-sm-4 col-md-4 col-lg-3 col-xl-2 control-label">{{ form.unit.label }}</label>
<div class="col-sm-3 col-md-3 col-lg-2 col-xl-2 {% if form.unit.errors %}has-danger{% endif %}">
{{ form.unit }}
{% if form.unit.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.unit.errors %}{{error}}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_unit_price" class="col-sm-4 col-md-4 col-lg-3 col-xl-2 control-label">{{ form.unit_price.label }}</label>
<div class="col-sm-3 col-md-3 col-lg-2 col-xl-2 {% if form.unit_price.errors %}has-danger{% endif %}">
{{ form.unit_price }}
{% if form.unit_price.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.unit_price.errors %}{{error}}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group text-center">
<input type="submit" value="{% if prestation_id %}Modifier{% else %}Sauvegarder{% endif %}" class="btn btn-primary" />
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" >
$(function(){
$('#id_contract_related').autocomplete({
source: function(request, response){
$.ajax({
url: '/billing/contract/lookup/?pattern=' + $('#id_contract_related').val(),
dataType: "json",
success: function(data){
if(data.length != 0){
response($.map(data, function(item){
return {
label: item.Name,
value: item.Name,
contractid: item.ID
}
}))
} else {
response([{ label: 'No result found.', value: '' }]);
};
},
error: function(exception){
console.log(exception);
}
});
},
minLength: 3,
select: function(event, ui){
$($(this).data('ref')).val(ui.item.contractid);
}
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,44 @@
{% extends "base.html" %}
{% block content %}
<div id="page" class=" sidebar_right">
<div class="container">
<div id="frame2">
<div id="content">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<h1>Prestations</h1>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<table class="table table-striped table-bordered table-condensed">
<thead>
<th>Date</th>
<th>Prestation</th>
<th>Contrat</th>
<th class="centered">Unité</th>
<th class="centered">Prix</th>
<th class="centered">Total</th>
</thead>
{% for prestation in prestation_list %}
<tr>
<td>{{ prestation.date|date:"d-m-Y" }}</td>
<td>{{ prestation.label }}</td>
<td><a href="/billing/contract/{{ prestation.contract.id }}">{{ prestation.contract }}</a></td>
<td class="push-right">{{ prestation.unit }}</td>
<td class="push-right">{{ prestation.unit_price }}</td>
<td class="push-right">{{ prestation.total_amount }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

106
src/templates/login.html Normal file
View File

@ -0,0 +1,106 @@
{% load static %}
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="{% static "css/normalize_v801.css" %}" media="all" />
<link rel="stylesheet" href="{% static "css/bootstrap.min.css" %}" media="all" />
<!-- JQuery UI CSS -->
<link href="{% static "js/jqueryui/jquery-ui.theme.min.css" %}" rel="stylesheet" />
<link href="{% static "js/jqueryui/jquery-ui.min.css" %}" rel="stylesheet" />
<link rel="stylesheet" href="{% static "css/app_screen.css" %}" media="all" />
<link rel="stylesheet" href="{% static "css/app_printer.css" %}" media="print" />
{% block extra_head %}{% endblock %}
<title>{% block page_title %}Comptabilité{% endblock %}</title>
</head>
<body>
<div id="page" class=" sidebar_right">
<div class="container">
<div id="frame2">
<div id="content">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<h1>{{ transaction.description }}</h1>
</div>
<br />
<div class="col-xs-12 col-sm-12 col-md-8 col-lg-6 col-md-offset-2 col-lg-offset-3">
<div class="well well-small text-right">
<form class="form-horizontal" action="/login/" method="post" if="formulaire">
{% csrf_token %}
<div class="form-group">
<label for="login" class="col-lg-4 control-label">Login</label>
<div class="col-lg-8">
<input type="login" name="login" class="form-control" id="login" placeholder="Login">
</div>
</div>
<div class="form-group">
<label for="login" class="col-lg-4 control-label">Password</label>
<div class="col-lg-8">
<input type="password" name="password" class="form-control" id="password" placeholder="Password">
</div>
</div>
{% if message %}
<p class="text-danger"><b>{{message}}</b></p>
{% endif %}
<div class="form-group">
<div class="col-lg-6 col-lg-offset-3">
<button type="submit" class="btn btn-primary btn-block mb-3">Log me in</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="{% static "js/jquery-2.1.4.min.js" %}"></script>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script src="{% static "js/highcharts/highcharts.js" %}"></script>
<script src="{% static "js/highcharts/exporting.js" %}"></script>
<script src="{% static "js/highcharts/dark-unica.js" %}"></script>
<!-- <script src="{% static "js/tablesort.js" %}"></script> -->
<!-- <link href="{% static "css/theme.default.min.css" %}" rel="stylesheet"> -->
<script src="{% static "js/jquery.tablesorter.js" %}"></script>
<script src="{% static "js/jquery.tablesorter.widgets.js" %}"></script>
<script src="{% static "js/jqueryui/jquery-ui.min.js" %}"></script>
{% block extra_script %}{% endblock %}
<script type="text/javascript">
$(document).ready(function() {
Highcharts.setOptions({
lang: {
months: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
shortMonths: [ "Jan" , "Feb" , "Mar" , "Apr" , "May" , "Jun" , "Jul" , "Aug" , "Sep" , "Oct" , "Nov" , "Dec"],
weekdays: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
decimalPoint: ",",
thousandsSep: ".",
downloadJPEG: "Telecharger en JPEG",
downloadPDF: "Telecharger en PDF",
downloadPNG: "Telecharger en PNG",
downloadSVG: "Telecharger en SVG",
printChart: "Imprimer",
resetZoom: "Rétablir",
resetZoomTitle: "Rétablir",
loading: "Chargement…",
noData: "Pas de données disponibles."
}
});
});
</script>
<section id="main" class="container">{% block content %}{% endblock %}</section>
<footer>
<div class="footer-content"></div>
</footer>
</body>
</html>

View File

@ -3,6 +3,6 @@ max-line-length = 100
exclude = migrations, manage.py, ._*
[pytest]
DJANGO_SETTINGS_MODULE = comptaClub.settings
DJANGO_SETTINGS_MODULE = config.settings
# -- recommended but optional:
python_files = tests.py test_*.py *_tests.py