lingo/lingo/agendas/models.py

244 lines
8.2 KiB
Python

# lingo - payment and billing system
# Copyright (C) 2022 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/>.
import copy
from django.conf import settings
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(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)
category_slug = models.SlugField(_('Category identifier'), max_length=160, null=True)
partial_bookings = models.BooleanField(default=False)
check_type_group = models.ForeignKey(
'CheckTypeGroup',
verbose_name=_('Check type group'),
blank=True,
null=True,
on_delete=models.SET_NULL,
)
regie = models.ForeignKey(
'invoicing.Regie',
verbose_name=_('Regie'),
blank=True,
null=True,
on_delete=models.SET_NULL,
)
application_component_type = 'lingo_agendas'
application_label_singular = _('Agenda (payment)')
application_label_plural = _('Agendas (payment)')
class Meta:
ordering = ['label']
def __str__(self):
return self.label
def save(self, *args, **kwargs):
if not self.slug:
self.slug = generate_slug(self)
super().save(*args, **kwargs)
@property
def base_slug(self):
return slugify(self.label)
def get_dependencies(self):
yield self.check_type_group
yield self.regie
if not settings.KNOWN_SERVICES.get('chrono'):
return
chrono = list(settings.KNOWN_SERVICES['chrono'].values())[0]
chrono_url = chrono.get('url') or ''
urls = {
'export': f'{chrono_url}api/export-import/agendas/{self.slug}/',
'dependencies': f'{chrono_url}api/export-import/agendas/{self.slug}/dependencies/',
'redirect': f'{chrono_url}api/export-import/agendas/{self.slug}/redirect/',
}
yield {'type': 'agendas', 'id': self.slug, 'text': self.label, 'urls': urls}
def export_json(self):
return {
'slug': self.slug,
'check_type_group': self.check_type_group.slug if self.check_type_group else None,
'regie': self.regie.slug if self.regie else None,
}
@classmethod
def import_json(cls, data):
from lingo.invoicing.models import Regie
data = copy.deepcopy(data)
try:
agenda = Agenda.objects.get(slug=data['slug'])
except Agenda.DoesNotExist:
raise LingoImportError(_('Missing "%s" agenda') % data['slug'])
if data.get('check_type_group'):
try:
data['check_type_group'] = CheckTypeGroup.objects.get(slug=data['check_type_group'])
except CheckTypeGroup.DoesNotExist:
raise LingoImportError(_('Missing "%s" check type group') % data['check_type_group'])
if data.get('regie'):
try:
data['regie'] = Regie.objects.get(slug=data['regie'])
except Regie.DoesNotExist:
raise LingoImportError(_('Missing "%s" regie') % data['regie'])
agenda.check_type_group = data.get('check_type_group')
agenda.regie = data.get('regie')
agenda.save()
return False, agenda
def get_chrono_url(self):
if not settings.KNOWN_SERVICES.get('chrono'):
return
chrono = list(settings.KNOWN_SERVICES['chrono'].values())[0]
chrono_url = chrono.get('url') or ''
return '%smanage/agendas/%s/settings/' % (chrono_url, self.slug)
def get_real_kind_display(self):
if self.partial_bookings:
return _('Partial bookings')
return _('Events')
class CheckTypeGroup(WithApplicationMixin, models.Model):
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
label = models.CharField(_('Label'), max_length=150)
unexpected_presence = models.ForeignKey(
'agendas.CheckType',
verbose_name=_('Check type to be used in case of unexpected presence'),
on_delete=models.SET_NULL,
null=True,
blank=True,
)
application_component_type = 'check_type_groups'
application_label_singular = _('Check type group')
application_label_plural = _('Check type groups')
class Meta:
ordering = ['label']
def __str__(self):
return self.label
def save(self, *args, **kwargs):
if not self.slug:
self.slug = generate_slug(self)
super().save(*args, **kwargs)
@property
def base_slug(self):
return slugify(self.label)
def get_dependencies(self):
return []
@classmethod
def import_json(cls, data):
check_types = data.pop('check_types', [])
unexpected_presence = data.pop('unexpected_presence', None)
data = clean_import_data(cls, data)
group, created = cls.objects.update_or_create(slug=data['slug'], defaults=data)
for check_type in check_types:
check_type['group'] = group
CheckType.import_json(check_type)
if unexpected_presence:
try:
group.unexpected_presence = group.check_types.get(slug=unexpected_presence)
except CheckType.DoesNotExist:
raise LingoImportError(_('Missing "%s" check type') % unexpected_presence)
group.save()
return created, group
def export_json(self):
return {
'label': self.label,
'slug': self.slug,
'check_types': [a.export_json() for a in self.check_types.all()],
'unexpected_presence': self.unexpected_presence.slug if self.unexpected_presence else None,
}
class CheckTypeManager(models.Manager):
def absences(self):
return self.filter(kind='absence', disabled=False)
def presences(self):
return self.filter(kind='presence', disabled=False)
class CheckType(models.Model):
group = models.ForeignKey(CheckTypeGroup, on_delete=models.CASCADE, related_name='check_types')
slug = models.SlugField(_('Identifier'), max_length=160)
label = models.CharField(_('Label'), max_length=150)
kind = models.CharField(
_('Kind'),
max_length=8,
choices=[('absence', _('Absence')), ('presence', _('Presence'))],
default='absence',
)
pricing = models.DecimalField(
_('Pricing'), max_digits=5, decimal_places=2, help_text=_('Fixed pricing'), blank=True, null=True
)
pricing_rate = models.PositiveIntegerField(
_('Pricing rate'), help_text=_('Percentage rate'), blank=True, null=True
)
disabled = models.BooleanField(_('Disabled'), default=False)
objects = CheckTypeManager()
class Meta:
ordering = ['label']
unique_together = ['group', 'slug']
def __str__(self):
return self.label
def save(self, *args, **kwargs):
if not self.slug:
self.slug = generate_slug(self, group=self.group)
super().save(*args, **kwargs)
@property
def base_slug(self):
return slugify(self.label)
@classmethod
def import_json(cls, data):
data = clean_import_data(cls, data)
cls.objects.update_or_create(slug=data['slug'], group=data['group'], defaults=data)
def export_json(self):
return {
'label': self.label,
'slug': self.slug,
'kind': self.kind,
}