manager: display applications (#86636) #162
|
@ -33,6 +33,7 @@ Depends: python3-django-mellon,
|
|||
python3-hobo,
|
||||
python3-lingo (= ${binary:Version}),
|
||||
python3-psycopg2,
|
||||
python3-sorl-thumbnail,
|
||||
python3-uwsgidecorators,
|
||||
uwsgi,
|
||||
uwsgi-plugin-python3,
|
||||
|
|
|
@ -21,10 +21,11 @@ from django.db import models
|
|||
from django.utils.text import slugify
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from lingo.export_import.models import WithApplicationMixin
|
||||
from lingo.utils.misc import LingoImportError, clean_import_data, generate_slug
|
||||
|
||||
|
||||
class Agenda(models.Model):
|
||||
class Agenda(WithApplicationMixin, models.Model):
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
category_label = models.CharField(_('Category label'), max_length=150, null=True)
|
||||
|
@ -125,7 +126,7 @@ class Agenda(models.Model):
|
|||
return _('Events')
|
||||
|
||||
|
||||
class CheckTypeGroup(models.Model):
|
||||
class CheckTypeGroup(WithApplicationMixin, models.Model):
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
|
||||
|
|
|
@ -21,6 +21,14 @@ from django.contrib.contenttypes.models import ContentType
|
|||
from django.db import models
|
||||
|
||||
|
||||
class WithApplicationMixin:
|
||||
@property
|
||||
def applications(self):
|
||||
pmarillonnet marked this conversation as resolved
Outdated
|
||||
if getattr(self, '_applications', None) is None:
|
||||
Application.load_for_object(self)
|
||||
return self._applications
|
||||
|
||||
|
||||
class Application(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
slug = models.SlugField(max_length=100, unique=True)
|
||||
|
@ -71,34 +79,23 @@ class Application(models.Model):
|
|||
@classmethod
|
||||
def populate_objects(cls, object_class, objects):
|
||||
content_type = ContentType.objects.get_for_model(object_class)
|
||||
elements = ApplicationElement.objects.filter(content_type=content_type)
|
||||
elements = ApplicationElement.objects.filter(
|
||||
content_type=content_type, application__visible=True
|
||||
).prefetch_related('application')
|
||||
elements_by_objects = collections.defaultdict(list)
|
||||
for element in elements:
|
||||
elements_by_objects[element.content_object].append(element)
|
||||
applications_by_ids = {
|
||||
a.pk: a for a in cls.objects.filter(pk__in=elements.values('application'), visible=True)
|
||||
}
|
||||
elements_by_objects[element.object_id].append(element)
|
||||
for obj in objects:
|
||||
applications = []
|
||||
elements = elements_by_objects.get(obj) or []
|
||||
for element in elements:
|
||||
application = applications_by_ids.get(element.application_id)
|
||||
if application:
|
||||
applications.append(application)
|
||||
applications = [element.application for element in elements_by_objects.get(obj.pk) or []]
|
||||
obj._applications = sorted(applications, key=lambda a: a.name)
|
||||
|
||||
@classmethod
|
||||
def load_for_object(cls, obj):
|
||||
content_type = ContentType.objects.get_for_model(obj.__class__)
|
||||
elements = ApplicationElement.objects.filter(content_type=content_type, object_id=obj.pk)
|
||||
applications_by_ids = {
|
||||
a.pk: a for a in cls.objects.filter(pk__in=elements.values('application'), visible=True)
|
||||
}
|
||||
applications = []
|
||||
for element in elements:
|
||||
application = applications_by_ids.get(element.application_id)
|
||||
if application:
|
||||
applications.append(application)
|
||||
elements = ApplicationElement.objects.filter(
|
||||
content_type=content_type, object_id=obj.pk, application__visible=True
|
||||
).prefetch_related('application')
|
||||
applications = [element.application for element in elements]
|
||||
obj._applications = sorted(applications, key=lambda a: a.name)
|
||||
|
||||
def get_objects_for_object_class(self, object_class):
|
||||
|
@ -106,6 +103,12 @@ class Application(models.Model):
|
|||
elements = ApplicationElement.objects.filter(content_type=content_type, application=self)
|
||||
return object_class.objects.filter(pk__in=elements.values('object_id'))
|
||||
|
||||
@classmethod
|
||||
def get_orphan_objects_for_object_class(cls, object_class):
|
||||
content_type = ContentType.objects.get_for_model(object_class)
|
||||
elements = ApplicationElement.objects.filter(content_type=content_type, application__visible=True)
|
||||
pmarillonnet marked this conversation as resolved
Outdated
pmarillonnet
commented
Pour quelle raison ici la recherche des objets orphelins se soucie de Pour quelle raison ici la recherche des objets orphelins se soucie de `application__visible=True` alors que la méthode similaire `get_objects_for_object_class` (juste au dessus) ne prend pas en compte ce flag `visible` ?
lguerin
commented
(cf WithApplicationsMixin)
`get_objects_for_object_class` s'applique à une Application déjà connue (on l'a dans l'url), et visible:
```
if 'application' in self.request.GET:
self.application = get_object_or_404(
Application, slug=self.request.GET['application'], visible=True
)
```
(cf WithApplicationsMixin)
`get_orphan_objects_for_object_class`cherche les objets ne faisant partie d'aucune application visible
pmarillonnet
commented
Hmm, okay, j’avais loupé ça, merci pour l’explication. Hmm, okay, j’avais loupé ça, merci pour l’explication.
|
||||
return object_class.objects.exclude(pk__in=elements.values('object_id'))
|
||||
|
||||
|
||||
class ApplicationElement(models.Model):
|
||||
application = models.ForeignKey(Application, on_delete=models.CASCADE)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{% load thumbnail %}
|
||||
{% if application %}
|
||||
<h2>
|
||||
{% thumbnail application.icon '64x64' format='PNG' as im %}
|
||||
<img src="{{ im.url }}" alt="" class="application-logo" />
|
||||
{% endthumbnail %}
|
||||
{{ application }}
|
||||
</h2>
|
||||
{% elif no_application %}
|
||||
<h2>{{ title_no_application }}</h2>
|
||||
{% else %}
|
||||
<h2>{{ title_object_list }}</h2>
|
||||
{% endif %}
|
|
@ -0,0 +1,5 @@
|
|||
{% if application %}
|
||||
<a href="{{ object_list_url }}?application={{ application.slug }}">{{ application }}</a>
|
||||
{% elif no_application %}
|
||||
<a href="{{ object_list_url }}?no-application">{{ title_no_application }}</a>
|
||||
{% endif %}
|
|
@ -0,0 +1,12 @@
|
|||
{% load i18n thumbnail %}
|
||||
{% if object.applications %}
|
||||
<h3>{% trans "Applications" %}</h3>
|
||||
{% for application in object.applications %}
|
||||
<a class="button button-paragraph" href="{{ object_list_url }}?application={{ application.slug }}">
|
||||
{% thumbnail application.icon '16x16' format='PNG' as im %}
|
||||
<img src="{{ im.url }}" alt="" class="application-icon" width="16" />
|
||||
{% endthumbnail %}
|
||||
{{ application }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
|
@ -0,0 +1,8 @@
|
|||
{% load thumbnail %}
|
||||
{% if not application and not no_application %}
|
||||
{% for application in object.applications %}
|
||||
{% thumbnail application.icon '16x16' format='PNG' as im %}
|
||||
<img src="{{ im.url }}" alt="" class="application-icon" width="16" />
|
||||
{% endthumbnail %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
|
@ -0,0 +1,15 @@
|
|||
{% load i18n thumbnail %}
|
||||
{% if applications %}
|
||||
<h3>{% trans "Applications" %}</h3>
|
||||
{% for application in applications %}
|
||||
<a class="button button-paragraph" href="?application={{ application.slug }}">
|
||||
{% thumbnail application.icon '16x16' format='PNG' as im %}
|
||||
<img src="{{ im.url }}" alt="" class="application-icon" width="16" />
|
||||
{% endthumbnail %}
|
||||
{{ application }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
<a class="button button-paragraph" href="?no-application">
|
||||
{{ title_no_application }}
|
||||
</a>
|
||||
{% endif %}
|
|
@ -0,0 +1,47 @@
|
|||
# lingo - payment and billing system
|
||||
# Copyright (C) 2022-2024 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from lingo.export_import.models import Application
|
||||
|
||||
|
||||
class WithApplicationsMixin:
|
||||
def with_applications_dispatch(self, request):
|
||||
self.application = None
|
||||
self.no_application = False
|
||||
if 'application' in self.request.GET:
|
||||
self.application = get_object_or_404(
|
||||
Application, slug=self.request.GET['application'], visible=True
|
||||
)
|
||||
elif 'no-application' in self.request.GET:
|
||||
self.no_application = True
|
||||
|
||||
def with_applications_context_data(self, context):
|
||||
if self.application:
|
||||
context['application'] = self.application
|
||||
elif not self.no_application:
|
||||
Application.populate_objects(self.model, self.object_list)
|
||||
context['applications'] = Application.select_for_object_class(self.model)
|
||||
context['no_application'] = self.no_application
|
||||
return context
|
||||
|
||||
def with_applications_queryset(self):
|
||||
if self.application:
|
||||
return self.application.get_objects_for_object_class(self.model)
|
||||
if self.no_application:
|
||||
return Application.get_orphan_objects_for_object_class(self.model)
|
||||
return super().get_queryset()
|
|
@ -40,6 +40,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
|
||||
from lingo.agendas.chrono import ChronoError, lock_events_check
|
||||
from lingo.agendas.models import Agenda
|
||||
from lingo.export_import.models import WithApplicationMixin
|
||||
from lingo.utils.fields import RichTextField
|
||||
from lingo.utils.misc import LingoImportError, clean_import_data, generate_slug
|
||||
from lingo.utils.wcs import (
|
||||
|
@ -74,7 +75,7 @@ class PayerDataError(InvoicingError):
|
|||
pass
|
||||
|
||||
|
||||
class Payer(models.Model):
|
||||
class Payer(WithApplicationMixin, models.Model):
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
description = models.TextField(_('Description'), null=True, blank=True)
|
||||
|
@ -296,7 +297,7 @@ INVOICE_MODELS = [
|
|||
]
|
||||
|
||||
|
||||
class Regie(models.Model):
|
||||
class Regie(WithApplicationMixin, models.Model):
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
description = models.TextField(
|
||||
|
|
|
@ -93,5 +93,8 @@
|
|||
<a class="button button-paragraph" href="{% url 'lingo-manager-invoicing-payer-edit' pk=payer.pk %}" rel="popup">{% trans "Edit" %}</a>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-invoicing-payer-export' pk=payer.pk %}">{% trans 'Export' %}</a>
|
||||
|
||||
{% url 'lingo-manager-invoicing-payer-list' as object_list_url %}
|
||||
{% include 'lingo/includes/application_detail_fragment.html' %}
|
||||
|
||||
</aside>
|
||||
{% endblock %}
|
||||
|
|
|
@ -4,12 +4,15 @@
|
|||
{% block page-title-extra-label %}{% trans "Payers" %}{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'lingo-manager-invoicing-payer-list' %}">{% trans "Payers" %}</a>
|
||||
<a href="{% url 'lingo-manager-homepage' %}">{% trans "Payments" context 'lingo title' %}</a>
|
||||
<a href="{% url 'lingo-manager-invoicing-regie-list' %}">{% trans "Regies" %}</a>
|
||||
{% url 'lingo-manager-invoicing-payer-list' as object_list_url %}
|
||||
<a href="{{ object_list_url }}">{% trans "Payers" %}</a>
|
||||
{% include 'lingo/includes/application_breadcrumb_fragment.html' with title_no_application=_('Payers outside applications') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans 'Payers' %}</h2>
|
||||
{% include 'lingo/includes/application_appbar_fragment.html' with title_no_application=_('Payers outside applications') title_object_list=_('Payers') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -19,6 +22,7 @@
|
|||
{% for payer in object_list %}
|
||||
<li>
|
||||
<a href="{% url 'lingo-manager-invoicing-payer-detail' pk=payer.pk %}">
|
||||
{% include 'lingo/includes/application_icon_fragment.html' with object=payer %}
|
||||
{{ payer.label }}
|
||||
<span class="extra-info"> [{% trans "identifier:" %} {{ payer.slug }}]</span>
|
||||
</a>
|
||||
|
@ -26,8 +30,7 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
{% elif not no_application %}
|
||||
<div class="big-msg-info">
|
||||
{% blocktrans %}
|
||||
This site doesn't have any payer yet. Click on the "New" button in the top
|
||||
|
@ -38,10 +41,14 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
<aside id="sidebar">
|
||||
{% if not application and not no_application %}
|
||||
<aside id="sidebar">
|
||||
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-invoicing-payer-add' %}">{% trans 'New payer' %}</a>
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-invoicing-payer-add' %}">{% trans 'New payer' %}</a>
|
||||
|
||||
</aside>
|
||||
{% include 'lingo/includes/application_list_fragment.html' with title_no_application=_('Payers outside applications') %}
|
||||
|
||||
</aside>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -54,5 +54,8 @@
|
|||
<a class="button button-paragraph" href="{% url 'lingo-manager-invoicing-regie-refund-list' regie_pk=regie.pk %}">{% trans 'Refunds' %}</a>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-invoicing-non-invoiced-line-list' regie_pk=regie.pk %}">{% trans 'Non invoiced lines' %}</a>
|
||||
|
||||
{% url 'lingo-manager-invoicing-regie-list' as object_list_url %}
|
||||
{% include 'lingo/includes/application_detail_fragment.html' %}
|
||||
|
||||
</aside>
|
||||
{% endblock %}
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'lingo-manager-invoicing-regie-list' %}">{% trans "Regies" %}</a>
|
||||
{% url 'lingo-manager-invoicing-regie-list' as object_list_url %}
|
||||
<a href="{{ object_list_url }}">{% trans "Regies" %}</a>
|
||||
{% include 'lingo/includes/application_breadcrumb_fragment.html' with title_no_application=_('Regies outside applications') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans 'Regies' %}</h2>
|
||||
{% include 'lingo/includes/application_appbar_fragment.html' with title_no_application=_('Regies outside applications') title_object_list=_('Regies') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -19,6 +21,7 @@
|
|||
{% for regie in object_list %}
|
||||
<li>
|
||||
<a href="{% url 'lingo-manager-invoicing-regie-detail' pk=regie.pk %}">
|
||||
{% include 'lingo/includes/application_icon_fragment.html' with object=regie %}
|
||||
{{ regie.label }}
|
||||
<span class="extra-info"> [{% trans "identifier:" %} {{ regie.slug }}]</span>
|
||||
</a>
|
||||
|
@ -26,8 +29,7 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
{% elif not no_application %}
|
||||
<div class="big-msg-info">
|
||||
{% blocktrans %}
|
||||
This site doesn't have any regie yet. Click on the "New" button in the top
|
||||
|
@ -38,16 +40,20 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
<aside id="sidebar">
|
||||
{% if not application and not no_application %}
|
||||
<aside id="sidebar">
|
||||
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-invoicing-regie-add' %}">{% trans 'New regie' %}</a>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-invoicing-config-import' %}">{% trans 'Import site' %}</a>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-invoicing-config-export' %}" data-autoclose-dialog="true">{% trans 'Export site' %}</a>
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-invoicing-regie-add' %}">{% trans 'New regie' %}</a>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-invoicing-config-import' %}">{% trans 'Import site' %}</a>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-invoicing-config-export' %}" data-autoclose-dialog="true">{% trans 'Export site' %}</a>
|
||||
|
||||
<h3>{% trans "Navigation" %}</h3>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-invoicing-appearance-settings' %}">{% trans "Appearance Settings" %}</a>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-invoicing-payer-list' %}">{% trans "Payers" %}</a>
|
||||
<h3>{% trans "Navigation" %}</h3>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-invoicing-appearance-settings' %}">{% trans "Appearance Settings" %}</a>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-invoicing-payer-list' %}">{% trans "Payers" %}</a>
|
||||
|
||||
</aside>
|
||||
{% include 'lingo/includes/application_list_fragment.html' with title_no_application=_('Regies outside applications') %}
|
||||
|
||||
</aside>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -21,14 +21,26 @@ from django.http import HttpResponse
|
|||
from django.urls import reverse
|
||||
from django.views.generic import CreateView, DeleteView, DetailView, ListView, UpdateView
|
||||
|
||||
from lingo.export_import.views import WithApplicationsMixin
|
||||
from lingo.invoicing.forms import NewPayerForm, PayerForm, PayerMappingForm
|
||||
from lingo.invoicing.models import Payer
|
||||
|
||||
|
||||
class PayersListView(ListView):
|
||||
class PayersListView(WithApplicationsMixin, ListView):
|
||||
template_name = 'lingo/invoicing/manager_payer_list.html'
|
||||
model = Payer
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.with_applications_dispatch(request)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return self.with_applications_queryset()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
return self.with_applications_context_data(context)
|
||||
|
||||
|
||||
payers_list = PayersListView.as_view()
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from django.views.generic import CreateView, DeleteView, DetailView, ListView, UpdateView
|
||||
|
||||
from lingo.agendas.models import Agenda
|
||||
from lingo.export_import.views import WithApplicationsMixin
|
||||
from lingo.invoicing.forms import (
|
||||
PaymentTypeForm,
|
||||
RegieCreditFilterSet,
|
||||
|
@ -69,10 +70,21 @@ def import_regies(data):
|
|||
return results
|
||||
|
||||
|
||||
class RegiesListView(ListView):
|
||||
class RegiesListView(WithApplicationsMixin, ListView):
|
||||
template_name = 'lingo/invoicing/manager_regie_list.html'
|
||||
model = Regie
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.with_applications_dispatch(request)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return self.with_applications_queryset()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
return self.with_applications_context_data(context)
|
||||
|
||||
|
||||
regies_list = RegiesListView.as_view()
|
||||
|
||||
|
|
|
@ -170,3 +170,7 @@ span.invoice-colour {
|
|||
border-radius: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.application-logo, .application-icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ from django.utils.text import slugify
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from lingo.agendas.models import Agenda, CheckType
|
||||
from lingo.export_import.models import WithApplicationMixin
|
||||
from lingo.utils.misc import LingoImportError, clean_import_data, generate_slug
|
||||
from lingo.utils.wcs import get_wcs_dependencies_from_template
|
||||
|
||||
|
@ -99,7 +100,7 @@ class PricingBookingCheckTypeError(PricingError):
|
|||
pass
|
||||
|
||||
|
||||
class CriteriaCategory(models.Model):
|
||||
class CriteriaCategory(WithApplicationMixin, models.Model):
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
|
||||
|
@ -261,7 +262,7 @@ class PricingMatrix:
|
|||
rows: list[PricingMatrixRow]
|
||||
|
||||
|
||||
class Pricing(models.Model):
|
||||
class Pricing(WithApplicationMixin, models.Model):
|
||||
label = models.CharField(_('Label'), max_length=150, null=True)
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, null=True)
|
||||
|
||||
|
|
|
@ -116,5 +116,8 @@
|
|||
<a class="button button-paragraph" href="{{ chrono_url }}">{% trans "Agenda options" %}</a>
|
||||
{% endif %}{% endwith %}
|
||||
|
||||
{% url 'lingo-manager-agenda-list' as object_list_url %}
|
||||
{% include 'lingo/includes/application_detail_fragment.html' %}
|
||||
|
||||
</aside>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
{% extends "lingo/pricing/manager_pricing_list.html" %}
|
||||
{% load i18n %}
|
||||
{% load i18n thumbnail %}
|
||||
|
||||
{% block page-title-extra-label %}{% trans "Agendas" %} | {{ block.super }}{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'lingo-manager-agenda-list' %}">{% trans 'Agendas' %}</a>
|
||||
<a href="{% url 'lingo-manager-homepage' %}">{% trans "Payments" context 'lingo title' %}</a>
|
||||
<a href="{% url 'lingo-manager-pricing-list' %}">{% trans "Pricings" %}</a>
|
||||
{% url 'lingo-manager-agenda-list' as object_list_url %}
|
||||
<a href="{{ object_list_url }}">{% trans "Agendas" %}</a>
|
||||
{% include 'lingo/includes/application_breadcrumb_fragment.html' with title_no_application=_('Agendas outside applications') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans 'Agendas' %}</h2>
|
||||
{% include 'lingo/includes/application_appbar_fragment.html' with title_no_application=_('Agendas outside applications') title_object_list=_('Agendas') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -21,14 +24,17 @@
|
|||
<ul class="objects-list single-links">
|
||||
{% for object in group.list %}
|
||||
<li>
|
||||
<a href="{% url 'lingo-manager-agenda-detail' object.pk %}">{{ object.label }} <span class="identifier">[{% trans "identifier:" %} {{ object.slug }}, {% trans "kind:" %} {{ object.get_real_kind_display }}]</a>
|
||||
{% with chrono_url=object.get_chrono_url %}{% if chrono_url %}<a href="{{ chrono_url }}" class="link-action-icon link">{% trans "view" %}</a>{% endif %}{% endwith %}
|
||||
</li>
|
||||
<a href="{% url 'lingo-manager-agenda-detail' object.pk %}">
|
||||
{% include 'lingo/includes/application_icon_fragment.html' %}
|
||||
{{ object.label }} <span class="identifier">[{% trans "identifier:" %} {{ object.slug }}, {% trans "kind:" %} {{ object.get_real_kind_display }}]</span>
|
||||
</a>
|
||||
{% with chrono_url=object.get_chrono_url %}{% if chrono_url %}<a href="{{ chrono_url }}" class="link-action-icon link">{% trans "view" %}</a>{% endif %}{% endwith %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% elif not no_application %}
|
||||
<div class="big-msg-info">
|
||||
{% blocktrans trimmed %}
|
||||
This site doesn't have any agenda yet. Click on the "Refresh agendas" button in the top
|
||||
|
@ -39,10 +45,14 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
<aside id="sidebar">
|
||||
{% if not application and not no_application %}
|
||||
<aside id="sidebar">
|
||||
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-agenda-sync' %}">{% trans 'Refresh agendas' %}</a>
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-agenda-sync' %}">{% trans 'Refresh agendas' %}</a>
|
||||
|
||||
</aside>
|
||||
{% include 'lingo/includes/application_list_fragment.html' with title_no_application=_('Agendas outside applications') %}
|
||||
|
||||
</aside>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -4,22 +4,30 @@
|
|||
{% block page-title-extra-label %}{% trans "Check types" %} | {{ block.super }}{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'lingo-manager-check-type-list' %}">{% trans "Check types" %}</a>
|
||||
<a href="{% url 'lingo-manager-homepage' %}">{% trans "Payments" context 'lingo title' %}</a>
|
||||
<a href="{% url 'lingo-manager-pricing-list' %}">{% trans "Pricings" %}</a>
|
||||
{% url 'lingo-manager-check-type-list' as object_list_url %}
|
||||
<a href="{{ object_list_url }}">{% trans "Check types" %}</a>
|
||||
{% include 'lingo/includes/application_breadcrumb_fragment.html' with title_no_application=_('Check types outside applications') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans 'Check types' %}</h2>
|
||||
{% include 'lingo/includes/application_appbar_fragment.html' with title_no_application=_('Check types outside applications') title_object_list=_('Check types') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pk-information">
|
||||
<p>{% trans "Define here check types used in events agendas to check bookings." %}</p>
|
||||
</div>
|
||||
{% if not application and not no_application %}
|
||||
<div class="pk-information">
|
||||
<p>{% trans "Define here check types used in events agendas to check bookings." %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for object in object_list %}
|
||||
<div class="section check-type-group">
|
||||
<h3>
|
||||
<a rel="popup" href="{% url 'lingo-manager-check-type-group-edit' object.pk %}">{{ object }}</a>
|
||||
<a rel="popup" href="{% url 'lingo-manager-check-type-group-edit' object.pk %}">
|
||||
{% include 'lingo/includes/application_icon_fragment.html' %}
|
||||
{{ object }}
|
||||
</a>
|
||||
<span>
|
||||
<a class="button" href="{% url 'lingo-manager-check-type-group-export' object.pk %}">{% trans "Export"%}</a>
|
||||
<a class="button" rel="popup" href="{% url 'lingo-manager-check-type-group-delete' object.pk %}">{% trans "Delete"%}</a>
|
||||
|
@ -48,20 +56,26 @@
|
|||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="big-msg-info">
|
||||
{% blocktrans trimmed %}
|
||||
This site doesn't have any check type group yet. Click on the "New group" button in the top
|
||||
right of the page to add a first one.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% if not no_application %}
|
||||
<div class="big-msg-info">
|
||||
{% blocktrans trimmed %}
|
||||
This site doesn't have any check type group yet. Click on the "New group" button in the top
|
||||
right of the page to add a first one.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
<aside id="sidebar">
|
||||
{% if not application and not no_application %}
|
||||
<aside id="sidebar">
|
||||
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-check-type-group-add' %}">{% trans 'New group' %}</a>
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-check-type-group-add' %}">{% trans 'New group' %}</a>
|
||||
|
||||
</aside>
|
||||
{% include 'lingo/includes/application_list_fragment.html' with title_no_application=_('Check types outside applications') %}
|
||||
|
||||
</aside>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -4,30 +4,40 @@
|
|||
{% block page-title-extra-label %}{% trans "Criterias" %} | {{ block.super }}{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'lingo-manager-pricing-criteria-list' %}">{% trans "Criterias" %}</a>
|
||||
<a href="{% url 'lingo-manager-homepage' %}">{% trans "Payments" context 'lingo title' %}</a>
|
||||
<a href="{% url 'lingo-manager-pricing-list' %}">{% trans "Pricings" %}</a>
|
||||
{% url 'lingo-manager-pricing-criteria-list' as object_list_url %}
|
||||
<a href="{{ object_list_url }}">{% trans "Criterias" %}</a>
|
||||
{% include 'lingo/includes/application_breadcrumb_fragment.html' with title_no_application=_('Criterias outside applications') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans 'Criterias' %}</h2>
|
||||
{% include 'lingo/includes/application_appbar_fragment.html' with title_no_application=_('Criterias outside applications') title_object_list=_('Criterias') %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="pk-information">
|
||||
<p>{% trans "Define here pricing criterias used in pricings." %}</p>
|
||||
</div>
|
||||
{% if not application and not no_application %}
|
||||
<div class="pk-information">
|
||||
<p>{% trans "Define here pricing criterias used in pricings." %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if object_list %}
|
||||
<p class="hint">
|
||||
{% blocktrans trimmed %}
|
||||
Use drag and drop with the ⣿ handles to reorder criterias inside a category.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% if not application and not no_application %}
|
||||
<p class="hint">
|
||||
{% blocktrans trimmed %}
|
||||
Use drag and drop with the ⣿ handles to reorder criterias inside a category.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% for object in object_list %}
|
||||
<div class="section criteria-category">
|
||||
<h3>
|
||||
<a rel="popup" href="{% url 'lingo-manager-pricing-criteria-category-edit' object.pk %}">{{ object }} [{{ object.slug }}]</a>
|
||||
<a rel="popup" href="{% url 'lingo-manager-pricing-criteria-category-edit' object.pk %}">
|
||||
{% include 'lingo/includes/application_icon_fragment.html' %}
|
||||
{{ object }} [{{ object.slug }}]
|
||||
</a>
|
||||
<span>
|
||||
<a class="button" href="{% url 'lingo-manager-pricing-criteria-category-export' object.pk %}">{% trans "Export"%}</a>
|
||||
<a class="button" rel="popup" href="{% url 'lingo-manager-pricing-criteria-category-delete' object.pk %}">{% trans "Delete"%}</a>
|
||||
|
@ -37,7 +47,7 @@
|
|||
<ul class="objects-list single-links sortable" data-order-url="{% url 'lingo-manager-pricing-criteria-order' object.pk %}">
|
||||
{% for criteria in object.criterias.all %}
|
||||
<li{% if not criteria.default %} class="sortable-item" data-item-id="{{ criteria.pk }}"{% endif %}>
|
||||
{% if not criteria.default %}<span class="handle">⣿</span>{% endif %}
|
||||
{% if not criteria.default and not application and not no_application %}<span class="handle">⣿</span>{% endif %}
|
||||
<a rel="popup" href="{% url 'lingo-manager-pricing-criteria-edit' object.pk criteria.pk %}">{{ criteria }}{% if criteria.default %} <span class="extra-info">- {% trans "default" %}</span>{% endif %}</a>
|
||||
<a class="delete" rel="popup" href="{% url 'lingo-manager-pricing-criteria-delete' object.pk criteria.pk %}">{% trans "delete"%}</a>
|
||||
</li>
|
||||
|
@ -47,20 +57,26 @@
|
|||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="big-msg-info">
|
||||
{% blocktrans trimmed %}
|
||||
This site doesn't have any pricing category yet. Click on the "New category" button in the top
|
||||
right of the page to add a first one.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% if not no_application %}
|
||||
<div class="big-msg-info">
|
||||
{% blocktrans trimmed %}
|
||||
This site doesn't have any pricing category yet. Click on the "New category" button in the top
|
||||
right of the page to add a first one.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
<aside id="sidebar">
|
||||
{% if not application and not no_application %}
|
||||
<aside id="sidebar">
|
||||
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-pricing-criteria-category-add' %}">{% trans 'New category' %}</a>
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-pricing-criteria-category-add' %}">{% trans 'New category' %}</a>
|
||||
|
||||
</aside>
|
||||
{% include 'lingo/includes/application_list_fragment.html' with title_no_application=_('Criterias outside applications') %}
|
||||
|
||||
</aside>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -283,5 +283,8 @@
|
|||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-pricing-edit' object.pk %}">{% trans 'Options' %}</a>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-pricing-export' pk=object.pk %}">{% trans 'Export' %}</a>
|
||||
|
||||
{% url 'lingo-manager-pricing-list' as object_list_url %}
|
||||
{% include 'lingo/includes/application_detail_fragment.html' %}
|
||||
|
||||
</aside>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
{% extends "lingo/manager_homepage.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page-title-extra-label %}{% trans "Pricings" context 'agenda pricing' %}{% endblock %}
|
||||
{% block page-title-extra-label %}{% trans "Pricings" %}{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'lingo-manager-pricing-list' %}">{% trans "Pricings" context 'agenda pricing' %}</a>
|
||||
{% url 'lingo-manager-pricing-list' as object_list_url %}
|
||||
<a href="{{ object_list_url }}">{% trans "Pricings" %}</a>
|
||||
{% include 'lingo/includes/application_breadcrumb_fragment.html' with title_no_application=_('Pricings outside applications') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans 'Pricings' context 'agenda pricing' %}</h2>
|
||||
{% include 'lingo/includes/application_appbar_fragment.html' with title_no_application=_('Pricings outside applications') title_object_list=_('Pricings') %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pk-information">
|
||||
<p>{% trans "Define here pricings to attach to events agendas." %}</p>
|
||||
</div>
|
||||
{% if not application and not no_application %}
|
||||
<div class="pk-information">
|
||||
<p>{% trans "Define here pricings to attach to events agendas." %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if object_list %}
|
||||
<div>
|
||||
<h3>{% trans "Pricings" context 'agenda pricing' %}</h3>
|
||||
|
@ -23,6 +27,7 @@
|
|||
{% for object in object_list %}{% if not object.flat_fee_schedule %}
|
||||
<li>
|
||||
<a href="{% url 'lingo-manager-pricing-detail' pk=object.pk %}">
|
||||
{% include 'lingo/includes/application_icon_fragment.html' %}
|
||||
{{ object }}
|
||||
- {% blocktrans trimmed with start=object.date_start|date:'d/m/Y' end=object.date_end|date:'d/m/Y' %}From {{ start }} to {{ end }}{% endblocktrans %})
|
||||
<span class="extra-info"> [{% trans "identifier:" %} {{ object.slug }}]</span>
|
||||
|
@ -35,6 +40,7 @@
|
|||
{% for object in object_list %}{% if object.flat_fee_schedule %}
|
||||
<li>
|
||||
<a href="{% url 'lingo-manager-pricing-detail' pk=object.pk %}">
|
||||
{% include 'lingo/includes/application_icon_fragment.html' %}
|
||||
{{ object }}
|
||||
- {% blocktrans trimmed with start=object.date_start|date:'d/m/Y' end=object.date_end|date:'d/m/Y' %}From {{ start }} to {{ end }}{% endblocktrans %})
|
||||
<span class="extra-info"> [{% trans "identifier:" %} {{ object.slug }}]</span>
|
||||
|
@ -43,7 +49,7 @@
|
|||
{% endif %}{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
{% elif not no_application %}
|
||||
<div class="big-msg-info">
|
||||
{% blocktrans trimmed %}
|
||||
This site doesn't have any pricing yet. Click on the "New pricing" button in the top
|
||||
|
@ -54,17 +60,21 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
<aside id="sidebar">
|
||||
{% if not application and not no_application %}
|
||||
<aside id="sidebar">
|
||||
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-pricing-add' %}">{% trans 'New pricing' %}</a>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-pricing-config-import' %}">{% trans 'Import site' %}</a>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-pricing-config-export' %}" data-autoclose-dialog="true">{% trans 'Export site' %}</a>
|
||||
<h3>{% trans "Actions" %}</h3>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-pricing-add' %}">{% trans 'New pricing' %}</a>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-pricing-config-import' %}">{% trans 'Import site' %}</a>
|
||||
<a class="button button-paragraph" rel="popup" href="{% url 'lingo-manager-pricing-config-export' %}" data-autoclose-dialog="true">{% trans 'Export site' %}</a>
|
||||
|
||||
<h3>{% trans "Navigation" %}</h3>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-pricing-criteria-list' %}">{% trans "Criterias" %}</a>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-agenda-list' %}">{% trans "Agendas" %}</a>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-check-type-list' %}">{% trans "Check types" %}</a>
|
||||
<h3>{% trans "Navigation" %}</h3>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-pricing-criteria-list' %}">{% trans "Criterias" %}</a>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-agenda-list' %}">{% trans "Agendas" %}</a>
|
||||
<a class="button button-paragraph" href="{% url 'lingo-manager-check-type-list' %}">{% trans "Check types" %}</a>
|
||||
|
||||
</aside>
|
||||
{% include 'lingo/includes/application_list_fragment.html' with title_no_application=_('Pricings outside applications') %}
|
||||
|
||||
</aside>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -41,6 +41,7 @@ from django.views.generic.detail import SingleObjectMixin
|
|||
from lingo.agendas.chrono import refresh_agendas
|
||||
from lingo.agendas.models import Agenda, CheckType, CheckTypeGroup
|
||||
from lingo.agendas.views import AgendaMixin
|
||||
from lingo.export_import.views import WithApplicationsMixin
|
||||
from lingo.pricing.forms import (
|
||||
CheckTypeForm,
|
||||
CriteriaForm,
|
||||
|
@ -220,12 +221,21 @@ class ConfigImportView(FormView):
|
|||
config_import = ConfigImportView.as_view()
|
||||
|
||||
|
||||
class CriteriaListView(ListView):
|
||||
class CriteriaListView(WithApplicationsMixin, ListView):
|
||||
template_name = 'lingo/pricing/manager_criteria_list.html'
|
||||
model = CriteriaCategory
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.with_applications_dispatch(request)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return CriteriaCategory.objects.prefetch_related('criterias')
|
||||
queryset = self.with_applications_queryset()
|
||||
return queryset.prefetch_related('criterias')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
return self.with_applications_context_data(context)
|
||||
|
||||
|
||||
criteria_list = CriteriaListView.as_view()
|
||||
|
@ -367,14 +377,22 @@ class CriteriaDeleteView(DeleteView):
|
|||
criteria_delete = CriteriaDeleteView.as_view()
|
||||
|
||||
|
||||
class AgendaListView(ListView):
|
||||
class AgendaListView(WithApplicationsMixin, ListView):
|
||||
template_name = 'lingo/pricing/manager_agenda_list.html'
|
||||
model = Agenda
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.with_applications_dispatch(request)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
queryset = self.with_applications_queryset()
|
||||
return queryset.order_by('category_label', 'label')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
return self.with_applications_context_data(context)
|
||||
|
||||
|
||||
agenda_list = AgendaListView.as_view()
|
||||
|
||||
|
@ -466,12 +484,21 @@ class AgendaInvoicingSettingsView(AgendaMixin, UpdateView):
|
|||
agenda_invoicing_settings = AgendaInvoicingSettingsView.as_view()
|
||||
|
||||
|
||||
class PricingListView(ListView):
|
||||
class PricingListView(WithApplicationsMixin, ListView):
|
||||
template_name = 'lingo/pricing/manager_pricing_list.html'
|
||||
model = Pricing
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.with_applications_dispatch(request)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return Pricing.objects.all().order_by('flat_fee_schedule', 'date_start', 'date_end')
|
||||
queryset = self.with_applications_queryset()
|
||||
return queryset.order_by('flat_fee_schedule', 'date_start', 'date_end')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
return self.with_applications_context_data(context)
|
||||
|
||||
|
||||
pricing_list = PricingListView.as_view()
|
||||
|
@ -964,12 +991,21 @@ class PricingMatrixEdit(FormView):
|
|||
pricing_matrix_edit = PricingMatrixEdit.as_view()
|
||||
|
||||
|
||||
class CheckTypeListView(ListView):
|
||||
class CheckTypeListView(WithApplicationsMixin, ListView):
|
||||
template_name = 'lingo/pricing/manager_check_type_list.html'
|
||||
model = CheckTypeGroup
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.with_applications_dispatch(request)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
return CheckTypeGroup.objects.prefetch_related('check_types')
|
||||
queryset = self.with_applications_queryset()
|
||||
return queryset.prefetch_related('check_types')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
return self.with_applications_context_data(context)
|
||||
|
||||
|
||||
check_type_list = CheckTypeListView.as_view()
|
||||
|
|
|
@ -57,6 +57,7 @@ INSTALLED_APPS = (
|
|||
'gadjo',
|
||||
'rest_framework',
|
||||
'django_filters',
|
||||
'sorl.thumbnail',
|
||||
'lingo.agendas',
|
||||
'lingo.api',
|
||||
'lingo.basket',
|
||||
|
@ -231,6 +232,10 @@ CKEDITOR_CONFIGS = {
|
|||
|
||||
BASKET_EXPIRY_DELAY = 60 # 1 hour by default
|
||||
|
||||
# from solr.thumbnail -- https://sorl-thumbnail.readthedocs.io/en/latest/reference/settings.html
|
||||
THUMBNAIL_PRESERVE_FORMAT = True
|
||||
THUMBNAIL_FORCE_OVERWRITE = False
|
||||
|
||||
local_settings_file = os.environ.get(
|
||||
'LINGO_SETTINGS_FILE', os.path.join(os.path.dirname(__file__), 'local_settings.py')
|
||||
)
|
||||
|
|
1
setup.py
1
setup.py
|
@ -168,6 +168,7 @@ setup(
|
|||
'djangorestframework>=3.3, <3.15',
|
||||
'django-filter',
|
||||
'weasyprint',
|
||||
'sorl-thumbnail',
|
||||
],
|
||||
zip_safe=False,
|
||||
cmdclass={
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 558 B |
|
@ -0,0 +1,538 @@
|
|||
import datetime
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from django.core.files import File
|
||||
from pyquery import PyQuery
|
||||
|
||||
from lingo.agendas.models import Agenda, CheckTypeGroup
|
||||
from lingo.export_import.models import Application, ApplicationElement
|
||||
from lingo.invoicing.models import Payer, Regie
|
||||
from lingo.pricing.models import CriteriaCategory, Pricing
|
||||
from tests.utils import login
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
TESTS_DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def application_with_icon():
|
||||
application = Application.objects.create(
|
||||
name='App 1',
|
||||
slug='app-1',
|
||||
version_number='1',
|
||||
)
|
||||
with open(os.path.join(TESTS_DATA_DIR, 'black.jpeg'), mode='rb') as fd:
|
||||
application.icon.save('black.jpeg', File(fd), save=True)
|
||||
return application
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def application_without_icon():
|
||||
application = Application.objects.create(
|
||||
name='App 2',
|
||||
slug='app-2',
|
||||
version_number='1',
|
||||
)
|
||||
return application
|
||||
|
||||
|
||||
@pytest.mark.parametrize('icon', [True, False])
|
||||
def test_agenda(app, admin_user, application_with_icon, application_without_icon, icon):
|
||||
if icon:
|
||||
application = application_with_icon
|
||||
else:
|
||||
application = application_without_icon
|
||||
|
||||
agenda1 = Agenda.objects.create(label='Agenda 1')
|
||||
agenda2 = Agenda.objects.create(label='Agenda 2')
|
||||
ApplicationElement.objects.create(content_object=agenda2, application=application)
|
||||
agenda3 = Agenda.objects.create(label='Agenda 3')
|
||||
ApplicationElement.objects.create(content_object=agenda3, application=application)
|
||||
|
||||
app = login(app)
|
||||
|
||||
resp = app.get('/manage/pricing/agendas/')
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 3
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(1)').text()
|
||||
== 'Agenda 1 [identifier: agenda-1, kind: Events] view'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(2)').text()
|
||||
== 'Agenda 2 [identifier: agenda-2, kind: Events] view'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(3)').text()
|
||||
== 'Agenda 3 [identifier: agenda-3, kind: Events] view'
|
||||
)
|
||||
if icon:
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 2
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(1) img')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(2) img.application-icon')) == 1
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(3) img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 0
|
||||
assert resp.pyquery('h3:contains("Applications") + .button-paragraph').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img')) == 0
|
||||
assert 'Agendas outside applications' in resp
|
||||
|
||||
# check application view
|
||||
resp = resp.click(application.name)
|
||||
assert resp.pyquery('h2').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h2 img.application-logo')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h2 img')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 2
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(1)').text()
|
||||
== 'Agenda 2 [identifier: agenda-2, kind: Events] view'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(2)').text()
|
||||
== 'Agenda 3 [identifier: agenda-3, kind: Events] view'
|
||||
)
|
||||
assert len(resp.pyquery('ul.objects-list li img')) == 0
|
||||
|
||||
# check elements outside applications
|
||||
resp = app.get('/manage/pricing/agendas/')
|
||||
resp = resp.click('Agendas outside applications')
|
||||
assert resp.pyquery('h2').text() == 'Agendas outside applications'
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 1
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(1)').text()
|
||||
== 'Agenda 1 [identifier: agenda-1, kind: Events] view'
|
||||
)
|
||||
|
||||
# check detail page
|
||||
resp = app.get('/manage/pricing/agenda/%s/' % agenda1.pk)
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph')) == 0
|
||||
resp = app.get('/manage/pricing/agenda/%s/' % agenda2.pk)
|
||||
assert resp.pyquery('h3:contains("Applications") + .button-paragraph').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img')) == 0
|
||||
|
||||
# check visible flag
|
||||
application.visible = False
|
||||
application.save()
|
||||
resp = app.get('/manage/pricing/agendas/')
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 0
|
||||
app.get('/manage/pricing/agendas/?application=%s' % application.slug, status=404)
|
||||
resp = app.get('/manage/pricing/agenda/%s/' % agenda2.pk)
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph')) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize('icon', [True, False])
|
||||
def test_check_type(app, admin_user, application_with_icon, application_without_icon, icon):
|
||||
if icon:
|
||||
application = application_with_icon
|
||||
else:
|
||||
application = application_without_icon
|
||||
|
||||
CheckTypeGroup.objects.create(label='CheckTypeGroup 1')
|
||||
check_type_group2 = CheckTypeGroup.objects.create(label='CheckTypeGroup 2')
|
||||
ApplicationElement.objects.create(content_object=check_type_group2, application=application)
|
||||
check_type_group3 = CheckTypeGroup.objects.create(label='CheckTypeGroup 3')
|
||||
ApplicationElement.objects.create(content_object=check_type_group3, application=application)
|
||||
|
||||
app = login(app)
|
||||
|
||||
resp = app.get('/manage/pricing/check-types/')
|
||||
assert len(resp.pyquery('.section')) == 3
|
||||
assert len(resp.pyquery('.section h3')) == 3
|
||||
assert PyQuery(resp.pyquery('.section')[0]).find('h3').text() == 'CheckTypeGroup 1 Export Delete'
|
||||
assert PyQuery(resp.pyquery('.section')[1]).find('h3').text() == 'CheckTypeGroup 2 Export Delete'
|
||||
assert PyQuery(resp.pyquery('.section')[2]).find('h3').text() == 'CheckTypeGroup 3 Export Delete'
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3 img')) == 2
|
||||
assert len(PyQuery(resp.pyquery('.section')[0]).find('h3 img')) == 0
|
||||
assert len(PyQuery(resp.pyquery('.section')[1]).find('h3 img.application-icon')) == 1
|
||||
assert len(PyQuery(resp.pyquery('.section')[2]).find('h3 img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3 img')) == 0
|
||||
assert resp.pyquery('h3:contains("Applications") + .button-paragraph').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img')) == 0
|
||||
assert 'Check types outside applications' in resp
|
||||
|
||||
# check application view
|
||||
resp = resp.click(application.name)
|
||||
assert resp.pyquery('h2').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h2 img.application-logo')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h2 img')) == 0
|
||||
assert len(resp.pyquery('.section')) == 2
|
||||
assert len(resp.pyquery('.section h3')) == 2
|
||||
assert PyQuery(resp.pyquery('.section')[0]).find('h3').text() == 'CheckTypeGroup 2 Export Delete'
|
||||
assert PyQuery(resp.pyquery('.section')[1]).find('h3').text() == 'CheckTypeGroup 3 Export Delete'
|
||||
assert len(resp.pyquery('h3 img')) == 0
|
||||
|
||||
# check elements outside applications
|
||||
resp = app.get('/manage/pricing/check-types/')
|
||||
resp = resp.click('Check types outside applications')
|
||||
assert resp.pyquery('h2').text() == 'Check types outside applications'
|
||||
assert len(resp.pyquery('.section')) == 1
|
||||
assert len(resp.pyquery('.section h3')) == 1
|
||||
assert PyQuery(resp.pyquery('.section')[0]).find('h3').text() == 'CheckTypeGroup 1 Export Delete'
|
||||
|
||||
# check visible flag
|
||||
application.visible = False
|
||||
application.save()
|
||||
resp = app.get('/manage/pricing/check-types/')
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 0
|
||||
app.get('/manage/pricing/check-types/?application=%s' % application.slug, status=404)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('icon', [True, False])
|
||||
def test_pricing(app, admin_user, application_with_icon, application_without_icon, icon):
|
||||
if icon:
|
||||
application = application_with_icon
|
||||
else:
|
||||
application = application_without_icon
|
||||
|
||||
pricing1 = Pricing.objects.create(
|
||||
label='Pricing 1',
|
||||
date_start=datetime.date(year=2021, month=9, day=1),
|
||||
date_end=datetime.date(year=2021, month=10, day=1),
|
||||
)
|
||||
pricing2 = Pricing.objects.create(
|
||||
label='Pricing 2',
|
||||
date_start=datetime.date(year=2021, month=9, day=1),
|
||||
date_end=datetime.date(year=2021, month=10, day=1),
|
||||
)
|
||||
ApplicationElement.objects.create(content_object=pricing2, application=application)
|
||||
pricing3 = Pricing.objects.create(
|
||||
label='Pricing 3',
|
||||
date_start=datetime.date(year=2021, month=9, day=1),
|
||||
date_end=datetime.date(year=2021, month=10, day=1),
|
||||
)
|
||||
ApplicationElement.objects.create(content_object=pricing3, application=application)
|
||||
|
||||
app = login(app)
|
||||
|
||||
resp = app.get('/manage/pricing/')
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 3
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(1)').text()
|
||||
== 'Pricing 1 - From 01/09/2021 to 01/10/2021) [identifier: pricing-1]'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(2)').text()
|
||||
== 'Pricing 2 - From 01/09/2021 to 01/10/2021) [identifier: pricing-2]'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(3)').text()
|
||||
== 'Pricing 3 - From 01/09/2021 to 01/10/2021) [identifier: pricing-3]'
|
||||
)
|
||||
if icon:
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 2
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(1) img')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(2) img.application-icon')) == 1
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(3) img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 0
|
||||
assert resp.pyquery('h3:contains("Applications") + .button-paragraph').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img')) == 0
|
||||
assert 'Pricings outside applications' in resp
|
||||
|
||||
# check application view
|
||||
resp = resp.click(application.name)
|
||||
assert resp.pyquery('h2').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h2 img.application-logo')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h2 img')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 2
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(1)').text()
|
||||
== 'Pricing 2 - From 01/09/2021 to 01/10/2021) [identifier: pricing-2]'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(2)').text()
|
||||
== 'Pricing 3 - From 01/09/2021 to 01/10/2021) [identifier: pricing-3]'
|
||||
)
|
||||
assert len(resp.pyquery('ul.objects-list li img')) == 0
|
||||
|
||||
# check elements outside applications
|
||||
resp = app.get('/manage/pricing/')
|
||||
resp = resp.click('Pricings outside applications')
|
||||
assert resp.pyquery('h2').text() == 'Pricings outside applications'
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 1
|
||||
assert (
|
||||
resp.pyquery('ul.objects-list li:nth-child(1)').text()
|
||||
== 'Pricing 1 - From 01/09/2021 to 01/10/2021) [identifier: pricing-1]'
|
||||
)
|
||||
|
||||
# check detail page
|
||||
resp = app.get('/manage/pricing/%s/' % pricing1.pk)
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph')) == 0
|
||||
resp = app.get('/manage/pricing/%s/' % pricing2.pk)
|
||||
assert resp.pyquery('h3:contains("Applications") + .button-paragraph').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img')) == 0
|
||||
|
||||
# check visible flag
|
||||
application.visible = False
|
||||
application.save()
|
||||
resp = app.get('/manage/pricing/')
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 0
|
||||
app.get('/manage/pricing/?application=%s' % application.slug, status=404)
|
||||
resp = app.get('/manage/pricing/%s/' % pricing2.pk)
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph')) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize('icon', [True, False])
|
||||
def test_criteria_category(app, admin_user, application_with_icon, application_without_icon, icon):
|
||||
if icon:
|
||||
application = application_with_icon
|
||||
else:
|
||||
application = application_without_icon
|
||||
|
||||
CriteriaCategory.objects.create(label='CriteriaCategory 1')
|
||||
criteria_category2 = CriteriaCategory.objects.create(label='CriteriaCategory 2')
|
||||
ApplicationElement.objects.create(content_object=criteria_category2, application=application)
|
||||
criteria_category3 = CriteriaCategory.objects.create(label='CriteriaCategory 3')
|
||||
ApplicationElement.objects.create(content_object=criteria_category3, application=application)
|
||||
|
||||
app = login(app)
|
||||
|
||||
resp = app.get('/manage/pricing/criterias/')
|
||||
assert len(resp.pyquery('.section')) == 3
|
||||
assert len(resp.pyquery('.section h3')) == 3
|
||||
assert (
|
||||
PyQuery(resp.pyquery('.section')[0]).find('h3').text()
|
||||
== 'CriteriaCategory 1 [criteriacategory-1] Export Delete'
|
||||
)
|
||||
assert (
|
||||
PyQuery(resp.pyquery('.section')[1]).find('h3').text()
|
||||
== 'CriteriaCategory 2 [criteriacategory-2] Export Delete'
|
||||
)
|
||||
assert (
|
||||
PyQuery(resp.pyquery('.section')[2]).find('h3').text()
|
||||
== 'CriteriaCategory 3 [criteriacategory-3] Export Delete'
|
||||
)
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3 img')) == 2
|
||||
assert len(PyQuery(resp.pyquery('.section')[0]).find('h3 img')) == 0
|
||||
assert len(PyQuery(resp.pyquery('.section')[1]).find('h3 img.application-icon')) == 1
|
||||
assert len(PyQuery(resp.pyquery('.section')[2]).find('h3 img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3 img')) == 0
|
||||
assert resp.pyquery('h3:contains("Applications") + .button-paragraph').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img')) == 0
|
||||
assert 'Criterias outside applications' in resp
|
||||
|
||||
# check application view
|
||||
resp = resp.click(application.name)
|
||||
assert resp.pyquery('h2').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h2 img.application-logo')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h2 img')) == 0
|
||||
assert len(resp.pyquery('.section')) == 2
|
||||
assert len(resp.pyquery('.section h3')) == 2
|
||||
assert (
|
||||
PyQuery(resp.pyquery('.section')[0]).find('h3').text()
|
||||
== 'CriteriaCategory 2 [criteriacategory-2] Export Delete'
|
||||
)
|
||||
assert (
|
||||
PyQuery(resp.pyquery('.section')[1]).find('h3').text()
|
||||
== 'CriteriaCategory 3 [criteriacategory-3] Export Delete'
|
||||
)
|
||||
assert len(resp.pyquery('h3 img')) == 0
|
||||
|
||||
# check elements outside applications
|
||||
resp = app.get('/manage/pricing/criterias/')
|
||||
resp = resp.click('Criterias outside applications')
|
||||
assert resp.pyquery('h2').text() == 'Criterias outside applications'
|
||||
assert len(resp.pyquery('.section')) == 1
|
||||
assert len(resp.pyquery('.section h3')) == 1
|
||||
assert (
|
||||
PyQuery(resp.pyquery('.section')[0]).find('h3').text()
|
||||
== 'CriteriaCategory 1 [criteriacategory-1] Export Delete'
|
||||
)
|
||||
|
||||
# check visible flag
|
||||
application.visible = False
|
||||
application.save()
|
||||
resp = app.get('/manage/pricing/criterias/')
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 0
|
||||
app.get('/manage/pricing/criterias/?application=%s' % application.slug, status=404)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('icon', [True, False])
|
||||
def test_payer(app, admin_user, application_with_icon, application_without_icon, icon):
|
||||
if icon:
|
||||
application = application_with_icon
|
||||
else:
|
||||
application = application_without_icon
|
||||
|
||||
payer1 = Payer.objects.create(label='Payer 1')
|
||||
payer2 = Payer.objects.create(label='Payer 2')
|
||||
ApplicationElement.objects.create(content_object=payer2, application=application)
|
||||
payer3 = Payer.objects.create(label='Payer 3')
|
||||
ApplicationElement.objects.create(content_object=payer3, application=application)
|
||||
|
||||
app = login(app)
|
||||
|
||||
resp = app.get('/manage/invoicing/payers/')
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 3
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(1)').text() == 'Payer 1 [identifier: payer-1]'
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(2)').text() == 'Payer 2 [identifier: payer-2]'
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(3)').text() == 'Payer 3 [identifier: payer-3]'
|
||||
if icon:
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 2
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(1) img')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(2) img.application-icon')) == 1
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(3) img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 0
|
||||
assert resp.pyquery('h3:contains("Applications") + .button-paragraph').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img')) == 0
|
||||
assert 'Payers outside applications' in resp
|
||||
|
||||
# check application view
|
||||
resp = resp.click(application.name)
|
||||
assert resp.pyquery('h2').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h2 img.application-logo')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h2 img')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 2
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(1)').text() == 'Payer 2 [identifier: payer-2]'
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(2)').text() == 'Payer 3 [identifier: payer-3]'
|
||||
assert len(resp.pyquery('ul.objects-list li img')) == 0
|
||||
|
||||
# check elements outside applications
|
||||
resp = app.get('/manage/invoicing/payers/')
|
||||
resp = resp.click('Payers outside applications')
|
||||
assert resp.pyquery('h2').text() == 'Payers outside applications'
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 1
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(1)').text() == 'Payer 1 [identifier: payer-1]'
|
||||
|
||||
# check detail page
|
||||
resp = app.get('/manage/invoicing/payer/%s/' % payer1.pk)
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph')) == 0
|
||||
resp = app.get('/manage/invoicing/payer/%s/' % payer2.pk)
|
||||
assert resp.pyquery('h3:contains("Applications") + .button-paragraph').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img')) == 0
|
||||
|
||||
# check visible flag
|
||||
application.visible = False
|
||||
application.save()
|
||||
resp = app.get('/manage/invoicing/payers/')
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 0
|
||||
app.get('/manage/invoicing/payers/?application=%s' % application.slug, status=404)
|
||||
resp = app.get('/manage/invoicing/payer/%s/' % payer2.pk)
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph')) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize('icon', [True, False])
|
||||
def test_regie(app, admin_user, application_with_icon, application_without_icon, icon):
|
||||
if icon:
|
||||
application = application_with_icon
|
||||
else:
|
||||
application = application_without_icon
|
||||
|
||||
regie1 = Regie.objects.create(label='Regie 1')
|
||||
regie2 = Regie.objects.create(label='Regie 2')
|
||||
ApplicationElement.objects.create(content_object=regie2, application=application)
|
||||
regie3 = Regie.objects.create(label='Regie 3')
|
||||
ApplicationElement.objects.create(content_object=regie3, application=application)
|
||||
|
||||
app = login(app)
|
||||
|
||||
resp = app.get('/manage/invoicing/regies/')
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 3
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(1)').text() == 'Regie 1 [identifier: regie-1]'
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(2)').text() == 'Regie 2 [identifier: regie-2]'
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(3)').text() == 'Regie 3 [identifier: regie-3]'
|
||||
if icon:
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 2
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(1) img')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(2) img.application-icon')) == 1
|
||||
assert len(resp.pyquery('ul.objects-list li:nth-child(3) img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 0
|
||||
assert resp.pyquery('h3:contains("Applications") + .button-paragraph').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img')) == 0
|
||||
assert 'Regies outside applications' in resp
|
||||
|
||||
# check application view
|
||||
resp = resp.click(application.name)
|
||||
assert resp.pyquery('h2').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h2 img.application-logo')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h2 img')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 2
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(1)').text() == 'Regie 2 [identifier: regie-2]'
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(2)').text() == 'Regie 3 [identifier: regie-3]'
|
||||
assert len(resp.pyquery('ul.objects-list li img')) == 0
|
||||
|
||||
# check elements outside applications
|
||||
resp = app.get('/manage/invoicing/regies/')
|
||||
resp = resp.click('Regies outside applications')
|
||||
assert resp.pyquery('h2').text() == 'Regies outside applications'
|
||||
assert len(resp.pyquery('ul.objects-list li')) == 1
|
||||
assert resp.pyquery('ul.objects-list li:nth-child(1)').text() == 'Regie 1 [identifier: regie-1]'
|
||||
|
||||
# check detail page
|
||||
resp = app.get('/manage/invoicing/regie/%s/' % regie1.pk)
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph')) == 0
|
||||
resp = app.get('/manage/invoicing/regie/%s/' % regie2.pk)
|
||||
assert resp.pyquery('h3:contains("Applications") + .button-paragraph').text() == application.name
|
||||
if icon:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img.application-icon')) == 1
|
||||
else:
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph img')) == 0
|
||||
|
||||
# check visible flag
|
||||
application.visible = False
|
||||
application.save()
|
||||
resp = app.get('/manage/invoicing/regies/')
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('ul.objects-list img')) == 0
|
||||
app.get('/manage/invoicing/regies/?application=%s' % application.slug, status=404)
|
||||
resp = app.get('/manage/invoicing/regie/%s/' % regie2.pk)
|
||||
assert len(resp.pyquery('h3:contains("Applications")')) == 0
|
||||
assert len(resp.pyquery('h3:contains("Applications") + .button-paragraph')) == 0
|
Loading…
Reference in New Issue
Ok, j’ai loupé un bout de l’UI applification je pense alors je ne suis pas sûr de comprendre : je vois plus bas cette propriété utilisée dans les templates, mais c’est dans quels cas qu’on a intérêt à consulter les applications dont l’objet fait partie plutôt que le (a priori plus intuitif) inverse à savoir les objets contenus dans une application ?
Edit: Ah, ok, pigé dans la suite de la relecture, on affiche l’info de la liste des applications concernées pour chaque objet concerné dans le BO.