Merge branch 'feature/bo_employee_update' into 'develop'

Feature/bo employee update

See merge request gm/gm-backend!119
This commit is contained in:
d.kuzmenko 2019-11-22 14:21:25 +00:00
commit 39b9b2b7e9
8 changed files with 289 additions and 12 deletions

View File

@ -55,3 +55,23 @@ class EstablishmentTypeTagFilter(filters.FilterSet):
fields = ( fields = (
'type_id', 'type_id',
) )
class EmployeeBackFilter(filters.FilterSet):
"""Employee filter set."""
search = filters.CharFilter(method='search_by_name_or_last_name')
class Meta:
"""Meta class."""
model = models.Employee
fields = (
'search',
)
def search_by_name_or_last_name(self, queryset, name, value):
"""Search by name or last name."""
if value not in EMPTY_VALUES:
return queryset.search_by_name_or_last_name(value)
return queryset

View File

@ -0,0 +1,28 @@
# Generated by Django 2.2.7 on 2019-11-22 11:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('establishment', '0065_establishment_purchased_products'),
]
operations = [
migrations.AddField(
model_name='employee',
name='last_name',
field=models.CharField(default=None, max_length=255, null=True, verbose_name='Last Name'),
),
migrations.AddField(
model_name='establishmentemployee',
name='status',
field=models.CharField(choices=[('I', 'Idle'), ('A', 'Accepted'), ('D', 'Declined')], default='I', max_length=1),
),
migrations.AlterField(
model_name='employee',
name='name',
field=models.CharField(max_length=255, verbose_name='Name'),
),
]

View File

@ -0,0 +1,39 @@
# Generated by Django 2.2.7 on 2019-11-22 12:44
from django.db import migrations, models
import phonenumber_field.modelfields
class Migration(migrations.Migration):
dependencies = [
('establishment', '0066_auto_20191122_1144'),
]
operations = [
migrations.AddField(
model_name='employee',
name='birth_date',
field=models.DateTimeField(default=None, null=True, verbose_name='Birth date'),
),
migrations.AddField(
model_name='employee',
name='email',
field=models.EmailField(blank=True, default=None, max_length=254, null=True, verbose_name='Email'),
),
migrations.AddField(
model_name='employee',
name='phone',
field=phonenumber_field.modelfields.PhoneNumberField(default=None, max_length=128, null=True),
),
migrations.AddField(
model_name='employee',
name='sex',
field=models.PositiveSmallIntegerField(choices=[(0, 'Male'), (1, 'Female')], default=None, null=True, verbose_name='Sex'),
),
migrations.AddField(
model_name='employee',
name='toque_number',
field=models.PositiveSmallIntegerField(default=None, null=True, verbose_name='Toque number'),
),
]

View File

@ -1,6 +1,7 @@
"""Establishment models.""" """Establishment models."""
from datetime import datetime from datetime import datetime
from functools import reduce from functools import reduce
from typing import List
from operator import or_ from operator import or_
import elasticsearch_dsl import elasticsearch_dsl
@ -438,8 +439,9 @@ class Establishment(GalleryModelMixin, ProjectBaseMixin, URLImageMixin,
return super().visible_tags \ return super().visible_tags \
.exclude(category__index_name__in=['guide', 'collection', 'purchased_item', .exclude(category__index_name__in=['guide', 'collection', 'purchased_item',
'business_tag', 'business_tags_de']) \ 'business_tag', 'business_tags_de']) \
\
# todo: recalculate toque_number # todo: recalculate toque_number
def recalculate_toque_number(self): def recalculate_toque_number(self):
toque_number = 0 toque_number = 0
if self.address and self.public_mark: if self.address and self.public_mark:
@ -612,7 +614,6 @@ class EstablishmentNote(ProjectBaseMixin):
class EstablishmentGallery(IntermediateGalleryModelMixin): class EstablishmentGallery(IntermediateGalleryModelMixin):
establishment = models.ForeignKey(Establishment, null=True, establishment = models.ForeignKey(Establishment, null=True,
related_name='establishment_gallery', related_name='establishment_gallery',
on_delete=models.CASCADE, on_delete=models.CASCADE,
@ -663,6 +664,16 @@ class EstablishmentEmployeeQuerySet(models.QuerySet):
class EstablishmentEmployee(BaseAttributes): class EstablishmentEmployee(BaseAttributes):
"""EstablishmentEmployee model.""" """EstablishmentEmployee model."""
IDLE = 'I'
ACCEPTED = 'A'
DECLINED = 'D'
STATUS_CHOICES = (
(IDLE, 'Idle'),
(ACCEPTED, 'Accepted'),
(DECLINED, 'Declined'),
)
establishment = models.ForeignKey(Establishment, on_delete=models.PROTECT, establishment = models.ForeignKey(Establishment, on_delete=models.PROTECT,
verbose_name=_('Establishment')) verbose_name=_('Establishment'))
employee = models.ForeignKey('establishment.Employee', on_delete=models.PROTECT, employee = models.ForeignKey('establishment.Employee', on_delete=models.PROTECT,
@ -673,19 +684,53 @@ class EstablishmentEmployee(BaseAttributes):
verbose_name=_('To date')) verbose_name=_('To date'))
position = models.ForeignKey(Position, on_delete=models.PROTECT, position = models.ForeignKey(Position, on_delete=models.PROTECT,
verbose_name=_('Position')) verbose_name=_('Position'))
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default=IDLE)
# old_id = affiliations_id # old_id = affiliations_id
old_id = models.IntegerField(verbose_name=_('Old id'), null=True, blank=True) old_id = models.IntegerField(verbose_name=_('Old id'), null=True, blank=True)
objects = EstablishmentEmployeeQuerySet.as_manager() objects = EstablishmentEmployeeQuerySet.as_manager()
class EmployeeQuerySet(models.QuerySet):
def _generic_search(self, value, filter_fields_names: List[str]):
"""Generic method for searching value in specified fields"""
filters = [
{f'{field}__icontains': value}
for field in filter_fields_names
]
return self.filter(reduce(lambda x, y: x | y, [models.Q(**i) for i in filters]))
def search_by_name_or_last_name(self, value):
"""Search by name or last_name."""
return self._generic_search(value, ['name', 'last_name'])
class Employee(BaseAttributes): class Employee(BaseAttributes):
"""Employee model.""" """Employee model."""
user = models.OneToOneField('account.User', on_delete=models.PROTECT, user = models.OneToOneField('account.User', on_delete=models.PROTECT,
null=True, blank=True, default=None, null=True, blank=True, default=None,
verbose_name=_('User')) verbose_name=_('User'))
name = models.CharField(max_length=255, verbose_name=_('Last name')) name = models.CharField(max_length=255, verbose_name=_('Name'))
last_name = models.CharField(max_length=255, verbose_name=_('Last Name'), null=True, default=None)
# SEX CHOICES
MALE = 0
FEMALE = 1
SEX_CHOICES = (
(MALE, _('Male')),
(FEMALE, _('Female'))
)
sex = models.PositiveSmallIntegerField(choices=SEX_CHOICES, verbose_name=_('Sex'), null=True, default=None)
birth_date = models.DateTimeField(editable=True, verbose_name=_('Birth date'), null=True, default=None)
email = models.EmailField(blank=True, null=True, default=None, verbose_name=_('Email'))
phone = PhoneNumberField(null=True, default=None)
toque_number = models.PositiveSmallIntegerField(verbose_name=_('Toque number'), null=True, default=None)
establishments = models.ManyToManyField(Establishment, related_name='employees', establishments = models.ManyToManyField(Establishment, related_name='employees',
through=EstablishmentEmployee, ) through=EstablishmentEmployee, )
awards = generic.GenericRelation(to='main.Award', related_query_name='employees') awards = generic.GenericRelation(to='main.Award', related_query_name='employees')
@ -694,6 +739,8 @@ class Employee(BaseAttributes):
# old_id = profile_id # old_id = profile_id
old_id = models.IntegerField(verbose_name=_('Old id'), null=True, blank=True) old_id = models.IntegerField(verbose_name=_('Old id'), null=True, blank=True)
objects = EmployeeQuerySet.as_manager()
class Meta: class Meta:
"""Meta class.""" """Meta class."""

View File

@ -2,8 +2,9 @@ from rest_framework import serializers
from establishment import models from establishment import models
from establishment import serializers as model_serializers from establishment import serializers as model_serializers
from location.serializers import AddressDetailSerializer from location.serializers import AddressDetailSerializer, TranslatedField
from main.models import Currency from main.models import Currency
from main.serializers import AwardSerializer
from utils.decorators import with_base_attributes from utils.decorators import with_base_attributes
from utils.serializers import TimeZoneChoiceField from utils.serializers import TimeZoneChoiceField
from gallery.models import Image from gallery.models import Image
@ -161,12 +162,53 @@ class ContactEmailBackSerializers(model_serializers.PlateSerializer):
class EmployeeBackSerializers(serializers.ModelSerializer): class EmployeeBackSerializers(serializers.ModelSerializer):
"""Employee serializers.""" """Employee serializers."""
awards = AwardSerializer(many=True)
class Meta: class Meta:
model = models.Employee model = models.Employee
fields = [ fields = [
'id', 'id',
'user', 'user',
'name' 'name',
'last_name',
'sex',
'birth_date',
'email',
'phone',
'toque_number',
'awards',
]
class PositionBackSerializer(serializers.ModelSerializer):
"""Position Back serializer."""
name_translated = TranslatedField()
class Meta:
model = models.Position
fields = [
'id',
'name_translated',
'priority',
'index_name',
]
class EstablishmentEmployeeBackSerializer(serializers.ModelSerializer):
"""Establishment Employee serializer."""
employee = EmployeeBackSerializers()
position = PositionBackSerializer()
class Meta:
model = models.EstablishmentEmployee
fields = [
'id',
'employee',
'from_date',
'to_date',
'position',
] ]

View File

@ -168,12 +168,51 @@ class EstablishmentEmployeeSerializer(serializers.ModelSerializer):
awards = AwardSerializer(source='employee.awards', many=True) awards = AwardSerializer(source='employee.awards', many=True)
priority = serializers.IntegerField(source='position.priority') priority = serializers.IntegerField(source='position.priority')
position_index_name = serializers.CharField(source='position.index_name') position_index_name = serializers.CharField(source='position.index_name')
status = serializers.CharField()
class Meta: class Meta:
"""Meta class.""" """Meta class."""
model = models.Employee model = models.Employee
fields = ('id', 'name', 'position_translated', 'awards', 'priority', 'position_index_name') fields = ('id', 'name', 'position_translated', 'awards', 'priority', 'position_index_name', 'status')
class EstablishmentEmployeeCreateSerializer(serializers.ModelSerializer):
"""Serializer for establishment employee relation."""
class Meta:
"""Meta class."""
model = models.EstablishmentEmployee
fields = ('id',)
def _validate_entity(self, entity_id_param: str, entity_class):
entity_id = self.context.get('request').parser_context.get('kwargs').get(entity_id_param)
entity_qs = entity_class.objects.filter(id=entity_id)
if not entity_qs.exists():
raise serializers.ValidationError({'detail': _(f'{entity_class.__name__} not found.')})
return entity_qs.first()
def validate(self, attrs):
"""Override validate method"""
establishment = self._validate_entity("establishment_id", models.Establishment)
employee = self._validate_entity("employee_id", models.Employee)
position = self._validate_entity("position_id", models.Position)
attrs['establishment'] = establishment
attrs['employee'] = employee
attrs['position'] = position
return attrs
def create(self, validated_data, *args, **kwargs):
"""Override create method"""
validated_data.update({
'employee': validated_data.pop('employee'),
'establishment': validated_data.pop('establishment'),
'position': validated_data.pop("position")
})
return super().create(validated_data)
class EstablishmentShortSerializer(serializers.ModelSerializer): class EstablishmentShortSerializer(serializers.ModelSerializer):
@ -396,6 +435,22 @@ class EstablishmentCommentCreateSerializer(comment_serializers.CommentSerializer
return super().create(validated_data) return super().create(validated_data)
class EstablishmentCommentRUDSerializer(comment_serializers.CommentSerializer):
"""Retrieve/Update/Destroy comment serializer."""
class Meta:
"""Meta class."""
model = comment_models.Comment
fields = [
'id',
'created',
'text',
'mark',
'nickname',
'profile_pic',
]
class EstablishmentFavoritesCreateSerializer(FavoritesCreateSerializer): class EstablishmentFavoritesCreateSerializer(FavoritesCreateSerializer):
"""Serializer to favorite object w/ model Establishment.""" """Serializer to favorite object w/ model Establishment."""

View File

@ -38,8 +38,16 @@ urlpatterns = [
path('phones/<int:pk>/', views.PhonesRUDView.as_view(), name='phones-rud'), path('phones/<int:pk>/', views.PhonesRUDView.as_view(), name='phones-rud'),
path('emails/', views.EmailListCreateView.as_view(), name='emails'), path('emails/', views.EmailListCreateView.as_view(), name='emails'),
path('emails/<int:pk>/', views.EmailRUDView.as_view(), name='emails-rud'), path('emails/<int:pk>/', views.EmailRUDView.as_view(), name='emails-rud'),
path('<int:establishment_id>/employees/', views.EstablishmentEmployeeListView.as_view(),
name='establishment-employees'),
path('employees/', views.EmployeeListCreateView.as_view(), name='employees'), path('employees/', views.EmployeeListCreateView.as_view(), name='employees'),
path('employees/<int:pk>/', views.EmployeeRUDView.as_view(), name='employees-rud'), path('employees/<int:pk>/', views.EmployeeRUDView.as_view(), name='employees-rud'),
path('<int:establishment_id>/employee/<int:employee_id>/position/<int:position_id>',
views.EstablishmentEmployeeCreateView.as_view(),
name='employees-establishment-create'),
path('<int:establishment_id>/employee/<int:employee_id>',
views.EstablishmentEmployeeDeleteView.as_view(),
name='employees-establishment-delete'),
path('types/', views.EstablishmentTypeListCreateView.as_view(), name='type-list'), path('types/', views.EstablishmentTypeListCreateView.as_view(), name='type-list'),
path('types/<int:pk>/', views.EstablishmentTypeRUDView.as_view(), name='type-rud'), path('types/<int:pk>/', views.EstablishmentTypeRUDView.as_view(), name='type-rud'),
path('subtypes/', views.EstablishmentSubtypeListCreateView.as_view(), name='subtype-list'), path('subtypes/', views.EstablishmentSubtypeListCreateView.as_view(), name='subtype-list'),

View File

@ -1,7 +1,9 @@
"""Establishment app views.""" """Establishment app views."""
from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework import generics, permissions from rest_framework import generics, permissions, status
from utils.permissions import IsCountryAdmin, IsEstablishmentManager
from establishment import filters, models, serializers from establishment import filters, models, serializers
from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer from timetable.serialziers import ScheduleRUDSerializer, ScheduleCreateSerializer
from utils.permissions import IsCountryAdmin, IsEstablishmentManager from utils.permissions import IsCountryAdmin, IsEstablishmentManager
@ -43,8 +45,8 @@ class EstablishmentScheduleRUDView(generics.RetrieveUpdateDestroyAPIView):
""" """
Returns the object the view is displaying. Returns the object the view is displaying.
""" """
establishment_pk = self.kwargs.get('pk') establishment_pk = self.kwargs['pk']
schedule_id = self.kwargs.get('schedule_id') schedule_id = self.kwargs['schedule_id']
establishment = get_object_or_404(klass=models.Establishment.objects.all(), establishment = get_object_or_404(klass=models.Establishment.objects.all(),
pk=establishment_pk) pk=establishment_pk)
@ -156,11 +158,23 @@ class EmailRUDView(generics.RetrieveUpdateDestroyAPIView):
class EmployeeListCreateView(generics.ListCreateAPIView): class EmployeeListCreateView(generics.ListCreateAPIView):
"""Emplyoee list create view.""" """Emplyoee list create view."""
permission_classes = (permissions.AllowAny, )
filter_class = filters.EmployeeBackFilter
serializer_class = serializers.EmployeeBackSerializers serializer_class = serializers.EmployeeBackSerializers
queryset = models.Employee.objects.all() queryset = models.Employee.objects.all()
pagination_class = None pagination_class = None
class EstablishmentEmployeeListView(generics.ListAPIView):
"""Establishment emplyoees list view."""
permission_classes = (permissions.AllowAny, )
serializer_class = serializers.EstablishmentEmployeeBackSerializer
def get_queryset(self):
establishment_id = self.kwargs['establishment_id']
return models.EstablishmentEmployee.objects.filter(establishment__id=establishment_id)
class EmployeeRUDView(generics.RetrieveUpdateDestroyAPIView): class EmployeeRUDView(generics.RetrieveUpdateDestroyAPIView):
"""Employee RUD view.""" """Employee RUD view."""
serializer_class = serializers.EmployeeBackSerializers serializer_class = serializers.EmployeeBackSerializers
@ -318,3 +332,27 @@ class EstablishmentNoteRUDView(EstablishmentMixinViews,
self.check_object_permissions(self.request, note) self.check_object_permissions(self.request, note)
return note return note
class EstablishmentEmployeeCreateView(generics.CreateAPIView):
serializer_class = serializers.EstablishmentEmployeeCreateSerializer
queryset = models.EstablishmentEmployee.objects.all()
# TODO send email to all admins and add endpoint for changing status
class EstablishmentEmployeeDeleteView(generics.DestroyAPIView):
def _get_object_to_delete(self, establishment_id, employee_id):
result_qs = models.EstablishmentEmployee\
.objects\
.filter(establishment_id=establishment_id, employee_id=employee_id)
if not result_qs.exists():
raise Http404
return result_qs.first()
def delete(self, request, *args, **kwargs):
establishment_id = self.kwargs["establishment_id"]
employee_id = self.kwargs["employee_id"]
object_to_delete = self._get_object_to_delete(establishment_id, employee_id)
object_to_delete.delete()
return HttpResponse(status=status.HTTP_204_NO_CONTENT)