Make the client the final approver of hours
This commit is contained in:
parent
967a061d3d
commit
18510cc0b2
30
caremyway/api/migrations/0004_auto_20180622_0516.py
Normal file
30
caremyway/api/migrations/0004_auto_20180622_0516.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.1 on 2018-06-22 05:16
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('api', '0003_auto_20180622_0338'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='shift',
|
||||||
|
old_name='set_date',
|
||||||
|
new_name='approved_date',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='shift',
|
||||||
|
name='approved_end',
|
||||||
|
field=models.DateTimeField(null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='shift',
|
||||||
|
name='approved_start',
|
||||||
|
field=models.DateTimeField(null=True),
|
||||||
|
),
|
||||||
|
]
|
20
caremyway/api/migrations/0005_auto_20180622_0521.py
Normal file
20
caremyway/api/migrations/0005_auto_20180622_0521.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.1 on 2018-06-22 05:21
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('api', '0004_auto_20180622_0516'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='shift',
|
||||||
|
name='approved_date',
|
||||||
|
field=models.DateField(null=True),
|
||||||
|
),
|
||||||
|
]
|
25
caremyway/api/migrations/0006_auto_20180622_0533.py
Normal file
25
caremyway/api/migrations/0006_auto_20180622_0533.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.1 on 2018-06-22 05:33
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('api', '0005_auto_20180622_0521'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='shift',
|
||||||
|
name='set_end',
|
||||||
|
field=models.DateTimeField(null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='shift',
|
||||||
|
name='set_start',
|
||||||
|
field=models.DateTimeField(null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -58,11 +58,13 @@ class Shift(models.Model):
|
||||||
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
|
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
|
||||||
price = models.ForeignKey(Price, on_delete=models.CASCADE)
|
price = models.ForeignKey(Price, on_delete=models.CASCADE)
|
||||||
set_price = models.DecimalField(max_digits=8, decimal_places=2, null=True)
|
set_price = models.DecimalField(max_digits=8, decimal_places=2, null=True)
|
||||||
set_date = models.DateField()
|
set_start = models.DateTimeField(null=True)
|
||||||
set_start = models.DateTimeField()
|
set_end = models.DateTimeField(null=True)
|
||||||
set_end = models.DateTimeField()
|
|
||||||
actual_start = models.DateTimeField(null=True)
|
actual_start = models.DateTimeField(null=True)
|
||||||
actual_end = models.DateTimeField(null=True)
|
actual_end = models.DateTimeField(null=True)
|
||||||
|
approved_start = models.DateTimeField(null=True)
|
||||||
|
approved_end = models.DateTimeField(null=True)
|
||||||
|
approved_date = models.DateField(null=True)
|
||||||
description = models.CharField(max_length=100, null=True)
|
description = models.CharField(max_length=100, null=True)
|
||||||
chart = models.TextField(max_length=1000, null=True)
|
chart = models.TextField(max_length=1000, null=True)
|
||||||
provider_approved = models.NullBooleanField(blank=True)
|
provider_approved = models.NullBooleanField(blank=True)
|
||||||
|
|
|
@ -185,8 +185,8 @@ class ShiftSerializer(serializers.ModelSerializer):
|
||||||
return obj.price.uuid
|
return obj.price.uuid
|
||||||
|
|
||||||
def get_amount(self, obj):
|
def get_amount(self, obj):
|
||||||
if obj.actual_end:
|
if obj.approved_end:
|
||||||
shift_length = obj.actual_end - obj.actual_start
|
shift_length = obj.approved_end - obj.approved_start
|
||||||
return Decimal(shift_length.total_seconds()) / 3600 * obj.set_price
|
return Decimal(shift_length.total_seconds()) / 3600 * obj.set_price
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
@ -196,8 +196,8 @@ class CShiftSerializer(ShiftSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Shift
|
model = Shift
|
||||||
fields = ('uuid', 'chart', 'get_price_uuid', 'price', 'set_price', 'set_date', 'set_start', 'set_end', 'actual_start', 'actual_end', 'amount', 'description', 'provider_approved', 'deleted')
|
fields = ('uuid', 'chart', 'get_price_uuid', 'price', 'set_price', 'set_start', 'set_end', 'actual_start', 'actual_end', 'approved_start', 'approved_end', 'approved_date', 'amount', 'description', 'provider_approved', 'deleted')
|
||||||
read_only_fields = ('chart', 'price', 'set_price', 'set_date', 'actual_start', 'actual_end', 'provider_approved', 'deleted')
|
read_only_fields = ('chart', 'price', 'set_price', 'actual_start', 'actual_end', 'approved_date', 'provider_approved', 'deleted')
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
user = self.context['request'].user
|
user = self.context['request'].user
|
||||||
|
@ -220,7 +220,13 @@ class CShiftSerializer(ShiftSerializer):
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
set_start = validated_data['set_start']
|
set_start = validated_data['set_start']
|
||||||
set_end = validated_data['set_end']
|
set_end = validated_data['set_end']
|
||||||
|
approved_start = validated_data['approved_start']
|
||||||
|
approved_end = validated_data['approved_end']
|
||||||
|
|
||||||
|
if approved_start or approved_end:
|
||||||
|
raise serializers.ValidationError("Can't set approved time on creation.")
|
||||||
|
if not set_start or not set_end:
|
||||||
|
raise serializers.ValidationError("Must include shift start and end times.")
|
||||||
if set_start > set_end:
|
if set_start > set_end:
|
||||||
raise serializers.ValidationError("Shift ends before it starts.")
|
raise serializers.ValidationError("Shift ends before it starts.")
|
||||||
if set_start > set_end - timedelta(hours=1):
|
if set_start > set_end - timedelta(hours=1):
|
||||||
|
@ -230,27 +236,58 @@ class CShiftSerializer(ShiftSerializer):
|
||||||
|
|
||||||
# TODO: Make sure provider isn't double-booked?????
|
# TODO: Make sure provider isn't double-booked?????
|
||||||
|
|
||||||
validated_data['set_date'] = set_start.date()
|
|
||||||
validated_data['price'] = price
|
validated_data['price'] = price
|
||||||
return serializers.ModelSerializer.create(self, validated_data)
|
return serializers.ModelSerializer.create(self, validated_data)
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
|
set_start = validated_data['set_start']
|
||||||
|
set_end = validated_data['set_end']
|
||||||
|
approved_start = validated_data['approved_start']
|
||||||
|
approved_end = validated_data['approved_end']
|
||||||
|
|
||||||
|
if set_start and set_end:
|
||||||
if instance.actual_start:
|
if instance.actual_start:
|
||||||
raise serializers.ValidationError("Can't update after check in.")
|
raise serializers.ValidationError("Can't update after checkin.")
|
||||||
|
|
||||||
# Reset approval on update
|
now = datetime.now(timezone.utc)
|
||||||
|
if set_start > set_end:
|
||||||
|
raise serializers.ValidationError("Shift ends before it starts.")
|
||||||
|
if set_start > set_end - timedelta(hours=1):
|
||||||
|
raise serializers.ValidationError("Shift is less than one hour long.")
|
||||||
|
if set_start < now or set_end < now:
|
||||||
|
raise serializers.ValidationError("Shift is in the past.")
|
||||||
|
|
||||||
|
# Reset provider approval on update
|
||||||
instance.provider_approved = None
|
instance.provider_approved = None
|
||||||
|
|
||||||
return super().update(instance, validated_data)
|
instance.set_start = set_start
|
||||||
|
instance.set_end = set_end
|
||||||
|
|
||||||
|
elif approved_start and approved_end:
|
||||||
|
if not instance.actual_end:
|
||||||
|
raise serializers.ValidationError("Can't approve / edit time until after checkout.")
|
||||||
|
|
||||||
|
if approved_start > approved_end:
|
||||||
|
raise serializers.ValidationError("Shift ends before it starts.")
|
||||||
|
if approved_start > approved_end - timedelta(hours=1):
|
||||||
|
raise serializers.ValidationError("Shift is less than one hour long.")
|
||||||
|
|
||||||
|
instance.approved_start = approved_start
|
||||||
|
instance.approved_end = approved_end
|
||||||
|
instance.approved_date = approved_start.date()
|
||||||
|
else:
|
||||||
|
raise serializers.ValidationError("Missing field.")
|
||||||
|
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
class PShiftSerializer(ShiftSerializer):
|
class PShiftSerializer(ShiftSerializer):
|
||||||
action = serializers.ChoiceField(write_only=True, allow_blank=True, choices=['checkin', 'checkout'])
|
action = serializers.ChoiceField(write_only=True, allow_blank=True, choices=['checkin', 'checkout'])
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Shift
|
model = Shift
|
||||||
fields = ('uuid', 'action', 'chart', 'price', 'set_price', 'set_date', 'set_start', 'set_end', 'actual_start', 'actual_end', 'amount', 'description', 'provider_approved', 'deleted')
|
fields = ('uuid', 'action', 'chart', 'price', 'set_price', 'set_start', 'set_end', 'actual_start', 'actual_end', 'approved_start', 'approved_end', 'approved_date', 'amount', 'description', 'provider_approved', 'deleted')
|
||||||
read_only_fields = ('uuid', 'price', 'set_price', 'set_date', 'set_start', 'set_end', 'actual_start', 'actual_end', 'description', 'deleted')
|
read_only_fields = ('uuid', 'price', 'set_price', 'set_start', 'set_end', 'actual_start', 'actual_end', 'approved_start', 'approved_end', 'approved_date', 'description', 'deleted')
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
provider_approved = validated_data['provider_approved']
|
provider_approved = validated_data['provider_approved']
|
||||||
|
|
|
@ -149,10 +149,6 @@ class CShiftViewSet(viewsets.ModelViewSet):
|
||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
|
|
||||||
if instance.actual_start:
|
|
||||||
return Response("Can't delete after check in.", status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
instance.deleted = True
|
instance.deleted = True
|
||||||
instance.save()
|
instance.save()
|
||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
@ -191,8 +187,8 @@ def gen_timesheets(user, payday, manage=None):
|
||||||
.filter(
|
.filter(
|
||||||
Q(price__management__client__user__username=user)
|
Q(price__management__client__user__username=user)
|
||||||
| Q(price__management__provider__user__username=user)) \
|
| Q(price__management__provider__user__username=user)) \
|
||||||
.filter(set_date__gte=paystart) \
|
.filter(approved_date=paystart) \
|
||||||
.filter(set_date__lte=payend) \
|
.filter(approved_date=payend) \
|
||||||
.order_by('price__management', 'actual_start')
|
.order_by('price__management', 'actual_start')
|
||||||
if manage:
|
if manage:
|
||||||
shifts = shifts.filter(price__management__uuid=manage)
|
shifts = shifts.filter(price__management__uuid=manage)
|
||||||
|
@ -211,7 +207,7 @@ def gen_timesheets(user, payday, manage=None):
|
||||||
|
|
||||||
for s in shifts:
|
for s in shifts:
|
||||||
shift = {}
|
shift = {}
|
||||||
shift['date'] = s.set_date
|
shift['date'] = s.approved_date
|
||||||
shift['start'] = s.actual_start
|
shift['start'] = s.actual_start
|
||||||
shift['end'] = s.actual_end
|
shift['end'] = s.actual_end
|
||||||
shift['hours'] = (shift['end'] - shift['start']).total_seconds() / 3600
|
shift['hours'] = (shift['end'] - shift['start']).total_seconds() / 3600
|
||||||
|
|
Loading…
Reference in New Issue
Block a user