Merge branch 'aureplop/kfet_config' into 'master'

Configuration utilisateur K-Fêt

See merge request !210
This commit is contained in:
Martin Pepin 2017-04-10 21:14:37 +02:00
commit 495c6ac3d1
19 changed files with 358 additions and 229 deletions

View file

@ -47,6 +47,7 @@ INSTALLED_APPS = (
'channels', 'channels',
'widget_tweaks', 'widget_tweaks',
'custommail', 'custommail',
'djconfig',
) )
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
@ -60,6 +61,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'djconfig.middleware.DjConfigMiddleware',
) )
ROOT_URLCONF = 'cof.urls' ROOT_URLCONF = 'cof.urls'
@ -78,8 +80,10 @@ TEMPLATES = [
'django.core.context_processors.i18n', 'django.core.context_processors.i18n',
'django.core.context_processors.media', 'django.core.context_processors.media',
'django.core.context_processors.static', 'django.core.context_processors.static',
'djconfig.context_processors.config',
'gestioncof.shared.context_processor', 'gestioncof.shared.context_processor',
'kfet.context_processors.auth', 'kfet.context_processors.auth',
'kfet.context_processors.config',
], ],
}, },
}, },

View file

@ -12,3 +12,9 @@ class KFetConfig(AppConfig):
def ready(self): def ready(self):
import kfet.signals import kfet.signals
self.register_config()
def register_config(self):
import djconfig
from kfet.forms import KFetConfigForm
djconfig.register(KFetConfigForm)

71
kfet/config.py Normal file
View file

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
from django.core.exceptions import ValidationError
from django.db import models
from djconfig import config
class KFetConfig(object):
"""kfet app configuration.
Enhance isolation with backend used to store config.
Usable after DjConfig middleware was called.
"""
prefix = 'kfet_'
def __getattr__(self, key):
if key == 'subvention_cof':
# Allows accessing to the reduction as a subvention
# Other reason: backward compatibility
reduction_mult = 1 - self.reduction_cof/100
return (1/reduction_mult - 1) * 100
return getattr(config, self._get_dj_key(key))
def list(self):
"""Get list of kfet app configuration.
Returns:
(key, value) for each configuration entry as list.
"""
# prevent circular imports
from kfet.forms import KFetConfigForm
return [(field.label, getattr(config, name), )
for name, field in KFetConfigForm.base_fields.items()]
def _get_dj_key(self, key):
return '{}{}'.format(self.prefix, key)
def set(self, **kwargs):
"""Update configuration value(s).
Args:
**kwargs: Keyword arguments. Keys must be in kfet config.
Config entries are updated to given values.
"""
# prevent circular imports
from kfet.forms import KFetConfigForm
# get old config
new_cfg = KFetConfigForm().initial
# update to new config
for key, value in kwargs.items():
dj_key = self._get_dj_key(key)
if isinstance(value, models.Model):
new_cfg[dj_key] = value.pk
else:
new_cfg[dj_key] = value
# save new config
cfg_form = KFetConfigForm(new_cfg)
if cfg_form.is_valid():
cfg_form.save()
else:
raise ValidationError(
'Invalid values in kfet_config.set: %(fields)s',
params={'fields': list(cfg_form.errors)})
kfet_config = KFetConfig()

View file

@ -1,11 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import *
from django.contrib.auth.context_processors import PermWrapper from django.contrib.auth.context_processors import PermWrapper
from kfet.config import kfet_config
def auth(request): def auth(request):
if hasattr(request, 'real_user'): if hasattr(request, 'real_user'):
return { return {
@ -13,3 +12,7 @@ def auth(request):
'perms': PermWrapper(request.real_user), 'perms': PermWrapper(request.real_user),
} }
return {} return {}
def config(request):
return {'kfet_config': kfet_config}

View file

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from datetime import timedelta
from decimal import Decimal from decimal import Decimal
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import MinLengthValidator from django.core.validators import MinLengthValidator
@ -8,12 +10,16 @@ from django.contrib.auth.models import User, Group, Permission
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.forms import modelformset_factory from django.forms import modelformset_factory
from django.utils import timezone from django.utils import timezone
from djconfig.forms import ConfigForm
from kfet.models import ( from kfet.models import (
Account, Checkout, Article, OperationGroup, Operation, Account, Checkout, Article, OperationGroup, Operation,
CheckoutStatement, ArticleCategory, Settings, AccountNegative, Transfer, CheckoutStatement, ArticleCategory, AccountNegative, Transfer,
TransferGroup, Supplier) TransferGroup, Supplier)
from gestioncof.models import CofProfile from gestioncof.models import CofProfile
# ----- # -----
# Widgets # Widgets
# ----- # -----
@ -389,40 +395,46 @@ class AddcostForm(forms.Form):
self.cleaned_data['amount'] = 0 self.cleaned_data['amount'] = 0
super(AddcostForm, self).clean() super(AddcostForm, self).clean()
# ----- # -----
# Settings forms # Settings forms
# ----- # -----
class SettingsForm(forms.ModelForm):
class Meta:
model = Settings
fields = ['value_decimal', 'value_account', 'value_duration']
def clean(self): class KFetConfigForm(ConfigForm):
name = self.instance.name
value_decimal = self.cleaned_data.get('value_decimal')
value_account = self.cleaned_data.get('value_account')
value_duration = self.cleaned_data.get('value_duration')
type_decimal = ['SUBVENTION_COF', 'ADDCOST_AMOUNT', 'OVERDRAFT_AMOUNT'] kfet_reduction_cof = forms.DecimalField(
type_account = ['ADDCOST_FOR'] label='Réduction COF', initial=Decimal('20'),
type_duration = ['OVERDRAFT_DURATION', 'CANCEL_DURATION'] max_digits=6, decimal_places=2,
help_text="Réduction, à donner en pourcentage, appliquée lors d'un "
"achat par un-e membre du COF sur le montant en euros.",
)
kfet_addcost_amount = forms.DecimalField(
label='Montant de la majoration (en €)', initial=Decimal('0'),
required=False,
max_digits=6, decimal_places=2,
)
kfet_addcost_for = forms.ModelChoiceField(
label='Destinataire de la majoration', initial=None, required=False,
help_text='Laissez vide pour désactiver la majoration.',
queryset=(Account.objects
.select_related('cofprofile', 'cofprofile__user')
.all()),
)
kfet_overdraft_duration = forms.DurationField(
label='Durée du découvert autorisé par défaut',
initial=timedelta(days=1),
)
kfet_overdraft_amount = forms.DecimalField(
label='Montant du découvert autorisé par défaut (en €)',
initial=Decimal('20'),
max_digits=6, decimal_places=2,
)
kfet_cancel_duration = forms.DurationField(
label='Durée pour annuler une commande sans mot de passe',
initial=timedelta(minutes=5),
)
self.cleaned_data['name'] = name
if name in type_decimal:
if not value_decimal:
raise ValidationError('Renseignez une valeur décimale')
self.cleaned_data['value_account'] = None
self.cleaned_data['value_duration'] = None
elif name in type_account:
self.cleaned_data['value_decimal'] = None
self.cleaned_data['value_duration'] = None
elif name in type_duration:
if not value_duration:
raise ValidationError('Renseignez une durée')
self.cleaned_data['value_decimal'] = None
self.cleaned_data['value_account'] = None
super(SettingsForm, self).clean()
class FilterHistoryForm(forms.Form): class FilterHistoryForm(forms.Form):
checkouts = forms.ModelMultipleChoiceField(queryset = Checkout.objects.all()) checkouts = forms.ModelMultipleChoiceField(queryset = Checkout.objects.all())

View file

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
from kfet.forms import KFetConfigForm
def adapt_settings(apps, schema_editor):
Settings = apps.get_model('kfet', 'Settings')
db_alias = schema_editor.connection.alias
obj = Settings.objects.using(db_alias)
cfg = {}
def try_get(new, old, type_field):
try:
value = getattr(obj.get(name=old), type_field)
cfg[new] = value
except Settings.DoesNotExist:
pass
try:
subvention = obj.get(name='SUBVENTION_COF').value_decimal
subvention_mult = 1 + subvention/100
reduction = (1 - 1/subvention_mult) * 100
cfg['kfet_reduction_cof'] = reduction
except Settings.DoesNotExist:
pass
try_get('kfet_addcost_amount', 'ADDCOST_AMOUNT', 'value_decimal')
try_get('kfet_addcost_for', 'ADDCOST_FOR', 'value_account')
try_get('kfet_overdraft_duration', 'OVERDRAFT_DURATION', 'value_duration')
try_get('kfet_overdraft_amount', 'OVERDRAFT_AMOUNT', 'value_decimal')
try_get('kfet_cancel_duration', 'CANCEL_DURATION', 'value_duration')
cfg_form = KFetConfigForm(initial=cfg)
if cfg_form.is_valid():
cfg_form.save()
class Migration(migrations.Migration):
dependencies = [
('kfet', '0053_created_at'),
('djconfig', '0001_initial'),
]
operations = [
migrations.RunPython(adapt_settings),
migrations.RemoveField(
model_name='settings',
name='value_account',
),
migrations.DeleteModel(
name='Settings',
),
]

View file

@ -1,12 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import *
from django.db import models from django.db import models
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.contrib.auth.models import User from django.contrib.auth.models import User
from gestioncof.models import CofProfile from gestioncof.models import CofProfile
@ -15,11 +10,12 @@ from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from django.db import transaction from django.db import transaction
from django.db.models import F from django.db.models import F
from django.core.cache import cache from datetime import date
from datetime import date, timedelta
import re import re
import hashlib import hashlib
from kfet.config import kfet_config
def choices_length(choices): def choices_length(choices):
return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0) return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0)
@ -113,8 +109,8 @@ class Account(models.Model):
return data return data
def perms_to_perform_operation(self, amount): def perms_to_perform_operation(self, amount):
overdraft_duration_max = Settings.OVERDRAFT_DURATION() overdraft_duration_max = kfet_config.overdraft_duration
overdraft_amount_max = Settings.OVERDRAFT_AMOUNT() overdraft_amount_max = kfet_config.overdraft_amount
perms = set() perms = set()
stop_ope = False stop_ope = False
# Checking is cash account # Checking is cash account
@ -632,116 +628,6 @@ class GlobalPermissions(models.Model):
('special_add_account', "Créer un compte avec une balance initiale") ('special_add_account', "Créer un compte avec une balance initiale")
) )
class Settings(models.Model):
name = models.CharField(
max_length = 45,
unique = True,
db_index = True)
value_decimal = models.DecimalField(
max_digits = 6, decimal_places = 2,
blank = True, null = True, default = None)
value_account = models.ForeignKey(
Account, on_delete = models.PROTECT,
blank = True, null = True, default = None)
value_duration = models.DurationField(
blank = True, null = True, default = None)
@staticmethod
def setting_inst(name):
return Settings.objects.get(name=name)
@staticmethod
def SUBVENTION_COF():
subvention_cof = cache.get('SUBVENTION_COF')
if subvention_cof:
return subvention_cof
try:
subvention_cof = Settings.setting_inst("SUBVENTION_COF").value_decimal
except Settings.DoesNotExist:
subvention_cof = 0
cache.set('SUBVENTION_COF', subvention_cof)
return subvention_cof
@staticmethod
def ADDCOST_AMOUNT():
try:
return Settings.setting_inst("ADDCOST_AMOUNT").value_decimal
except Settings.DoesNotExist:
return 0
@staticmethod
def ADDCOST_FOR():
try:
return Settings.setting_inst("ADDCOST_FOR").value_account
except Settings.DoesNotExist:
return None;
@staticmethod
def OVERDRAFT_DURATION():
overdraft_duration = cache.get('OVERDRAFT_DURATION')
if overdraft_duration:
return overdraft_duration
try:
overdraft_duration = Settings.setting_inst("OVERDRAFT_DURATION").value_duration
except Settings.DoesNotExist:
overdraft_duration = timedelta()
cache.set('OVERDRAFT_DURATION', overdraft_duration)
return overdraft_duration
@staticmethod
def OVERDRAFT_AMOUNT():
overdraft_amount = cache.get('OVERDRAFT_AMOUNT')
if overdraft_amount:
return overdraft_amount
try:
overdraft_amount = Settings.setting_inst("OVERDRAFT_AMOUNT").value_decimal
except Settings.DoesNotExist:
overdraft_amount = 0
cache.set('OVERDRAFT_AMOUNT', overdraft_amount)
return overdraft_amount
@staticmethod
def CANCEL_DURATION():
cancel_duration = cache.get('CANCEL_DURATION')
if cancel_duration:
return cancel_duration
try:
cancel_duration = Settings.setting_inst("CANCEL_DURATION").value_duration
except Settings.DoesNotExist:
cancel_duration = timedelta()
cache.set('CANCEL_DURATION', cancel_duration)
return cancel_duration
@staticmethod
def create_missing():
s, created = Settings.objects.get_or_create(name='SUBVENTION_COF')
if created:
s.value_decimal = 25
s.save()
s, created = Settings.objects.get_or_create(name='ADDCOST_AMOUNT')
if created:
s.value_decimal = 0.5
s.save()
s, created = Settings.objects.get_or_create(name='ADDCOST_FOR')
s, created = Settings.objects.get_or_create(name='OVERDRAFT_DURATION')
if created:
s.value_duration = timedelta(days=1) # 24h
s.save()
s, created = Settings.objects.get_or_create(name='OVERDRAFT_AMOUNT')
if created:
s.value_decimal = 20
s.save()
s, created = Settings.objects.get_or_create(name='CANCEL_DURATION')
if created:
s.value_duration = timedelta(minutes=5) # 5min
s.save()
@staticmethod
def empty_cache():
cache.delete_many([
'SUBVENTION_COF', 'OVERDRAFT_DURATION', 'OVERDRAFT_AMOUNT',
'CANCEL_DURATION', 'ADDCOST_AMOUNT', 'ADDCOST_FOR',
])
class GenericTeamToken(models.Model): class GenericTeamToken(models.Model):
token = models.CharField(max_length = 50, unique = True) token = models.CharField(max_length = 50, unique = True)

View file

@ -16,8 +16,8 @@
</div> </div>
<div class="block"> <div class="block">
<div class="line"><b>Découvert autorisé par défaut</b></div> <div class="line"><b>Découvert autorisé par défaut</b></div>
<div class="line">Montant: {{ settings.overdraft_amount }}€</div> <div class="line">Montant: {{ kfet_config.overdraft_amount }}€</div>
<div class="line">Pendant: {{ settings.overdraft_duration }}</div> <div class="line">Pendant: {{ kfet_config.overdraft_duration }}</div>
</div> </div>
</div> </div>
{% if perms.kfet.change_settings %} {% if perms.kfet.change_settings %}

View file

@ -95,7 +95,7 @@ $(document).ready(function() {
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function() { $(document).ready(function() {
settings = { 'subvention_cof': parseFloat({{ settings.subvention_cof|unlocalize }})} settings = { 'subvention_cof': parseFloat({{ kfet_config.subvention_cof|unlocalize }})}
khistory = new KHistory({ khistory = new KHistory({
display_trigramme: false, display_trigramme: false,

View file

@ -60,7 +60,7 @@
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function() { $(document).ready(function() {
settings = { 'subvention_cof': parseFloat({{ settings.subvention_cof|unlocalize }})} settings = { 'subvention_cof': parseFloat({{ kfet_config.subvention_cof|unlocalize }})}
khistory = new KHistory(); khistory = new KHistory();

View file

@ -5,20 +5,42 @@
{% block content %} {% block content %}
<div class="row">
<div class="col-sm-4 col-md-3 col-content-left">
<div class="content-left">
<div class="buttons">
<a class="btn btn-primary btn-lg" href="{% url 'kfet.settings.update' %}">
Modifier
</a>
</div>
</div>
</div>
<div class="col-sm-8 col-md-9 col-content-right">
{% include 'kfet/base_messages.html' %} {% include 'kfet/base_messages.html' %}
<table> <div class="content-right">
<div class="content-right-block">
<h2>Valeurs</h2>
<div class="table-responsive">
<table class="table table-condensed">
<thead>
<tr> <tr>
<td></td>
<td>Nom</td> <td>Nom</td>
<td>Valeur</td> <td>Valeur</td>
</tr> </tr>
{% for setting in settings %} </thead>
<tbody>
{% for key, value in kfet_config.list %}
<tr> <tr>
<td><a href="{% url 'kfet.settings.update' setting.pk %}">Modifier</a></td> <td>{{ key }}</td>
<td>{{ setting.name }}</td> <td>{{ value }}</td>
<td>{% firstof setting.value_decimal setting.value_duration setting.value_account %}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody>
</table> </table>
</div>
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}

View file

@ -1,14 +1,25 @@
{% extends 'kfet/base.html' %} {% extends 'kfet/base.html' %}
{% block title %}Modification de {{ settings.name }}{% endblock %} {% block title %}Modification des paramètres{% endblock %}
{% block content-header-title %}Modification de {{ settings.name }}{% endblock %} {% block content-header-title %}Modification des paramètres{% endblock %}
{% block content %} {% block content %}
<form action="" method="post"> {% include "kfet/base_messages.html" %}
<div class="row form-only">
<div class="col-sm-12 col-md-8 col-md-offset-2">
<div class="content-form">
<form submit="" method="post" class="form-horizontal">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {% include 'kfet/form_snippet.html' with form=form %}
<input type="submit" value="Mettre à jour"> {% if not perms.kfet.change_settings %}
{% include 'kfet/form_authentication_snippet.html' %}
{% endif %}
{% include 'kfet/form_submit_snippet.html' with value="Mettre à jour" %}
</form> </form>
</div>
</div>
</div>
{% endblock %} {% endblock %}

View file

@ -3,10 +3,11 @@
from django import template from django import template
from django.utils.html import escape from django.utils.html import escape
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from kfet.models import Settings
from math import floor from math import floor
import re import re
from kfet.config import kfet_config
register = template.Library() register = template.Library()
@ -40,5 +41,5 @@ def highlight_clipper(clipper, q):
@register.filter() @register.filter()
def ukf(balance, is_cof): def ukf(balance, is_cof):
grant = is_cof and (1 + Settings.SUBVENTION_COF() / 100) or 1 grant = is_cof and (1 + kfet_config.subvention_cof / 100) or 1
return floor(balance * 10 * grant) return floor(balance * 10 * grant)

0
kfet/tests/__init__.py Normal file
View file

56
kfet/tests/test_config.py Normal file
View file

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
from decimal import Decimal
from django.test import TestCase
from django.utils import timezone
import djconfig
from gestioncof.models import User
from kfet.config import kfet_config
from kfet.models import Account
class ConfigTest(TestCase):
"""Tests suite for kfet configuration."""
def setUp(self):
# load configuration as in djconfig middleware
djconfig.reload_maybe()
def test_get(self):
self.assertTrue(hasattr(kfet_config, 'subvention_cof'))
def test_subvention_cof(self):
reduction_cof = Decimal('20')
subvention_cof = Decimal('25')
kfet_config.set(reduction_cof=reduction_cof)
self.assertEqual(kfet_config.subvention_cof, subvention_cof)
def test_set_decimal(self):
"""Test field of decimal type."""
reduction_cof = Decimal('10')
# IUT
kfet_config.set(reduction_cof=reduction_cof)
# check
self.assertEqual(kfet_config.reduction_cof, reduction_cof)
def test_set_modelinstance(self):
"""Test field of model instance type."""
user = User.objects.create(username='foo_user')
account = Account.objects.create(trigramme='FOO',
cofprofile=user.profile)
# IUT
kfet_config.set(addcost_for=account)
# check
self.assertEqual(kfet_config.addcost_for, account)
def test_set_duration(self):
"""Test field of duration type."""
cancel_duration = timezone.timedelta(days=2, hours=4)
# IUT
kfet_config.set(cancel_duration=cancel_duration)
# check
self.assertEqual(kfet_config.cancel_duration, cancel_duration)

View file

@ -5,7 +5,7 @@ from unittest.mock import patch
from django.test import TestCase, Client from django.test import TestCase, Client
from django.contrib.auth.models import User, Permission from django.contrib.auth.models import User, Permission
from .models import Account, Article, ArticleCategory from kfet.models import Account, Article, ArticleCategory
class TestStats(TestCase): class TestStats(TestCase):

View file

@ -193,7 +193,7 @@ urlpatterns = [
permission_required('kfet.change_settings') permission_required('kfet.change_settings')
(views.SettingsList.as_view()), (views.SettingsList.as_view()),
name='kfet.settings'), name='kfet.settings'),
url(r'^settings/(?P<pk>\d+)/edit$', url(r'^settings/edit$',
permission_required('kfet.change_settings') permission_required('kfet.change_settings')
(views.SettingsUpdate.as_view()), (views.SettingsUpdate.as_view()),
name='kfet.settings.update'), name='kfet.settings.update'),

View file

@ -6,7 +6,7 @@ from urllib.parse import urlencode
from django.shortcuts import render, get_object_or_404, redirect from django.shortcuts import render, get_object_or_404, redirect
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.core.cache import cache from django.core.cache import cache
from django.views.generic import ListView, DetailView, TemplateView from django.views.generic import ListView, DetailView, TemplateView, FormView
from django.views.generic.detail import BaseDetailView from django.views.generic.detail import BaseDetailView
from django.views.generic.edit import CreateView, UpdateView from django.views.generic.edit import CreateView, UpdateView
from django.core.urlresolvers import reverse, reverse_lazy from django.core.urlresolvers import reverse, reverse_lazy
@ -24,9 +24,11 @@ from django.utils import timezone
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from gestioncof.models import CofProfile from gestioncof.models import CofProfile
from kfet.config import kfet_config
from kfet.decorators import teamkfet_required from kfet.decorators import teamkfet_required
from kfet.models import ( from kfet.models import (
Account, Checkout, Article, Settings, AccountNegative, Account, Checkout, Article, AccountNegative,
CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory, CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory,
InventoryArticle, Order, OrderArticle, Operation, OperationGroup, InventoryArticle, Order, OrderArticle, Operation, OperationGroup,
TransferGroup, Transfer, ArticleCategory) TransferGroup, Transfer, ArticleCategory)
@ -37,9 +39,9 @@ from kfet.forms import (
GroupForm, CheckoutForm, CheckoutRestrictForm, CheckoutStatementCreateForm, GroupForm, CheckoutForm, CheckoutRestrictForm, CheckoutStatementCreateForm,
CheckoutStatementUpdateForm, ArticleForm, ArticleRestrictForm, CheckoutStatementUpdateForm, ArticleForm, ArticleRestrictForm,
KPsulOperationGroupForm, KPsulAccountForm, KPsulCheckoutForm, KPsulOperationGroupForm, KPsulAccountForm, KPsulCheckoutForm,
KPsulOperationFormSet, AddcostForm, FilterHistoryForm, SettingsForm, KPsulOperationFormSet, AddcostForm, FilterHistoryForm,
TransferFormSet, InventoryArticleForm, OrderArticleForm, TransferFormSet, InventoryArticleForm, OrderArticleForm,
OrderArticleToInventoryForm, CategoryForm OrderArticleToInventoryForm, CategoryForm, KFetConfigForm
) )
from collections import defaultdict from collections import defaultdict
from kfet import consumers from kfet import consumers
@ -346,12 +348,14 @@ def account_create_ajax(request, username=None, login_clipper=None,
'user_form' : forms['user_form'], 'user_form' : forms['user_form'],
}) })
# Account - Read # Account - Read
@login_required @login_required
def account_read(request, trigramme): def account_read(request, trigramme):
try: try:
account = Account.objects.select_related('negative').get(trigramme=trigramme) account = (Account.objects.select_related('negative')
.get(trigramme=trigramme))
except Account.DoesNotExist: except Account.DoesNotExist:
raise Http404 raise Http404
@ -361,16 +365,17 @@ def account_read(request, trigramme):
raise PermissionDenied raise PermissionDenied
addcosts = (OperationGroup.objects addcosts = (OperationGroup.objects
.filter(opes__addcost_for=account,opes__canceled_at=None) .filter(opes__addcost_for=account,
opes__canceled_at=None)
.extra({'date': "date(at)"}) .extra({'date': "date(at)"})
.values('date') .values('date')
.annotate(sum_addcosts=Sum('opes__addcost_amount')) .annotate(sum_addcosts=Sum('opes__addcost_amount'))
.order_by('-date')) .order_by('-date')
)
return render(request, "kfet/account_read.html", { return render(request, "kfet/account_read.html", {
'account': account, 'account': account,
'addcosts': addcosts, 'addcosts': addcosts,
'settings': { 'subvention_cof': Settings.SUBVENTION_COF() },
}) })
# Account - Update # Account - Update
@ -548,10 +553,6 @@ class AccountNegativeList(ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(AccountNegativeList, self).get_context_data(**kwargs) context = super(AccountNegativeList, self).get_context_data(**kwargs)
context['settings'] = {
'overdraft_amount': Settings.OVERDRAFT_AMOUNT(),
'overdraft_duration': Settings.OVERDRAFT_DURATION(),
}
negs_sum = (AccountNegative.objects negs_sum = (AccountNegative.objects
.exclude(account__trigramme='#13') .exclude(account__trigramme='#13')
.aggregate( .aggregate(
@ -902,16 +903,18 @@ def kpsul(request):
data['operation_formset'] = operation_formset data['operation_formset'] = operation_formset
return render(request, 'kfet/kpsul.html', data) return render(request, 'kfet/kpsul.html', data)
@teamkfet_required @teamkfet_required
def kpsul_get_settings(request): def kpsul_get_settings(request):
addcost_for = Settings.ADDCOST_FOR() addcost_for = kfet_config.addcost_for
data = { data = {
'subvention_cof': Settings.SUBVENTION_COF(), 'subvention_cof': kfet_config.subvention_cof,
'addcost_for': addcost_for and addcost_for.trigramme or '', 'addcost_for': addcost_for and addcost_for.trigramme or '',
'addcost_amount': Settings.ADDCOST_AMOUNT(), 'addcost_amount': kfet_config.addcost_amount,
} }
return JsonResponse(data) return JsonResponse(data)
@teamkfet_required @teamkfet_required
def account_read_json(request): def account_read_json(request):
trigramme = request.POST.get('trigramme', '') trigramme = request.POST.get('trigramme', '')
@ -951,6 +954,7 @@ def kpsul_checkout_data(request):
raise Http404 raise Http404
return JsonResponse(data) return JsonResponse(data)
@teamkfet_required @teamkfet_required
def kpsul_update_addcost(request): def kpsul_update_addcost(request):
addcost_form = AddcostForm(request.POST) addcost_form = AddcostForm(request.POST)
@ -970,15 +974,15 @@ def kpsul_update_addcost(request):
trigramme = addcost_form.cleaned_data['trigramme'] trigramme = addcost_form.cleaned_data['trigramme']
account = trigramme and Account.objects.get(trigramme=trigramme) or None account = trigramme and Account.objects.get(trigramme=trigramme) or None
Settings.objects.filter(name='ADDCOST_FOR').update(value_account=account) amount = addcost_form.cleaned_data['amount']
(Settings.objects.filter(name='ADDCOST_AMOUNT')
.update(value_decimal=addcost_form.cleaned_data['amount'])) kfet_config.set(addcost_for=account,
cache.delete('ADDCOST_FOR') addcost_amount=amount)
cache.delete('ADDCOST_AMOUNT')
data = { data = {
'addcost': { 'addcost': {
'for': trigramme and account.trigramme or None, 'for': account and account.trigramme or None,
'amount': addcost_form.cleaned_data['amount'], 'amount': amount,
} }
} }
consumers.KPsul.group_send('kfet.kpsul', data) consumers.KPsul.group_send('kfet.kpsul', data)
@ -1022,10 +1026,10 @@ def kpsul_perform_operations(request):
operations = operation_formset.save(commit=False) operations = operation_formset.save(commit=False)
# Retrieving COF grant # Retrieving COF grant
cof_grant = Settings.SUBVENTION_COF() cof_grant = kfet_config.subvention_cof
# Retrieving addcosts data # Retrieving addcosts data
addcost_amount = Settings.ADDCOST_AMOUNT() addcost_amount = kfet_config.addcost_amount
addcost_for = Settings.ADDCOST_FOR() addcost_for = kfet_config.addcost_for
# Initializing vars # Initializing vars
required_perms = set() # Required perms to perform all operations required_perms = set() # Required perms to perform all operations
@ -1216,7 +1220,7 @@ def kpsul_cancel_operations(request):
opes = [] # Pas déjà annulée opes = [] # Pas déjà annulée
required_perms = set() required_perms = set()
stop_all = False stop_all = False
cancel_duration = Settings.CANCEL_DURATION() cancel_duration = kfet_config.cancel_duration
to_accounts_balances = defaultdict(lambda:0) # Modifs à faire sur les balances des comptes to_accounts_balances = defaultdict(lambda:0) # Modifs à faire sur les balances des comptes
to_groups_amounts = defaultdict(lambda:0) # ------ sur les montants des groupes d'opé to_groups_amounts = defaultdict(lambda:0) # ------ sur les montants des groupes d'opé
to_checkouts_balances = defaultdict(lambda:0) # ------ sur les balances de caisses to_checkouts_balances = defaultdict(lambda:0) # ------ sur les balances de caisses
@ -1444,34 +1448,28 @@ def kpsul_articles_data(request):
.filter(is_sold=True)) .filter(is_sold=True))
return JsonResponse({ 'articles': list(articles) }) return JsonResponse({ 'articles': list(articles) })
@teamkfet_required @teamkfet_required
def history(request): def history(request):
data = { data = {
'filter_form': FilterHistoryForm(), 'filter_form': FilterHistoryForm(),
'settings': {
'subvention_cof': Settings.SUBVENTION_COF(),
}
} }
return render(request, 'kfet/history.html', data) return render(request, 'kfet/history.html', data)
# ----- # -----
# Settings views # Settings views
# ----- # -----
class SettingsList(ListView):
model = Settings class SettingsList(TemplateView):
context_object_name = 'settings'
template_name = 'kfet/settings.html' template_name = 'kfet/settings.html'
def get_context_data(self, **kwargs):
Settings.create_missing()
return super(SettingsList, self).get_context_data(**kwargs)
class SettingsUpdate(SuccessMessageMixin, UpdateView): class SettingsUpdate(SuccessMessageMixin, FormView):
model = Settings form_class = KFetConfigForm
form_class = SettingsForm
template_name = 'kfet/settings_update.html' template_name = 'kfet/settings_update.html'
success_message = 'Paramètre %(name)s mis à jour' success_message = 'Paramètres mis à jour'
success_url = reverse_lazy('kfet.settings') success_url = reverse_lazy('kfet.settings')
def form_valid(self, form): def form_valid(self, form):
@ -1479,9 +1477,9 @@ class SettingsUpdate(SuccessMessageMixin, UpdateView):
if not self.request.user.has_perm('kfet.change_settings'): if not self.request.user.has_perm('kfet.change_settings'):
form.add_error(None, 'Permission refusée') form.add_error(None, 'Permission refusée')
return self.form_invalid(form) return self.form_invalid(form)
# Creating form.save()
Settings.empty_cache() return super().form_valid(form)
return super(SettingsUpdate, self).form_valid(form)
# ----- # -----
# Transfer views # Transfer views
@ -1607,7 +1605,7 @@ def cancel_transfers(request):
transfers = [] # Pas déjà annulée transfers = [] # Pas déjà annulée
required_perms = set() required_perms = set()
stop_all = False stop_all = False
cancel_duration = Settings.CANCEL_DURATION() cancel_duration = kfet_config.cancel_duration
to_accounts_balances = defaultdict(lambda:0) # Modifs à faire sur les balances des comptes to_accounts_balances = defaultdict(lambda:0) # Modifs à faire sur les balances des comptes
for transfer in transfers_all: for transfer in transfers_all:
if transfer.canceled_at: if transfer.canceled_at:

View file

@ -3,6 +3,7 @@ Django==1.8.*
django-autocomplete-light==2.3.3 django-autocomplete-light==2.3.3
django-autoslug==1.9.3 django-autoslug==1.9.3
django-cas-ng==3.5.5 django-cas-ng==3.5.5
django-djconfig==0.5.3
django-grappelli==2.8.1 django-grappelli==2.8.1
django-recaptcha==1.0.5 django-recaptcha==1.0.5
mysqlclient==1.3.7 mysqlclient==1.3.7