8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-24 16:03:47 +00:00

Release : 2.8

This commit is contained in:
Hugo Levy-Falk 2019-12-28 13:04:04 +01:00
commit 56fdfca1aa
759 changed files with 27074 additions and 16710 deletions

View file

@ -178,3 +178,28 @@ Be carefull, you need the proper rights to edit a DocumentTemplate.
Re2o now sends subscription voucher when an invoice is controlled. It uses one Re2o now sends subscription voucher when an invoice is controlled. It uses one
of the templates. You also need to set the name of the president of your association of the templates. You also need to set the name of the president of your association
to be set in your settings. to be set in your settings.
## MR 427: Tickets
Manually edit `settings_local.py` to provide the new `OPTIONNAL_APPS` lists:
```python
OPTIONNAL_APPS_RE2O = ('tickets',)
OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + (...,...,)
```
Don't forget to run migrations afterwards.
## MR 433 : upgrade django-ldapdb to 1.3.0
Uninstall the existing django-ldapdb installation
pip3 uninstall django-ldapdb
Install debian(buster) supported version
apt install python3-django-ldapdb
If you use MySQL, please run
```
SET GLOBAL SQL_MODE=ANSI_QUOTES;
```

View file

@ -40,14 +40,14 @@ def _create_api_permission():
""" """
api_content_type, created = ContentType.objects.get_or_create( api_content_type, created = ContentType.objects.get_or_create(
app_label=settings.API_CONTENT_TYPE_APP_LABEL, app_label=settings.API_CONTENT_TYPE_APP_LABEL,
model=settings.API_CONTENT_TYPE_MODEL model=settings.API_CONTENT_TYPE_MODEL,
) )
if created: if created:
api_content_type.save() api_content_type.save()
api_permission, created = Permission.objects.get_or_create( api_permission, created = Permission.objects.get_or_create(
name=settings.API_PERMISSION_NAME, name=settings.API_PERMISSION_NAME,
content_type=api_content_type, content_type=api_content_type,
codename=settings.API_PERMISSION_CODENAME codename=settings.API_PERMISSION_CODENAME,
) )
if created: if created:
api_permission.save() api_permission.save()
@ -67,9 +67,13 @@ def can_view(user):
viewing is granted and msg is a message (can be None). viewing is granted and msg is a message (can be None).
""" """
kwargs = { kwargs = {
'app_label': settings.API_CONTENT_TYPE_APP_LABEL, "app_label": settings.API_CONTENT_TYPE_APP_LABEL,
'codename': settings.API_PERMISSION_CODENAME "codename": settings.API_PERMISSION_CODENAME,
} }
can = user.has_perm('%(app_label)s.%(codename)s' % kwargs) permission = "%(app_label)s.%(codename)s" % kwargs
return can, None if can else _("You don't have the right to see this" can = user.has_perm(permission)
" application.") return (
can,
None if can else _("You don't have the right to view this application."),
(permission,),
)

View file

@ -41,9 +41,7 @@ class ExpiringTokenAuthentication(TokenAuthentication):
user, token = base.authenticate_credentials(key) user, token = base.authenticate_credentials(key)
# Check that the genration time of the token is not too old # Check that the genration time of the token is not too old
token_duration = datetime.timedelta( token_duration = datetime.timedelta(seconds=settings.API_TOKEN_DURATION)
seconds=settings.API_TOKEN_DURATION
)
utc_now = datetime.datetime.now(datetime.timezone.utc) utc_now = datetime.datetime.now(datetime.timezone.utc)
if token.created < utc_now - token_duration: if token.created < utc_now - token_duration:
raise exceptions.AuthenticationFailed(_("The token has expired.")) raise exceptions.AuthenticationFailed(_("The token has expired."))

View file

@ -21,7 +21,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 2.5\n" "Project-Id-Version: 2.5\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-01-08 23:06+0100\n" "POT-Creation-Date: 2019-11-19 23:43+0100\n"
"PO-Revision-Date: 2019-01-07 01:37+0100\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n"
"Last-Translator: Laouen Fernet <laouen.fernet@supelec.fr>\n" "Last-Translator: Laouen Fernet <laouen.fernet@supelec.fr>\n"
"Language-Team: \n" "Language-Team: \n"
@ -31,10 +31,10 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: acl.py:74 #: api/acl.py:77
msgid "You don't have the right to see this application." msgid "You don't have the right to view this application."
msgstr "Vous n'avez pas le droit de voir cette application." msgstr "Vous n'avez pas le droit de voir cette application."
#: authentication.py:49 #: api/authentication.py:47
msgid "The token has expired." msgid "The token has expired."
msgstr "Le jeton a expiré." msgstr "Le jeton a expiré."

View file

@ -38,8 +38,9 @@ class PageSizedPagination(pagination.PageNumberPagination):
max_page_size: The maximum number of results a page can output no max_page_size: The maximum number of results a page can output no
matter what is requested. matter what is requested.
""" """
page_size_query_param = 'page_size'
all_pages_strings = ('all',) page_size_query_param = "page_size"
all_pages_strings = ("all",)
max_page_size = 10000 max_page_size = 10000
def get_page_size(self, request): def get_page_size(self, request):

View file

@ -55,17 +55,21 @@ def _get_param_in_view(view, param_name):
AssertionError: None of the getter function or the attribute are AssertionError: None of the getter function or the attribute are
defined in the view. defined in the view.
""" """
assert hasattr(view, 'get_' + param_name) \ assert (
or getattr(view, param_name, None) is not None, ( hasattr(view, "get_" + param_name)
'cannot apply {} on a view that does not set ' or getattr(view, param_name, None) is not None
'`.{}` or have a `.get_{}()` method.' ), (
).format(self.__class__.__name__, param_name, param_name) "cannot apply {} on a view that does not set "
"`.{}` or have a `.get_{}()` method."
).format(
self.__class__.__name__, param_name, param_name
)
if hasattr(view, 'get_' + param_name): if hasattr(view, "get_" + param_name):
param = getattr(view, 'get_' + param_name)() param = getattr(view, "get_" + param_name)()
assert param is not None, ( assert param is not None, ("{}.get_{}() returned None").format(
'{}.get_{}() returned None' view.__class__.__name__, param_name
).format(view.__class__.__name__, param_name) )
return param return param
return getattr(view, param_name) return getattr(view, param_name)
@ -97,7 +101,7 @@ class ACLPermission(permissions.BasePermission):
rest_framework.exception.MethodNotAllowed: The requested method rest_framework.exception.MethodNotAllowed: The requested method
is not allowed for this view. is not allowed for this view.
""" """
perms_map = _get_param_in_view(view, 'perms_map') perms_map = _get_param_in_view(view, "perms_map")
if method not in perms_map: if method not in perms_map:
raise exceptions.MethodNotAllowed(method) raise exceptions.MethodNotAllowed(method)
@ -123,7 +127,7 @@ class ACLPermission(permissions.BasePermission):
""" """
# Workaround to ensure ACLPermissions are not applied # Workaround to ensure ACLPermissions are not applied
# to the root view when using DefaultRouter. # to the root view when using DefaultRouter.
if getattr(view, '_ignore_model_permissions', False): if getattr(view, "_ignore_model_permissions", False):
return True return True
if not request.user or not request.user.is_authenticated: if not request.user or not request.user.is_authenticated:
@ -148,22 +152,22 @@ class AutodetectACLPermission(permissions.BasePermission):
""" """
perms_map = { perms_map = {
'GET': [can_see_api, lambda model: model.can_view_all], "GET": [can_see_api, lambda model: model.can_view_all],
'OPTIONS': [can_see_api, lambda model: model.can_view_all], "OPTIONS": [can_see_api, lambda model: model.can_view_all],
'HEAD': [can_see_api, lambda model: model.can_view_all], "HEAD": [can_see_api, lambda model: model.can_view_all],
'POST': [can_see_api, lambda model: model.can_create], "POST": [can_see_api, lambda model: model.can_create],
'PUT': [], # No restrictions, apply to objects "PUT": [], # No restrictions, apply to objects
'PATCH': [], # No restrictions, apply to objects "PATCH": [], # No restrictions, apply to objects
'DELETE': [], # No restrictions, apply to objects "DELETE": [], # No restrictions, apply to objects
} }
perms_obj_map = { perms_obj_map = {
'GET': [can_see_api, lambda obj: obj.can_view], "GET": [can_see_api, lambda obj: obj.can_view],
'OPTIONS': [can_see_api, lambda obj: obj.can_view], "OPTIONS": [can_see_api, lambda obj: obj.can_view],
'HEAD': [can_see_api, lambda obj: obj.can_view], "HEAD": [can_see_api, lambda obj: obj.can_view],
'POST': [], # No restrictions, apply to models "POST": [], # No restrictions, apply to models
'PUT': [can_see_api, lambda obj: obj.can_edit], "PUT": [can_see_api, lambda obj: obj.can_edit],
'PATCH': [can_see_api, lambda obj: obj.can_edit], "PATCH": [can_see_api, lambda obj: obj.can_edit],
'DELETE': [can_see_api, lambda obj: obj.can_delete], "DELETE": [can_see_api, lambda obj: obj.can_delete],
} }
def get_required_permissions(self, method, model): def get_required_permissions(self, method, model):
@ -210,7 +214,7 @@ class AutodetectACLPermission(permissions.BasePermission):
@staticmethod @staticmethod
def _queryset(view): def _queryset(view):
return _get_param_in_view(view, 'queryset') return _get_param_in_view(view, "queryset")
def has_permission(self, request, view): def has_permission(self, request, view):
"""Check that the user has the model-based permissions to perform """Check that the user has the model-based permissions to perform
@ -232,7 +236,7 @@ class AutodetectACLPermission(permissions.BasePermission):
""" """
# Workaround to ensure ACLPermissions are not applied # Workaround to ensure ACLPermissions are not applied
# to the root view when using DefaultRouter. # to the root view when using DefaultRouter.
if getattr(view, '_ignore_model_permissions', False): if getattr(view, "_ignore_model_permissions", False):
return True return True
if not request.user or not request.user.is_authenticated: if not request.user or not request.user.is_authenticated:
@ -274,7 +278,7 @@ class AutodetectACLPermission(permissions.BasePermission):
# to make another lookup. # to make another lookup.
raise Http404 raise Http404
read_perms = self.get_required_object_permissions('GET', obj) read_perms = self.get_required_object_permissions("GET", obj)
if not read_perms(request.user)[0]: if not read_perms(request.user)[0]:
raise Http404 raise Http404

View file

@ -74,9 +74,9 @@ class AllViewsRouter(DefaultRouter):
Returns: Returns:
The name to use for this route. The name to use for this route.
""" """
return pattern.split('/')[-1] return pattern.split("/")[-1]
def get_api_root_view(self, schema_urls=None): def get_api_root_view(self, schema_urls=None, api_urls=None):
"""Create a class-based view to use as the API root. """Create a class-based view to use as the API root.
Highly inspired by the base class. See details on the implementation Highly inspired by the base class. See details on the implementation
@ -102,12 +102,10 @@ class AllViewsRouter(DefaultRouter):
if schema_urls and self.schema_title: if schema_urls and self.schema_title:
view_renderers += list(self.schema_renderers) view_renderers += list(self.schema_renderers)
schema_generator = SchemaGenerator( schema_generator = SchemaGenerator(
title=self.schema_title, title=self.schema_title, patterns=schema_urls
patterns=schema_urls
) )
schema_media_types = [ schema_media_types = [
renderer.media_type renderer.media_type for renderer in self.schema_renderers
for renderer in self.schema_renderers
] ]
class APIRoot(views.APIView): class APIRoot(views.APIView):
@ -128,14 +126,14 @@ class AllViewsRouter(DefaultRouter):
namespace = request.resolver_match.namespace namespace = request.resolver_match.namespace
for key, url_name in api_root_dict.items(): for key, url_name in api_root_dict.items():
if namespace: if namespace:
url_name = namespace + ':' + url_name url_name = namespace + ":" + url_name
try: try:
ret[key] = reverse( ret[key] = reverse(
url_name, url_name,
args=args, args=args,
kwargs=kwargs, kwargs=kwargs,
request=request, request=request,
format=kwargs.get('format', None) format=kwargs.get("format", None),
) )
except NoReverseMatch: except NoReverseMatch:
# Don't bail out if eg. no list routes exist, only detail routes. # Don't bail out if eg. no list routes exist, only detail routes.

File diff suppressed because it is too large Load diff

View file

@ -24,28 +24,24 @@
# RestFramework config for API # RestFramework config for API
REST_FRAMEWORK = { REST_FRAMEWORK = {
'URL_FIELD_NAME': 'api_url', "URL_FIELD_NAME": "api_url",
'DEFAULT_AUTHENTICATION_CLASSES': ( "DEFAULT_AUTHENTICATION_CLASSES": (
'api.authentication.ExpiringTokenAuthentication', "api.authentication.ExpiringTokenAuthentication",
'rest_framework.authentication.SessionAuthentication', "rest_framework.authentication.SessionAuthentication",
), ),
'DEFAULT_PERMISSION_CLASSES': ( "DEFAULT_PERMISSION_CLASSES": ("api.permissions.AutodetectACLPermission",),
'api.permissions.AutodetectACLPermission', "DEFAULT_PAGINATION_CLASS": "api.pagination.PageSizedPagination",
), "PAGE_SIZE": 100,
'DEFAULT_PAGINATION_CLASS': 'api.pagination.PageSizedPagination',
'PAGE_SIZE': 100
} }
# API permission settings # API permission settings
API_CONTENT_TYPE_APP_LABEL = 'api' API_CONTENT_TYPE_APP_LABEL = "api"
API_CONTENT_TYPE_MODEL = 'api' API_CONTENT_TYPE_MODEL = "api"
API_PERMISSION_NAME = 'Can use the API' API_PERMISSION_NAME = "Can use the API"
API_PERMISSION_CODENAME = 'use_api' API_PERMISSION_CODENAME = "use_api"
# Activate token authentication # Activate token authentication
API_APPS = ( API_APPS = ("rest_framework.authtoken",)
'rest_framework.authtoken',
)
# The expiration time for an authentication token # The expiration time for an authentication token
API_TOKEN_DURATION = 86400 # 24 hours API_TOKEN_DURATION = 86400 # 24 hours

View file

@ -50,171 +50,170 @@ class APIEndpointsTestCase(APITestCase):
superuser: A superuser (with all permissions) used for the tests and superuser: A superuser (with all permissions) used for the tests and
initialized at the beggining of this test case. initialized at the beggining of this test case.
""" """
no_auth_endpoints = [
'/api/' no_auth_endpoints = ["/api/"]
]
auth_no_perm_endpoints = [] auth_no_perm_endpoints = []
auth_perm_endpoints = [ auth_perm_endpoints = [
'/api/cotisations/article/', "/api/cotisations/article/",
'/api/cotisations/article/1/', "/api/cotisations/article/1/",
'/api/cotisations/banque/', "/api/cotisations/banque/",
'/api/cotisations/banque/1/', "/api/cotisations/banque/1/",
'/api/cotisations/cotisation/', "/api/cotisations/cotisation/",
'/api/cotisations/cotisation/1/', "/api/cotisations/cotisation/1/",
'/api/cotisations/facture/', "/api/cotisations/facture/",
'/api/cotisations/facture/1/', "/api/cotisations/facture/1/",
'/api/cotisations/paiement/', "/api/cotisations/paiement/",
'/api/cotisations/paiement/1/', "/api/cotisations/paiement/1/",
'/api/cotisations/vente/', "/api/cotisations/vente/",
'/api/cotisations/vente/1/', "/api/cotisations/vente/1/",
'/api/machines/domain/', "/api/machines/domain/",
'/api/machines/domain/1/', "/api/machines/domain/1/",
'/api/machines/extension/', "/api/machines/extension/",
'/api/machines/extension/1/', "/api/machines/extension/1/",
'/api/machines/interface/', "/api/machines/interface/",
'/api/machines/interface/1/', "/api/machines/interface/1/",
'/api/machines/iplist/', "/api/machines/iplist/",
'/api/machines/iplist/1/', "/api/machines/iplist/1/",
'/api/machines/iptype/', "/api/machines/iptype/",
'/api/machines/iptype/1/', "/api/machines/iptype/1/",
'/api/machines/ipv6list/', "/api/machines/ipv6list/",
'/api/machines/ipv6list/1/', "/api/machines/ipv6list/1/",
'/api/machines/machine/', "/api/machines/machine/",
'/api/machines/machine/1/', "/api/machines/machine/1/",
'/api/machines/machinetype/', "/api/machines/machinetype/",
'/api/machines/machinetype/1/', "/api/machines/machinetype/1/",
'/api/machines/mx/', "/api/machines/mx/",
'/api/machines/mx/1/', "/api/machines/mx/1/",
'/api/machines/nas/', "/api/machines/nas/",
'/api/machines/nas/1/', "/api/machines/nas/1/",
'/api/machines/ns/', "/api/machines/ns/",
'/api/machines/ns/1/', "/api/machines/ns/1/",
'/api/machines/ouvertureportlist/', "/api/machines/ouvertureportlist/",
'/api/machines/ouvertureportlist/1/', "/api/machines/ouvertureportlist/1/",
'/api/machines/ouvertureport/', "/api/machines/ouvertureport/",
'/api/machines/ouvertureport/1/', "/api/machines/ouvertureport/1/",
'/api/machines/servicelink/', "/api/machines/servicelink/",
'/api/machines/servicelink/1/', "/api/machines/servicelink/1/",
'/api/machines/service/', "/api/machines/service/",
'/api/machines/service/1/', "/api/machines/service/1/",
'/api/machines/soa/', "/api/machines/soa/",
'/api/machines/soa/1/', "/api/machines/soa/1/",
'/api/machines/srv/', "/api/machines/srv/",
'/api/machines/srv/1/', "/api/machines/srv/1/",
'/api/machines/txt/', "/api/machines/txt/",
'/api/machines/txt/1/', "/api/machines/txt/1/",
'/api/machines/vlan/', "/api/machines/vlan/",
'/api/machines/vlan/1/', "/api/machines/vlan/1/",
'/api/preferences/optionaluser/', "/api/preferences/optionaluser/",
'/api/preferences/optionalmachine/', "/api/preferences/optionalmachine/",
'/api/preferences/optionaltopologie/', "/api/preferences/optionaltopologie/",
'/api/preferences/generaloption/', "/api/preferences/generaloption/",
'/api/preferences/service/', "/api/preferences/service/",
'/api/preferences/service/1/', "/api/preferences/service/1/",
'/api/preferences/assooption/', "/api/preferences/assooption/",
'/api/preferences/homeoption/', "/api/preferences/homeoption/",
'/api/preferences/mailmessageoption/', "/api/preferences/mailmessageoption/",
'/api/topologie/acesspoint/', "/api/topologie/acesspoint/",
# 2nd machine to be create (machines_machine_1, topologie_accesspoint_1) # 2nd machine to be create (machines_machine_1, topologie_accesspoint_1)
'/api/topologie/acesspoint/2/', "/api/topologie/acesspoint/2/",
'/api/topologie/building/', "/api/topologie/building/",
'/api/topologie/building/1/', "/api/topologie/building/1/",
'/api/topologie/constructorswitch/', "/api/topologie/constructorswitch/",
'/api/topologie/constructorswitch/1/', "/api/topologie/constructorswitch/1/",
'/api/topologie/modelswitch/', "/api/topologie/modelswitch/",
'/api/topologie/modelswitch/1/', "/api/topologie/modelswitch/1/",
'/api/topologie/room/', "/api/topologie/room/",
'/api/topologie/room/1/', "/api/topologie/room/1/",
'/api/topologie/server/', "/api/topologie/server/",
# 3rd machine to be create (machines_machine_1, topologie_accesspoint_1, # 3rd machine to be create (machines_machine_1, topologie_accesspoint_1,
# topologie_server_1) # topologie_server_1)
'/api/topologie/server/3/', "/api/topologie/server/3/",
'/api/topologie/stack/', "/api/topologie/stack/",
'/api/topologie/stack/1/', "/api/topologie/stack/1/",
'/api/topologie/switch/', "/api/topologie/switch/",
# 4th machine to be create (machines_machine_1, topologie_accesspoint_1, # 4th machine to be create (machines_machine_1, topologie_accesspoint_1,
# topologie_server_1, topologie_switch_1) # topologie_server_1, topologie_switch_1)
'/api/topologie/switch/4/', "/api/topologie/switch/4/",
'/api/topologie/switchbay/', "/api/topologie/switchbay/",
'/api/topologie/switchbay/1/', "/api/topologie/switchbay/1/",
'/api/topologie/switchport/', "/api/topologie/switchport/",
'/api/topologie/switchport/1/', "/api/topologie/switchport/1/",
'/api/topologie/switchport/2/', "/api/topologie/switchport/2/",
'/api/topologie/switchport/3/', "/api/topologie/switchport/3/",
'/api/users/adherent/', "/api/users/adherent/",
# 3rd user to be create (stduser, superuser, users_adherent_1) # 3rd user to be create (stduser, superuser, users_adherent_1)
'/api/users/adherent/3/', "/api/users/adherent/3/",
'/api/users/ban/', "/api/users/ban/",
'/api/users/ban/1/', "/api/users/ban/1/",
'/api/users/club/', "/api/users/club/",
# 4th user to be create (stduser, superuser, users_adherent_1, # 4th user to be create (stduser, superuser, users_adherent_1,
# users_club_1) # users_club_1)
'/api/users/club/4/', "/api/users/club/4/",
'/api/users/listright/', "/api/users/listright/",
# TODO: Merge !145 # TODO: Merge !145
# '/api/users/listright/1/', # '/api/users/listright/1/',
'/api/users/school/', "/api/users/school/",
'/api/users/school/1/', "/api/users/school/1/",
'/api/users/serviceuser/', "/api/users/serviceuser/",
'/api/users/serviceuser/1/', "/api/users/serviceuser/1/",
'/api/users/shell/', "/api/users/shell/",
'/api/users/shell/1/', "/api/users/shell/1/",
'/api/users/user/', "/api/users/user/",
'/api/users/user/1/', "/api/users/user/1/",
'/api/users/whitelist/', "/api/users/whitelist/",
'/api/users/whitelist/1/', "/api/users/whitelist/1/",
'/api/dns/zones/', "/api/dns/zones/",
'/api/dhcp/hostmacip/', "/api/dhcp/hostmacip/",
'/api/mailing/standard', "/api/mailing/standard",
'/api/mailing/club', "/api/mailing/club",
'/api/services/regen/', "/api/services/regen/",
] ]
not_found_endpoints = [ not_found_endpoints = [
'/api/cotisations/article/4242/', "/api/cotisations/article/4242/",
'/api/cotisations/banque/4242/', "/api/cotisations/banque/4242/",
'/api/cotisations/cotisation/4242/', "/api/cotisations/cotisation/4242/",
'/api/cotisations/facture/4242/', "/api/cotisations/facture/4242/",
'/api/cotisations/paiement/4242/', "/api/cotisations/paiement/4242/",
'/api/cotisations/vente/4242/', "/api/cotisations/vente/4242/",
'/api/machines/domain/4242/', "/api/machines/domain/4242/",
'/api/machines/extension/4242/', "/api/machines/extension/4242/",
'/api/machines/interface/4242/', "/api/machines/interface/4242/",
'/api/machines/iplist/4242/', "/api/machines/iplist/4242/",
'/api/machines/iptype/4242/', "/api/machines/iptype/4242/",
'/api/machines/ipv6list/4242/', "/api/machines/ipv6list/4242/",
'/api/machines/machine/4242/', "/api/machines/machine/4242/",
'/api/machines/machinetype/4242/', "/api/machines/machinetype/4242/",
'/api/machines/mx/4242/', "/api/machines/mx/4242/",
'/api/machines/nas/4242/', "/api/machines/nas/4242/",
'/api/machines/ns/4242/', "/api/machines/ns/4242/",
'/api/machines/ouvertureportlist/4242/', "/api/machines/ouvertureportlist/4242/",
'/api/machines/ouvertureport/4242/', "/api/machines/ouvertureport/4242/",
'/api/machines/servicelink/4242/', "/api/machines/servicelink/4242/",
'/api/machines/service/4242/', "/api/machines/service/4242/",
'/api/machines/soa/4242/', "/api/machines/soa/4242/",
'/api/machines/srv/4242/', "/api/machines/srv/4242/",
'/api/machines/txt/4242/', "/api/machines/txt/4242/",
'/api/machines/vlan/4242/', "/api/machines/vlan/4242/",
'/api/preferences/service/4242/', "/api/preferences/service/4242/",
'/api/topologie/acesspoint/4242/', "/api/topologie/acesspoint/4242/",
'/api/topologie/building/4242/', "/api/topologie/building/4242/",
'/api/topologie/constructorswitch/4242/', "/api/topologie/constructorswitch/4242/",
'/api/topologie/modelswitch/4242/', "/api/topologie/modelswitch/4242/",
'/api/topologie/room/4242/', "/api/topologie/room/4242/",
'/api/topologie/server/4242/', "/api/topologie/server/4242/",
'/api/topologie/stack/4242/', "/api/topologie/stack/4242/",
'/api/topologie/switch/4242/', "/api/topologie/switch/4242/",
'/api/topologie/switchbay/4242/', "/api/topologie/switchbay/4242/",
'/api/topologie/switchport/4242/', "/api/topologie/switchport/4242/",
'/api/users/adherent/4242/', "/api/users/adherent/4242/",
'/api/users/ban/4242/', "/api/users/ban/4242/",
'/api/users/club/4242/', "/api/users/club/4242/",
'/api/users/listright/4242/', "/api/users/listright/4242/",
'/api/users/school/4242/', "/api/users/school/4242/",
'/api/users/serviceuser/4242/', "/api/users/serviceuser/4242/",
'/api/users/shell/4242/', "/api/users/shell/4242/",
'/api/users/user/4242/', "/api/users/user/4242/",
'/api/users/whitelist/4242/', "/api/users/whitelist/4242/",
] ]
stduser = None stduser = None
@ -232,26 +231,18 @@ class APIEndpointsTestCase(APITestCase):
# A user with no rights # A user with no rights
cls.stduser = users.User.objects.create_user( cls.stduser = users.User.objects.create_user(
"apistduser", "apistduser", "apistduser", "apistduser@example.net", "apistduser"
"apistduser",
"apistduser@example.net",
"apistduser"
) )
# A user with all the rights # A user with all the rights
cls.superuser = users.User.objects.create_superuser( cls.superuser = users.User.objects.create_superuser(
"apisuperuser", "apisuperuser", "apisuperuser", "apisuperuser@example.net", "apisuperuser"
"apisuperuser",
"apisuperuser@example.net",
"apisuperuser"
) )
# Creates 1 instance for each object so the "details" endpoints # Creates 1 instance for each object so the "details" endpoints
# can be tested too. Objects need to be created in the right order. # can be tested too. Objects need to be created in the right order.
# Dependencies (relatedFields, ...) are highlighted by a comment at # Dependencies (relatedFields, ...) are highlighted by a comment at
# the end of the concerned line (# Dep <model>). # the end of the concerned line (# Dep <model>).
cls.users_school_1 = users.School.objects.create( cls.users_school_1 = users.School.objects.create(name="users_school_1")
name="users_school_1"
)
cls.users_school_1.save() cls.users_school_1.save()
cls.users_listshell_1 = users.ListShell.objects.create( cls.users_listshell_1 = users.ListShell.objects.create(
shell="users_listshell_1" shell="users_listshell_1"
@ -270,7 +261,7 @@ class APIEndpointsTestCase(APITestCase):
registered=datetime.datetime.now(datetime.timezone.utc), registered=datetime.datetime.now(datetime.timezone.utc),
telephone="0123456789", telephone="0123456789",
uid_number=21102, uid_number=21102,
rezo_rez_uid=21102 rezo_rez_uid=21102,
) )
cls.users_user_1 = cls.users_adherent_1 cls.users_user_1 = cls.users_adherent_1
cls.cotisations_article_1 = cotisations.Article.objects.create( cls.cotisations_article_1 = cotisations.Article.objects.create(
@ -278,14 +269,14 @@ class APIEndpointsTestCase(APITestCase):
prix=10, prix=10,
duration=1, duration=1,
type_user=cotisations.Article.USER_TYPES[0][0], type_user=cotisations.Article.USER_TYPES[0][0],
type_cotisation=cotisations.Article.COTISATION_TYPE[0][0] type_cotisation=cotisations.Article.COTISATION_TYPE[0][0],
) )
cls.cotisations_banque_1 = cotisations.Banque.objects.create( cls.cotisations_banque_1 = cotisations.Banque.objects.create(
name="cotisations_banque_1" name="cotisations_banque_1"
) )
cls.cotisations_paiement_1 = cotisations.Paiement.objects.create( cls.cotisations_paiement_1 = cotisations.Paiement.objects.create(
moyen="cotisations_paiement_1", moyen="cotisations_paiement_1",
type_paiement=cotisations.Paiement.PAYMENT_TYPES[0][0] type_paiement=cotisations.Paiement.PAYMENT_TYPES[0][0],
) )
cls.cotisations_facture_1 = cotisations.Facture.objects.create( cls.cotisations_facture_1 = cotisations.Facture.objects.create(
user=cls.users_user_1, # Dep users.User user=cls.users_user_1, # Dep users.User
@ -294,7 +285,7 @@ class APIEndpointsTestCase(APITestCase):
cheque="1234567890", cheque="1234567890",
date=datetime.datetime.now(datetime.timezone.utc), date=datetime.datetime.now(datetime.timezone.utc),
valid=True, valid=True,
control=False control=False,
) )
cls.cotisations_vente_1 = cotisations.Vente.objects.create( cls.cotisations_vente_1 = cotisations.Vente.objects.create(
facture=cls.cotisations_facture_1, # Dep cotisations.Facture facture=cls.cotisations_facture_1, # Dep cotisations.Facture
@ -302,18 +293,18 @@ class APIEndpointsTestCase(APITestCase):
name="cotisations_vente_1", name="cotisations_vente_1",
prix=10, prix=10,
duration=1, duration=1,
type_cotisation=cotisations.Vente.COTISATION_TYPE[0][0] type_cotisation=cotisations.Vente.COTISATION_TYPE[0][0],
) )
# A cotisation is automatically created by the Vente object and # A cotisation is automatically created by the Vente object and
# trying to create another cotisation associated with this vente # trying to create another cotisation associated with this vente
# will fail so we simply retrieve it so it can be used in the tests # will fail so we simply retrieve it so it can be used in the tests
cls.cotisations_cotisation_1 = cotisations.Cotisation.objects.get( cls.cotisations_cotisation_1 = cotisations.Cotisation.objects.get(
vente=cls.cotisations_vente_1, # Dep cotisations.Vente vente=cls.cotisations_vente_1 # Dep cotisations.Vente
) )
cls.machines_machine_1 = machines.Machine.objects.create( cls.machines_machine_1 = machines.Machine.objects.create(
user=cls.users_user_1, # Dep users.User user=cls.users_user_1, # Dep users.User
name="machines_machine_1", name="machines_machine_1",
active=True active=True,
) )
cls.machines_ouvertureportlist_1 = machines.OuverturePortList.objects.create( cls.machines_ouvertureportlist_1 = machines.OuverturePortList.objects.create(
name="machines_ouvertureportlist_1" name="machines_ouvertureportlist_1"
@ -324,19 +315,17 @@ class APIEndpointsTestCase(APITestCase):
refresh=86400, refresh=86400,
retry=7200, retry=7200,
expire=3600000, expire=3600000,
ttl=172800 ttl=172800,
) )
cls.machines_extension_1 = machines.Extension.objects.create( cls.machines_extension_1 = machines.Extension.objects.create(
name="machines_extension_1", name="machines_extension_1",
need_infra=False, need_infra=False,
# Do not set origin because of circular dependency # Do not set origin because of circular dependency
origin_v6="2001:db8:1234::", origin_v6="2001:db8:1234::",
soa=cls.machines_soa_1 # Dep machines.SOA soa=cls.machines_soa_1, # Dep machines.SOA
) )
cls.machines_vlan_1 = machines.Vlan.objects.create( cls.machines_vlan_1 = machines.Vlan.objects.create(
vlan_id=0, vlan_id=0, name="machines_vlan_1", comment="machines Vlan 1"
name="machines_vlan_1",
comment="machines Vlan 1"
) )
cls.machines_iptype_1 = machines.IpType.objects.create( cls.machines_iptype_1 = machines.IpType.objects.create(
type="machines_iptype_1", type="machines_iptype_1",
@ -346,13 +335,12 @@ class APIEndpointsTestCase(APITestCase):
domaine_ip_stop="10.0.0.255", domaine_ip_stop="10.0.0.255",
prefix_v6="2001:db8:1234::", prefix_v6="2001:db8:1234::",
vlan=cls.machines_vlan_1, # Dep machines.Vlan vlan=cls.machines_vlan_1, # Dep machines.Vlan
ouverture_ports=cls.machines_ouvertureportlist_1 # Dep machines.OuverturePortList ouverture_ports=cls.machines_ouvertureportlist_1, # Dep machines.OuverturePortList
) )
# All IPs in the IpType range are autocreated so we can't create # All IPs in the IpType range are autocreated so we can't create
# new ones and thus we only retrieve it if needed in the tests # new ones and thus we only retrieve it if needed in the tests
cls.machines_iplist_1 = machines.IpList.objects.get( cls.machines_iplist_1 = machines.IpList.objects.get(
ipv4="10.0.0.1", ipv4="10.0.0.1", ip_type=cls.machines_iptype_1 # Dep machines.IpType
ip_type=cls.machines_iptype_1, # Dep machines.IpType
) )
cls.machines_machinetype_1 = machines.MachineType.objects.create( cls.machines_machinetype_1 = machines.MachineType.objects.create(
type="machines_machinetype_1", type="machines_machinetype_1",
@ -375,16 +363,16 @@ class APIEndpointsTestCase(APITestCase):
cls.machines_mx_1 = machines.Mx.objects.create( cls.machines_mx_1 = machines.Mx.objects.create(
zone=cls.machines_extension_1, # Dep machines.Extension zone=cls.machines_extension_1, # Dep machines.Extension
priority=10, priority=10,
name=cls.machines_domain_1 # Dep machines.Domain name=cls.machines_domain_1, # Dep machines.Domain
) )
cls.machines_ns_1 = machines.Ns.objects.create( cls.machines_ns_1 = machines.Ns.objects.create(
zone=cls.machines_extension_1, # Dep machines.Extension zone=cls.machines_extension_1, # Dep machines.Extension
ns=cls.machines_domain_1 # Dep machines.Domain ns=cls.machines_domain_1, # Dep machines.Domain
) )
cls.machines_txt_1 = machines.Txt.objects.create( cls.machines_txt_1 = machines.Txt.objects.create(
zone=cls.machines_extension_1, # Dep machines.Extension zone=cls.machines_extension_1, # Dep machines.Extension
field1="machines_txt_1", field1="machines_txt_1",
field2="machies Txt 1" field2="machies Txt 1",
) )
cls.machines_srv_1 = machines.Srv.objects.create( cls.machines_srv_1 = machines.Srv.objects.create(
service="machines_srv_1", service="machines_srv_1",
@ -398,7 +386,7 @@ class APIEndpointsTestCase(APITestCase):
cls.machines_ipv6list_1 = machines.Ipv6List.objects.create( cls.machines_ipv6list_1 = machines.Ipv6List.objects.create(
ipv6="2001:db8:1234::", ipv6="2001:db8:1234::",
interface=cls.machines_interface_1, # Dep machines.Interface interface=cls.machines_interface_1, # Dep machines.Interface
slaac_ip=False slaac_ip=False,
) )
cls.machines_service_1 = machines.Service.objects.create( cls.machines_service_1 = machines.Service.objects.create(
service_type="machines_service_1", service_type="machines_service_1",
@ -410,45 +398,45 @@ class APIEndpointsTestCase(APITestCase):
service=cls.machines_service_1, # Dep machines.Service service=cls.machines_service_1, # Dep machines.Service
server=cls.machines_interface_1, # Dep machines.Interface server=cls.machines_interface_1, # Dep machines.Interface
last_regen=datetime.datetime.now(datetime.timezone.utc), last_regen=datetime.datetime.now(datetime.timezone.utc),
asked_regen=False asked_regen=False,
) )
cls.machines_ouvertureport_1 = machines.OuverturePort.objects.create( cls.machines_ouvertureport_1 = machines.OuverturePort.objects.create(
begin=1, begin=1,
end=2, end=2,
port_list=cls.machines_ouvertureportlist_1, # Dep machines.OuverturePortList port_list=cls.machines_ouvertureportlist_1, # Dep machines.OuverturePortList
protocole=machines.OuverturePort.TCP, protocole=machines.OuverturePort.TCP,
io=machines.OuverturePort.OUT io=machines.OuverturePort.OUT,
) )
cls.machines_nas_1 = machines.Nas.objects.create( cls.machines_nas_1 = machines.Nas.objects.create(
name="machines_nas_1", name="machines_nas_1",
nas_type=cls.machines_machinetype_1, # Dep machines.MachineType nas_type=cls.machines_machinetype_1, # Dep machines.MachineType
machine_type=cls.machines_machinetype_1, # Dep machines.MachineType machine_type=cls.machines_machinetype_1, # Dep machines.MachineType
port_access_mode=machines.Nas.AUTH[0][0], port_access_mode=machines.Nas.AUTH[0][0],
autocapture_mac=False autocapture_mac=False,
) )
cls.preferences_service_1 = preferences.Service.objects.create( cls.preferences_service_1 = preferences.Service.objects.create(
name="preferences_service_1", name="preferences_service_1",
url="https://example.net", url="https://example.net",
description="preferences Service 1", description="preferences Service 1",
image="/media/logo/none.png" image="/media/logo/none.png",
) )
cls.topologie_stack_1 = topologie.Stack.objects.create( cls.topologie_stack_1 = topologie.Stack.objects.create(
name="topologie_stack_1", name="topologie_stack_1",
stack_id="1", stack_id="1",
details="topologie Stack 1", details="topologie Stack 1",
member_id_min=1, member_id_min=1,
member_id_max=10 member_id_max=10,
) )
cls.topologie_accespoint_1 = topologie.AccessPoint.objects.create( cls.topologie_accespoint_1 = topologie.AccessPoint.objects.create(
user=cls.users_user_1, # Dep users.User user=cls.users_user_1, # Dep users.User
name="machines_machine_1", name="machines_machine_1",
active=True, active=True,
location="topologie AccessPoint 1" location="topologie AccessPoint 1",
) )
cls.topologie_server_1 = topologie.Server.objects.create( cls.topologie_server_1 = topologie.Server.objects.create(
user=cls.users_user_1, # Dep users.User user=cls.users_user_1, # Dep users.User
name="machines_machine_1", name="machines_machine_1",
active=True active=True,
) )
cls.topologie_building_1 = topologie.Building.objects.create( cls.topologie_building_1 = topologie.Building.objects.create(
name="topologie_building_1" name="topologie_building_1"
@ -456,14 +444,14 @@ class APIEndpointsTestCase(APITestCase):
cls.topologie_switchbay_1 = topologie.SwitchBay.objects.create( cls.topologie_switchbay_1 = topologie.SwitchBay.objects.create(
name="topologie_switchbay_1", name="topologie_switchbay_1",
building=cls.topologie_building_1, # Dep topologie.Building building=cls.topologie_building_1, # Dep topologie.Building
info="topologie SwitchBay 1" info="topologie SwitchBay 1",
) )
cls.topologie_constructorswitch_1 = topologie.ConstructorSwitch.objects.create( cls.topologie_constructorswitch_1 = topologie.ConstructorSwitch.objects.create(
name="topologie_constructorswitch_1" name="topologie_constructorswitch_1"
) )
cls.topologie_modelswitch_1 = topologie.ModelSwitch.objects.create( cls.topologie_modelswitch_1 = topologie.ModelSwitch.objects.create(
reference="topologie_modelswitch_1", reference="topologie_modelswitch_1",
constructor=cls.topologie_constructorswitch_1 # Dep topologie.ConstructorSwitch constructor=cls.topologie_constructorswitch_1, # Dep topologie.ConstructorSwitch
) )
cls.topologie_switch_1 = topologie.Switch.objects.create( cls.topologie_switch_1 = topologie.Switch.objects.create(
user=cls.users_user_1, # Dep users.User user=cls.users_user_1, # Dep users.User
@ -473,11 +461,10 @@ class APIEndpointsTestCase(APITestCase):
stack=cls.topologie_stack_1, # Dep topologie.Stack stack=cls.topologie_stack_1, # Dep topologie.Stack
stack_member_id=1, stack_member_id=1,
model=cls.topologie_modelswitch_1, # Dep topologie.ModelSwitch model=cls.topologie_modelswitch_1, # Dep topologie.ModelSwitch
switchbay=cls.topologie_switchbay_1 # Dep topologie.SwitchBay switchbay=cls.topologie_switchbay_1, # Dep topologie.SwitchBay
) )
cls.topologie_room_1 = topologie.Room.objects.create( cls.topologie_room_1 = topologie.Room.objects.create(
name="topologie_romm_1", name="topologie_romm_1", details="topologie Room 1"
details="topologie Room 1"
) )
cls.topologie_port_1 = topologie.Port.objects.create( cls.topologie_port_1 = topologie.Port.objects.create(
switch=cls.topologie_switch_1, # Dep topologie.Switch switch=cls.topologie_switch_1, # Dep topologie.Switch
@ -485,7 +472,7 @@ class APIEndpointsTestCase(APITestCase):
room=cls.topologie_room_1, # Dep topologie.Room room=cls.topologie_room_1, # Dep topologie.Room
radius=topologie.Port.STATES[0][0], radius=topologie.Port.STATES[0][0],
vlan_force=cls.machines_vlan_1, # Dep machines.Vlan vlan_force=cls.machines_vlan_1, # Dep machines.Vlan
details="topologie_switch_1" details="topologie_switch_1",
) )
cls.topologie_port_2 = topologie.Port.objects.create( cls.topologie_port_2 = topologie.Port.objects.create(
switch=cls.topologie_switch_1, # Dep topologie.Switch switch=cls.topologie_switch_1, # Dep topologie.Switch
@ -493,7 +480,7 @@ class APIEndpointsTestCase(APITestCase):
machine_interface=cls.machines_interface_1, # Dep machines.Interface machine_interface=cls.machines_interface_1, # Dep machines.Interface
radius=topologie.Port.STATES[0][0], radius=topologie.Port.STATES[0][0],
vlan_force=cls.machines_vlan_1, # Dep machines.Vlan vlan_force=cls.machines_vlan_1, # Dep machines.Vlan
details="topologie_switch_1" details="topologie_switch_1",
) )
cls.topologie_port_3 = topologie.Port.objects.create( cls.topologie_port_3 = topologie.Port.objects.create(
switch=cls.topologie_switch_1, # Dep topologie.Switch switch=cls.topologie_switch_1, # Dep topologie.Switch
@ -501,14 +488,15 @@ class APIEndpointsTestCase(APITestCase):
room=cls.topologie_room_1, # Dep topologie.Room room=cls.topologie_room_1, # Dep topologie.Room
radius=topologie.Port.STATES[0][0], radius=topologie.Port.STATES[0][0],
# Do not defines related because circular dependency # Dep machines.Vlan # Do not defines related because circular dependency # Dep machines.Vlan
details="topologie_switch_1" details="topologie_switch_1",
) )
cls.users_ban_1 = users.Ban.objects.create( cls.users_ban_1 = users.Ban.objects.create(
user=cls.users_user_1, # Dep users.User user=cls.users_user_1, # Dep users.User
raison="users Ban 1", raison="users Ban 1",
date_start=datetime.datetime.now(datetime.timezone.utc), date_start=datetime.datetime.now(datetime.timezone.utc),
date_end=datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=1), date_end=datetime.datetime.now(datetime.timezone.utc)
state=users.Ban.STATES[0][0] + datetime.timedelta(days=1),
state=users.Ban.STATES[0][0],
) )
cls.users_club_1 = users.Club.objects.create( cls.users_club_1 = users.Club.objects.create(
password="password", password="password",
@ -524,7 +512,7 @@ class APIEndpointsTestCase(APITestCase):
registered=datetime.datetime.now(datetime.timezone.utc), registered=datetime.datetime.now(datetime.timezone.utc),
telephone="0123456789", telephone="0123456789",
uid_number=21103, uid_number=21103,
rezo_rez_uid=21103 rezo_rez_uid=21103,
) )
# Need merge of MR145 to work # Need merge of MR145 to work
# TODO: Merge !145 # TODO: Merge !145
@ -539,17 +527,17 @@ class APIEndpointsTestCase(APITestCase):
last_login=datetime.datetime.now(datetime.timezone.utc), last_login=datetime.datetime.now(datetime.timezone.utc),
pseudo="usersserviceuser1", pseudo="usersserviceuser1",
access_group=users.ServiceUser.ACCESS[0][0], access_group=users.ServiceUser.ACCESS[0][0],
comment="users ServiceUser 1" comment="users ServiceUser 1",
) )
cls.users_whitelist_1 = users.Whitelist.objects.create( cls.users_whitelist_1 = users.Whitelist.objects.create(
user=cls.users_user_1, user=cls.users_user_1,
raison="users Whitelist 1", raison="users Whitelist 1",
date_start=datetime.datetime.now(datetime.timezone.utc), date_start=datetime.datetime.now(datetime.timezone.utc),
date_end=datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=1) date_end=datetime.datetime.now(datetime.timezone.utc)
+ datetime.timedelta(days=1),
) )
def check_responses_code(self, urls, expected_code, formats=None, def check_responses_code(self, urls, expected_code, formats=None, assert_more=None):
assert_more=None):
"""Utility function to test if a list of urls answer an expected code. """Utility function to test if a list of urls answer an expected code.
Args: Args:
@ -665,17 +653,20 @@ class APIEndpointsTestCase(APITestCase):
""" """
self.client.force_authenticate(user=self.superuser) self.client.force_authenticate(user=self.superuser)
urls = self.no_auth_endpoints + self.auth_no_perm_endpoints + \ urls = (
self.auth_perm_endpoints self.no_auth_endpoints
+ self.auth_no_perm_endpoints
+ self.auth_perm_endpoints
)
def assert_more(response, url, format): def assert_more(response, url, format):
"""Assert the response is valid json when format is json""" """Assert the response is valid json when format is json"""
if format is 'json': if format is "json":
json.loads(response.content.decode()) json.loads(response.content.decode())
self.check_responses_code(urls, codes.ok, self.check_responses_code(
formats=[None, 'json', 'api'], urls, codes.ok, formats=[None, "json", "api"], assert_more=assert_more
assert_more=assert_more) )
class APIPaginationTestCase(APITestCase): class APIPaginationTestCase(APITestCase):
@ -688,56 +679,56 @@ class APIPaginationTestCase(APITestCase):
""" """
endpoints = [ endpoints = [
'/api/cotisations/article/', "/api/cotisations/article/",
'/api/cotisations/banque/', "/api/cotisations/banque/",
'/api/cotisations/cotisation/', "/api/cotisations/cotisation/",
'/api/cotisations/facture/', "/api/cotisations/facture/",
'/api/cotisations/paiement/', "/api/cotisations/paiement/",
'/api/cotisations/vente/', "/api/cotisations/vente/",
'/api/machines/domain/', "/api/machines/domain/",
'/api/machines/extension/', "/api/machines/extension/",
'/api/machines/interface/', "/api/machines/interface/",
'/api/machines/iplist/', "/api/machines/iplist/",
'/api/machines/iptype/', "/api/machines/iptype/",
'/api/machines/ipv6list/', "/api/machines/ipv6list/",
'/api/machines/machine/', "/api/machines/machine/",
'/api/machines/machinetype/', "/api/machines/machinetype/",
'/api/machines/mx/', "/api/machines/mx/",
'/api/machines/nas/', "/api/machines/nas/",
'/api/machines/ns/', "/api/machines/ns/",
'/api/machines/ouvertureportlist/', "/api/machines/ouvertureportlist/",
'/api/machines/ouvertureport/', "/api/machines/ouvertureport/",
'/api/machines/servicelink/', "/api/machines/servicelink/",
'/api/machines/service/', "/api/machines/service/",
'/api/machines/soa/', "/api/machines/soa/",
'/api/machines/srv/', "/api/machines/srv/",
'/api/machines/txt/', "/api/machines/txt/",
'/api/machines/vlan/', "/api/machines/vlan/",
'/api/preferences/service/', "/api/preferences/service/",
'/api/topologie/acesspoint/', "/api/topologie/acesspoint/",
'/api/topologie/building/', "/api/topologie/building/",
'/api/topologie/constructorswitch/', "/api/topologie/constructorswitch/",
'/api/topologie/modelswitch/', "/api/topologie/modelswitch/",
'/api/topologie/room/', "/api/topologie/room/",
'/api/topologie/server/', "/api/topologie/server/",
'/api/topologie/stack/', "/api/topologie/stack/",
'/api/topologie/switch/', "/api/topologie/switch/",
'/api/topologie/switchbay/', "/api/topologie/switchbay/",
'/api/topologie/switchport/', "/api/topologie/switchport/",
'/api/users/adherent/', "/api/users/adherent/",
'/api/users/ban/', "/api/users/ban/",
'/api/users/club/', "/api/users/club/",
'/api/users/listright/', "/api/users/listright/",
'/api/users/school/', "/api/users/school/",
'/api/users/serviceuser/', "/api/users/serviceuser/",
'/api/users/shell/', "/api/users/shell/",
'/api/users/user/', "/api/users/user/",
'/api/users/whitelist/', "/api/users/whitelist/",
'/api/dns/zones/', "/api/dns/zones/",
'/api/dhcp/hostmacip/', "/api/dhcp/hostmacip/",
'/api/mailing/standard', "/api/mailing/standard",
'/api/mailing/club', "/api/mailing/club",
'/api/services/regen/', "/api/services/regen/",
] ]
superuser = None superuser = None
@ -752,7 +743,7 @@ class APIPaginationTestCase(APITestCase):
"apisuperuser2", "apisuperuser2",
"apisuperuser2", "apisuperuser2",
"apisuperuser2@example.net", "apisuperuser2@example.net",
"apisuperuser2" "apisuperuser2",
) )
@classmethod @classmethod
@ -771,10 +762,10 @@ class APIPaginationTestCase(APITestCase):
self.client.force_authenticate(self.superuser) self.client.force_authenticate(self.superuser)
for url in self.endpoints: for url in self.endpoints:
with self.subTest(url=url): with self.subTest(url=url):
response = self.client.get(url, format='json') response = self.client.get(url, format="json")
res_json = json.loads(response.content.decode()) res_json = json.loads(response.content.decode())
assert 'count' in res_json.keys() assert "count" in res_json.keys()
assert 'next' in res_json.keys() assert "next" in res_json.keys()
assert 'previous' in res_json.keys() assert "previous" in res_json.keys()
assert 'results' in res_json.keys() assert "results" in res_json.keys()
assert not len('results') > 100 assert not len("results") > 100

View file

@ -34,95 +34,109 @@ from .routers import AllViewsRouter
router = AllViewsRouter() router = AllViewsRouter()
# COTISATIONS # COTISATIONS
router.register_viewset(r'cotisations/facture', views.FactureViewSet) router.register_viewset(r"cotisations/facture", views.FactureViewSet)
router.register_viewset(r'cotisations/vente', views.VenteViewSet) router.register_viewset(r"cotisations/vente", views.VenteViewSet)
router.register_viewset(r'cotisations/article', views.ArticleViewSet) router.register_viewset(r"cotisations/article", views.ArticleViewSet)
router.register_viewset(r'cotisations/banque', views.BanqueViewSet) router.register_viewset(r"cotisations/banque", views.BanqueViewSet)
router.register_viewset(r'cotisations/paiement', views.PaiementViewSet) router.register_viewset(r"cotisations/paiement", views.PaiementViewSet)
router.register_viewset(r'cotisations/cotisation', views.CotisationViewSet) router.register_viewset(r"cotisations/cotisation", views.CotisationViewSet)
# MACHINES # MACHINES
router.register_viewset(r'machines/machine', views.MachineViewSet) router.register_viewset(r"machines/machine", views.MachineViewSet)
router.register_viewset(r'machines/machinetype', views.MachineTypeViewSet) router.register_viewset(r"machines/machinetype", views.MachineTypeViewSet)
router.register_viewset(r'machines/iptype', views.IpTypeViewSet) router.register_viewset(r"machines/iptype", views.IpTypeViewSet)
router.register_viewset(r'machines/vlan', views.VlanViewSet) router.register_viewset(r"machines/vlan", views.VlanViewSet)
router.register_viewset(r'machines/nas', views.NasViewSet) router.register_viewset(r"machines/nas", views.NasViewSet)
router.register_viewset(r'machines/soa', views.SOAViewSet) router.register_viewset(r"machines/soa", views.SOAViewSet)
router.register_viewset(r'machines/extension', views.ExtensionViewSet) router.register_viewset(r"machines/extension", views.ExtensionViewSet)
router.register_viewset(r'machines/mx', views.MxViewSet) router.register_viewset(r"machines/mx", views.MxViewSet)
router.register_viewset(r'machines/ns', views.NsViewSet) router.register_viewset(r"machines/ns", views.NsViewSet)
router.register_viewset(r'machines/txt', views.TxtViewSet) router.register_viewset(r"machines/txt", views.TxtViewSet)
router.register_viewset(r'machines/dname', views.DNameViewSet) router.register_viewset(r"machines/dname", views.DNameViewSet)
router.register_viewset(r'machines/srv', views.SrvViewSet) router.register_viewset(r"machines/srv", views.SrvViewSet)
router.register_viewset(r'machines/sshfp', views.SshFpViewSet) router.register_viewset(r"machines/sshfp", views.SshFpViewSet)
router.register_viewset(r'machines/interface', views.InterfaceViewSet) router.register_viewset(r"machines/interface", views.InterfaceViewSet)
router.register_viewset(r'machines/ipv6list', views.Ipv6ListViewSet) router.register_viewset(r"machines/ipv6list", views.Ipv6ListViewSet)
router.register_viewset(r'machines/domain', views.DomainViewSet) router.register_viewset(r"machines/domain", views.DomainViewSet)
router.register_viewset(r'machines/iplist', views.IpListViewSet) router.register_viewset(r"machines/iplist", views.IpListViewSet)
router.register_viewset(r'machines/service', views.ServiceViewSet) router.register_viewset(r"machines/service", views.ServiceViewSet)
router.register_viewset(r'machines/servicelink', views.ServiceLinkViewSet, base_name='servicelink') router.register_viewset(
router.register_viewset(r'machines/ouvertureportlist', views.OuverturePortListViewSet) r"machines/servicelink", views.ServiceLinkViewSet, base_name="servicelink"
router.register_viewset(r'machines/ouvertureport', views.OuverturePortViewSet) )
router.register_viewset(r'machines/role', views.RoleViewSet) router.register_viewset(r"machines/ouvertureportlist", views.OuverturePortListViewSet)
router.register_viewset(r"machines/ouvertureport", views.OuverturePortViewSet)
router.register_viewset(r"machines/role", views.RoleViewSet)
# PREFERENCES # PREFERENCES
router.register_view(r'preferences/optionaluser', views.OptionalUserView), router.register_view(r"preferences/optionaluser", views.OptionalUserView),
router.register_view(r'preferences/optionalmachine', views.OptionalMachineView), router.register_view(r"preferences/optionalmachine", views.OptionalMachineView),
router.register_view(r'preferences/optionaltopologie', views.OptionalTopologieView), router.register_view(r"preferences/optionaltopologie", views.OptionalTopologieView),
router.register_view(r'preferences/radiusoption', views.RadiusOptionView), router.register_view(r"preferences/radiusoption", views.RadiusOptionView),
router.register_view(r'preferences/generaloption', views.GeneralOptionView), router.register_view(r"preferences/generaloption", views.GeneralOptionView),
router.register_viewset(r'preferences/service', views.HomeServiceViewSet, base_name='homeservice'), router.register_viewset(
router.register_view(r'preferences/assooption', views.AssoOptionView), r"preferences/service", views.HomeServiceViewSet, base_name="homeservice"
router.register_view(r'preferences/homeoption', views.HomeOptionView), ),
router.register_view(r'preferences/mailmessageoption', views.MailMessageOptionView), router.register_view(r"preferences/assooption", views.AssoOptionView),
router.register_view(r"preferences/homeoption", views.HomeOptionView),
router.register_view(r"preferences/mailmessageoption", views.MailMessageOptionView),
# TOPOLOGIE # TOPOLOGIE
router.register_viewset(r'topologie/stack', views.StackViewSet) router.register_viewset(r"topologie/stack", views.StackViewSet)
router.register_viewset(r'topologie/acesspoint', views.AccessPointViewSet) router.register_viewset(r"topologie/acesspoint", views.AccessPointViewSet)
router.register_viewset(r'topologie/switch', views.SwitchViewSet) router.register_viewset(r"topologie/switch", views.SwitchViewSet)
router.register_viewset(r'topologie/server', views.ServerViewSet) router.register_viewset(r"topologie/server", views.ServerViewSet)
router.register_viewset(r'topologie/modelswitch', views.ModelSwitchViewSet) router.register_viewset(r"topologie/modelswitch", views.ModelSwitchViewSet)
router.register_viewset(r'topologie/constructorswitch', views.ConstructorSwitchViewSet) router.register_viewset(r"topologie/constructorswitch", views.ConstructorSwitchViewSet)
router.register_viewset(r'topologie/switchbay', views.SwitchBayViewSet) router.register_viewset(r"topologie/switchbay", views.SwitchBayViewSet)
router.register_viewset(r'topologie/building', views.BuildingViewSet) router.register_viewset(r"topologie/building", views.BuildingViewSet)
router.register_viewset(r'topologie/switchport', views.SwitchPortViewSet, base_name='switchport') router.register_viewset(
router.register_viewset(r'topologie/portprofile', views.PortProfileViewSet, base_name='portprofile') r"topologie/switchport", views.SwitchPortViewSet, base_name="switchport"
router.register_viewset(r'topologie/room', views.RoomViewSet) )
router.register(r'topologie/portprofile', views.PortProfileViewSet) router.register_viewset(
r"topologie/portprofile", views.PortProfileViewSet, base_name="portprofile"
)
router.register_viewset(r"topologie/room", views.RoomViewSet)
router.register(r"topologie/portprofile", views.PortProfileViewSet)
# USERS # USERS
router.register_viewset(r'users/user', views.UserViewSet, base_name='user') router.register_viewset(r"users/user", views.UserViewSet, base_name="user")
router.register_viewset(r'users/homecreation', views.HomeCreationViewSet, base_name='homecreation') router.register_viewset(
router.register_viewset(r'users/normaluser', views.NormalUserViewSet, base_name='normaluser') r"users/homecreation", views.HomeCreationViewSet, base_name="homecreation"
router.register_viewset(r'users/criticaluser', views.CriticalUserViewSet, base_name='criticaluser') )
router.register_viewset(r'users/club', views.ClubViewSet) router.register_viewset(
router.register_viewset(r'users/adherent', views.AdherentViewSet) r"users/normaluser", views.NormalUserViewSet, base_name="normaluser"
router.register_viewset(r'users/serviceuser', views.ServiceUserViewSet) )
router.register_viewset(r'users/school', views.SchoolViewSet) router.register_viewset(
router.register_viewset(r'users/listright', views.ListRightViewSet) r"users/criticaluser", views.CriticalUserViewSet, base_name="criticaluser"
router.register_viewset(r'users/shell', views.ShellViewSet, base_name='shell') )
router.register_viewset(r'users/ban', views.BanViewSet) router.register_viewset(r"users/club", views.ClubViewSet)
router.register_viewset(r'users/whitelist', views.WhitelistViewSet) router.register_viewset(r"users/adherent", views.AdherentViewSet)
router.register_viewset(r'users/emailaddress', views.EMailAddressViewSet) router.register_viewset(r"users/serviceuser", views.ServiceUserViewSet)
router.register_viewset(r"users/school", views.SchoolViewSet)
router.register_viewset(r"users/listright", views.ListRightViewSet)
router.register_viewset(r"users/shell", views.ShellViewSet, base_name="shell")
router.register_viewset(r"users/ban", views.BanViewSet)
router.register_viewset(r"users/whitelist", views.WhitelistViewSet)
router.register_viewset(r"users/emailaddress", views.EMailAddressViewSet)
# SERVICE REGEN # SERVICE REGEN
router.register_viewset(r'services/regen', views.ServiceRegenViewSet, base_name='serviceregen') router.register_viewset(
r"services/regen", views.ServiceRegenViewSet, base_name="serviceregen"
)
# DHCP # DHCP
router.register_view(r'dhcp/hostmacip', views.HostMacIpView), router.register_view(r"dhcp/hostmacip", views.HostMacIpView),
# LOCAL EMAILS # LOCAL EMAILS
router.register_view(r'localemail/users', views.LocalEmailUsersView), router.register_view(r"localemail/users", views.LocalEmailUsersView),
# Firewall # Firewall
router.register_view(r'firewall/subnet-ports', views.SubnetPortsOpenView), router.register_view(r"firewall/subnet-ports", views.SubnetPortsOpenView),
router.register_view(r'firewall/interface-ports', views.InterfacePortsOpenView), router.register_view(r"firewall/interface-ports", views.InterfacePortsOpenView),
# Switches config # Switches config
router.register_view(r'switchs/ports-config', views.SwitchPortView), router.register_view(r"switchs/ports-config", views.SwitchPortView),
router.register_view(r'switchs/role', views.RoleView), router.register_view(r"switchs/role", views.RoleView),
# Reminder # Reminder
router.register_view(r'reminder/get-users', views.ReminderView), router.register_view(r"reminder/get-users", views.ReminderView),
# DNS # DNS
router.register_view(r'dns/zones', views.DNSZonesView), router.register_view(r"dns/zones", views.DNSZonesView),
router.register_view(r'dns/reverse-zones', views.DNSReverseZonesView), router.register_view(r"dns/reverse-zones", views.DNSReverseZonesView),
# MAILING # MAILING
router.register_view(r'mailing/standard', views.StandardMailingView), router.register_view(r"mailing/standard", views.StandardMailingView),
router.register_view(r'mailing/club', views.ClubMailingView), router.register_view(r"mailing/club", views.ClubMailingView),
# TOKEN AUTHENTICATION # TOKEN AUTHENTICATION
router.register_view(r'token-auth', views.ObtainExpiringAuthToken) router.register_view(r"token-auth", views.ObtainExpiringAuthToken)
urlpatterns = [ urlpatterns = [url(r"^", include(router.urls))]
url(r'^', include(router.urls)),
]

View file

@ -52,12 +52,15 @@ from .permissions import ACLPermission
class FactureViewSet(viewsets.ReadOnlyModelViewSet): class FactureViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Facture` objects. """Exposes list and details of `cotisations.models.Facture` objects.
""" """
queryset = cotisations.Facture.objects.all() queryset = cotisations.Facture.objects.all()
serializer_class = serializers.FactureSerializer serializer_class = serializers.FactureSerializer
class FactureViewSet(viewsets.ReadOnlyModelViewSet): class FactureViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Facture` objects. """Exposes list and details of `cotisations.models.Facture` objects.
""" """
queryset = cotisations.BaseInvoice.objects.all() queryset = cotisations.BaseInvoice.objects.all()
serializer_class = serializers.BaseInvoiceSerializer serializer_class = serializers.BaseInvoiceSerializer
@ -65,6 +68,7 @@ class FactureViewSet(viewsets.ReadOnlyModelViewSet):
class VenteViewSet(viewsets.ReadOnlyModelViewSet): class VenteViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Vente` objects. """Exposes list and details of `cotisations.models.Vente` objects.
""" """
queryset = cotisations.Vente.objects.all() queryset = cotisations.Vente.objects.all()
serializer_class = serializers.VenteSerializer serializer_class = serializers.VenteSerializer
@ -72,6 +76,7 @@ class VenteViewSet(viewsets.ReadOnlyModelViewSet):
class ArticleViewSet(viewsets.ReadOnlyModelViewSet): class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Article` objects. """Exposes list and details of `cotisations.models.Article` objects.
""" """
queryset = cotisations.Article.objects.all() queryset = cotisations.Article.objects.all()
serializer_class = serializers.ArticleSerializer serializer_class = serializers.ArticleSerializer
@ -79,6 +84,7 @@ class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
class BanqueViewSet(viewsets.ReadOnlyModelViewSet): class BanqueViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Banque` objects. """Exposes list and details of `cotisations.models.Banque` objects.
""" """
queryset = cotisations.Banque.objects.all() queryset = cotisations.Banque.objects.all()
serializer_class = serializers.BanqueSerializer serializer_class = serializers.BanqueSerializer
@ -86,6 +92,7 @@ class BanqueViewSet(viewsets.ReadOnlyModelViewSet):
class PaiementViewSet(viewsets.ReadOnlyModelViewSet): class PaiementViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Paiement` objects. """Exposes list and details of `cotisations.models.Paiement` objects.
""" """
queryset = cotisations.Paiement.objects.all() queryset = cotisations.Paiement.objects.all()
serializer_class = serializers.PaiementSerializer serializer_class = serializers.PaiementSerializer
@ -93,6 +100,7 @@ class PaiementViewSet(viewsets.ReadOnlyModelViewSet):
class CotisationViewSet(viewsets.ReadOnlyModelViewSet): class CotisationViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Cotisation` objects. """Exposes list and details of `cotisations.models.Cotisation` objects.
""" """
queryset = cotisations.Cotisation.objects.all() queryset = cotisations.Cotisation.objects.all()
serializer_class = serializers.CotisationSerializer serializer_class = serializers.CotisationSerializer
@ -103,6 +111,7 @@ class CotisationViewSet(viewsets.ReadOnlyModelViewSet):
class MachineViewSet(viewsets.ReadOnlyModelViewSet): class MachineViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Machine` objects. """Exposes list and details of `machines.models.Machine` objects.
""" """
queryset = machines.Machine.objects.all() queryset = machines.Machine.objects.all()
serializer_class = serializers.MachineSerializer serializer_class = serializers.MachineSerializer
@ -110,6 +119,7 @@ class MachineViewSet(viewsets.ReadOnlyModelViewSet):
class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet): class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.MachineType` objects. """Exposes list and details of `machines.models.MachineType` objects.
""" """
queryset = machines.MachineType.objects.all() queryset = machines.MachineType.objects.all()
serializer_class = serializers.MachineTypeSerializer serializer_class = serializers.MachineTypeSerializer
@ -117,6 +127,7 @@ class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet):
class IpTypeViewSet(viewsets.ReadOnlyModelViewSet): class IpTypeViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.IpType` objects. """Exposes list and details of `machines.models.IpType` objects.
""" """
queryset = machines.IpType.objects.all() queryset = machines.IpType.objects.all()
serializer_class = serializers.IpTypeSerializer serializer_class = serializers.IpTypeSerializer
@ -124,6 +135,7 @@ class IpTypeViewSet(viewsets.ReadOnlyModelViewSet):
class VlanViewSet(viewsets.ReadOnlyModelViewSet): class VlanViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Vlan` objects. """Exposes list and details of `machines.models.Vlan` objects.
""" """
queryset = machines.Vlan.objects.all() queryset = machines.Vlan.objects.all()
serializer_class = serializers.VlanSerializer serializer_class = serializers.VlanSerializer
@ -131,6 +143,7 @@ class VlanViewSet(viewsets.ReadOnlyModelViewSet):
class NasViewSet(viewsets.ReadOnlyModelViewSet): class NasViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Nas` objects. """Exposes list and details of `machines.models.Nas` objects.
""" """
queryset = machines.Nas.objects.all() queryset = machines.Nas.objects.all()
serializer_class = serializers.NasSerializer serializer_class = serializers.NasSerializer
@ -138,6 +151,7 @@ class NasViewSet(viewsets.ReadOnlyModelViewSet):
class SOAViewSet(viewsets.ReadOnlyModelViewSet): class SOAViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.SOA` objects. """Exposes list and details of `machines.models.SOA` objects.
""" """
queryset = machines.SOA.objects.all() queryset = machines.SOA.objects.all()
serializer_class = serializers.SOASerializer serializer_class = serializers.SOASerializer
@ -145,6 +159,7 @@ class SOAViewSet(viewsets.ReadOnlyModelViewSet):
class ExtensionViewSet(viewsets.ReadOnlyModelViewSet): class ExtensionViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Extension` objects. """Exposes list and details of `machines.models.Extension` objects.
""" """
queryset = machines.Extension.objects.all() queryset = machines.Extension.objects.all()
serializer_class = serializers.ExtensionSerializer serializer_class = serializers.ExtensionSerializer
@ -152,6 +167,7 @@ class ExtensionViewSet(viewsets.ReadOnlyModelViewSet):
class MxViewSet(viewsets.ReadOnlyModelViewSet): class MxViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Mx` objects. """Exposes list and details of `machines.models.Mx` objects.
""" """
queryset = machines.Mx.objects.all() queryset = machines.Mx.objects.all()
serializer_class = serializers.MxSerializer serializer_class = serializers.MxSerializer
@ -159,6 +175,7 @@ class MxViewSet(viewsets.ReadOnlyModelViewSet):
class NsViewSet(viewsets.ReadOnlyModelViewSet): class NsViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Ns` objects. """Exposes list and details of `machines.models.Ns` objects.
""" """
queryset = machines.Ns.objects.all() queryset = machines.Ns.objects.all()
serializer_class = serializers.NsSerializer serializer_class = serializers.NsSerializer
@ -166,6 +183,7 @@ class NsViewSet(viewsets.ReadOnlyModelViewSet):
class TxtViewSet(viewsets.ReadOnlyModelViewSet): class TxtViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Txt` objects. """Exposes list and details of `machines.models.Txt` objects.
""" """
queryset = machines.Txt.objects.all() queryset = machines.Txt.objects.all()
serializer_class = serializers.TxtSerializer serializer_class = serializers.TxtSerializer
@ -173,6 +191,7 @@ class TxtViewSet(viewsets.ReadOnlyModelViewSet):
class DNameViewSet(viewsets.ReadOnlyModelViewSet): class DNameViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.DName` objects. """Exposes list and details of `machines.models.DName` objects.
""" """
queryset = machines.DName.objects.all() queryset = machines.DName.objects.all()
serializer_class = serializers.DNameSerializer serializer_class = serializers.DNameSerializer
@ -180,6 +199,7 @@ class DNameViewSet(viewsets.ReadOnlyModelViewSet):
class SrvViewSet(viewsets.ReadOnlyModelViewSet): class SrvViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Srv` objects. """Exposes list and details of `machines.models.Srv` objects.
""" """
queryset = machines.Srv.objects.all() queryset = machines.Srv.objects.all()
serializer_class = serializers.SrvSerializer serializer_class = serializers.SrvSerializer
@ -187,6 +207,7 @@ class SrvViewSet(viewsets.ReadOnlyModelViewSet):
class SshFpViewSet(viewsets.ReadOnlyModelViewSet): class SshFpViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.SshFp` objects. """Exposes list and details of `machines.models.SshFp` objects.
""" """
queryset = machines.SshFp.objects.all() queryset = machines.SshFp.objects.all()
serializer_class = serializers.SshFpSerializer serializer_class = serializers.SshFpSerializer
@ -194,6 +215,7 @@ class SshFpViewSet(viewsets.ReadOnlyModelViewSet):
class InterfaceViewSet(viewsets.ReadOnlyModelViewSet): class InterfaceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Interface` objects. """Exposes list and details of `machines.models.Interface` objects.
""" """
queryset = machines.Interface.objects.all() queryset = machines.Interface.objects.all()
serializer_class = serializers.InterfaceSerializer serializer_class = serializers.InterfaceSerializer
@ -201,6 +223,7 @@ class InterfaceViewSet(viewsets.ReadOnlyModelViewSet):
class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet): class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Ipv6List` objects. """Exposes list and details of `machines.models.Ipv6List` objects.
""" """
queryset = machines.Ipv6List.objects.all() queryset = machines.Ipv6List.objects.all()
serializer_class = serializers.Ipv6ListSerializer serializer_class = serializers.Ipv6ListSerializer
@ -208,6 +231,7 @@ class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet):
class DomainViewSet(viewsets.ReadOnlyModelViewSet): class DomainViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Domain` objects. """Exposes list and details of `machines.models.Domain` objects.
""" """
queryset = machines.Domain.objects.all() queryset = machines.Domain.objects.all()
serializer_class = serializers.DomainSerializer serializer_class = serializers.DomainSerializer
@ -215,6 +239,7 @@ class DomainViewSet(viewsets.ReadOnlyModelViewSet):
class IpListViewSet(viewsets.ReadOnlyModelViewSet): class IpListViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.IpList` objects. """Exposes list and details of `machines.models.IpList` objects.
""" """
queryset = machines.IpList.objects.all() queryset = machines.IpList.objects.all()
serializer_class = serializers.IpListSerializer serializer_class = serializers.IpListSerializer
@ -222,6 +247,7 @@ class IpListViewSet(viewsets.ReadOnlyModelViewSet):
class ServiceViewSet(viewsets.ReadOnlyModelViewSet): class ServiceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Service` objects. """Exposes list and details of `machines.models.Service` objects.
""" """
queryset = machines.Service.objects.all() queryset = machines.Service.objects.all()
serializer_class = serializers.ServiceSerializer serializer_class = serializers.ServiceSerializer
@ -229,6 +255,7 @@ class ServiceViewSet(viewsets.ReadOnlyModelViewSet):
class ServiceLinkViewSet(viewsets.ReadOnlyModelViewSet): class ServiceLinkViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Service_link` objects. """Exposes list and details of `machines.models.Service_link` objects.
""" """
queryset = machines.Service_link.objects.all() queryset = machines.Service_link.objects.all()
serializer_class = serializers.ServiceLinkSerializer serializer_class = serializers.ServiceLinkSerializer
@ -237,6 +264,7 @@ class OuverturePortListViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.OuverturePortList` """Exposes list and details of `machines.models.OuverturePortList`
objects. objects.
""" """
queryset = machines.OuverturePortList.objects.all() queryset = machines.OuverturePortList.objects.all()
serializer_class = serializers.OuverturePortListSerializer serializer_class = serializers.OuverturePortListSerializer
@ -244,6 +272,7 @@ class OuverturePortListViewSet(viewsets.ReadOnlyModelViewSet):
class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet): class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.OuverturePort` objects. """Exposes list and details of `machines.models.OuverturePort` objects.
""" """
queryset = machines.OuverturePort.objects.all() queryset = machines.OuverturePort.objects.all()
serializer_class = serializers.OuverturePortSerializer serializer_class = serializers.OuverturePortSerializer
@ -251,6 +280,7 @@ class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet):
class RoleViewSet(viewsets.ReadOnlyModelViewSet): class RoleViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Machine` objects. """Exposes list and details of `machines.models.Machine` objects.
""" """
queryset = machines.Role.objects.all() queryset = machines.Role.objects.all()
serializer_class = serializers.RoleSerializer serializer_class = serializers.RoleSerializer
@ -259,11 +289,13 @@ class RoleViewSet(viewsets.ReadOnlyModelViewSet):
# Those views differ a bit because there is only one object # Those views differ a bit because there is only one object
# to display, so we don't bother with the listing part # to display, so we don't bother with the listing part
class OptionalUserView(generics.RetrieveAPIView): class OptionalUserView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.` settings. """Exposes details of `preferences.models.` settings.
""" """
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {'GET': [preferences.OptionalUser.can_view_all]} perms_map = {"GET": [preferences.OptionalUser.can_view_all]}
serializer_class = serializers.OptionalUserSerializer serializer_class = serializers.OptionalUserSerializer
def get_object(self): def get_object(self):
@ -273,8 +305,9 @@ class OptionalUserView(generics.RetrieveAPIView):
class OptionalMachineView(generics.RetrieveAPIView): class OptionalMachineView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalMachine` settings. """Exposes details of `preferences.models.OptionalMachine` settings.
""" """
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {'GET': [preferences.OptionalMachine.can_view_all]} perms_map = {"GET": [preferences.OptionalMachine.can_view_all]}
serializer_class = serializers.OptionalMachineSerializer serializer_class = serializers.OptionalMachineSerializer
def get_object(self): def get_object(self):
@ -284,8 +317,9 @@ class OptionalMachineView(generics.RetrieveAPIView):
class OptionalTopologieView(generics.RetrieveAPIView): class OptionalTopologieView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalTopologie` settings. """Exposes details of `preferences.models.OptionalTopologie` settings.
""" """
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {'GET': [preferences.OptionalTopologie.can_view_all]} perms_map = {"GET": [preferences.OptionalTopologie.can_view_all]}
serializer_class = serializers.OptionalTopologieSerializer serializer_class = serializers.OptionalTopologieSerializer
def get_object(self): def get_object(self):
@ -295,8 +329,9 @@ class OptionalTopologieView(generics.RetrieveAPIView):
class RadiusOptionView(generics.RetrieveAPIView): class RadiusOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalTopologie` settings. """Exposes details of `preferences.models.OptionalTopologie` settings.
""" """
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {'GET': [preferences.RadiusOption.can_view_all]} perms_map = {"GET": [preferences.RadiusOption.can_view_all]}
serializer_class = serializers.RadiusOptionSerializer serializer_class = serializers.RadiusOptionSerializer
def get_object(self): def get_object(self):
@ -306,8 +341,9 @@ class RadiusOptionView(generics.RetrieveAPIView):
class GeneralOptionView(generics.RetrieveAPIView): class GeneralOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.GeneralOption` settings. """Exposes details of `preferences.models.GeneralOption` settings.
""" """
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {'GET': [preferences.GeneralOption.can_view_all]} perms_map = {"GET": [preferences.GeneralOption.can_view_all]}
serializer_class = serializers.GeneralOptionSerializer serializer_class = serializers.GeneralOptionSerializer
def get_object(self): def get_object(self):
@ -317,6 +353,7 @@ class GeneralOptionView(generics.RetrieveAPIView):
class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet): class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `preferences.models.Service` objects. """Exposes list and details of `preferences.models.Service` objects.
""" """
queryset = preferences.Service.objects.all() queryset = preferences.Service.objects.all()
serializer_class = serializers.HomeServiceSerializer serializer_class = serializers.HomeServiceSerializer
@ -324,8 +361,9 @@ class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet):
class AssoOptionView(generics.RetrieveAPIView): class AssoOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.AssoOption` settings. """Exposes details of `preferences.models.AssoOption` settings.
""" """
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {'GET': [preferences.AssoOption.can_view_all]} perms_map = {"GET": [preferences.AssoOption.can_view_all]}
serializer_class = serializers.AssoOptionSerializer serializer_class = serializers.AssoOptionSerializer
def get_object(self): def get_object(self):
@ -335,8 +373,9 @@ class AssoOptionView(generics.RetrieveAPIView):
class HomeOptionView(generics.RetrieveAPIView): class HomeOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.HomeOption` settings. """Exposes details of `preferences.models.HomeOption` settings.
""" """
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {'GET': [preferences.HomeOption.can_view_all]} perms_map = {"GET": [preferences.HomeOption.can_view_all]}
serializer_class = serializers.HomeOptionSerializer serializer_class = serializers.HomeOptionSerializer
def get_object(self): def get_object(self):
@ -346,8 +385,9 @@ class HomeOptionView(generics.RetrieveAPIView):
class MailMessageOptionView(generics.RetrieveAPIView): class MailMessageOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.MailMessageOption` settings. """Exposes details of `preferences.models.MailMessageOption` settings.
""" """
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {'GET': [preferences.MailMessageOption.can_view_all]} perms_map = {"GET": [preferences.MailMessageOption.can_view_all]}
serializer_class = serializers.MailMessageOptionSerializer serializer_class = serializers.MailMessageOptionSerializer
def get_object(self): def get_object(self):
@ -360,6 +400,7 @@ class MailMessageOptionView(generics.RetrieveAPIView):
class StackViewSet(viewsets.ReadOnlyModelViewSet): class StackViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Stack` objects. """Exposes list and details of `topologie.models.Stack` objects.
""" """
queryset = topologie.Stack.objects.all() queryset = topologie.Stack.objects.all()
serializer_class = serializers.StackSerializer serializer_class = serializers.StackSerializer
@ -367,6 +408,7 @@ class StackViewSet(viewsets.ReadOnlyModelViewSet):
class AccessPointViewSet(viewsets.ReadOnlyModelViewSet): class AccessPointViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.AccessPoint` objects. """Exposes list and details of `topologie.models.AccessPoint` objects.
""" """
queryset = topologie.AccessPoint.objects.all() queryset = topologie.AccessPoint.objects.all()
serializer_class = serializers.AccessPointSerializer serializer_class = serializers.AccessPointSerializer
@ -374,6 +416,7 @@ class AccessPointViewSet(viewsets.ReadOnlyModelViewSet):
class SwitchViewSet(viewsets.ReadOnlyModelViewSet): class SwitchViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Switch` objects. """Exposes list and details of `topologie.models.Switch` objects.
""" """
queryset = topologie.Switch.objects.all() queryset = topologie.Switch.objects.all()
serializer_class = serializers.SwitchSerializer serializer_class = serializers.SwitchSerializer
@ -381,6 +424,7 @@ class SwitchViewSet(viewsets.ReadOnlyModelViewSet):
class ServerViewSet(viewsets.ReadOnlyModelViewSet): class ServerViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Server` objects. """Exposes list and details of `topologie.models.Server` objects.
""" """
queryset = topologie.Server.objects.all() queryset = topologie.Server.objects.all()
serializer_class = serializers.ServerSerializer serializer_class = serializers.ServerSerializer
@ -388,6 +432,7 @@ class ServerViewSet(viewsets.ReadOnlyModelViewSet):
class ModelSwitchViewSet(viewsets.ReadOnlyModelViewSet): class ModelSwitchViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.ModelSwitch` objects. """Exposes list and details of `topologie.models.ModelSwitch` objects.
""" """
queryset = topologie.ModelSwitch.objects.all() queryset = topologie.ModelSwitch.objects.all()
serializer_class = serializers.ModelSwitchSerializer serializer_class = serializers.ModelSwitchSerializer
@ -396,6 +441,7 @@ class ConstructorSwitchViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.ConstructorSwitch` """Exposes list and details of `topologie.models.ConstructorSwitch`
objects. objects.
""" """
queryset = topologie.ConstructorSwitch.objects.all() queryset = topologie.ConstructorSwitch.objects.all()
serializer_class = serializers.ConstructorSwitchSerializer serializer_class = serializers.ConstructorSwitchSerializer
@ -403,6 +449,7 @@ class ConstructorSwitchViewSet(viewsets.ReadOnlyModelViewSet):
class SwitchBayViewSet(viewsets.ReadOnlyModelViewSet): class SwitchBayViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.SwitchBay` objects. """Exposes list and details of `topologie.models.SwitchBay` objects.
""" """
queryset = topologie.SwitchBay.objects.all() queryset = topologie.SwitchBay.objects.all()
serializer_class = serializers.SwitchBaySerializer serializer_class = serializers.SwitchBaySerializer
@ -410,6 +457,7 @@ class SwitchBayViewSet(viewsets.ReadOnlyModelViewSet):
class BuildingViewSet(viewsets.ReadOnlyModelViewSet): class BuildingViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Building` objects. """Exposes list and details of `topologie.models.Building` objects.
""" """
queryset = topologie.Building.objects.all() queryset = topologie.Building.objects.all()
serializer_class = serializers.BuildingSerializer serializer_class = serializers.BuildingSerializer
@ -417,6 +465,7 @@ class BuildingViewSet(viewsets.ReadOnlyModelViewSet):
class SwitchPortViewSet(viewsets.ReadOnlyModelViewSet): class SwitchPortViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Port` objects. """Exposes list and details of `topologie.models.Port` objects.
""" """
queryset = topologie.Port.objects.all() queryset = topologie.Port.objects.all()
serializer_class = serializers.SwitchPortSerializer serializer_class = serializers.SwitchPortSerializer
@ -424,6 +473,7 @@ class SwitchPortViewSet(viewsets.ReadOnlyModelViewSet):
class PortProfileViewSet(viewsets.ReadOnlyModelViewSet): class PortProfileViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.PortProfile` objects. """Exposes list and details of `topologie.models.PortProfile` objects.
""" """
queryset = topologie.PortProfile.objects.all() queryset = topologie.PortProfile.objects.all()
serializer_class = serializers.PortProfileSerializer serializer_class = serializers.PortProfileSerializer
@ -431,6 +481,7 @@ class PortProfileViewSet(viewsets.ReadOnlyModelViewSet):
class RoomViewSet(viewsets.ReadOnlyModelViewSet): class RoomViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Room` objects. """Exposes list and details of `topologie.models.Room` objects.
""" """
queryset = topologie.Room.objects.all() queryset = topologie.Room.objects.all()
serializer_class = serializers.RoomSerializer serializer_class = serializers.RoomSerializer
@ -438,6 +489,7 @@ class RoomViewSet(viewsets.ReadOnlyModelViewSet):
class PortProfileViewSet(viewsets.ReadOnlyModelViewSet): class PortProfileViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.PortProfile` objects. """Exposes list and details of `topologie.models.PortProfile` objects.
""" """
queryset = topologie.PortProfile.objects.all() queryset = topologie.PortProfile.objects.all()
serializer_class = serializers.PortProfileSerializer serializer_class = serializers.PortProfileSerializer
@ -448,6 +500,7 @@ class PortProfileViewSet(viewsets.ReadOnlyModelViewSet):
class UserViewSet(viewsets.ReadOnlyModelViewSet): class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.Users` objects. """Exposes list and details of `users.models.Users` objects.
""" """
queryset = users.User.objects.all() queryset = users.User.objects.all()
serializer_class = serializers.UserSerializer serializer_class = serializers.UserSerializer
@ -455,18 +508,25 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet):
class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet): class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes infos of `users.models.Users` objects to create homes. """Exposes infos of `users.models.Users` objects to create homes.
""" """
queryset = users.User.objects.exclude(Q(state=users.User.STATE_DISABLED) | Q(state=users.User.STATE_NOT_YET_ACTIVE))
queryset = users.User.objects.exclude(
Q(state=users.User.STATE_DISABLED)
| Q(state=users.User.STATE_NOT_YET_ACTIVE)
| Q(state=users.User.STATE_FULL_ARCHIVE)
)
serializer_class = serializers.BasicUserSerializer serializer_class = serializers.BasicUserSerializer
class NormalUserViewSet(viewsets.ReadOnlyModelViewSet): class NormalUserViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes infos of `users.models.Users`without specific rights objects.""" """Exposes infos of `users.models.Users`without specific rights objects."""
queryset = users.User.objects.exclude(groups__listright__critical=True).distinct() queryset = users.User.objects.exclude(groups__listright__critical=True).distinct()
serializer_class = serializers.BasicUserSerializer serializer_class = serializers.BasicUserSerializer
class CriticalUserViewSet(viewsets.ReadOnlyModelViewSet): class CriticalUserViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes infos of `users.models.Users`without specific rights objects.""" """Exposes infos of `users.models.Users`without specific rights objects."""
queryset = users.User.objects.filter(groups__listright__critical=True).distinct() queryset = users.User.objects.filter(groups__listright__critical=True).distinct()
serializer_class = serializers.BasicUserSerializer serializer_class = serializers.BasicUserSerializer
@ -474,6 +534,7 @@ class CriticalUserViewSet(viewsets.ReadOnlyModelViewSet):
class ClubViewSet(viewsets.ReadOnlyModelViewSet): class ClubViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.Club` objects. """Exposes list and details of `users.models.Club` objects.
""" """
queryset = users.Club.objects.all() queryset = users.Club.objects.all()
serializer_class = serializers.ClubSerializer serializer_class = serializers.ClubSerializer
@ -481,6 +542,7 @@ class ClubViewSet(viewsets.ReadOnlyModelViewSet):
class AdherentViewSet(viewsets.ReadOnlyModelViewSet): class AdherentViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.Adherent` objects. """Exposes list and details of `users.models.Adherent` objects.
""" """
queryset = users.Adherent.objects.all() queryset = users.Adherent.objects.all()
serializer_class = serializers.AdherentSerializer serializer_class = serializers.AdherentSerializer
@ -488,6 +550,7 @@ class AdherentViewSet(viewsets.ReadOnlyModelViewSet):
class ServiceUserViewSet(viewsets.ReadOnlyModelViewSet): class ServiceUserViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.ServiceUser` objects. """Exposes list and details of `users.models.ServiceUser` objects.
""" """
queryset = users.ServiceUser.objects.all() queryset = users.ServiceUser.objects.all()
serializer_class = serializers.ServiceUserSerializer serializer_class = serializers.ServiceUserSerializer
@ -495,6 +558,7 @@ class ServiceUserViewSet(viewsets.ReadOnlyModelViewSet):
class SchoolViewSet(viewsets.ReadOnlyModelViewSet): class SchoolViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.School` objects. """Exposes list and details of `users.models.School` objects.
""" """
queryset = users.School.objects.all() queryset = users.School.objects.all()
serializer_class = serializers.SchoolSerializer serializer_class = serializers.SchoolSerializer
@ -502,6 +566,7 @@ class SchoolViewSet(viewsets.ReadOnlyModelViewSet):
class ListRightViewSet(viewsets.ReadOnlyModelViewSet): class ListRightViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.ListRight` objects. """Exposes list and details of `users.models.ListRight` objects.
""" """
queryset = users.ListRight.objects.all() queryset = users.ListRight.objects.all()
serializer_class = serializers.ListRightSerializer serializer_class = serializers.ListRightSerializer
@ -509,6 +574,7 @@ class ListRightViewSet(viewsets.ReadOnlyModelViewSet):
class ShellViewSet(viewsets.ReadOnlyModelViewSet): class ShellViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.ListShell` objects. """Exposes list and details of `users.models.ListShell` objects.
""" """
queryset = users.ListShell.objects.all() queryset = users.ListShell.objects.all()
serializer_class = serializers.ShellSerializer serializer_class = serializers.ShellSerializer
@ -516,6 +582,7 @@ class ShellViewSet(viewsets.ReadOnlyModelViewSet):
class BanViewSet(viewsets.ReadOnlyModelViewSet): class BanViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.Ban` objects. """Exposes list and details of `users.models.Ban` objects.
""" """
queryset = users.Ban.objects.all() queryset = users.Ban.objects.all()
serializer_class = serializers.BanSerializer serializer_class = serializers.BanSerializer
@ -523,6 +590,7 @@ class BanViewSet(viewsets.ReadOnlyModelViewSet):
class WhitelistViewSet(viewsets.ReadOnlyModelViewSet): class WhitelistViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.Whitelist` objects. """Exposes list and details of `users.models.Whitelist` objects.
""" """
queryset = users.Whitelist.objects.all() queryset = users.Whitelist.objects.all()
serializer_class = serializers.WhitelistSerializer serializer_class = serializers.WhitelistSerializer
@ -530,14 +598,13 @@ class WhitelistViewSet(viewsets.ReadOnlyModelViewSet):
class EMailAddressViewSet(viewsets.ReadOnlyModelViewSet): class EMailAddressViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.EMailAddress` objects. """Exposes list and details of `users.models.EMailAddress` objects.
""" """
serializer_class = serializers.EMailAddressSerializer serializer_class = serializers.EMailAddressSerializer
queryset = users.EMailAddress.objects.none() queryset = users.EMailAddress.objects.none()
def get_queryset(self): def get_queryset(self):
if preferences.OptionalUser.get_cached_value( if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"):
'local_email_accounts_enabled'): return users.EMailAddress.objects.filter(user__local_email_enabled=True)
return (users.EMailAddress.objects
.filter(user__local_email_enabled=True))
else: else:
return users.EMailAddress.objects.none() return users.EMailAddress.objects.none()
@ -548,34 +615,47 @@ class EMailAddressViewSet(viewsets.ReadOnlyModelViewSet):
class ServiceRegenViewSet(viewsets.ModelViewSet): class ServiceRegenViewSet(viewsets.ModelViewSet):
"""Exposes list and details of the services to regen """Exposes list and details of the services to regen
""" """
serializer_class = serializers.ServiceRegenSerializer serializer_class = serializers.ServiceRegenSerializer
def get_queryset(self): def get_queryset(self):
queryset = machines.Service_link.objects.select_related( queryset = machines.Service_link.objects.select_related(
'server__domain' "server__domain"
).select_related( ).select_related("service")
'service' if "hostname" in self.request.GET:
) hostname = self.request.GET["hostname"]
if 'hostname' in self.request.GET:
hostname = self.request.GET['hostname']
queryset = queryset.filter(server__domain__name__iexact=hostname) queryset = queryset.filter(server__domain__name__iexact=hostname)
return queryset return queryset
# Config des switches # Config des switches
class SwitchPortView(generics.ListAPIView): class SwitchPortView(generics.ListAPIView):
"""Output each port of a switch, to be serialized with """Output each port of a switch, to be serialized with
additionnal informations (profiles etc) additionnal informations (profiles etc)
""" """
queryset = topologie.Switch.objects.all().select_related("switchbay").select_related("model__constructor").prefetch_related("ports__custom_profile__vlan_tagged").prefetch_related("ports__custom_profile__vlan_untagged").prefetch_related("ports__machine_interface__domain__extension").prefetch_related("ports__room")
queryset = (
topologie.Switch.objects.all()
.select_related("switchbay")
.select_related("model__constructor")
.prefetch_related("ports__custom_profile__vlan_tagged")
.prefetch_related("ports__custom_profile__vlan_untagged")
.prefetch_related("ports__machine_interface__domain__extension")
.prefetch_related("ports__room")
)
serializer_class = serializers.SwitchPortSerializer serializer_class = serializers.SwitchPortSerializer
# Rappel fin adhésion # Rappel fin adhésion
class ReminderView(generics.ListAPIView): class ReminderView(generics.ListAPIView):
"""Output for users to remind an end of their subscription. """Output for users to remind an end of their subscription.
""" """
queryset = preferences.Reminder.objects.all() queryset = preferences.Reminder.objects.all()
serializer_class = serializers.ReminderSerializer serializer_class = serializers.ReminderSerializer
@ -583,7 +663,8 @@ class ReminderView(generics.ListAPIView):
class RoleView(generics.ListAPIView): class RoleView(generics.ListAPIView):
"""Output of roles for each server """Output of roles for each server
""" """
queryset = machines.Role.objects.all().prefetch_related('servers')
queryset = machines.Role.objects.all().prefetch_related("servers")
serializer_class = serializers.RoleSerializer serializer_class = serializers.RoleSerializer
@ -593,13 +674,12 @@ class RoleView(generics.ListAPIView):
class LocalEmailUsersView(generics.ListAPIView): class LocalEmailUsersView(generics.ListAPIView):
"""Exposes all the aliases of the users that activated the internal address """Exposes all the aliases of the users that activated the internal address
""" """
serializer_class = serializers.LocalEmailUsersSerializer serializer_class = serializers.LocalEmailUsersSerializer
def get_queryset(self): def get_queryset(self):
if preferences.OptionalUser.get_cached_value( if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"):
'local_email_accounts_enabled'): return users.User.objects.filter(local_email_enabled=True)
return (users.User.objects
.filter(local_email_enabled=True))
else: else:
return users.User.objects.none() return users.User.objects.none()
@ -611,6 +691,7 @@ class HostMacIpView(generics.ListAPIView):
"""Exposes the associations between hostname, mac address and IPv4 in """Exposes the associations between hostname, mac address and IPv4 in
order to build the DHCP lease files. order to build the DHCP lease files.
""" """
serializer_class = serializers.HostMacIpSerializer serializer_class = serializers.HostMacIpSerializer
def get_queryset(self): def get_queryset(self):
@ -619,6 +700,7 @@ class HostMacIpView(generics.ListAPIView):
# Firewall # Firewall
class SubnetPortsOpenView(generics.ListAPIView): class SubnetPortsOpenView(generics.ListAPIView):
queryset = machines.IpType.objects.all() queryset = machines.IpType.objects.all()
serializer_class = serializers.SubnetPortsOpenSerializer serializer_class = serializers.SubnetPortsOpenSerializer
@ -636,14 +718,19 @@ class DNSZonesView(generics.ListAPIView):
"""Exposes the detailed information about each extension (hostnames, """Exposes the detailed information about each extension (hostnames,
IPs, DNS records, etc.) in order to build the DNS zone files. IPs, DNS records, etc.) in order to build the DNS zone files.
""" """
queryset = (machines.Extension.objects
.prefetch_related('soa') queryset = (
.prefetch_related('ns_set').prefetch_related('ns_set__ns') machines.Extension.objects.prefetch_related("soa")
.prefetch_related('origin') .prefetch_related("ns_set")
.prefetch_related('mx_set').prefetch_related('mx_set__name') .prefetch_related("ns_set__ns")
.prefetch_related('txt_set') .prefetch_related("origin")
.prefetch_related('srv_set').prefetch_related('srv_set__target') .prefetch_related("mx_set")
.all()) .prefetch_related("mx_set__name")
.prefetch_related("txt_set")
.prefetch_related("srv_set")
.prefetch_related("srv_set__target")
.all()
)
serializer_class = serializers.DNSZonesSerializer serializer_class = serializers.DNSZonesSerializer
@ -651,7 +738,8 @@ class DNSReverseZonesView(generics.ListAPIView):
"""Exposes the detailed information about each extension (hostnames, """Exposes the detailed information about each extension (hostnames,
IPs, DNS records, etc.) in order to build the DNS zone files. IPs, DNS records, etc.) in order to build the DNS zone files.
""" """
queryset = (machines.IpType.objects.all())
queryset = machines.IpType.objects.all()
serializer_class = serializers.DNSReverseZonesSerializer serializer_class = serializers.DNSReverseZonesSerializer
@ -662,13 +750,16 @@ class StandardMailingView(views.APIView):
"""Exposes list and details of standard mailings (name and members) in """Exposes list and details of standard mailings (name and members) in
order to building the corresponding mailing lists. order to building the corresponding mailing lists.
""" """
pagination_class = PageSizedPagination pagination_class = PageSizedPagination
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {'GET': [users.User.can_view_all]} perms_map = {"GET": [users.User.can_view_all]}
def get(self, request, format=None): def get(self, request, format=None):
adherents_data = serializers.MailingMemberSerializer(all_has_access(), many=True).data adherents_data = serializers.MailingMemberSerializer(
data = [{'name': 'adherents', 'members': adherents_data}] all_has_access(), many=True
).data
data = [{"name": "adherents", "members": adherents_data}]
paginator = self.pagination_class() paginator = self.pagination_class()
paginator.paginate_queryset(data, request) paginator.paginate_queryset(data, request)
return paginator.get_paginated_response(data) return paginator.get_paginated_response(data)
@ -678,6 +769,7 @@ class ClubMailingView(generics.ListAPIView):
"""Exposes list and details of club mailings (name, members and admins) in """Exposes list and details of club mailings (name, members and admins) in
order to build the corresponding mailing lists. order to build the corresponding mailing lists.
""" """
queryset = users.Club.objects.all() queryset = users.Club.objects.all()
serializer_class = serializers.MailingSerializer serializer_class = serializers.MailingSerializer
@ -696,12 +788,10 @@ class ObtainExpiringAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data) serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user'] user = serializer.validated_data["user"]
token, created = Token.objects.get_or_create(user=user) token, created = Token.objects.get_or_create(user=user)
token_duration = datetime.timedelta( token_duration = datetime.timedelta(seconds=settings.API_TOKEN_DURATION)
seconds=settings.API_TOKEN_DURATION
)
utc_now = datetime.datetime.now(datetime.timezone.utc) utc_now = datetime.datetime.now(datetime.timezone.utc)
if not created and token.created < utc_now - token_duration: if not created and token.created < utc_now - token_duration:
token.delete() token.delete()
@ -709,7 +799,6 @@ class ObtainExpiringAuthToken(ObtainAuthToken):
token.created = datetime.datetime.utcnow() token.created = datetime.datetime.utcnow()
token.save() token.save()
return Response({ return Response(
'token': token.key, {"token": token.key, "expiration": token.created + token_duration}
'expiration': token.created + token_duration )
})

View file

@ -5,6 +5,7 @@ texlive-fonts-recommended
python3-djangorestframework python3-djangorestframework
python3-django-reversion python3-django-reversion
python3-pip python3-pip
python3-pil
libsasl2-dev libldap2-dev libsasl2-dev libldap2-dev
libssl-dev libssl-dev
python3-crypto python3-crypto
@ -18,3 +19,4 @@ fonts-font-awesome
graphviz graphviz
git git
gettext gettext
python3-django-ldapdb

View file

@ -0,0 +1,23 @@
python-django
python-dateutil
texlive-latex-base
texlive-fonts-recommended
python-djangorestframework
python-django-reversion
python-pip
libsasl2-dev libldap2-dev
libssl-dev
python-crypto
python-git
javascript-common
libjs-jquery
libjs-jquery-ui
libjs-jquery-timepicker
libjs-bootstrap
fonts-font-awesome
graphviz
git
gettext
freeradius-common
freeradius-python2
python-mysqldb

View file

@ -4,7 +4,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify

View file

@ -4,7 +4,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -38,9 +38,12 @@ def can_view(user):
A couple (allowed, msg) where allowed is a boolean which is True if A couple (allowed, msg) where allowed is a boolean which is True if
viewing is granted and msg is a message (can be None). viewing is granted and msg is a message (can be None).
""" """
can = user.has_module_perms('cotisations') can = user.has_module_perms("cotisations")
if can: if can:
return can, None return can, None, ("cotisations",)
else: else:
return can, _("You don't have the right to view this application.") return (
can,
_("You don't have the right to view this application."),
("cotisations",),
)

View file

@ -4,7 +4,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -35,42 +35,50 @@ from .models import CustomInvoice, CostEstimate
class FactureAdmin(VersionAdmin): class FactureAdmin(VersionAdmin):
"""Class admin d'une facture, tous les champs""" """Class admin d'une facture, tous les champs"""
pass pass
class CostEstimateAdmin(VersionAdmin): class CostEstimateAdmin(VersionAdmin):
"""Admin class for cost estimates.""" """Admin class for cost estimates."""
pass pass
class CustomInvoiceAdmin(VersionAdmin): class CustomInvoiceAdmin(VersionAdmin):
"""Admin class for custom invoices.""" """Admin class for custom invoices."""
pass pass
class VenteAdmin(VersionAdmin): class VenteAdmin(VersionAdmin):
"""Class admin d'une vente, tous les champs (facture related)""" """Class admin d'une vente, tous les champs (facture related)"""
pass pass
class ArticleAdmin(VersionAdmin): class ArticleAdmin(VersionAdmin):
"""Class admin d'un article en vente""" """Class admin d'un article en vente"""
pass pass
class BanqueAdmin(VersionAdmin): class BanqueAdmin(VersionAdmin):
"""Class admin de la liste des banques (facture related)""" """Class admin de la liste des banques (facture related)"""
pass pass
class PaiementAdmin(VersionAdmin): class PaiementAdmin(VersionAdmin):
"""Class admin d'un moyen de paiement (facture related""" """Class admin d'un moyen de paiement (facture related"""
pass pass
class CotisationAdmin(VersionAdmin): class CotisationAdmin(VersionAdmin):
"""Class admin d'une cotisation (date de debut et de fin), """Class admin d'une cotisation (date de debut et de fin),
Vente related""" Vente related"""
pass pass

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# Copyright © 2018 Hugo Levy-Falk # Copyright © 2018 Hugo Levy-Falk
# #
@ -47,8 +47,13 @@ from django.shortcuts import get_object_or_404
from re2o.field_permissions import FieldPermissionFormMixin from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin from re2o.mixins import FormRevMixin
from .models import ( from .models import (
Article, Paiement, Facture, Banque, Article,
CustomInvoice, Vente, CostEstimate, Paiement,
Facture,
Banque,
CustomInvoice,
Vente,
CostEstimate,
) )
from .payment_methods import balance from .payment_methods import balance
@ -59,31 +64,27 @@ class FactureForm(FieldPermissionFormMixin, FormRevMixin, ModelForm):
""" """
def __init__(self, *args, creation=False, **kwargs): def __init__(self, *args, creation=False, **kwargs):
user = kwargs['user'] user = kwargs["user"]
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(FactureForm, self).__init__(*args, prefix=prefix, **kwargs) super(FactureForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['paiement'].empty_label = \ self.fields["paiement"].empty_label = _("Select a payment method")
_("Select a payment method") self.fields["paiement"].queryset = Paiement.find_allowed_payments(user)
self.fields['paiement'].queryset = Paiement.find_allowed_payments(user)
if not creation: if not creation:
self.fields['user'].label = _("Member") self.fields["user"].label = _("Member")
self.fields['user'].empty_label = \ self.fields["user"].empty_label = _("Select the proprietary member")
_("Select the proprietary member") self.fields["valid"].label = _("Validated invoice")
self.fields['valid'].label = _("Validated invoice")
else: else:
self.fields = {'paiement': self.fields['paiement']} self.fields = {"paiement": self.fields["paiement"]}
class Meta: class Meta:
model = Facture model = Facture
fields = '__all__' fields = "__all__"
def clean(self): def clean(self):
cleaned_data = super(FactureForm, self).clean() cleaned_data = super(FactureForm, self).clean()
paiement = cleaned_data.get('paiement') paiement = cleaned_data.get("paiement")
if not paiement: if not paiement:
raise forms.ValidationError( raise forms.ValidationError(_("A payment method must be specified."))
_("A payment method must be specified.")
)
return cleaned_data return cleaned_data
@ -92,32 +93,30 @@ class SelectArticleForm(FormRevMixin, Form):
Form used to select an article during the creation of an invoice for a Form used to select an article during the creation of an invoice for a
member. member.
""" """
article = forms.ModelChoiceField( article = forms.ModelChoiceField(
queryset=Article.objects.none(), queryset=Article.objects.none(), label=_("Article"), required=True
label=_("Article"),
required=True
) )
quantity = forms.IntegerField( quantity = forms.IntegerField(
label=_("Quantity"), label=_("Quantity"), validators=[MinValueValidator(1)], required=True
validators=[MinValueValidator(1)],
required=True
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
user = kwargs.pop('user') user = kwargs.pop("user")
target_user = kwargs.pop('target_user', None) target_user = kwargs.pop("target_user", None)
super(SelectArticleForm, self).__init__(*args, **kwargs) super(SelectArticleForm, self).__init__(*args, **kwargs)
self.fields['article'].queryset = Article.find_allowed_articles( self.fields["article"].queryset = Article.find_allowed_articles(
user, target_user) user, target_user
)
class DiscountForm(Form): class DiscountForm(Form):
""" """
Form used in oder to create a discount on an invoice. Form used in oder to create a discount on an invoice.
""" """
is_relative = forms.BooleanField( is_relative = forms.BooleanField(
label=_("Discount is on percentage."), label=_("Discount is in percentage."), required=False
required=False,
) )
discount = forms.DecimalField( discount = forms.DecimalField(
label=_("Discount"), label=_("Discount"),
@ -130,53 +129,51 @@ class DiscountForm(Form):
def apply_to_invoice(self, invoice): def apply_to_invoice(self, invoice):
invoice_price = invoice.prix_total() invoice_price = invoice.prix_total()
discount = self.cleaned_data['discount'] discount = self.cleaned_data["discount"]
is_relative = self.cleaned_data['is_relative'] is_relative = self.cleaned_data["is_relative"]
if is_relative: if is_relative:
amount = discount/100 * invoice_price amount = discount / 100 * invoice_price
else: else:
amount = discount amount = discount
if amount: if amount:
name = _("{}% discount") if is_relative else _("{}€ discount") name = _("{}% discount") if is_relative else _("{} € discount")
name = name.format(discount) name = name.format(discount)
Vente.objects.create( Vente.objects.create(facture=invoice, name=name, prix=-amount, number=1)
facture=invoice,
name=name,
prix=-amount,
number=1
)
class CustomInvoiceForm(FormRevMixin, ModelForm): class CustomInvoiceForm(FormRevMixin, ModelForm):
""" """
Form used to create a custom invoice. Form used to create a custom invoice.
""" """
class Meta: class Meta:
model = CustomInvoice model = CustomInvoice
fields = '__all__' fields = "__all__"
class CostEstimateForm(FormRevMixin, ModelForm): class CostEstimateForm(FormRevMixin, ModelForm):
""" """
Form used to create a cost estimate. Form used to create a cost estimate.
""" """
class Meta: class Meta:
model = CostEstimate model = CostEstimate
exclude = ['paid', 'final_invoice'] exclude = ["paid", "final_invoice"]
class ArticleForm(FormRevMixin, ModelForm): class ArticleForm(FormRevMixin, ModelForm):
""" """
Form used to create an article. Form used to create an article.
""" """
class Meta: class Meta:
model = Article model = Article
fields = '__all__' fields = "__all__"
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(ArticleForm, self).__init__(*args, prefix=prefix, **kwargs) super(ArticleForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['name'].label = _("Article name") self.fields["name"].label = _("Article name")
class DelArticleForm(FormRevMixin, Form): class DelArticleForm(FormRevMixin, Form):
@ -184,19 +181,20 @@ class DelArticleForm(FormRevMixin, Form):
Form used to delete one or more of the currently available articles. Form used to delete one or more of the currently available articles.
The user must choose the one to delete by checking the boxes. The user must choose the one to delete by checking the boxes.
""" """
articles = forms.ModelMultipleChoiceField( articles = forms.ModelMultipleChoiceField(
queryset=Article.objects.none(), queryset=Article.objects.none(),
label=_("Available articles"), label=_("Current articles"),
widget=forms.CheckboxSelectMultiple widget=forms.CheckboxSelectMultiple,
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
instances = kwargs.pop('instances', None) instances = kwargs.pop("instances", None)
super(DelArticleForm, self).__init__(*args, **kwargs) super(DelArticleForm, self).__init__(*args, **kwargs)
if instances: if instances:
self.fields['articles'].queryset = instances self.fields["articles"].queryset = instances
else: else:
self.fields['articles'].queryset = Article.objects.all() self.fields["articles"].queryset = Article.objects.all()
# TODO : change Paiement to Payment # TODO : change Paiement to Payment
@ -206,15 +204,16 @@ class PaiementForm(FormRevMixin, ModelForm):
The 'cheque' type is used to associate a specific behaviour requiring The 'cheque' type is used to associate a specific behaviour requiring
a cheque number and a bank. a cheque number and a bank.
""" """
class Meta: class Meta:
model = Paiement model = Paiement
# TODO : change moyen to method and type_paiement to payment_type # TODO : change moyen to method and type_paiement to payment_type
fields = ['moyen', 'available_for_everyone'] fields = ["moyen", "available_for_everyone"]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(PaiementForm, self).__init__(*args, prefix=prefix, **kwargs) super(PaiementForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['moyen'].label = _("Payment method name") self.fields["moyen"].label = _("Payment method name")
# TODO : change paiement to payment # TODO : change paiement to payment
@ -223,20 +222,21 @@ class DelPaiementForm(FormRevMixin, Form):
Form used to delete one or more payment methods. Form used to delete one or more payment methods.
The user must choose the one to delete by checking the boxes. The user must choose the one to delete by checking the boxes.
""" """
# TODO : change paiement to payment # TODO : change paiement to payment
paiements = forms.ModelMultipleChoiceField( paiements = forms.ModelMultipleChoiceField(
queryset=Paiement.objects.none(), queryset=Paiement.objects.none(),
label=_("Available payment methods"), label=_("Current payment methods"),
widget=forms.CheckboxSelectMultiple widget=forms.CheckboxSelectMultiple,
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
instances = kwargs.pop('instances', None) instances = kwargs.pop("instances", None)
super(DelPaiementForm, self).__init__(*args, **kwargs) super(DelPaiementForm, self).__init__(*args, **kwargs)
if instances: if instances:
self.fields['paiements'].queryset = instances self.fields["paiements"].queryset = instances
else: else:
self.fields['paiements'].queryset = Paiement.objects.all() self.fields["paiements"].queryset = Paiement.objects.all()
# TODO : change banque to bank # TODO : change banque to bank
@ -244,15 +244,16 @@ class BanqueForm(FormRevMixin, ModelForm):
""" """
Form used to create a bank. Form used to create a bank.
""" """
class Meta: class Meta:
# TODO : change banque to bank # TODO : change banque to bank
model = Banque model = Banque
fields = ['name'] fields = ["name"]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(BanqueForm, self).__init__(*args, prefix=prefix, **kwargs) super(BanqueForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['name'].label = _("Bank name") self.fields["name"].label = _("Bank name")
# TODO : change banque to bank # TODO : change banque to bank
@ -261,20 +262,21 @@ class DelBanqueForm(FormRevMixin, Form):
Form used to delete one or more banks. Form used to delete one or more banks.
The use must choose the one to delete by checking the boxes. The use must choose the one to delete by checking the boxes.
""" """
# TODO : change banque to bank # TODO : change banque to bank
banques = forms.ModelMultipleChoiceField( banques = forms.ModelMultipleChoiceField(
queryset=Banque.objects.none(), queryset=Banque.objects.none(),
label=_("Available banks"), label=_("Current banks"),
widget=forms.CheckboxSelectMultiple widget=forms.CheckboxSelectMultiple,
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
instances = kwargs.pop('instances', None) instances = kwargs.pop("instances", None)
super(DelBanqueForm, self).__init__(*args, **kwargs) super(DelBanqueForm, self).__init__(*args, **kwargs)
if instances: if instances:
self.fields['banques'].queryset = instances self.fields["banques"].queryset = instances
else: else:
self.fields['banques'].queryset = Banque.objects.all() self.fields["banques"].queryset = Banque.objects.all()
# TODO : Better name and docstring # TODO : Better name and docstring
@ -282,37 +284,36 @@ class RechargeForm(FormRevMixin, Form):
""" """
Form used to refill a user's balance Form used to refill a user's balance
""" """
value = forms.DecimalField(
label=_("Amount"), value = forms.DecimalField(label=_("Amount"), decimal_places=2)
min_value=0.01,
validators=[]
)
payment = forms.ModelChoiceField( payment = forms.ModelChoiceField(
queryset=Paiement.objects.none(), queryset=Paiement.objects.none(), label=_("Payment method")
label=_("Payment method")
) )
def __init__(self, *args, user=None, user_source=None, **kwargs): def __init__(self, *args, user=None, user_source=None, **kwargs):
self.user = user self.user = user
super(RechargeForm, self).__init__(*args, **kwargs) super(RechargeForm, self).__init__(*args, **kwargs)
self.fields['payment'].empty_label = \ self.fields["payment"].empty_label = _("Select a payment method")
_("Select a payment method") self.fields["payment"].queryset = Paiement.find_allowed_payments(
self.fields['payment'].queryset = Paiement.find_allowed_payments( user_source
user_source).exclude(is_balance=True) ).exclude(is_balance=True)
def clean(self): def clean(self):
""" """
Returns a cleaned value from the received form by validating Returns a cleaned value from the received form by validating
the value is well inside the possible limits the value is well inside the possible limits
""" """
value = self.cleaned_data['value'] value = self.cleaned_data["value"]
balance_method = get_object_or_404(balance.PaymentMethod) balance_method = get_object_or_404(balance.PaymentMethod)
if balance_method.maximum_balance is not None and \ if (
value + self.user.solde > balance_method.maximum_balance: balance_method.maximum_balance is not None
and value + self.user.solde > balance_method.maximum_balance
):
raise forms.ValidationError( raise forms.ValidationError(
_("Requested amount is too high. Your balance can't exceed" _(
" %(max_online_balance)s €.") % { "Requested amount is too high. Your balance can't exceed"
'max_online_balance': balance_method.maximum_balance " %(max_online_balance)s €."
} )
% {"max_online_balance": balance_method.maximum_balance}
) )
return self.cleaned_data return self.cleaned_data

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -29,54 +29,100 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("users", "0005_auto_20160702_0006")]
('users', '0005_auto_20160702_0006'),
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Article', name="Article",
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), (
('name', models.CharField(max_length=255)), "id",
('prix', models.DecimalField(decimal_places=2, max_digits=5)), models.AutoField(
verbose_name="ID",
auto_created=True,
primary_key=True,
serialize=False,
),
),
("name", models.CharField(max_length=255)),
("prix", models.DecimalField(decimal_places=2, max_digits=5)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Banque', name="Banque",
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), (
('name', models.CharField(max_length=255)), "id",
models.AutoField(
verbose_name="ID",
auto_created=True,
primary_key=True,
serialize=False,
),
),
("name", models.CharField(max_length=255)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Facture', name="Facture",
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), (
('cheque', models.CharField(max_length=255)), "id",
('number', models.IntegerField()), models.AutoField(
('date', models.DateTimeField(auto_now_add=True)), verbose_name="ID",
('name', models.CharField(max_length=255)), auto_created=True,
('prix', models.DecimalField(decimal_places=2, max_digits=5)), primary_key=True,
('article', models.ForeignKey(to='cotisations.Article', on_delete=django.db.models.deletion.PROTECT)), serialize=False,
('banque', models.ForeignKey(to='cotisations.Banque', on_delete=django.db.models.deletion.PROTECT)), ),
),
("cheque", models.CharField(max_length=255)),
("number", models.IntegerField()),
("date", models.DateTimeField(auto_now_add=True)),
("name", models.CharField(max_length=255)),
("prix", models.DecimalField(decimal_places=2, max_digits=5)),
(
"article",
models.ForeignKey(
to="cotisations.Article",
on_delete=django.db.models.deletion.PROTECT,
),
),
(
"banque",
models.ForeignKey(
to="cotisations.Banque",
on_delete=django.db.models.deletion.PROTECT,
),
),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Paiement', name="Paiement",
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, primary_key=True, serialize=False)), (
('moyen', models.CharField(max_length=255)), "id",
models.AutoField(
verbose_name="ID",
auto_created=True,
primary_key=True,
serialize=False,
),
),
("moyen", models.CharField(max_length=255)),
], ],
), ),
migrations.AddField( migrations.AddField(
model_name='facture', model_name="facture",
name='paiement', name="paiement",
field=models.ForeignKey(to='cotisations.Paiement', on_delete=django.db.models.deletion.PROTECT), field=models.ForeignKey(
to="cotisations.Paiement", on_delete=django.db.models.deletion.PROTECT
),
), ),
migrations.AddField( migrations.AddField(
model_name='facture', model_name="facture",
name='user', name="user",
field=models.ForeignKey(to='users.User', on_delete=django.db.models.deletion.PROTECT), field=models.ForeignKey(
to="users.User", on_delete=django.db.models.deletion.PROTECT
),
), ),
] ]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -28,13 +28,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0001_initial")]
('cotisations', '0001_initial'),
]
operations = [ operations = [migrations.RemoveField(model_name="facture", name="article")]
migrations.RemoveField(
model_name='facture',
name='article',
),
]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -29,14 +29,17 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0002_remove_facture_article")]
('cotisations', '0002_remove_facture_article'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='banque', name="banque",
field=models.ForeignKey(blank=True, to='cotisations.Banque', on_delete=django.db.models.deletion.PROTECT, null=True), field=models.ForeignKey(
), blank=True,
to="cotisations.Banque",
on_delete=django.db.models.deletion.PROTECT,
null=True,
),
)
] ]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -28,19 +28,17 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0003_auto_20160702_1448")]
('cotisations', '0003_auto_20160702_1448'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='name', name="name",
field=models.CharField(null=True, max_length=255), field=models.CharField(null=True, max_length=255),
), ),
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='prix', name="prix",
field=models.DecimalField(max_digits=5, null=True, decimal_places=2), field=models.DecimalField(max_digits=5, null=True, decimal_places=2),
), ),
] ]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -28,14 +28,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0004_auto_20160702_1528")]
('cotisations', '0004_auto_20160702_1528'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='cheque', name="cheque",
field=models.CharField(max_length=255, blank=True), field=models.CharField(max_length=255, blank=True),
), )
] ]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -28,19 +28,19 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0005_auto_20160702_1532")]
('cotisations', '0005_auto_20160702_1532'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='name', name="name",
field=models.CharField(null=True, default='plop', max_length=255), field=models.CharField(null=True, default="plop", max_length=255),
), ),
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='prix', name="prix",
field=models.DecimalField(null=True, decimal_places=2, default=1, max_digits=5), field=models.DecimalField(
null=True, decimal_places=2, default=1, max_digits=5
),
), ),
] ]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -28,20 +28,18 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0006_auto_20160702_1534")]
('cotisations', '0006_auto_20160702_1534'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='name', name="name",
field=models.CharField(default='plop', max_length=255), field=models.CharField(default="plop", max_length=255),
preserve_default=False, preserve_default=False,
), ),
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='prix', name="prix",
field=models.DecimalField(default=1, max_digits=5, decimal_places=2), field=models.DecimalField(default=1, max_digits=5, decimal_places=2),
preserve_default=False, preserve_default=False,
), ),

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -30,43 +30,53 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('users', '0005_auto_20160702_0006'), ("users", "0005_auto_20160702_0006"),
('cotisations', '0007_auto_20160702_1543'), ("cotisations", "0007_auto_20160702_1543"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Cotisation', name="Cotisation",
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), (
('date_start', models.DateTimeField(auto_now_add=True)), "id",
('date_end', models.DateTimeField()), models.AutoField(
verbose_name="ID",
primary_key=True,
serialize=False,
auto_created=True,
),
),
("date_start", models.DateTimeField(auto_now_add=True)),
("date_end", models.DateTimeField()),
], ],
), ),
migrations.AddField( migrations.AddField(
model_name='article', model_name="article",
name='cotisation', name="cotisation",
field=models.BooleanField(default=True), field=models.BooleanField(default=True),
preserve_default=False, preserve_default=False,
), ),
migrations.AddField( migrations.AddField(
model_name='article', model_name="article",
name='duration', name="duration",
field=models.DurationField(blank=True, null=True), field=models.DurationField(blank=True, null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='facture', model_name="facture", name="valid", field=models.BooleanField(default=True)
name='valid',
field=models.BooleanField(default=True),
), ),
migrations.AddField( migrations.AddField(
model_name='cotisation', model_name="cotisation",
name='facture', name="facture",
field=models.ForeignKey(to='cotisations.Facture', on_delete=django.db.models.deletion.PROTECT), field=models.ForeignKey(
to="cotisations.Facture", on_delete=django.db.models.deletion.PROTECT
),
), ),
migrations.AddField( migrations.AddField(
model_name='cotisation', model_name="cotisation",
name='user', name="user",
field=models.ForeignKey(to='users.User', on_delete=django.db.models.deletion.PROTECT), field=models.ForeignKey(
to="users.User", on_delete=django.db.models.deletion.PROTECT
),
), ),
] ]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -28,13 +28,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0008_auto_20160702_1614")]
('cotisations', '0008_auto_20160702_1614'),
]
operations = [ operations = [migrations.RemoveField(model_name="cotisation", name="user")]
migrations.RemoveField(
model_name='cotisation',
name='user',
),
]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -28,18 +28,15 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0009_remove_cotisation_user")]
('cotisations', '0009_remove_cotisation_user'),
]
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(model_name="article", name="duration"),
model_name='article',
name='duration',
),
migrations.AddField( migrations.AddField(
model_name='article', model_name="article",
name='duration', name="duration",
field=models.IntegerField(null=True, help_text='Durée exprimée en mois entiers', blank=True), field=models.IntegerField(
null=True, help_text="Durée exprimée en mois entiers", blank=True
),
), ),
] ]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -28,14 +28,10 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0010_auto_20160702_1840")]
('cotisations', '0010_auto_20160702_1840'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='cotisation', model_name="cotisation", name="date_start", field=models.DateTimeField()
name='date_start', )
field=models.DateTimeField(),
),
] ]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -29,14 +29,14 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0011_auto_20160702_1911")]
('cotisations', '0011_auto_20160702_1911'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='cotisation', model_name="cotisation",
name='facture', name="facture",
field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='cotisations.Facture'), field=models.OneToOneField(
), on_delete=django.db.models.deletion.PROTECT, to="cotisations.Facture"
),
)
] ]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -29,32 +29,41 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0012_auto_20160704_0118")]
('cotisations', '0012_auto_20160704_0118'),
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Vente', name="Vente",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
('name', models.CharField(max_length=255)), "id",
('prix', models.DecimalField(decimal_places=2, max_digits=5)), models.AutoField(
('cotisation', models.BooleanField()), auto_created=True,
('duration', models.IntegerField(null=True, blank=True, help_text='Durée exprimée en mois entiers')), primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=255)),
("prix", models.DecimalField(decimal_places=2, max_digits=5)),
("cotisation", models.BooleanField()),
(
"duration",
models.IntegerField(
null=True,
blank=True,
help_text="Durée exprimée en mois entiers",
),
),
], ],
), ),
migrations.RemoveField( migrations.RemoveField(model_name="facture", name="name"),
model_name='facture', migrations.RemoveField(model_name="facture", name="prix"),
name='name',
),
migrations.RemoveField(
model_name='facture',
name='prix',
),
migrations.AddField( migrations.AddField(
model_name='vente', model_name="vente",
name='facture', name="facture",
field=models.ForeignKey(to='cotisations.Facture', on_delete=django.db.models.deletion.PROTECT), field=models.ForeignKey(
to="cotisations.Facture", on_delete=django.db.models.deletion.PROTECT
),
), ),
] ]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -28,18 +28,13 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0013_auto_20160711_2240")]
('cotisations', '0013_auto_20160711_2240'),
]
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(model_name="facture", name="number"),
model_name='facture',
name='number',
),
migrations.AddField( migrations.AddField(
model_name='vente', model_name="vente",
name='number', name="number",
field=models.IntegerField(default=1), field=models.IntegerField(default=1),
preserve_default=False, preserve_default=False,
), ),

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -29,29 +29,29 @@ import django.core.validators
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0014_auto_20160712_0245")]
('cotisations', '0014_auto_20160712_0245'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='facture', model_name="facture",
name='control', name="control",
field=models.BooleanField(default=False), field=models.BooleanField(default=False),
), ),
migrations.AlterField( migrations.AlterField(
model_name='cotisation', model_name="cotisation",
name='facture', name="facture",
field=models.OneToOneField(to='cotisations.Facture'), field=models.OneToOneField(to="cotisations.Facture"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='facture', name="facture",
field=models.ForeignKey(to='cotisations.Facture'), field=models.ForeignKey(to="cotisations.Facture"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='number', name="number",
field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)]), field=models.IntegerField(
validators=[django.core.validators.MinValueValidator(1)]
),
), ),
] ]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -28,29 +28,20 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0015_auto_20160714_2142")]
('cotisations', '0015_auto_20160714_2142'),
]
operations = [ operations = [
migrations.RenameField( migrations.RenameField(
model_name='article', model_name="article", old_name="cotisation", new_name="iscotisation"
old_name='cotisation',
new_name='iscotisation',
), ),
migrations.RenameField( migrations.RenameField(
model_name='vente', model_name="vente", old_name="cotisation", new_name="iscotisation"
old_name='cotisation',
new_name='iscotisation',
),
migrations.RemoveField(
model_name='cotisation',
name='facture',
), ),
migrations.RemoveField(model_name="cotisation", name="facture"),
migrations.AddField( migrations.AddField(
model_name='cotisation', model_name="cotisation",
name='vente', name="vente",
field=models.OneToOneField(to='cotisations.Vente', null=True), field=models.OneToOneField(to="cotisations.Vente", null=True),
preserve_default=False, preserve_default=False,
), ),
] ]

View file

@ -8,19 +8,22 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0016_auto_20160715_0110")]
('cotisations', '0016_auto_20160715_0110'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='duration', name="duration",
field=models.IntegerField(blank=True, help_text='Durée exprimée en mois entiers', null=True, validators=[django.core.validators.MinValueValidator(0)]), field=models.IntegerField(
blank=True,
help_text="Durée exprimée en mois entiers",
null=True,
validators=[django.core.validators.MinValueValidator(0)],
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='name', name="name",
field=models.CharField(max_length=255, unique=True), field=models.CharField(max_length=255, unique=True),
), ),
] ]

View file

@ -7,15 +7,17 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0017_auto_20170718_2329")]
('cotisations', '0017_auto_20170718_2329'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='paiement', model_name="paiement",
name='type_paiement', name="type_paiement",
field=models.CharField(choices=[('check', 'Chèque'), (None, 'Autre')], default=None, max_length=255), field=models.CharField(
choices=[("check", "Chèque"), (None, "Autre")],
default=None,
max_length=255,
),
preserve_default=False, preserve_default=False,
), )
] ]

View file

@ -7,14 +7,14 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0018_paiement_type_paiement")]
('cotisations', '0018_paiement_type_paiement'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='paiement', model_name="paiement",
name='type_paiement', name="type_paiement",
field=models.CharField(choices=[(0, 'Autre'), (1, 'Chèque')], default=0, max_length=255), field=models.CharField(
), choices=[(0, "Autre"), (1, "Chèque")], default=0, max_length=255
),
)
] ]

View file

@ -7,14 +7,14 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0019_auto_20170819_0055")]
('cotisations', '0019_auto_20170819_0055'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='paiement', model_name="paiement",
name='type_paiement', name="type_paiement",
field=models.IntegerField(choices=[(0, 'Autre'), (1, 'Chèque')], default=0, max_length=255), field=models.IntegerField(
), choices=[(0, "Autre"), (1, "Chèque")], default=0, max_length=255
),
)
] ]

View file

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0020_auto_20170819_0057")]
('cotisations', '0020_auto_20170819_0057'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='paiement', model_name="paiement",
name='type_paiement', name="type_paiement",
field=models.IntegerField(choices=[(0, 'Autre'), (1, 'Chèque')], default=0), field=models.IntegerField(choices=[(0, "Autre"), (1, "Chèque")], default=0),
), )
] ]

View file

@ -7,14 +7,14 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0021_auto_20170819_0104")]
('cotisations', '0021_auto_20170819_0104'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='paiement', model_name="paiement",
name='type_paiement', name="type_paiement",
field=models.IntegerField(choices=[(0, 'Autre'), (1, 'Chèque')], default=0, max_length=255), field=models.IntegerField(
), choices=[(0, "Autre"), (1, "Chèque")], default=0, max_length=255
),
)
] ]

View file

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0022_auto_20170824_0128")]
('cotisations', '0022_auto_20170824_0128'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='paiement', model_name="paiement",
name='type_paiement', name="type_paiement",
field=models.IntegerField(choices=[(0, 'Autre'), (1, 'Chèque')], default=0), field=models.IntegerField(choices=[(0, "Autre"), (1, "Chèque")], default=0),
), )
] ]

View file

@ -8,19 +8,24 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0023_auto_20170902_1303")]
('cotisations', '0023_auto_20170902_1303'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='duration', name="duration",
field=models.PositiveIntegerField(blank=True, help_text='Durée exprimée en mois entiers', null=True, validators=[django.core.validators.MinValueValidator(0)]), field=models.PositiveIntegerField(
blank=True,
help_text="Durée exprimée en mois entiers",
null=True,
validators=[django.core.validators.MinValueValidator(0)],
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='duration', name="duration",
field=models.PositiveIntegerField(blank=True, help_text='Durée exprimée en mois entiers', null=True), field=models.PositiveIntegerField(
blank=True, help_text="Durée exprimée en mois entiers", null=True
),
), ),
] ]

View file

@ -7,14 +7,16 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0024_auto_20171015_2033")]
('cotisations', '0024_auto_20171015_2033'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='article', model_name="article",
name='type_user', name="type_user",
field=models.CharField(choices=[('Adherent', 'Adherent'), ('Club', 'Club'), ('All', 'All')], default='All', max_length=255), field=models.CharField(
), choices=[("Adherent", "Adherent"), ("Club", "Club"), ("All", "All")],
default="All",
max_length=255,
),
)
] ]

View file

@ -6,73 +6,94 @@ from django.db import migrations, models
def create_type(apps, schema_editor): def create_type(apps, schema_editor):
Cotisation = apps.get_model('cotisations', 'Cotisation') Cotisation = apps.get_model("cotisations", "Cotisation")
Vente = apps.get_model('cotisations', 'Vente') Vente = apps.get_model("cotisations", "Vente")
Article = apps.get_model('cotisations', 'Article') Article = apps.get_model("cotisations", "Article")
db_alias = schema_editor.connection.alias db_alias = schema_editor.connection.alias
articles = Article.objects.using(db_alias).all() articles = Article.objects.using(db_alias).all()
ventes = Vente.objects.using(db_alias).all() ventes = Vente.objects.using(db_alias).all()
cotisations = Cotisation.objects.using(db_alias).all() cotisations = Cotisation.objects.using(db_alias).all()
for article in articles: for article in articles:
if article.iscotisation: if article.iscotisation:
article.type_cotisation='All' article.type_cotisation = "All"
article.save(using=db_alias) article.save(using=db_alias)
for vente in ventes: for vente in ventes:
if vente.iscotisation: if vente.iscotisation:
vente.type_cotisation='All' vente.type_cotisation = "All"
vente.save(using=db_alias) vente.save(using=db_alias)
for cotisation in cotisations: for cotisation in cotisations:
cotisation.type_cotisation='All' cotisation.type_cotisation = "All"
cotisation.save(using=db_alias) cotisation.save(using=db_alias)
def delete_type(apps, schema_editor): def delete_type(apps, schema_editor):
Vente = apps.get_model('cotisations', 'Vente') Vente = apps.get_model("cotisations", "Vente")
Article = apps.get_model('cotisations', 'Article') Article = apps.get_model("cotisations", "Article")
db_alias = schema_editor.connection.alias db_alias = schema_editor.connection.alias
articles = Articles.objects.using(db_alias).all() articles = Article.objects.using(db_alias).all()
ventes = Vente.objects.using(db_alias).all() ventes = Vente.objects.using(db_alias).all()
for article in articles: for article in articles:
if article.type_cotisation: if article.type_cotisation:
article.iscotisation=True article.iscotisation = True
else: else:
article.iscotisation=False article.iscotisation = False
article.save(using=db_alias) article.save(using=db_alias)
for vente in ventes: for vente in ventes:
if vente.iscotisation: if vente.iscotisation:
vente.iscotisation=True vente.iscotisation = True
else: else:
vente.iscotisation=False vente.iscotisation = False
vente.save(using=db_alias) vente.save(using=db_alias)
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0025_article_type_user")]
('cotisations', '0025_article_type_user'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='article', model_name="article",
name='type_cotisation', name="type_cotisation",
field=models.CharField(blank=True, choices=[('Connexion', 'Connexion'), ('Adhesion', 'Adhesion'), ('All', 'All')], default=None, max_length=255, null=True), field=models.CharField(
blank=True,
choices=[
("Connexion", "Connexion"),
("Adhesion", "Adhesion"),
("All", "All"),
],
default=None,
max_length=255,
null=True,
),
), ),
migrations.AddField( migrations.AddField(
model_name='cotisation', model_name="cotisation",
name='type_cotisation', name="type_cotisation",
field=models.CharField(choices=[('Connexion', 'Connexion'), ('Adhesion', 'Adhesion'), ('All', 'All')], max_length=255, default='All'), field=models.CharField(
choices=[
("Connexion", "Connexion"),
("Adhesion", "Adhesion"),
("All", "All"),
],
max_length=255,
default="All",
),
), ),
migrations.AddField( migrations.AddField(
model_name='vente', model_name="vente",
name='type_cotisation', name="type_cotisation",
field=models.CharField(blank=True, choices=[('Connexion', 'Connexion'), ('Adhesion', 'Adhesion'), ('All', 'All')], max_length=255, null=True), field=models.CharField(
blank=True,
choices=[
("Connexion", "Connexion"),
("Adhesion", "Adhesion"),
("All", "All"),
],
max_length=255,
null=True,
),
), ),
migrations.RunPython(create_type, delete_type), migrations.RunPython(create_type, delete_type),
migrations.RemoveField( migrations.RemoveField(model_name="article", name="iscotisation"),
model_name='article', migrations.RemoveField(model_name="vente", name="iscotisation"),
name='iscotisation',
),
migrations.RemoveField(
model_name='vente',
name='iscotisation',
),
] ]

View file

@ -7,14 +7,10 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0026_auto_20171028_0126")]
('cotisations', '0026_auto_20171028_0126'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article", name="name", field=models.CharField(max_length=255)
name='name', )
field=models.CharField(max_length=255),
),
] ]

View file

@ -7,33 +7,56 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0027_auto_20171029_1156")]
('cotisations', '0027_auto_20171029_1156'),
]
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='article', name="article",
options={'permissions': (('view_article', 'Peut voir un objet article'),)}, options={"permissions": (("view_article", "Peut voir un objet article"),)},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='banque', name="banque",
options={'permissions': (('view_banque', 'Peut voir un objet banque'),)}, options={"permissions": (("view_banque", "Peut voir un objet banque"),)},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='cotisation', name="cotisation",
options={'permissions': (('view_cotisation', 'Peut voir un objet cotisation'), ('change_all_cotisation', 'Superdroit, peut modifier toutes les cotisations'))}, options={
"permissions": (
("view_cotisation", "Peut voir un objet cotisation"),
(
"change_all_cotisation",
"Superdroit, peut modifier toutes les cotisations",
),
)
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='facture', name="facture",
options={'permissions': (('change_facture_control', "Peut changer l'etat de controle"), ('change_facture_pdf', 'Peut éditer une facture pdf'), ('view_facture', 'Peut voir un objet facture'), ('change_all_facture', 'Superdroit, peut modifier toutes les factures'))}, options={
"permissions": (
("change_facture_control", "Peut changer l'etat de controle"),
("change_facture_pdf", "Peut éditer une facture pdf"),
("view_facture", "Peut voir un objet facture"),
(
"change_all_facture",
"Superdroit, peut modifier toutes les factures",
),
)
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='paiement', name="paiement",
options={'permissions': (('view_paiement', 'Peut voir un objet paiement'),)}, options={
"permissions": (("view_paiement", "Peut voir un objet paiement"),)
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='vente', name="vente",
options={'permissions': (('view_vente', 'Peut voir un objet vente'), ('change_all_vente', 'Superdroit, peut modifier toutes les ventes'))}, options={
"permissions": (
("view_vente", "Peut voir un objet vente"),
("change_all_vente", "Superdroit, peut modifier toutes les ventes"),
)
},
), ),
] ]

View file

@ -9,143 +9,242 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0028_auto_20171231_0007")]
('cotisations', '0028_auto_20171231_0007'),
]
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='article', name="article",
options={'permissions': (('view_article', "Can see an article's details"),), 'verbose_name': 'Article', 'verbose_name_plural': 'Articles'}, options={
"permissions": (("view_article", "Can see an article's details"),),
"verbose_name": "Article",
"verbose_name_plural": "Articles",
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='banque', name="banque",
options={'permissions': (('view_banque', "Can see a bank's details"),), 'verbose_name': 'Bank', 'verbose_name_plural': 'Banks'}, options={
"permissions": (("view_banque", "Can see a bank's details"),),
"verbose_name": "Bank",
"verbose_name_plural": "Banks",
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='cotisation', name="cotisation",
options={'permissions': (('view_cotisation', "Can see a cotisation's details"), ('change_all_cotisation', 'Can edit the previous cotisations'))}, options={
"permissions": (
("view_cotisation", "Can see a cotisation's details"),
("change_all_cotisation", "Can edit the previous cotisations"),
)
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='facture', name="facture",
options={'permissions': (('change_facture_control', 'Can change the "controlled" state'), ('change_facture_pdf', 'Can create a custom PDF invoice'), ('view_facture', "Can see an invoice's details"), ('change_all_facture', 'Can edit all the previous invoices')), 'verbose_name': 'Invoice', 'verbose_name_plural': 'Invoices'}, options={
"permissions": (
("change_facture_control", 'Can change the "controlled" state'),
("change_facture_pdf", "Can create a custom PDF invoice"),
("view_facture", "Can see an invoice's details"),
("change_all_facture", "Can edit all the previous invoices"),
),
"verbose_name": "Invoice",
"verbose_name_plural": "Invoices",
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='paiement', name="paiement",
options={'permissions': (('view_paiement', "Can see a payement's details"),), 'verbose_name': 'Payment method', 'verbose_name_plural': 'Payment methods'}, options={
"permissions": (("view_paiement", "Can see a payement's details"),),
"verbose_name": "Payment method",
"verbose_name_plural": "Payment methods",
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='vente', name="vente",
options={'permissions': (('view_vente', "Can see a purchase's details"), ('change_all_vente', 'Can edit all the previous purchases')), 'verbose_name': 'Purchase', 'verbose_name_plural': 'Purchases'}, options={
"permissions": (
("view_vente", "Can see a purchase's details"),
("change_all_vente", "Can edit all the previous purchases"),
),
"verbose_name": "Purchase",
"verbose_name_plural": "Purchases",
},
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='duration', name="duration",
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Duration (in whole month)'), field=models.PositiveIntegerField(
blank=True,
null=True,
validators=[django.core.validators.MinValueValidator(0)],
verbose_name="Duration (in whole month)",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='name', name="name",
field=models.CharField(max_length=255, verbose_name='Designation'), field=models.CharField(max_length=255, verbose_name="Designation"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='prix', name="prix",
field=models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Unitary price'), field=models.DecimalField(
decimal_places=2, max_digits=5, verbose_name="Unitary price"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='type_cotisation', name="type_cotisation",
field=models.CharField(blank=True, choices=[('Connexion', 'Connexion'), ('Adhesion', 'Membership'), ('All', 'Both of them')], default=None, max_length=255, null=True, verbose_name='Type of cotisation'), field=models.CharField(
blank=True,
choices=[
("Connexion", "Connexion"),
("Adhesion", "Membership"),
("All", "Both of them"),
],
default=None,
max_length=255,
null=True,
verbose_name="Type of cotisation",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='type_user', name="type_user",
field=models.CharField(choices=[('Adherent', 'Member'), ('Club', 'Club'), ('All', 'Both of them')], default='All', max_length=255, verbose_name='Type of users concerned'), field=models.CharField(
choices=[
("Adherent", "Member"),
("Club", "Club"),
("All", "Both of them"),
],
default="All",
max_length=255,
verbose_name="Type of users concerned",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='banque', model_name="banque",
name='name', name="name",
field=models.CharField(max_length=255, verbose_name='Name'), field=models.CharField(max_length=255, verbose_name="Name"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='cotisation', model_name="cotisation",
name='date_end', name="date_end",
field=models.DateTimeField(verbose_name='Ending date'), field=models.DateTimeField(verbose_name="Ending date"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='cotisation', model_name="cotisation",
name='date_start', name="date_start",
field=models.DateTimeField(verbose_name='Starting date'), field=models.DateTimeField(verbose_name="Starting date"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='cotisation', model_name="cotisation",
name='type_cotisation', name="type_cotisation",
field=models.CharField(choices=[('Connexion', 'Connexion'), ('Adhesion', 'Membership'), ('All', 'Both of them')], default='All', max_length=255, verbose_name='Type of cotisation'), field=models.CharField(
choices=[
("Connexion", "Connexion"),
("Adhesion", "Membership"),
("All", "Both of them"),
],
default="All",
max_length=255,
verbose_name="Type of cotisation",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='cotisation', model_name="cotisation",
name='vente', name="vente",
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='cotisations.Vente', verbose_name='Purchase'), field=models.OneToOneField(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="cotisations.Vente",
verbose_name="Purchase",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='cheque', name="cheque",
field=models.CharField(blank=True, max_length=255, verbose_name='Cheque number'), field=models.CharField(
blank=True, max_length=255, verbose_name="Cheque number"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='control', name="control",
field=models.BooleanField(default=False, verbose_name='Controlled'), field=models.BooleanField(default=False, verbose_name="Controlled"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='date', name="date",
field=models.DateTimeField(auto_now_add=True, verbose_name='Date'), field=models.DateTimeField(auto_now_add=True, verbose_name="Date"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='valid', name="valid",
field=models.BooleanField(default=True, verbose_name='Validated'), field=models.BooleanField(default=True, verbose_name="Validated"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='paiement', model_name="paiement",
name='moyen', name="moyen",
field=models.CharField(max_length=255, verbose_name='Method'), field=models.CharField(max_length=255, verbose_name="Method"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='paiement', model_name="paiement",
name='type_paiement', name="type_paiement",
field=models.IntegerField(choices=[(0, 'Standard'), (1, 'Cheque')], default=0, verbose_name='Payment type'), field=models.IntegerField(
choices=[(0, "Standard"), (1, "Cheque")],
default=0,
verbose_name="Payment type",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='duration', name="duration",
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Duration (in whole month)'), field=models.PositiveIntegerField(
blank=True, null=True, verbose_name="Duration (in whole month)"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='facture', name="facture",
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cotisations.Facture', verbose_name='Invoice'), field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="cotisations.Facture",
verbose_name="Invoice",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='name', name="name",
field=models.CharField(max_length=255, verbose_name='Article'), field=models.CharField(max_length=255, verbose_name="Article"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='number', name="number",
field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)], verbose_name='Amount'), field=models.IntegerField(
validators=[django.core.validators.MinValueValidator(1)],
verbose_name="Amount",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='prix', name="prix",
field=models.DecimalField(decimal_places=2, max_digits=5, verbose_name='Price'), field=models.DecimalField(
decimal_places=2, max_digits=5, verbose_name="Price"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='type_cotisation', name="type_cotisation",
field=models.CharField(blank=True, choices=[('Connexion', 'Connexion'), ('Adhesion', 'Membership'), ('All', 'Both of them')], max_length=255, null=True, verbose_name='Type of cotisation'), field=models.CharField(
blank=True,
choices=[
("Connexion", "Connexion"),
("Adhesion", "Membership"),
("All", "Both of them"),
],
max_length=255,
null=True,
verbose_name="Type of cotisation",
),
), ),
] ]

View file

@ -9,8 +9,8 @@ import django.db.models.deletion
def add_cheque(apps, schema_editor): def add_cheque(apps, schema_editor):
ChequePayment = apps.get_model('cotisations', 'ChequePayment') ChequePayment = apps.get_model("cotisations", "ChequePayment")
Payment = apps.get_model('cotisations', 'Paiement') Payment = apps.get_model("cotisations", "Paiement")
for p in Payment.objects.filter(type_paiement=1): for p in Payment.objects.filter(type_paiement=1):
cheque = ChequePayment() cheque = ChequePayment()
cheque.payment = p cheque.payment = p
@ -18,14 +18,12 @@ def add_cheque(apps, schema_editor):
def add_comnpay(apps, schema_editor): def add_comnpay(apps, schema_editor):
ComnpayPayment = apps.get_model('cotisations', 'ComnpayPayment') ComnpayPayment = apps.get_model("cotisations", "ComnpayPayment")
Payment = apps.get_model('cotisations', 'Paiement') Payment = apps.get_model("cotisations", "Paiement")
AssoOption = apps.get_model('preferences', 'AssoOption') AssoOption = apps.get_model("preferences", "AssoOption")
options, _created = AssoOption.objects.get_or_create() options, _created = AssoOption.objects.get_or_create()
try: try:
payment = Payment.objects.get( payment = Payment.objects.get(moyen="Rechargement en ligne")
moyen='Rechargement en ligne'
)
except Payment.DoesNotExist: except Payment.DoesNotExist:
return return
comnpay = ComnpayPayment() comnpay = ComnpayPayment()
@ -38,11 +36,11 @@ def add_comnpay(apps, schema_editor):
def add_solde(apps, schema_editor): def add_solde(apps, schema_editor):
OptionalUser = apps.get_model('preferences', 'OptionalUser') OptionalUser = apps.get_model("preferences", "OptionalUser")
options, _created = OptionalUser.objects.get_or_create() options, _created = OptionalUser.objects.get_or_create()
Payment = apps.get_model('cotisations', 'Paiement') Payment = apps.get_model("cotisations", "Paiement")
BalancePayment = apps.get_model('cotisations', 'BalancePayment') BalancePayment = apps.get_model("cotisations", "BalancePayment")
try: try:
solde = Payment.objects.get(moyen="solde") solde = Payment.objects.get(moyen="solde")
@ -60,73 +58,191 @@ def add_solde(apps, schema_editor):
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('preferences', '0044_remove_payment_pass'), ("preferences", "0044_remove_payment_pass"),
('cotisations', '0029_auto_20180414_2056'), ("cotisations", "0029_auto_20180414_2056"),
] ]
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='paiement', name="paiement",
options={'permissions': (('view_paiement', "Can see a payement's details"), ('use_every_payment', 'Can use every payement')), 'verbose_name': 'Payment method', 'verbose_name_plural': 'Payment methods'}, options={
"permissions": (
("view_paiement", "Can see a payement's details"),
("use_every_payment", "Can use every payement"),
),
"verbose_name": "Payment method",
"verbose_name_plural": "Payment methods",
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='article', name="article",
options={'permissions': (('view_article', "Can see an article's details"), ('buy_every_article', 'Can buy every_article')), 'verbose_name': 'Article', 'verbose_name_plural': 'Articles'}, options={
"permissions": (
("view_article", "Can see an article's details"),
("buy_every_article", "Can buy every_article"),
),
"verbose_name": "Article",
"verbose_name_plural": "Articles",
},
), ),
migrations.AddField( migrations.AddField(
model_name='paiement', model_name="paiement",
name='available_for_everyone', name="available_for_everyone",
field=models.BooleanField(default=False, verbose_name='Is available for every user'), field=models.BooleanField(
default=False, verbose_name="Is available for every user"
),
), ),
migrations.AddField( migrations.AddField(
model_name='paiement', model_name="paiement",
name='is_balance', name="is_balance",
field=models.BooleanField(default=False, editable=False, help_text='There should be only one balance payment method.', verbose_name='Is user balance', validators=[cotisations.models.check_no_balance]), field=models.BooleanField(
default=False,
editable=False,
help_text="There should be only one balance payment method.",
verbose_name="Is user balance",
validators=[cotisations.models.check_no_balance],
),
), ),
migrations.AddField( migrations.AddField(
model_name='article', model_name="article",
name='available_for_everyone', name="available_for_everyone",
field=models.BooleanField(default=False, verbose_name='Is available for every user'), field=models.BooleanField(
default=False, verbose_name="Is available for every user"
),
), ),
migrations.CreateModel( migrations.CreateModel(
name='ChequePayment', name="ChequePayment",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')), "id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"payment",
models.OneToOneField(
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="payment_method",
to="cotisations.Paiement",
),
),
], ],
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model),
options={'verbose_name': 'Cheque'}, options={"verbose_name": "Cheque"},
), ),
migrations.CreateModel( migrations.CreateModel(
name='ComnpayPayment', name="ComnpayPayment",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
('payment_credential', models.CharField(blank=True, default='', max_length=255, verbose_name='ComNpay VAD Number')), "id",
('payment_pass', re2o.aes_field.AESEncryptedField(blank=True, max_length=255, null=True, verbose_name='ComNpay Secret Key')), models.AutoField(
('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')), auto_created=True,
('minimum_payment', models.DecimalField(decimal_places=2, default=1, help_text='The minimal amount of money you have to use when paying with ComNpay', max_digits=5, verbose_name='Minimum payment')), primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"payment_credential",
models.CharField(
blank=True,
default="",
max_length=255,
verbose_name="ComNpay VAD Number",
),
),
(
"payment_pass",
re2o.aes_field.AESEncryptedField(
blank=True,
max_length=255,
null=True,
verbose_name="ComNpay Secret Key",
),
),
(
"payment",
models.OneToOneField(
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="payment_method",
to="cotisations.Paiement",
),
),
(
"minimum_payment",
models.DecimalField(
decimal_places=2,
default=1,
help_text="The minimal amount of money you have to use when paying with ComNpay",
max_digits=5,
verbose_name="Minimum payment",
),
),
], ],
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model),
options={'verbose_name': 'ComNpay'}, options={"verbose_name": "ComNpay"},
), ),
migrations.CreateModel( migrations.CreateModel(
name='BalancePayment', name="BalancePayment",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
('minimum_balance', models.DecimalField(decimal_places=2, default=0, help_text='The minimal amount of money allowed for the balance at the end of a payment. You can specify negative amount.', max_digits=5, verbose_name='Minimum balance')), "id",
('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')), models.AutoField(
('maximum_balance', models.DecimalField(decimal_places=2, default=50, help_text='The maximal amount of money allowed for the balance.', max_digits=5, verbose_name='Maximum balance', null=True, blank=True)), auto_created=True,
('credit_balance_allowed', models.BooleanField(default=False, verbose_name='Allow user to credit their balance')), primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"minimum_balance",
models.DecimalField(
decimal_places=2,
default=0,
help_text="The minimal amount of money allowed for the balance at the end of a payment. You can specify negative amount.",
max_digits=5,
verbose_name="Minimum balance",
),
),
(
"payment",
models.OneToOneField(
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="payment_method",
to="cotisations.Paiement",
),
),
(
"maximum_balance",
models.DecimalField(
decimal_places=2,
default=50,
help_text="The maximal amount of money allowed for the balance.",
max_digits=5,
verbose_name="Maximum balance",
null=True,
blank=True,
),
),
(
"credit_balance_allowed",
models.BooleanField(
default=False, verbose_name="Allow user to credit their balance"
),
),
], ],
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model),
options={'verbose_name': 'User Balance'}, options={"verbose_name": "User Balance"},
), ),
migrations.RunPython(add_comnpay), migrations.RunPython(add_comnpay),
migrations.RunPython(add_cheque), migrations.RunPython(add_cheque),
migrations.RunPython(add_solde), migrations.RunPython(add_solde),
migrations.RemoveField( migrations.RemoveField(model_name="paiement", name="type_paiement"),
model_name='paiement',
name='type_paiement',
),
] ]

View file

@ -7,14 +7,15 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0030_custom_payment")]
('cotisations', '0030_custom_payment'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='comnpaypayment', model_name="comnpaypayment",
name='production', name="production",
field=models.BooleanField(default=True, verbose_name='Production mode enabled (production url, instead of homologation)'), field=models.BooleanField(
), default=True,
verbose_name="Production mode enabled (production url, instead of homologation)",
),
)
] ]

View file

@ -10,8 +10,8 @@ import re2o.mixins
def reattribute_ids(apps, schema_editor): def reattribute_ids(apps, schema_editor):
Facture = apps.get_model('cotisations', 'Facture') Facture = apps.get_model("cotisations", "Facture")
BaseInvoice = apps.get_model('cotisations', 'BaseInvoice') BaseInvoice = apps.get_model("cotisations", "BaseInvoice")
for f in Facture.objects.all(): for f in Facture.objects.all():
base = BaseInvoice.objects.create(id=f.pk) base = BaseInvoice.objects.create(id=f.pk)
@ -22,21 +22,23 @@ def reattribute_ids(apps, schema_editor):
def update_rights(apps, schema_editor): def update_rights(apps, schema_editor):
Permission = apps.get_model('auth', 'Permission') Permission = apps.get_model("auth", "Permission")
# creates needed permissions # creates needed permissions
app = apps.get_app_config('cotisations') app = apps.get_app_config("cotisations")
app.models_module = True app.models_module = True
create_permissions(app) create_permissions(app)
app.models_module = False app.models_module = False
ContentType = apps.get_model("contenttypes", "ContentType") ContentType = apps.get_model("contenttypes", "ContentType")
content_type = ContentType.objects.get_for_model(Permission) content_type = ContentType.objects.get_for_model(Permission)
former, created = Permission.objects.get_or_create(codename='change_facture_pdf', content_type=content_type) former, created = Permission.objects.get_or_create(
new_1 = Permission.objects.get(codename='add_custominvoice') codename="change_facture_pdf", content_type=content_type
new_2 = Permission.objects.get(codename='change_custominvoice') )
new_3 = Permission.objects.get(codename='view_custominvoice') new_1 = Permission.objects.get(codename="add_custominvoice")
new_4 = Permission.objects.get(codename='delete_custominvoice') new_2 = Permission.objects.get(codename="change_custominvoice")
new_3 = Permission.objects.get(codename="view_custominvoice")
new_4 = Permission.objects.get(codename="delete_custominvoice")
for group in former.group_set.all(): for group in former.group_set.all():
group.permissions.remove(former) group.permissions.remove(former)
group.permissions.add(new_1) group.permissions.add(new_1)
@ -48,59 +50,105 @@ def update_rights(apps, schema_editor):
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0031_comnpaypayment_production")]
('cotisations', '0031_comnpaypayment_production'),
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='BaseInvoice', name="BaseInvoice",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
('date', models.DateTimeField(auto_now_add=True, verbose_name='Date')), "id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("date", models.DateTimeField(auto_now_add=True, verbose_name="Date")),
], ],
bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, re2o.field_permissions.FieldPermissionModelMixin, models.Model), bases=(
re2o.mixins.RevMixin,
re2o.mixins.AclMixin,
re2o.field_permissions.FieldPermissionModelMixin,
models.Model,
),
), ),
migrations.CreateModel( migrations.CreateModel(
name='CustomInvoice', name="CustomInvoice",
fields=[ fields=[
('baseinvoice_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cotisations.BaseInvoice')), (
('recipient', models.CharField(max_length=255, verbose_name='Recipient')), "baseinvoice_ptr",
('payment', models.CharField(max_length=255, verbose_name='Payment type')), models.OneToOneField(
('address', models.CharField(max_length=255, verbose_name='Address')), auto_created=True,
('paid', models.BooleanField(verbose_name='Paid')), on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="cotisations.BaseInvoice",
),
),
(
"recipient",
models.CharField(max_length=255, verbose_name="Recipient"),
),
(
"payment",
models.CharField(max_length=255, verbose_name="Payment type"),
),
("address", models.CharField(max_length=255, verbose_name="Address")),
("paid", models.BooleanField(verbose_name="Paid")),
], ],
bases=('cotisations.baseinvoice',), bases=("cotisations.baseinvoice",),
options={'permissions': (('view_custominvoice', 'Can view a custom invoice'),)}, options={
"permissions": (("view_custominvoice", "Can view a custom invoice"),)
},
), ),
migrations.AddField( migrations.AddField(
model_name='facture', model_name="facture",
name='baseinvoice_ptr', name="baseinvoice_ptr",
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='cotisations.BaseInvoice', null=True), field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="cotisations.BaseInvoice",
null=True,
),
preserve_default=False, preserve_default=False,
), ),
migrations.RunPython(reattribute_ids), migrations.RunPython(reattribute_ids),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='facture', name="facture",
field=models.ForeignKey(on_delete=models.CASCADE, verbose_name='Invoice', to='cotisations.BaseInvoice') field=models.ForeignKey(
), on_delete=models.CASCADE,
migrations.RemoveField( verbose_name="Invoice",
model_name='facture', to="cotisations.BaseInvoice",
name='id', ),
),
migrations.RemoveField(
model_name='facture',
name='date',
), ),
migrations.RemoveField(model_name="facture", name="id"),
migrations.RemoveField(model_name="facture", name="date"),
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='baseinvoice_ptr', name="baseinvoice_ptr",
field=models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cotisations.BaseInvoice'), field=models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="cotisations.BaseInvoice",
),
), ),
migrations.RunPython(update_rights), migrations.RunPython(update_rights),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='facture', name="facture",
options={'permissions': (('change_facture_control', 'Can change the "controlled" state'), ('view_facture', "Can see an invoice's details"), ('change_all_facture', 'Can edit all the previous invoices')), 'verbose_name': 'Invoice', 'verbose_name_plural': 'Invoices'}, options={
"permissions": (
("change_facture_control", 'Can change the "controlled" state'),
("view_facture", "Can see an invoice's details"),
("change_all_facture", "Can edit all the previous invoices"),
),
"verbose_name": "Invoice",
"verbose_name_plural": "Invoices",
},
), ),
] ]

View file

@ -11,171 +11,294 @@ import re2o.aes_field
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0032_custom_invoice")]
('cotisations', '0032_custom_invoice'),
]
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='article', name="article",
options={'permissions': (('view_article', 'Can view an article object'), ('buy_every_article', 'Can buy every article')), 'verbose_name': 'article', 'verbose_name_plural': 'articles'}, options={
"permissions": (
("view_article", "Can view an article object"),
("buy_every_article", "Can buy every article"),
),
"verbose_name": "article",
"verbose_name_plural": "articles",
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='balancepayment', name="balancepayment", options={"verbose_name": "user balance"}
options={'verbose_name': 'user balance'},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='banque', name="banque",
options={'permissions': (('view_banque', 'Can view a bank object'),), 'verbose_name': 'bank', 'verbose_name_plural': 'banks'}, options={
"permissions": (("view_banque", "Can view a bank object"),),
"verbose_name": "bank",
"verbose_name_plural": "banks",
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='cotisation', name="cotisation",
options={'permissions': (('view_cotisation', 'Can view a subscription object'), ('change_all_cotisation', 'Can edit the previous subscriptions')), 'verbose_name': 'subscription', 'verbose_name_plural': 'subscriptions'}, options={
"permissions": (
("view_cotisation", "Can view a subscription object"),
("change_all_cotisation", "Can edit the previous subscriptions"),
),
"verbose_name": "subscription",
"verbose_name_plural": "subscriptions",
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='custominvoice', name="custominvoice",
options={'permissions': (('view_custominvoice', 'Can view a custom invoice object'),)}, options={
"permissions": (
("view_custominvoice", "Can view a custom invoice object"),
)
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='facture', name="facture",
options={'permissions': (('change_facture_control', 'Can edit the "controlled" state'), ('view_facture', 'Can view an invoice object'), ('change_all_facture', 'Can edit all the previous invoices')), 'verbose_name': 'invoice', 'verbose_name_plural': 'invoices'}, options={
"permissions": (
("change_facture_control", 'Can edit the "controlled" state'),
("view_facture", "Can view an invoice object"),
("change_all_facture", "Can edit all the previous invoices"),
),
"verbose_name": "invoice",
"verbose_name_plural": "invoices",
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='paiement', name="paiement",
options={'permissions': (('view_paiement', 'Can view a payment method object'), ('use_every_payment', 'Can use every payment method')), 'verbose_name': 'payment method', 'verbose_name_plural': 'payment methods'}, options={
"permissions": (
("view_paiement", "Can view a payment method object"),
("use_every_payment", "Can use every payment method"),
),
"verbose_name": "payment method",
"verbose_name_plural": "payment methods",
},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='vente', name="vente",
options={'permissions': (('view_vente', 'Can view a purchase object'), ('change_all_vente', 'Can edit all the previous purchases')), 'verbose_name': 'purchase', 'verbose_name_plural': 'purchases'}, options={
"permissions": (
("view_vente", "Can view a purchase object"),
("change_all_vente", "Can edit all the previous purchases"),
),
"verbose_name": "purchase",
"verbose_name_plural": "purchases",
},
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='available_for_everyone', name="available_for_everyone",
field=models.BooleanField(default=False, verbose_name='is available for every user'), field=models.BooleanField(
default=False, verbose_name="is available for every user"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='duration', name="duration",
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration (in months)'), field=models.PositiveIntegerField(
blank=True,
null=True,
validators=[django.core.validators.MinValueValidator(0)],
verbose_name="duration (in months)",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='name', name="name",
field=models.CharField(max_length=255, verbose_name='designation'), field=models.CharField(max_length=255, verbose_name="designation"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='prix', name="prix",
field=models.DecimalField(decimal_places=2, max_digits=5, verbose_name='unit price'), field=models.DecimalField(
decimal_places=2, max_digits=5, verbose_name="unit price"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='type_cotisation', name="type_cotisation",
field=models.CharField(blank=True, choices=[('Connexion', 'Connection'), ('Adhesion', 'Membership'), ('All', 'Both of them')], default=None, max_length=255, null=True, verbose_name='subscription type'), field=models.CharField(
blank=True,
choices=[
("Connexion", "Connection"),
("Adhesion", "Membership"),
("All", "Both of them"),
],
default=None,
max_length=255,
null=True,
verbose_name="subscription type",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='article', model_name="article",
name='type_user', name="type_user",
field=models.CharField(choices=[('Adherent', 'Member'), ('Club', 'Club'), ('All', 'Both of them')], default='All', max_length=255, verbose_name='type of users concerned'), field=models.CharField(
choices=[
("Adherent", "Member"),
("Club", "Club"),
("All", "Both of them"),
],
default="All",
max_length=255,
verbose_name="type of users concerned",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='banque', model_name="banque", name="name", field=models.CharField(max_length=255)
name='name',
field=models.CharField(max_length=255),
), ),
migrations.AlterField( migrations.AlterField(
model_name='comnpaypayment', model_name="comnpaypayment",
name='payment_credential', name="payment_credential",
field=models.CharField(blank=True, default='', max_length=255, verbose_name='ComNpay VAT Number'), field=models.CharField(
blank=True,
default="",
max_length=255,
verbose_name="ComNpay VAT Number",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='comnpaypayment', model_name="comnpaypayment",
name='payment_pass', name="payment_pass",
field=re2o.aes_field.AESEncryptedField(blank=True, max_length=255, null=True, verbose_name='ComNpay secret key'), field=re2o.aes_field.AESEncryptedField(
blank=True, max_length=255, null=True, verbose_name="ComNpay secret key"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='comnpaypayment', model_name="comnpaypayment",
name='production', name="production",
field=models.BooleanField(default=True, verbose_name='Production mode enabled (production URL, instead of homologation)'), field=models.BooleanField(
default=True,
verbose_name="Production mode enabled (production URL, instead of homologation)",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='cotisation', model_name="cotisation",
name='date_end', name="date_end",
field=models.DateTimeField(verbose_name='end date'), field=models.DateTimeField(verbose_name="end date"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='cotisation', model_name="cotisation",
name='date_start', name="date_start",
field=models.DateTimeField(verbose_name='start date'), field=models.DateTimeField(verbose_name="start date"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='cotisation', model_name="cotisation",
name='type_cotisation', name="type_cotisation",
field=models.CharField(choices=[('Connexion', 'Connection'), ('Adhesion', 'Membership'), ('All', 'Both of them')], default='All', max_length=255, verbose_name='subscription type'), field=models.CharField(
choices=[
("Connexion", "Connection"),
("Adhesion", "Membership"),
("All", "Both of them"),
],
default="All",
max_length=255,
verbose_name="subscription type",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='cotisation', model_name="cotisation",
name='vente', name="vente",
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='cotisations.Vente', verbose_name='purchase'), field=models.OneToOneField(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="cotisations.Vente",
verbose_name="purchase",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='cheque', name="cheque",
field=models.CharField(blank=True, max_length=255, verbose_name='cheque number'), field=models.CharField(
blank=True, max_length=255, verbose_name="cheque number"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='control', name="control",
field=models.BooleanField(default=False, verbose_name='controlled'), field=models.BooleanField(default=False, verbose_name="controlled"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='valid', name="valid",
field=models.BooleanField(default=True, verbose_name='validated'), field=models.BooleanField(default=True, verbose_name="validated"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='paiement', model_name="paiement",
name='available_for_everyone', name="available_for_everyone",
field=models.BooleanField(default=False, verbose_name='is available for every user'), field=models.BooleanField(
default=False, verbose_name="is available for every user"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='paiement', model_name="paiement",
name='is_balance', name="is_balance",
field=models.BooleanField(default=False, editable=False, help_text='There should be only one balance payment method.', validators=[cotisations.validators.check_no_balance], verbose_name='is user balance'), field=models.BooleanField(
default=False,
editable=False,
help_text="There should be only one balance payment method.",
validators=[cotisations.validators.check_no_balance],
verbose_name="is user balance",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='paiement', model_name="paiement",
name='moyen', name="moyen",
field=models.CharField(max_length=255, verbose_name='method'), field=models.CharField(max_length=255, verbose_name="method"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='duration', name="duration",
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='duration (in months)'), field=models.PositiveIntegerField(
blank=True, null=True, verbose_name="duration (in months)"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='facture', name="facture",
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cotisations.BaseInvoice', verbose_name='invoice'), field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="cotisations.BaseInvoice",
verbose_name="invoice",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='name', name="name",
field=models.CharField(max_length=255, verbose_name='article'), field=models.CharField(max_length=255, verbose_name="article"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='number', name="number",
field=models.IntegerField(validators=[django.core.validators.MinValueValidator(1)], verbose_name='amount'), field=models.IntegerField(
validators=[django.core.validators.MinValueValidator(1)],
verbose_name="amount",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='prix', name="prix",
field=models.DecimalField(decimal_places=2, max_digits=5, verbose_name='price'), field=models.DecimalField(
decimal_places=2, max_digits=5, verbose_name="price"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='vente', model_name="vente",
name='type_cotisation', name="type_cotisation",
field=models.CharField(blank=True, choices=[('Connexion', 'Connection'), ('Adhesion', 'Membership'), ('All', 'Both of them')], max_length=255, null=True, verbose_name='subscription type'), field=models.CharField(
blank=True,
choices=[
("Connexion", "Connection"),
("Adhesion", "Membership"),
("All", "Both of them"),
],
max_length=255,
null=True,
verbose_name="subscription type",
),
), ),
] ]

View file

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0033_auto_20180818_1319")]
('cotisations', '0033_auto_20180818_1319'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='facture', model_name="facture",
name='valid', name="valid",
field=models.BooleanField(default=False, verbose_name='validated'), field=models.BooleanField(default=False, verbose_name="validated"),
), )
] ]

View file

@ -9,23 +9,35 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0034_auto_20180831_1532")]
('cotisations', '0034_auto_20180831_1532'),
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='NotePayment', name="NotePayment",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
('server', models.CharField(max_length=255, verbose_name='server')), "id",
('port', models.PositiveIntegerField(blank=True, null=True)), models.AutoField(
('id_note', models.PositiveIntegerField(blank=True, null=True)), auto_created=True,
('payment', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method', to='cotisations.Paiement')), primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("server", models.CharField(max_length=255, verbose_name="server")),
("port", models.PositiveIntegerField(blank=True, null=True)),
("id_note", models.PositiveIntegerField(blank=True, null=True)),
(
"payment",
models.OneToOneField(
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="payment_method",
to="cotisations.Paiement",
),
),
], ],
options={ options={"verbose_name": "NoteKfet"},
'verbose_name': 'NoteKfet',
},
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model),
), )
] ]

View file

@ -7,14 +7,12 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0035_notepayment")]
('cotisations', '0035_notepayment'),
]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='custominvoice', model_name="custominvoice",
name='remark', name="remark",
field=models.TextField(blank=True, null=True, verbose_name='Remark'), field=models.TextField(blank=True, null=True, verbose_name="Remark"),
), )
] ]

View file

@ -8,21 +8,40 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0036_custominvoice_remark")]
('cotisations', '0036_custominvoice_remark'),
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='CostEstimate', name="CostEstimate",
fields=[ fields=[
('custominvoice_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='cotisations.CustomInvoice')), (
('validity', models.DurationField(verbose_name='Period of validity')), "custominvoice_ptr",
('final_invoice', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='origin_cost_estimate', to='cotisations.CustomInvoice')), models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="cotisations.CustomInvoice",
),
),
("validity", models.DurationField(verbose_name="Period of validity")),
(
"final_invoice",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="origin_cost_estimate",
to="cotisations.CustomInvoice",
),
),
], ],
options={ options={
'permissions': (('view_costestimate', 'Can view a cost estimate object'),), "permissions": (
("view_costestimate", "Can view a cost estimate object"),
)
}, },
bases=('cotisations.custominvoice',), bases=("cotisations.custominvoice",),
), )
] ]

View file

@ -8,24 +8,30 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [("cotisations", "0037_costestimate")]
('cotisations', '0037_costestimate'),
]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='costestimate', model_name="costestimate",
name='final_invoice', name="final_invoice",
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='origin_cost_estimate', to='cotisations.CustomInvoice'), field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="origin_cost_estimate",
to="cotisations.CustomInvoice",
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='costestimate', model_name="costestimate",
name='validity', name="validity",
field=models.DurationField(help_text='DD HH:MM:SS', verbose_name='Period of validity'), field=models.DurationField(
help_text="DD HH:MM:SS", verbose_name="Period of validity"
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='custominvoice', model_name="custominvoice",
name='paid', name="paid",
field=models.BooleanField(default=False, verbose_name='Paid'), field=models.BooleanField(default=False, verbose_name="Paid"),
), ),
] ]

View file

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.23 on 2019-09-30 09:19
from __future__ import unicode_literals
import cotisations.payment_methods.mixins
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [("cotisations", "0038_auto_20181231_1657")]
operations = [
migrations.CreateModel(
name="FreePayment",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"payment",
models.OneToOneField(
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="payment_method",
to="cotisations.Paiement",
),
),
],
options={"verbose_name": "Free payment"},
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model),
)
]

View file

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.23 on 2019-10-02 21:35
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("cotisations", "0039_freepayment")]
operations = [
migrations.AddField(
model_name="article",
name="duration_days",
field=models.PositiveIntegerField(
blank=True,
null=True,
validators=[django.core.validators.MinValueValidator(0)],
verbose_name="duration (in days, will be added to duration in months)",
),
),
migrations.AddField(
model_name="vente",
name="duration_days",
field=models.PositiveIntegerField(
blank=True,
null=True,
validators=[django.core.validators.MinValueValidator(0)],
verbose_name="duration (in days, will be added to duration in months)",
),
),
]

View file

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.23 on 2019-11-03 20:31
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [("cotisations", "0040_auto_20191002_2335")]
operations = [
migrations.AlterField(
model_name="balancepayment",
name="payment",
field=models.OneToOneField(
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="payment_method_balance",
to="cotisations.Paiement",
),
),
migrations.AlterField(
model_name="chequepayment",
name="payment",
field=models.OneToOneField(
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="payment_method_cheque",
to="cotisations.Paiement",
),
),
migrations.AlterField(
model_name="comnpaypayment",
name="payment",
field=models.OneToOneField(
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="payment_method_comnpay",
to="cotisations.Paiement",
),
),
migrations.AlterField(
model_name="freepayment",
name="payment",
field=models.OneToOneField(
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="payment_method_free",
to="cotisations.Paiement",
),
),
migrations.AlterField(
model_name="notepayment",
name="payment",
field=models.OneToOneField(
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="payment_method_note",
to="cotisations.Paiement",
),
),
]

View file

@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.23 on 2019-11-20 00:59
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0041_auto_20191103_2131'),
]
operations = [
migrations.AlterModelOptions(
name='chequepayment',
options={'verbose_name': 'cheque'},
),
migrations.AlterField(
model_name='balancepayment',
name='credit_balance_allowed',
field=models.BooleanField(default=False, verbose_name='allow user to credit their balance'),
),
migrations.AlterField(
model_name='balancepayment',
name='maximum_balance',
field=models.DecimalField(blank=True, decimal_places=2, default=50, help_text='The maximal amount of money allowed for the balance.', max_digits=5, null=True, verbose_name='maximum balance'),
),
migrations.AlterField(
model_name='balancepayment',
name='minimum_balance',
field=models.DecimalField(decimal_places=2, default=0, help_text='The minimal amount of money allowed for the balance at the end of a payment. You can specify a negative amount.', max_digits=5, verbose_name='minimum balance'),
),
migrations.AlterField(
model_name='baseinvoice',
name='date',
field=models.DateTimeField(auto_now_add=True, verbose_name='date'),
),
migrations.AlterField(
model_name='comnpaypayment',
name='minimum_payment',
field=models.DecimalField(decimal_places=2, default=1, help_text='The minimal amount of money you have to use when paying with ComNpay.', max_digits=5, verbose_name='minimum payment'),
),
migrations.AlterField(
model_name='comnpaypayment',
name='production',
field=models.BooleanField(default=True, verbose_name='production mode enabled (production URL, instead of homologation)'),
),
migrations.AlterField(
model_name='costestimate',
name='validity',
field=models.DurationField(help_text='DD HH:MM:SS', verbose_name='period of validity'),
),
migrations.AlterField(
model_name='custominvoice',
name='address',
field=models.CharField(max_length=255, verbose_name='address'),
),
migrations.AlterField(
model_name='custominvoice',
name='paid',
field=models.BooleanField(default=False, verbose_name='paid'),
),
migrations.AlterField(
model_name='custominvoice',
name='payment',
field=models.CharField(max_length=255, verbose_name='payment type'),
),
migrations.AlterField(
model_name='custominvoice',
name='recipient',
field=models.CharField(max_length=255, verbose_name='recipient'),
),
migrations.AlterField(
model_name='custominvoice',
name='remark',
field=models.TextField(blank=True, null=True, verbose_name='remark'),
),
]

View file

@ -3,7 +3,7 @@
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Goulven Kermarec # Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle # Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -19,4 +19,3 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

File diff suppressed because it is too large Load diff

View file

@ -127,11 +127,6 @@ method to your model, where `form` is an instance of
""" """
from . import comnpay, cheque, balance, note_kfet, urls from . import comnpay, cheque, balance, note_kfet, free, urls
PAYMENT_METHODS = [ PAYMENT_METHODS = [comnpay, cheque, balance, note_kfet, free]
comnpay,
cheque,
balance,
note_kfet
]

View file

@ -22,6 +22,7 @@
This module contains a method to pay online using user balance. This module contains a method to pay online using user balance.
""" """
from . import models from . import models
NAME = "BALANCE" NAME = "BALANCE"
PaymentMethod = models.BalancePayment PaymentMethod = models.BalancePayment

View file

@ -40,21 +40,21 @@ class BalancePayment(PaymentMethodMixin, models.Model):
payment = models.OneToOneField( payment = models.OneToOneField(
Paiement, Paiement,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='payment_method', related_name="payment_method_balance",
editable=False editable=False,
) )
minimum_balance = models.DecimalField( minimum_balance = models.DecimalField(
verbose_name=_("Minimum balance"), verbose_name=_("minimum balance"),
help_text=_("The minimal amount of money allowed for the balance" help_text=_(
" at the end of a payment. You can specify negative " "The minimal amount of money allowed for the balance at the end"
"amount." " of a payment. You can specify a negative amount."
), ),
max_digits=5, max_digits=5,
decimal_places=2, decimal_places=2,
default=0, default=0,
) )
maximum_balance = models.DecimalField( maximum_balance = models.DecimalField(
verbose_name=_("Maximum balance"), verbose_name=_("maximum balance"),
help_text=_("The maximal amount of money allowed for the balance."), help_text=_("The maximal amount of money allowed for the balance."),
max_digits=5, max_digits=5,
decimal_places=2, decimal_places=2,
@ -63,8 +63,7 @@ class BalancePayment(PaymentMethodMixin, models.Model):
null=True, null=True,
) )
credit_balance_allowed = models.BooleanField( credit_balance_allowed = models.BooleanField(
verbose_name=_("Allow user to credit their balance"), verbose_name=_("allow user to credit their balance"), default=False
default=False,
) )
def end_payment(self, invoice, request): def end_payment(self, invoice, request):
@ -74,27 +73,17 @@ class BalancePayment(PaymentMethodMixin, models.Model):
user = invoice.user user = invoice.user
total_price = invoice.prix_total() total_price = invoice.prix_total()
if user.solde - total_price < self.minimum_balance: if user.solde - total_price < self.minimum_balance:
messages.error( messages.error(request, _("Your balance is too low for this operation."))
request, return redirect(reverse("users:profil", kwargs={"userid": user.id}))
_("Your balance is too low for this operation.") return invoice.paiement.end_payment(invoice, request, use_payment_method=False)
)
return redirect(reverse(
'users:profil',
kwargs={'userid': user.id}
))
return invoice.paiement.end_payment(
invoice,
request,
use_payment_method=False
)
def valid_form(self, form): def valid_form(self, form):
"""Checks that there is not already a balance payment method.""" """Checks that there is not already a balance payment method."""
p = Paiement.objects.filter(is_balance=True) p = Paiement.objects.filter(is_balance=True)
if len(p) > 0: if len(p) > 0:
form.add_error( form.add_error(
'payment_method', "payment_method",
_("There is already a payment method for user balance.") _("There is already a payment method for user balance."),
) )
def alter_payment(self, payment): def alter_payment(self, payment):
@ -107,12 +96,11 @@ class BalancePayment(PaymentMethodMixin, models.Model):
""" """
return ( return (
user.solde - price >= self.minimum_balance, user.solde - price >= self.minimum_balance,
_("Your balance is too low for this operation.") _("Your balance is too low for this operation."),
) )
def can_credit_balance(self, user_request): def can_credit_balance(self, user_request):
return ( return (
len(Paiement.find_allowed_payments(user_request) len(Paiement.find_allowed_payments(user_request).exclude(is_balance=True))
.exclude(is_balance=True)) > 0 > 0
) and self.credit_balance_allowed ) and self.credit_balance_allowed

View file

@ -22,6 +22,7 @@
This module contains a method to pay online using cheque. This module contains a method to pay online using cheque.
""" """
from . import models, urls, views from . import models, urls, views
NAME = "CHEQUE" NAME = "CHEQUE"
PaymentMethod = models.ChequePayment PaymentMethod = models.ChequePayment

View file

@ -26,6 +26,7 @@ from cotisations.models import Facture as Invoice
class InvoiceForm(FormRevMixin, forms.ModelForm): class InvoiceForm(FormRevMixin, forms.ModelForm):
"""A simple form to get the bank a the cheque number.""" """A simple form to get the bank a the cheque number."""
class Meta: class Meta:
model = Invoice model = Invoice
fields = ['banque', 'cheque'] fields = ["banque", "cheque"]

View file

@ -33,21 +33,19 @@ class ChequePayment(PaymentMethodMixin, models.Model):
""" """
class Meta: class Meta:
verbose_name = _("Cheque") verbose_name = _("cheque")
payment = models.OneToOneField( payment = models.OneToOneField(
Paiement, Paiement,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='payment_method', related_name="payment_method_cheque",
editable=False editable=False,
) )
def end_payment(self, invoice, request): def end_payment(self, invoice, request):
"""Invalidates the invoice then redirect the user towards a view asking """Invalidates the invoice then redirect the user towards a view asking
for informations to add to the invoice before validating it. for informations to add to the invoice before validating it.
""" """
return redirect(reverse( return redirect(
'cotisations:cheque:validate', reverse("cotisations:cheque:validate", kwargs={"invoice_pk": invoice.pk})
kwargs={'invoice_pk': invoice.pk} )
))

View file

@ -21,10 +21,4 @@
from django.conf.urls import url from django.conf.urls import url
from . import views from . import views
urlpatterns = [ urlpatterns = [url(r"^validate/(?P<invoice_pk>[0-9]+)$", views.cheque, name="validate")]
url(
r'^validate/(?P<invoice_pk>[0-9]+)$',
views.cheque,
name='validate'
)
]

View file

@ -42,29 +42,17 @@ def cheque(request, invoice_pk):
invoice = get_object_or_404(Invoice, pk=invoice_pk) invoice = get_object_or_404(Invoice, pk=invoice_pk)
payment_method = find_payment_method(invoice.paiement) payment_method = find_payment_method(invoice.paiement)
if invoice.valid or not isinstance(payment_method, ChequePayment): if invoice.valid or not isinstance(payment_method, ChequePayment):
messages.error( messages.error(request, _("You can't pay this invoice with a cheque."))
request, return redirect(reverse("users:profil", kwargs={"userid": request.user.pk}))
_("You can't pay this invoice with a cheque.")
)
return redirect(reverse(
'users:profil',
kwargs={'userid': request.user.pk}
))
form = InvoiceForm(request.POST or None, instance=invoice) form = InvoiceForm(request.POST or None, instance=invoice)
if form.is_valid(): if form.is_valid():
form.instance.valid = True form.instance.valid = True
form.save() form.save()
return form.instance.paiement.end_payment( return form.instance.paiement.end_payment(
form.instance, form.instance, request, use_payment_method=False
request,
use_payment_method=False
) )
return render( return render(
request, request,
'cotisations/payment.html', "cotisations/payment.html",
{ {"form": form, "amount": invoice.prix_total()},
'form': form,
'amount': invoice.prix_total()
}
) )

View file

@ -22,5 +22,6 @@
This module contains a method to pay online using comnpay. This module contains a method to pay online using comnpay.
""" """
from . import models, urls, views from . import models, urls, views
NAME = "COMNPAY" NAME = "COMNPAY"
PaymentMethod = models.ComnpayPayment PaymentMethod = models.ComnpayPayment

View file

@ -10,13 +10,21 @@ import hashlib
from collections import OrderedDict from collections import OrderedDict
class Transaction(): class Transaction:
""" The class representing a transaction with all the functions """ The class representing a transaction with all the functions
used during the negociation used during the negociation
""" """
def __init__(self, vad_number="", secret_key="", urlRetourOK="", def __init__(
urlRetourNOK="", urlIPN="", source="", typeTr="D"): self,
vad_number="",
secret_key="",
urlRetourOK="",
urlRetourNOK="",
urlIPN="",
source="",
typeTr="D",
):
self.vad_number = vad_number self.vad_number = vad_number
self.secret_key = secret_key self.secret_key = secret_key
self.urlRetourOK = urlRetourOK self.urlRetourOK = urlRetourOK
@ -26,8 +34,7 @@ class Transaction():
self.typeTr = typeTr self.typeTr = typeTr
self.idTransaction = "" self.idTransaction = ""
def buildSecretHTML(self, produit="Produit", montant="0.00", def buildSecretHTML(self, produit="Produit", montant="0.00", idTransaction=""):
idTransaction=""):
""" Build an HTML hidden form with the different parameters for the """ Build an HTML hidden form with the different parameters for the
transaction transaction
""" """
@ -43,30 +50,26 @@ class Transaction():
idTPE=self.vad_number, idTPE=self.vad_number,
idTransaction=self.idTransaction, idTransaction=self.idTransaction,
devise="EUR", devise="EUR",
lang='fr', lang="fr",
nom_produit=produit, nom_produit=produit,
source=self.source, source=self.source,
urlRetourOK=self.urlRetourOK, urlRetourOK=self.urlRetourOK,
urlRetourNOK=self.urlRetourNOK, urlRetourNOK=self.urlRetourNOK,
typeTr=str(self.typeTr) typeTr=str(self.typeTr),
) )
if self.urlIPN != "": if self.urlIPN != "":
array_tpe['urlIPN'] = self.urlIPN array_tpe["urlIPN"] = self.urlIPN
array_tpe['key'] = self.secret_key array_tpe["key"] = self.secret_key
strWithKey = base64.b64encode(bytes( strWithKey = base64.b64encode(bytes("|".join(array_tpe.values()), "utf-8"))
'|'.join(array_tpe.values()),
'utf-8'
))
del array_tpe["key"] del array_tpe["key"]
array_tpe['sec'] = hashlib.sha512(strWithKey).hexdigest() array_tpe["sec"] = hashlib.sha512(strWithKey).hexdigest()
ret = "" ret = ""
for key in array_tpe: for key in array_tpe:
ret += '<input type="hidden" name="{k}" value="{v}"/>'.format( ret += '<input type="hidden" name="{k}" value="{v}"/>'.format(
k=key, k=key, v=array_tpe[key]
v=array_tpe[key]
) )
return ret return ret
@ -75,12 +78,13 @@ class Transaction():
def validSec(values, secret_key): def validSec(values, secret_key):
""" Check if the secret value is correct """ """ Check if the secret value is correct """
if "sec" in values: if "sec" in values:
sec = values['sec'] sec = values["sec"]
del values["sec"] del values["sec"]
strWithKey = hashlib.sha512(base64.b64encode(bytes( strWithKey = hashlib.sha512(
'|'.join(values.values()) + "|" + secret_key, base64.b64encode(
'utf-8' bytes("|".join(values.values()) + "|" + secret_key, "utf-8")
))).hexdigest() )
).hexdigest()
return strWithKey.upper() == sec.upper() return strWithKey.upper() == sec.upper()
else: else:
return False return False

View file

@ -41,39 +41,37 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
payment = models.OneToOneField( payment = models.OneToOneField(
Paiement, Paiement,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='payment_method', related_name="payment_method_comnpay",
editable=False editable=False,
) )
payment_credential = models.CharField( payment_credential = models.CharField(
max_length=255, max_length=255, default="", blank=True, verbose_name=_("ComNpay VAT Number")
default='',
blank=True,
verbose_name=_("ComNpay VAT Number"),
) )
payment_pass = AESEncryptedField( payment_pass = AESEncryptedField(
max_length=255, max_length=255, null=True, blank=True, verbose_name=_("ComNpay secret key")
null=True,
blank=True,
verbose_name=_("ComNpay secret key"),
) )
minimum_payment = models.DecimalField( minimum_payment = models.DecimalField(
verbose_name=_("Minimum payment"), verbose_name=_("minimum payment"),
help_text=_("The minimal amount of money you have to use when paying" help_text=_(
" with ComNpay"), "The minimal amount of money you have to use when paying with"
" ComNpay."
),
max_digits=5, max_digits=5,
decimal_places=2, decimal_places=2,
default=1, default=1,
) )
production = models.BooleanField( production = models.BooleanField(
default=True, default=True,
verbose_name=_("Production mode enabled (production URL, instead of homologation)"), verbose_name=_(
"production mode enabled (production URL, instead of homologation)"
),
) )
def return_url_comnpay(self): def return_url_comnpay(self):
if self.production: if self.production:
return 'https://secure.comnpay.com' return "https://secure.comnpay.com"
else: else:
return 'https://secure.homologation.comnpay.com' return "https://secure.homologation.comnpay.com"
def end_payment(self, invoice, request): def end_payment(self, invoice, request):
""" """
@ -85,32 +83,36 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
p = Transaction( p = Transaction(
str(self.payment_credential), str(self.payment_credential),
str(self.payment_pass), str(self.payment_pass),
'https://' + host + reverse( "https://"
'cotisations:comnpay:accept_payment', + host
kwargs={'factureid': invoice.id} + reverse(
"cotisations:comnpay:accept_payment", kwargs={"factureid": invoice.id}
), ),
'https://' + host + reverse('cotisations:comnpay:refuse_payment'), "https://" + host + reverse("cotisations:comnpay:refuse_payment"),
'https://' + host + reverse('cotisations:comnpay:ipn'), "https://" + host + reverse("cotisations:comnpay:ipn"),
"", "",
"D" "D",
) )
r = { r = {
'action': self.return_url_comnpay(), "action": self.return_url_comnpay(),
'method': 'POST', "method": "POST",
'content': p.buildSecretHTML( "content": p.buildSecretHTML(
_("Pay invoice number ")+str(invoice.id), _("Pay invoice number ") + str(invoice.id),
invoice.prix_total(), invoice.prix_total(),
idTransaction=str(invoice.id) idTransaction=str(invoice.id),
), ),
'amount': invoice.prix_total(), "amount": invoice.prix_total(),
} }
return render(request, 'cotisations/payment.html', r) return render(request, "cotisations/payment.html", r)
def check_price(self, price, *args, **kwargs): def check_price(self, price, *args, **kwargs):
"""Checks that the price meets the requirement to be paid with ComNpay. """Checks that the price meets the requirement to be paid with ComNpay.
""" """
return ((price >= self.minimum_payment), return (
_("In order to pay your invoice with ComNpay, the price must" (price >= self.minimum_payment),
" be greater than {} €.").format(self.minimum_payment)) _(
"In order to pay your invoice with ComNpay, the price must"
" be greater than {} €."
).format(self.minimum_payment),
)

View file

@ -22,19 +22,7 @@ from django.conf.urls import url
from . import views from . import views
urlpatterns = [ urlpatterns = [
url( url(r"^accept/(?P<factureid>[0-9]+)$", views.accept_payment, name="accept_payment"),
r'^accept/(?P<factureid>[0-9]+)$', url(r"^refuse/$", views.refuse_payment, name="refuse_payment"),
views.accept_payment, url(r"^ipn/$", views.ipn, name="ipn"),
name='accept_payment'
),
url(
r'^refuse/$',
views.refuse_payment,
name='refuse_payment'
),
url(
r'^ipn/$',
views.ipn,
name='ipn'
),
] ]

View file

@ -50,26 +50,24 @@ def accept_payment(request, factureid):
if invoice.valid: if invoice.valid:
messages.success( messages.success(
request, request,
_("The payment of %(amount)s € was accepted.") % { _("The payment of %(amount)s € was accepted.")
'amount': invoice.prix_total() % {"amount": invoice.prix_total()},
}
) )
# In case a cotisation was bought, inform the user, the # In case a cotisation was bought, inform the user, the
# cotisation time has been extended too # cotisation time has been extended too
if any(purchase.type_cotisation if any(purchase.type_cotisation for purchase in invoice.vente_set.all()):
for purchase in invoice.vente_set.all()):
messages.success( messages.success(
request, request,
_("The subscription of %(member_name)s was extended to" _(
" %(end_date)s.") % { "The subscription of %(member_name)s was extended to"
'member_name': invoice.user.pseudo, " %(end_date)s."
'end_date': invoice.user.end_adhesion() )
} % {
"member_name": invoice.user.pseudo,
"end_date": invoice.user.end_adhesion(),
},
) )
return redirect(reverse( return redirect(reverse("users:profil", kwargs={"userid": invoice.user.id}))
'users:profil',
kwargs={'userid': invoice.user.id}
))
@csrf_exempt @csrf_exempt
@ -79,14 +77,8 @@ def refuse_payment(request):
The view where the user is redirected when a comnpay payment has been The view where the user is redirected when a comnpay payment has been
refused. refused.
""" """
messages.error( messages.error(request, _("The payment was refused."))
request, return redirect(reverse("users:profil", kwargs={"userid": request.user.id}))
_("The payment was refused.")
)
return redirect(reverse(
'users:profil',
kwargs={'userid': request.user.id}
))
@csrf_exempt @csrf_exempt
@ -97,27 +89,26 @@ def ipn(request):
Comnpay with 400 response if not or with a 200 response if yes. Comnpay with 400 response if not or with a 200 response if yes.
""" """
p = Transaction() p = Transaction()
order = ('idTpe', 'idTransaction', 'montant', 'result', 'sec', ) order = ("idTpe", "idTransaction", "montant", "result", "sec")
try: try:
data = OrderedDict([(f, request.POST[f]) for f in order]) data = OrderedDict([(f, request.POST[f]) for f in order])
except MultiValueDictKeyError: except MultiValueDictKeyError:
return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") return HttpResponseBadRequest("HTTP/1.1 400 Bad Request")
idTransaction = request.POST['idTransaction'] idTransaction = request.POST["idTransaction"]
try: try:
factureid = int(idTransaction) factureid = int(idTransaction)
except ValueError: except ValueError:
return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") return HttpResponseBadRequest("HTTP/1.1 400 Bad Request")
facture = get_object_or_404(Facture, id=factureid) facture = get_object_or_404(Facture, id=factureid)
payment_method = get_object_or_404( payment_method = get_object_or_404(ComnpayPayment, payment=facture.paiement)
ComnpayPayment, payment=facture.paiement)
if not p.validSec(data, payment_method.payment_pass): if not p.validSec(data, payment_method.payment_pass):
return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") return HttpResponseBadRequest("HTTP/1.1 400 Bad Request")
result = True if (request.POST['result'] == 'OK') else False result = True if (request.POST["result"] == "OK") else False
idTpe = request.POST['idTpe'] idTpe = request.POST["idTpe"]
# Checking that the payment is actually for us. # Checking that the payment is actually for us.
if not idTpe == payment_method.payment_credential: if not idTpe == payment_method.payment_credential:
@ -136,4 +127,3 @@ def ipn(request):
# Everything worked we send a reponse to Comnpay indicating that # Everything worked we send a reponse to Comnpay indicating that
# it's ok for them to proceed # it's ok for them to proceed
return HttpResponse("HTTP/1.0 200 OK") return HttpResponse("HTTP/1.0 200 OK")

View file

@ -24,6 +24,7 @@ from django.utils.translation import ugettext_lazy as _
from . import PAYMENT_METHODS from . import PAYMENT_METHODS
from cotisations.utils import find_payment_method from cotisations.utils import find_payment_method
def payment_method_factory(payment, *args, creation=True, **kwargs): def payment_method_factory(payment, *args, creation=True, **kwargs):
"""This function finds the right payment method form for a given payment. """This function finds the right payment method form for a given payment.
@ -40,12 +41,10 @@ def payment_method_factory(payment, *args, creation=True, **kwargs):
Returns: Returns:
A form or None A form or None
""" """
payment_method = kwargs.pop('instance', find_payment_method(payment)) payment_method = kwargs.pop("instance", find_payment_method(payment))
if payment_method is not None: if payment_method is not None:
return forms.modelform_factory(type(payment_method), fields='__all__')( return forms.modelform_factory(type(payment_method), fields="__all__")(
*args, *args, instance=payment_method, **kwargs
instance=payment_method,
**kwargs
) )
elif creation: elif creation:
return PaymentMethodForm(*args, **kwargs) return PaymentMethodForm(*args, **kwargs)
@ -58,23 +57,24 @@ class PaymentMethodForm(forms.Form):
payment_method = forms.ChoiceField( payment_method = forms.ChoiceField(
label=_("Special payment method"), label=_("Special payment method"),
help_text=_("Warning: you will not be able to change the payment " help_text=_(
"method later. But you will be allowed to edit the other " "Warning: you will not be able to change the payment"
"options." " method later. But you will be allowed to edit the other"
" options."
), ),
required=False required=False,
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(PaymentMethodForm, self).__init__(*args, **kwargs) super(PaymentMethodForm, self).__init__(*args, **kwargs)
prefix = kwargs.get('prefix', None) prefix = kwargs.get("prefix", None)
self.fields['payment_method'].choices = [(i,p.NAME) for (i,p) in enumerate(PAYMENT_METHODS)] self.fields["payment_method"].choices = [
self.fields['payment_method'].choices.insert(0, ('', _('no'))) (i, p.NAME) for (i, p) in enumerate(PAYMENT_METHODS)
self.fields['payment_method'].widget.attrs = { ]
'id': 'paymentMethodSelect' self.fields["payment_method"].choices.insert(0, ("", _("No")))
} self.fields["payment_method"].widget.attrs = {"id": "paymentMethodSelect"}
self.templates = [ self.templates = [
forms.modelform_factory(p.PaymentMethod, fields='__all__')(prefix=prefix) forms.modelform_factory(p.PaymentMethod, fields="__all__")(prefix=prefix)
for p in PAYMENT_METHODS for p in PAYMENT_METHODS
] ]
@ -84,29 +84,29 @@ class PaymentMethodForm(forms.Form):
found. Tries to call `payment_method.valid_form` if it exists. found. Tries to call `payment_method.valid_form` if it exists.
""" """
super(PaymentMethodForm, self).clean() super(PaymentMethodForm, self).clean()
choice = self.cleaned_data['payment_method'] choice = self.cleaned_data["payment_method"]
if choice=='': if choice == "":
return return
choice = int(choice) choice = int(choice)
model = PAYMENT_METHODS[choice].PaymentMethod model = PAYMENT_METHODS[choice].PaymentMethod
form = forms.modelform_factory(model, fields='__all__')(self.data, prefix=self.prefix) form = forms.modelform_factory(model, fields="__all__")(
self.data, prefix=self.prefix
)
self.payment_method = form.save(commit=False) self.payment_method = form.save(commit=False)
if hasattr(self.payment_method, 'valid_form'): if hasattr(self.payment_method, "valid_form"):
self.payment_method.valid_form(self) self.payment_method.valid_form(self)
return self.cleaned_data return self.cleaned_data
def save(self, payment, *args, **kwargs): def save(self, payment, *args, **kwargs):
"""Saves the payment method. """Saves the payment method.
Tries to call `payment_method.alter_payment` if it exists. Tries to call `payment_method.alter_payment` if it exists.
""" """
commit = kwargs.pop('commit', True) commit = kwargs.pop("commit", True)
if not hasattr(self, 'payment_method'): if not hasattr(self, "payment_method"):
return None return None
self.payment_method.payment = payment self.payment_method.payment = payment
if hasattr(self.payment_method, 'alter_payment'): if hasattr(self.payment_method, "alter_payment"):
self.payment_method.alter_payment(payment) self.payment_method.alter_payment(payment)
if commit: if commit:
payment.save() payment.save()

View file

@ -1,10 +1,9 @@
# -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au rezometz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
# Copyright © 2017 Gabriel Détraz # Copyright © 2019 Hugo Levy-Falk
# Copyright © 2017 Goulven Kermarec
# Copyright © 2017 Augustin Lemesle
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -19,10 +18,11 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""cotisations.tests
The tests for the Cotisations module.
""" """
This module contains a method to pay online using user balance.
"""
from . import models
# from django.test import TestCase NAME = "FREE"
# Create your tests here. PaymentMethod = models.FreePayment

View file

@ -0,0 +1,54 @@
# -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
# Copyright © 2018 Hugo Levy-Falk
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from cotisations.models import Paiement
from cotisations.payment_methods.mixins import PaymentMethodMixin
class FreePayment(PaymentMethodMixin, models.Model):
"""
The model allowing you to bypass payment if the invoice is free.
"""
class Meta:
verbose_name = _("Free payment")
payment = models.OneToOneField(
Paiement,
on_delete=models.CASCADE,
related_name="payment_method_free",
editable=False,
)
def end_payment(self, invoice, request):
"""Ends the payment normally.
"""
return invoice.paiement.end_payment(invoice, request, use_payment_method=False)
def check_price(self, price, user, *args, **kwargs):
"""Checks that the price meets the requirement to be paid with user
balance.
"""
return (price == 0, _("You can't pay this invoice for free."))

View file

@ -29,5 +29,4 @@ class PaymentMethodMixin:
Must return a HttpResponse-like object. Must return a HttpResponse-like object.
""" """
return self.payment.end_payment( return self.payment.end_payment(invoice, request, use_payment_method=False)
invoice, request, use_payment_method=False)

View file

@ -22,5 +22,6 @@
This module contains a method to pay online using comnpay. This module contains a method to pay online using comnpay.
""" """
from . import models, urls from . import models, urls
NAME = "NOTE" NAME = "NOTE"
PaymentMethod = models.NotePayment PaymentMethod = models.NotePayment

View file

@ -24,15 +24,11 @@ from django.utils.translation import ugettext_lazy as _
from cotisations.utils import find_payment_method from cotisations.utils import find_payment_method
class NoteCredentialForm(forms.Form): class NoteCredentialForm(forms.Form):
"""A special form to get credential to connect to a NoteKfet2015 server throught his API """A special form to get credential to connect to a NoteKfet2015 server throught his API
object. object.
""" """
login = forms.CharField(
label=_("pseudo note")
)
password = forms.CharField(
label=_("Password"),
widget=forms.PasswordInput
)
login = forms.CharField(label=_("Username"))
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)

View file

@ -41,25 +41,17 @@ class NotePayment(PaymentMethodMixin, models.Model):
payment = models.OneToOneField( payment = models.OneToOneField(
Paiement, Paiement,
on_delete = models.CASCADE, on_delete=models.CASCADE,
related_name = 'payment_method', related_name="payment_method_note",
editable = False editable=False,
)
server = models.CharField(
max_length=255,
verbose_name=_("server")
)
port = models.PositiveIntegerField(
blank = True,
null = True
)
id_note = models.PositiveIntegerField(
blank = True,
null = True
) )
server = models.CharField(max_length=255, verbose_name=_("server"))
port = models.PositiveIntegerField(blank=True, null=True)
id_note = models.PositiveIntegerField(blank=True, null=True)
def end_payment(self, invoice, request): def end_payment(self, invoice, request):
return redirect(reverse( return redirect(
'cotisations:note_kfet:note_payment', reverse(
kwargs={'factureid': invoice.id} "cotisations:note_kfet:note_payment", kwargs={"factureid": invoice.id}
)) )
)

View file

@ -12,13 +12,14 @@ import traceback
def get_response(socket): def get_response(socket):
length_str = b'' length_str = b""
char = socket.recv(1) char = socket.recv(1)
while char != b'\n': while char != b"\n":
length_str += char length_str += char
char = socket.recv(1) char = socket.recv(1)
total = int(length_str) total = int(length_str)
return json.loads(socket.recv(total).decode('utf-8')) return json.loads(socket.recv(total).decode("utf-8"))
def connect(server, port): def connect(server, port):
sock = socket.socket() sock = socket.socket()
@ -35,7 +36,8 @@ def connect(server, port):
return (False, sock, "Serveur indisponible") return (False, sock, "Serveur indisponible")
return (True, sock, "") return (True, sock, "")
def login(server, port, username, password, masque = [[], [], True]):
def login(server, port, username, password, masque=[[], [], True]):
result, sock, err = connect(server, port) result, sock, err = connect(server, port)
if not result: if not result:
return (False, None, err) return (False, None, err)
@ -43,7 +45,7 @@ def login(server, port, username, password, masque = [[], [], True]):
commande = ["login", [username, password, "bdd", masque]] commande = ["login", [username, password, "bdd", masque]]
sock.send(json.dumps(commande).encode("utf-8")) sock.send(json.dumps(commande).encode("utf-8"))
response = get_response(sock) response = get_response(sock)
retcode = response['retcode'] retcode = response["retcode"]
if retcode == 0: if retcode == 0:
return (True, sock, "") return (True, sock, "")
elif retcode == 5: elif retcode == 5:
@ -60,11 +62,28 @@ def don(sock, montant, id_note, facture):
Faire faire un don à l'id_note Faire faire un don à l'id_note
""" """
try: try:
sock.send(json.dumps(["dons", [[id_note], round(montant*100), "Facture : id=%s, designation=%s" % (facture.id, facture.name())]]).encode("utf-8")) sock.send(
json.dumps(
[
"dons",
[
[id_note],
round(montant * 100),
"Facture : id=%s, designation=%s"
% (facture.id, facture.name()),
],
]
).encode("utf-8")
)
response = get_response(sock) response = get_response(sock)
retcode = response['retcode'] retcode = response["retcode"]
transaction_retcode = response["msg"][0][0] transaction_retcode = response["msg"][0][0]
if 0 < retcode < 100 or 200 <= retcode or 0 < transaction_retcode < 100 or 200 <= transaction_retcode: if (
0 < retcode < 100
or 200 <= retcode
or 0 < transaction_retcode < 100
or 200 <= transaction_retcode
):
return (False, "Transaction échouée. (Solde trop négatif ?)") return (False, "Transaction échouée. (Solde trop négatif ?)")
elif retcode == 0: elif retcode == 0:
return (True, "") return (True, "")

View file

@ -23,8 +23,6 @@ from . import views
urlpatterns = [ urlpatterns = [
url( url(
r'^note_payment/(?P<factureid>[0-9]+)$', r"^note_payment/(?P<factureid>[0-9]+)$", views.note_payment, name="note_payment"
views.note_payment, )
name='note_payment'
),
] ]

View file

@ -39,13 +39,11 @@ from cotisations.models import Facture
from cotisations.utils import find_payment_method from cotisations.utils import find_payment_method
from .models import NotePayment from .models import NotePayment
from re2o.views import form from re2o.views import form
from re2o.acl import ( from re2o.acl import can_create, can_edit
can_create,
can_edit
)
from .note import login, don from .note import login, don
from .forms import NoteCredentialForm from .forms import NoteCredentialForm
@login_required @login_required
@can_edit(Facture) @can_edit(Facture)
def note_payment(request, facture, factureid): def note_payment(request, facture, factureid):
@ -58,40 +56,38 @@ def note_payment(request, facture, factureid):
payment_method = find_payment_method(facture.paiement) payment_method = find_payment_method(facture.paiement)
if not payment_method or not isinstance(payment_method, NotePayment): if not payment_method or not isinstance(payment_method, NotePayment):
messages.error(request, _("Unknown error.")) messages.error(request, _("Unknown error."))
return redirect(reverse( return redirect(reverse("users:profil", kwargs={"userid": user.id}))
'users:profil',
kwargs={'userid': user.id}
))
noteform = NoteCredentialForm(request.POST or None) noteform = NoteCredentialForm(request.POST or None)
if noteform.is_valid(): if noteform.is_valid():
pseudo = noteform.cleaned_data['login'] pseudo = noteform.cleaned_data["login"]
password = noteform.cleaned_data['password'] password = noteform.cleaned_data["password"]
result, sock, err = login(payment_method.server, payment_method.port, pseudo, password) result, sock, err = login(
payment_method.server, payment_method.port, pseudo, password
)
if not result: if not result:
messages.error(request, err) messages.error(request, err)
return form( return form(
{'form': noteform, 'amount': facture.prix_total()}, {"form": noteform, "amount": facture.prix_total()},
"cotisations/payment.html", "cotisations/payment.html",
request request,
) )
else: else:
result, err = don(sock, facture.prix_total(), payment_method.id_note, facture) result, err = don(
sock, facture.prix_total(), payment_method.id_note, facture
)
if not result: if not result:
messages.error(request, err) messages.error(request, err)
return form( return form(
{'form': noteform, 'amount': facture.prix_total()}, {"form": noteform, "amount": facture.prix_total()},
"cotisations/payment.html", "cotisations/payment.html",
request request,
) )
facture.valid = True facture.valid = True
facture.save() facture.save()
messages.success(request, _("The payment with note was done.")) messages.success(request, _("The payment with note was done."))
return redirect(reverse( return redirect(reverse("users:profil", kwargs={"userid": user.id}))
'users:profil',
kwargs={'userid': user.id}
))
return form( return form(
{'form': noteform, 'amount': facture.prix_total()}, {"form": noteform, "amount": facture.prix_total()},
"cotisations/payment.html", "cotisations/payment.html",
request request,
) )

View file

@ -22,7 +22,7 @@ from django.conf.urls import include, url
from . import comnpay, cheque, note_kfet from . import comnpay, cheque, note_kfet
urlpatterns = [ urlpatterns = [
url(r'^comnpay/', include(comnpay.urls, namespace='comnpay')), url(r"^comnpay/", include(comnpay.urls, namespace="comnpay")),
url(r'^cheque/', include(cheque.urls, namespace='cheque')), url(r"^cheque/", include(cheque.urls, namespace="cheque")),
url(r'^note_kfet/', include(note_kfet.urls, namespace='note_kfet')), url(r"^note_kfet/", include(note_kfet.urls, namespace="note_kfet")),
] ]

View file

@ -4,7 +4,7 @@ se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
Copyright © 2017 Gabriel Détraz Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<th>{% trans "Price" %}</th> <th>{% trans "Price" %}</th>
<th>{% trans "Subscription type" %}</th> <th>{% trans "Subscription type" %}</th>
<th>{% trans "Duration (in months)" %}</th> <th>{% trans "Duration (in months)" %}</th>
<th>{% trans "Duration (in days)" %}</th>
<th>{% trans "Concerned users" %}</th> <th>{% trans "Concerned users" %}</th>
<th>{% trans "Available for everyone" %}</th> <th>{% trans "Available for everyone" %}</th>
<th></th> <th></th>
@ -45,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ article.prix }}</td> <td>{{ article.prix }}</td>
<td>{{ article.type_cotisation }}</td> <td>{{ article.type_cotisation }}</td>
<td>{{ article.duration }}</td> <td>{{ article.duration }}</td>
<td>{{ article.duration_days }}</td>
<td>{{ article.type_user }}</td> <td>{{ article.type_user }}</td>
<td>{{ article.available_for_everyone | tick }}</td> <td>{{ article.available_for_everyone | tick }}</td>
<td class="text-right"> <td class="text-right">

View file

@ -4,7 +4,7 @@ se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
Copyright © 2017 Gabriel Détraz Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify

View file

@ -4,7 +4,7 @@ se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
Copyright © 2017 Gabriel Détraz Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify

View file

@ -4,7 +4,7 @@ se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
Copyright © 2017 Gabriel Détraz Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
@ -31,7 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<thead> <thead>
<tr> <tr>
<th>{% trans "Payment type" %}</th> <th>{% trans "Payment type" %}</th>
<th>{% trans "Is available for everyone" %}</th> <th>{% trans "Available for everyone" %}</th>
<th>{% trans "Custom payment method" %}</th> <th>{% trans "Custom payment method" %}</th>
<th></th> <th></th>
</tr> </tr>

View file

@ -5,7 +5,7 @@ se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
Copyright © 2017 Gabriel Détraz Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
@ -45,12 +45,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<tr> <tr>
<th>{% trans "Profile" %}</th> <th>{% trans "Profile" %}</th>
<th> <th>
{% trans "Last name" as tr_last_name %} {% trans "First name" as tr_first_name %}
{% include 'buttons/sort.html' with prefix='control' col='name' text=tr_last_name %} {% include 'buttons/sort.html' with prefix='control' col='name' text=tr_first_name %}
</th> </th>
<th> <th>
{% trans "First name" as tr_first_name %} {% trans "Surname" as tr_surname %}
{% include 'buttons/sort.html' with prefix='control' col='surname' text=tr_first_name %} {% include 'buttons/sort.html' with prefix='control' col='surname' text=tr_surname %}
</th> </th>
<th> <th>
{% trans "Invoice ID" as tr_invoice_id %} {% trans "Invoice ID" as tr_invoice_id %}
@ -104,8 +104,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% trans "Edit" as tr_edit %} {% trans "Confirm" as tr_confirm %}
{% bootstrap_button tr_edit button_type='submit' icon='ok' button_class='btn-success' %} {% bootstrap_button tr_confirm button_type='submit' icon='ok' button_class='btn-success' %}
</form> </form>
{% endblock %} {% endblock %}

View file

@ -5,7 +5,7 @@ se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
Copyright © 2017 Gabriel Détraz Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<form class="form" method="post"> <form class="form" method="post">
{% csrf_token %} {% csrf_token %}
<h4> <h4>
{% blocktrans %}Warning: are you sure you really want to delete this {{ object_name }} object ( {{ objet }} )?{% endblocktrans %} {% blocktrans %}Warning: are you sure you really want to delete this {{ objet_name }} object ( {{ objet }} )?{% endblocktrans %}
</h4> </h4>
{% trans "Confirm" as tr_confirm %} {% trans "Confirm" as tr_confirm %}
{% bootstrap_button tr_confirm button_type='submit' icon='trash' button_class='btn-danger' %} {% bootstrap_button tr_confirm button_type='submit' icon='trash' button_class='btn-danger' %}

View file

@ -5,7 +5,7 @@ se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
Copyright © 2017 Gabriel Détraz Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify

View file

@ -6,17 +6,17 @@ Nous vous remercions pour votre achat auprès de {{asso_name}} et nous vous en j
En cas de question, nhésitez pas à nous contacter par mail à {{contact_mail}}. En cas de question, nhésitez pas à nous contacter par mail à {{contact_mail}}.
Cordialement, Respectueusement,
Léquipe de {{asso_name}} Léquipe de {{asso_name}}.
=== English version === === English version ===
Dear {{name}}, Hello {{name}},
Thank you for your purchase. Here is your invoice. Thank you for your purchase at {{asso_name}}. Here is your invoice.
Should you need extra information, you can email us at {{contact_mail}}. Should you need extra information, do not hesitate to email us at {{contact_mail}}.
Best regards, Regards,
{{ asso_name }}'s team The {{ asso_name }} team.

View file

@ -1,22 +1,23 @@
Bonjour {{name}} ! Bonjour {{name}} !
Nous vous informons que votre cotisation auprès de {{asso_name}} a été acceptée. Vous voilà donc membre de l'association. Nous vous informons que votre cotisation auprès de {{asso_name}} a été acceptée. Vous voilà donc membre de l'association jusqu'au {{ date_end|date:"d/m/Y" }}.
Vous trouverez en pièce jointe un reçu. Vous trouverez en pièce jointe un reçu.
Pour nous faire part de toute remarque, suggestion ou problème vous pouvez nous envoyer un mail à {{asso_email}}. Pour nous faire part de toute remarque, suggestion ou problème vous pouvez nous envoyer un mail à {{asso_email}}.
À bientôt, Respectueusement,
L'équipe de {{asso_name}}. L'équipe de {{asso_name}}.
--- ---
Your subscription to {{asso_name}} has just been accepted. You are now a full member of {{asso_name}}. Hello {{name}}!
Your subscription to {{asso_name}} has just been accepted. You are now a full member of {{asso_name}} until {{ date_end|date:"d/m/Y" }}.
You will find with this email a subscription voucher. You will find with this email a subscription voucher.
For any information, suggestion or problem, you can contact us via email at To express any comment, suggestion or problem, you can send us an email to {{asso_email}}.
{{asso_email}}.
Regards, Regards,
The {{asso_name}} team. The {{asso_name}} team.

View file

@ -5,7 +5,7 @@ se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
Copyright © 2017 Gabriel Détraz Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
@ -44,9 +44,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% blocktrans %}Current balance: {{ balance }} €{% endblocktrans %} {% blocktrans %}Current balance: {{ balance }} €{% endblocktrans %}
</p> </p>
{% endif %} {% endif %}
{% if factureform %}
{% bootstrap_form_errors factureform %}
{% endif %}
{% if discount_form %} {% if discount_form %}
{% bootstrap_form_errors discount_form %} {% bootstrap_form_errors discount_form %}
{% endif %} {% endif %}
@ -90,7 +87,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% if articlesformset %} {% if articlesformset %}
var prices = {}; var prices = {};
{% for article in articlelist %} {% for article in articlelist %}
prices[{{ article.id|escapejs }}] = {{ article.prix }}; prices[{{ article.id|escapejs }}] = "{{ article.prix }}";
{% endfor %} {% endfor %}
var template = `Article : &nbsp; var template = `Article : &nbsp;
@ -124,7 +121,7 @@ function update_price(){
if (article == '') { if (article == '') {
continue; continue;
} }
article_price = prices[article]; article_price = parseFloat(prices[article].replace(',', '.'));
quantity = document.getElementById( quantity = document.getElementById(
'id_form-' + i.toString() + '-quantity').value; 'id_form-' + i.toString() + '-quantity').value;
price += article_price * quantity; price += article_price * quantity;

View file

@ -5,7 +5,7 @@ se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
Copyright © 2017 Gabriel Détraz Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify

View file

@ -5,7 +5,7 @@ se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
Copyright © 2017 Gabriel Détraz Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
@ -30,14 +30,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% block title %}{% trans "Articles" %}{% endblock %} {% block title %}{% trans "Articles" %}{% endblock %}
{% block content %} {% block content %}
<h2>{% trans "List of article types" %}</h2> <h2>{% trans "List of articles" %}</h2>
{% can_create Article %} {% can_create Article %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-article' %}"> <a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-article' %}">
<i class="fa fa-cart-plus"></i> {% trans "Add an article type" %} <i class="fa fa-plus"></i> {% trans "Add an article" %}
</a> </a>
{% acl_end %} {% acl_end %}
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-article' %}"> <a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-article' %}">
<i class="fa fa-trash"></i> {% trans "Delete one or several article types" %} <i class="fa fa-trash"></i> {% trans "Delete one or several articles" %}
</a> </a>
{% include 'cotisations/aff_article.html' with article_list=article_list %} {% include 'cotisations/aff_article.html' with article_list=article_list %}
{% endblock %} {% endblock %}

View file

@ -5,7 +5,7 @@ se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
Copyright © 2017 Gabriel Détraz Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<h2>{% trans "List of banks" %}</h2> <h2>{% trans "List of banks" %}</h2>
{% can_create Banque %} {% can_create Banque %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-banque' %}"> <a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-banque' %}">
<i class="fa fa-cart-plus"></i> {% trans "Add a bank" %} <i class="fa fa-plus"></i> {% trans "Add a bank" %}
</a> </a>
{% acl_end %} {% acl_end %}
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-banque' %}"> <a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-banque' %}">

Some files were not shown because too many files have changed in this diff Show more