Merge branch 'master' into aureplop/clean_channels
This commit is contained in:
commit
b357283f87
19 changed files with 360 additions and 232 deletions
|
@ -47,6 +47,7 @@ INSTALLED_APPS = (
|
|||
'channels',
|
||||
'widget_tweaks',
|
||||
'custommail',
|
||||
'djconfig',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
|
@ -60,6 +61,7 @@ MIDDLEWARE_CLASSES = (
|
|||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'djconfig.middleware.DjConfigMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'cof.urls'
|
||||
|
@ -78,8 +80,10 @@ TEMPLATES = [
|
|||
'django.core.context_processors.i18n',
|
||||
'django.core.context_processors.media',
|
||||
'django.core.context_processors.static',
|
||||
'djconfig.context_processors.config',
|
||||
'gestioncof.shared.context_processor',
|
||||
'kfet.context_processors.auth',
|
||||
'kfet.context_processors.config',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
|
@ -12,3 +12,9 @@ class KFetConfig(AppConfig):
|
|||
|
||||
def ready(self):
|
||||
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
71
kfet/config.py
Normal 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()
|
|
@ -1,11 +1,10 @@
|
|||
# -*- 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 kfet.config import kfet_config
|
||||
|
||||
|
||||
def auth(request):
|
||||
if hasattr(request, 'real_user'):
|
||||
return {
|
||||
|
@ -13,3 +12,7 @@ def auth(request):
|
|||
'perms': PermWrapper(request.real_user),
|
||||
}
|
||||
return {}
|
||||
|
||||
|
||||
def config(request):
|
||||
return {'kfet_config': kfet_config}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
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.forms import modelformset_factory
|
||||
from django.utils import timezone
|
||||
|
||||
from djconfig.forms import ConfigForm
|
||||
|
||||
from kfet.models import (
|
||||
Account, Checkout, Article, OperationGroup, Operation,
|
||||
CheckoutStatement, ArticleCategory, Settings, AccountNegative, Transfer,
|
||||
CheckoutStatement, ArticleCategory, AccountNegative, Transfer,
|
||||
TransferGroup, Supplier)
|
||||
from gestioncof.models import CofProfile
|
||||
|
||||
|
||||
# -----
|
||||
# Widgets
|
||||
# -----
|
||||
|
@ -389,40 +395,46 @@ class AddcostForm(forms.Form):
|
|||
self.cleaned_data['amount'] = 0
|
||||
super(AddcostForm, self).clean()
|
||||
|
||||
|
||||
# -----
|
||||
# Settings forms
|
||||
# -----
|
||||
|
||||
class SettingsForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Settings
|
||||
fields = ['value_decimal', 'value_account', 'value_duration']
|
||||
|
||||
def clean(self):
|
||||
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')
|
||||
class KFetConfigForm(ConfigForm):
|
||||
|
||||
type_decimal = ['SUBVENTION_COF', 'ADDCOST_AMOUNT', 'OVERDRAFT_AMOUNT']
|
||||
type_account = ['ADDCOST_FOR']
|
||||
type_duration = ['OVERDRAFT_DURATION', 'CANCEL_DURATION']
|
||||
kfet_reduction_cof = forms.DecimalField(
|
||||
label='Réduction COF', initial=Decimal('20'),
|
||||
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):
|
||||
checkouts = forms.ModelMultipleChoiceField(queryset = Checkout.objects.all())
|
||||
|
|
58
kfet/migrations/0054_delete_settings.py
Normal file
58
kfet/migrations/0054_delete_settings.py
Normal 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',
|
||||
),
|
||||
]
|
124
kfet/models.py
124
kfet/models.py
|
@ -1,12 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import (absolute_import, division,
|
||||
print_function, unicode_literals)
|
||||
from builtins import *
|
||||
|
||||
from django.db import models
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.core.validators import RegexValidator
|
||||
from django.contrib.auth.models import User
|
||||
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.db import transaction
|
||||
from django.db.models import F
|
||||
from django.core.cache import cache
|
||||
from datetime import date, timedelta
|
||||
from datetime import date
|
||||
import re
|
||||
import hashlib
|
||||
|
||||
from kfet.config import kfet_config
|
||||
|
||||
def choices_length(choices):
|
||||
return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0)
|
||||
|
||||
|
@ -113,8 +109,8 @@ class Account(models.Model):
|
|||
return data
|
||||
|
||||
def perms_to_perform_operation(self, amount):
|
||||
overdraft_duration_max = Settings.OVERDRAFT_DURATION()
|
||||
overdraft_amount_max = Settings.OVERDRAFT_AMOUNT()
|
||||
overdraft_duration_max = kfet_config.overdraft_duration
|
||||
overdraft_amount_max = kfet_config.overdraft_amount
|
||||
perms = set()
|
||||
stop_ope = False
|
||||
# Checking is cash account
|
||||
|
@ -632,116 +628,6 @@ class GlobalPermissions(models.Model):
|
|||
('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):
|
||||
token = models.CharField(max_length = 50, unique = True)
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
</div>
|
||||
<div class="block">
|
||||
<div class="line"><b>Découvert autorisé par défaut</b></div>
|
||||
<div class="line">Montant: {{ settings.overdraft_amount }}€</div>
|
||||
<div class="line">Pendant: {{ settings.overdraft_duration }}</div>
|
||||
<div class="line">Montant: {{ kfet_config.overdraft_amount }}€</div>
|
||||
<div class="line">Pendant: {{ kfet_config.overdraft_duration }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if perms.kfet.change_settings %}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
<script type="text/javascript" src="{% static 'kfet/js/moment.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'kfet/js/moment-fr.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'kfet/js/moment-timezone-with-data-2010-2020.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'kfet/js/kfet.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'kfet/js/history.js' %}"></script>
|
||||
{% if account.user == request.user %}
|
||||
<script type="text/javascript" src="{% static 'kfet/js/Chart.bundle.js' %}"></script>
|
||||
|
@ -18,11 +17,11 @@
|
|||
$(document).ready(function() {
|
||||
var stat_last = new StatsGroup(
|
||||
"{% url 'kfet.account.stat.operation.list' trigramme=account.trigramme %}",
|
||||
$("#stat_last"),
|
||||
$("#stat_last")
|
||||
);
|
||||
var stat_balance = new StatsGroup(
|
||||
"{% url 'kfet.account.stat.balance.list' trigramme=account.trigramme %}",
|
||||
$("#stat_balance"),
|
||||
$("#stat_balance")
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
@ -95,7 +94,7 @@ $(document).ready(function() {
|
|||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
settings = { 'subvention_cof': parseFloat({{ settings.subvention_cof|unlocalize }})}
|
||||
settings = { 'subvention_cof': parseFloat({{ kfet_config.subvention_cof|unlocalize }})}
|
||||
|
||||
khistory = new KHistory({
|
||||
display_trigramme: false,
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
settings = { 'subvention_cof': parseFloat({{ settings.subvention_cof|unlocalize }})}
|
||||
settings = { 'subvention_cof': parseFloat({{ kfet_config.subvention_cof|unlocalize }})}
|
||||
|
||||
khistory = new KHistory();
|
||||
|
||||
|
|
|
@ -5,20 +5,42 @@
|
|||
|
||||
{% block content %}
|
||||
|
||||
{% include 'kfet/base_messages.html' %}
|
||||
<table>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Nom</td>
|
||||
<td>Valeur</td>
|
||||
</tr>
|
||||
{% for setting in settings %}
|
||||
<tr>
|
||||
<td><a href="{% url 'kfet.settings.update' setting.pk %}">Modifier</a></td>
|
||||
<td>{{ setting.name }}</td>
|
||||
<td>{% firstof setting.value_decimal setting.value_duration setting.value_account %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<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' %}
|
||||
<div class="content-right">
|
||||
<div class="content-right-block">
|
||||
<h2>Valeurs</h2>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Nom</td>
|
||||
<td>Valeur</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in kfet_config.list %}
|
||||
<tr>
|
||||
<td>{{ key }}</td>
|
||||
<td>{{ value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
{% extends 'kfet/base.html' %}
|
||||
|
||||
{% block title %}Modification de {{ settings.name }}{% endblock %}
|
||||
{% block content-header-title %}Modification de {{ settings.name }}{% endblock %}
|
||||
{% block title %}Modification des paramètres{% endblock %}
|
||||
{% block content-header-title %}Modification des paramètres{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Mettre à jour">
|
||||
</form>
|
||||
{% 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 %}
|
||||
{% include 'kfet/form_snippet.html' with form=form %}
|
||||
{% if not perms.kfet.change_settings %}
|
||||
{% include 'kfet/form_authentication_snippet.html' %}
|
||||
{% endif %}
|
||||
{% include 'kfet/form_submit_snippet.html' with value="Mettre à jour" %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
from django import template
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from kfet.models import Settings
|
||||
from math import floor
|
||||
import re
|
||||
|
||||
from kfet.config import kfet_config
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
|
@ -40,5 +41,5 @@ def highlight_clipper(clipper, q):
|
|||
|
||||
@register.filter()
|
||||
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)
|
||||
|
|
0
kfet/tests/__init__.py
Normal file
0
kfet/tests/__init__.py
Normal file
56
kfet/tests/test_config.py
Normal file
56
kfet/tests/test_config.py
Normal 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)
|
|
@ -5,7 +5,7 @@ from unittest.mock import patch
|
|||
from django.test import TestCase, Client
|
||||
from django.contrib.auth.models import User, Permission
|
||||
|
||||
from .models import Account, Article, ArticleCategory
|
||||
from kfet.models import Account, Article, ArticleCategory
|
||||
|
||||
|
||||
class TestStats(TestCase):
|
|
@ -193,7 +193,7 @@ urlpatterns = [
|
|||
permission_required('kfet.change_settings')
|
||||
(views.SettingsList.as_view()),
|
||||
name='kfet.settings'),
|
||||
url(r'^settings/(?P<pk>\d+)/edit$',
|
||||
url(r'^settings/edit$',
|
||||
permission_required('kfet.change_settings')
|
||||
(views.SettingsUpdate.as_view()),
|
||||
name='kfet.settings.update'),
|
||||
|
|
|
@ -6,7 +6,7 @@ from urllib.parse import urlencode
|
|||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.core.exceptions import PermissionDenied
|
||||
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.edit import CreateView, UpdateView
|
||||
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.decorators import method_decorator
|
||||
from gestioncof.models import CofProfile
|
||||
|
||||
from kfet.config import kfet_config
|
||||
from kfet.decorators import teamkfet_required
|
||||
from kfet.models import (
|
||||
Account, Checkout, Article, Settings, AccountNegative,
|
||||
Account, Checkout, Article, AccountNegative,
|
||||
CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory,
|
||||
InventoryArticle, Order, OrderArticle, Operation, OperationGroup,
|
||||
TransferGroup, Transfer, ArticleCategory)
|
||||
|
@ -37,9 +39,9 @@ from kfet.forms import (
|
|||
GroupForm, CheckoutForm, CheckoutRestrictForm, CheckoutStatementCreateForm,
|
||||
CheckoutStatementUpdateForm, ArticleForm, ArticleRestrictForm,
|
||||
KPsulOperationGroupForm, KPsulAccountForm, KPsulCheckoutForm,
|
||||
KPsulOperationFormSet, AddcostForm, FilterHistoryForm, SettingsForm,
|
||||
KPsulOperationFormSet, AddcostForm, FilterHistoryForm,
|
||||
TransferFormSet, InventoryArticleForm, OrderArticleForm,
|
||||
OrderArticleToInventoryForm, CategoryForm
|
||||
OrderArticleToInventoryForm, CategoryForm, KFetConfigForm
|
||||
)
|
||||
from collections import defaultdict
|
||||
from kfet import consumers
|
||||
|
@ -346,12 +348,14 @@ def account_create_ajax(request, username=None, login_clipper=None,
|
|||
'user_form' : forms['user_form'],
|
||||
})
|
||||
|
||||
|
||||
# Account - Read
|
||||
|
||||
@login_required
|
||||
def account_read(request, trigramme):
|
||||
try:
|
||||
account = Account.objects.select_related('negative').get(trigramme=trigramme)
|
||||
account = (Account.objects.select_related('negative')
|
||||
.get(trigramme=trigramme))
|
||||
except Account.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
|
@ -361,16 +365,17 @@ def account_read(request, trigramme):
|
|||
raise PermissionDenied
|
||||
|
||||
addcosts = (OperationGroup.objects
|
||||
.filter(opes__addcost_for=account,opes__canceled_at=None)
|
||||
.extra({'date':"date(at)"})
|
||||
.values('date')
|
||||
.annotate(sum_addcosts=Sum('opes__addcost_amount'))
|
||||
.order_by('-date'))
|
||||
.filter(opes__addcost_for=account,
|
||||
opes__canceled_at=None)
|
||||
.extra({'date': "date(at)"})
|
||||
.values('date')
|
||||
.annotate(sum_addcosts=Sum('opes__addcost_amount'))
|
||||
.order_by('-date')
|
||||
)
|
||||
|
||||
return render(request, "kfet/account_read.html", {
|
||||
'account' : account,
|
||||
'account': account,
|
||||
'addcosts': addcosts,
|
||||
'settings': { 'subvention_cof': Settings.SUBVENTION_COF() },
|
||||
})
|
||||
|
||||
# Account - Update
|
||||
|
@ -548,10 +553,6 @@ class AccountNegativeList(ListView):
|
|||
|
||||
def get_context_data(self, **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
|
||||
.exclude(account__trigramme='#13')
|
||||
.aggregate(
|
||||
|
@ -902,16 +903,18 @@ def kpsul(request):
|
|||
data['operation_formset'] = operation_formset
|
||||
return render(request, 'kfet/kpsul.html', data)
|
||||
|
||||
|
||||
@teamkfet_required
|
||||
def kpsul_get_settings(request):
|
||||
addcost_for = Settings.ADDCOST_FOR()
|
||||
addcost_for = kfet_config.addcost_for
|
||||
data = {
|
||||
'subvention_cof': Settings.SUBVENTION_COF(),
|
||||
'addcost_for' : addcost_for and addcost_for.trigramme or '',
|
||||
'addcost_amount': Settings.ADDCOST_AMOUNT(),
|
||||
'subvention_cof': kfet_config.subvention_cof,
|
||||
'addcost_for': addcost_for and addcost_for.trigramme or '',
|
||||
'addcost_amount': kfet_config.addcost_amount,
|
||||
}
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
@teamkfet_required
|
||||
def account_read_json(request):
|
||||
trigramme = request.POST.get('trigramme', '')
|
||||
|
@ -951,6 +954,7 @@ def kpsul_checkout_data(request):
|
|||
raise Http404
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
@teamkfet_required
|
||||
def kpsul_update_addcost(request):
|
||||
addcost_form = AddcostForm(request.POST)
|
||||
|
@ -970,15 +974,15 @@ def kpsul_update_addcost(request):
|
|||
|
||||
trigramme = addcost_form.cleaned_data['trigramme']
|
||||
account = trigramme and Account.objects.get(trigramme=trigramme) or None
|
||||
Settings.objects.filter(name='ADDCOST_FOR').update(value_account=account)
|
||||
(Settings.objects.filter(name='ADDCOST_AMOUNT')
|
||||
.update(value_decimal=addcost_form.cleaned_data['amount']))
|
||||
cache.delete('ADDCOST_FOR')
|
||||
cache.delete('ADDCOST_AMOUNT')
|
||||
amount = addcost_form.cleaned_data['amount']
|
||||
|
||||
kfet_config.set(addcost_for=account,
|
||||
addcost_amount=amount)
|
||||
|
||||
data = {
|
||||
'addcost': {
|
||||
'for': trigramme and account.trigramme or None,
|
||||
'amount': addcost_form.cleaned_data['amount'],
|
||||
'for': account and account.trigramme or None,
|
||||
'amount': amount,
|
||||
}
|
||||
}
|
||||
consumers.KPsul.group_send('kfet.kpsul', data)
|
||||
|
@ -1022,10 +1026,10 @@ def kpsul_perform_operations(request):
|
|||
operations = operation_formset.save(commit=False)
|
||||
|
||||
# Retrieving COF grant
|
||||
cof_grant = Settings.SUBVENTION_COF()
|
||||
cof_grant = kfet_config.subvention_cof
|
||||
# Retrieving addcosts data
|
||||
addcost_amount = Settings.ADDCOST_AMOUNT()
|
||||
addcost_for = Settings.ADDCOST_FOR()
|
||||
addcost_amount = kfet_config.addcost_amount
|
||||
addcost_for = kfet_config.addcost_for
|
||||
|
||||
# Initializing vars
|
||||
required_perms = set() # Required perms to perform all operations
|
||||
|
@ -1216,7 +1220,7 @@ def kpsul_cancel_operations(request):
|
|||
opes = [] # Pas déjà annulée
|
||||
required_perms = set()
|
||||
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_groups_amounts = defaultdict(lambda:0) # ------ sur les montants des groupes d'opé
|
||||
to_checkouts_balances = defaultdict(lambda:0) # ------ sur les balances de caisses
|
||||
|
@ -1444,34 +1448,28 @@ def kpsul_articles_data(request):
|
|||
.filter(is_sold=True))
|
||||
return JsonResponse({ 'articles': list(articles) })
|
||||
|
||||
|
||||
@teamkfet_required
|
||||
def history(request):
|
||||
data = {
|
||||
'filter_form': FilterHistoryForm(),
|
||||
'settings': {
|
||||
'subvention_cof': Settings.SUBVENTION_COF(),
|
||||
}
|
||||
}
|
||||
return render(request, 'kfet/history.html', data)
|
||||
|
||||
|
||||
# -----
|
||||
# Settings views
|
||||
# -----
|
||||
|
||||
class SettingsList(ListView):
|
||||
model = Settings
|
||||
context_object_name = 'settings'
|
||||
|
||||
class SettingsList(TemplateView):
|
||||
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):
|
||||
model = Settings
|
||||
form_class = SettingsForm
|
||||
class SettingsUpdate(SuccessMessageMixin, FormView):
|
||||
form_class = KFetConfigForm
|
||||
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')
|
||||
|
||||
def form_valid(self, form):
|
||||
|
@ -1479,9 +1477,9 @@ class SettingsUpdate(SuccessMessageMixin, UpdateView):
|
|||
if not self.request.user.has_perm('kfet.change_settings'):
|
||||
form.add_error(None, 'Permission refusée')
|
||||
return self.form_invalid(form)
|
||||
# Creating
|
||||
Settings.empty_cache()
|
||||
return super(SettingsUpdate, self).form_valid(form)
|
||||
form.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
# -----
|
||||
# Transfer views
|
||||
|
@ -1607,7 +1605,7 @@ def cancel_transfers(request):
|
|||
transfers = [] # Pas déjà annulée
|
||||
required_perms = set()
|
||||
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
|
||||
for transfer in transfers_all:
|
||||
if transfer.canceled_at:
|
||||
|
|
|
@ -3,6 +3,7 @@ Django==1.8.*
|
|||
django-autocomplete-light==2.3.3
|
||||
django-autoslug==1.9.3
|
||||
django-cas-ng==3.5.5
|
||||
django-djconfig==0.5.3
|
||||
django-grappelli==2.8.1
|
||||
django-recaptcha==1.0.5
|
||||
mysqlclient==1.3.7
|
||||
|
|
Loading…
Reference in a new issue