diff --git a/caremyway/api/migrations/0004_auto_20180622_0516.py b/caremyway/api/migrations/0004_auto_20180622_0516.py new file mode 100644 index 0000000..c3079d1 --- /dev/null +++ b/caremyway/api/migrations/0004_auto_20180622_0516.py @@ -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), + ), + ] diff --git a/caremyway/api/migrations/0005_auto_20180622_0521.py b/caremyway/api/migrations/0005_auto_20180622_0521.py new file mode 100644 index 0000000..4feb37d --- /dev/null +++ b/caremyway/api/migrations/0005_auto_20180622_0521.py @@ -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), + ), + ] diff --git a/caremyway/api/migrations/0006_auto_20180622_0533.py b/caremyway/api/migrations/0006_auto_20180622_0533.py new file mode 100644 index 0000000..d42d402 --- /dev/null +++ b/caremyway/api/migrations/0006_auto_20180622_0533.py @@ -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), + ), + ] diff --git a/caremyway/api/models.py b/caremyway/api/models.py index 75e312b..5428147 100644 --- a/caremyway/api/models.py +++ b/caremyway/api/models.py @@ -58,11 +58,13 @@ class Shift(models.Model): uuid = models.UUIDField(default=uuid.uuid4, editable=False) price = models.ForeignKey(Price, on_delete=models.CASCADE) set_price = models.DecimalField(max_digits=8, decimal_places=2, null=True) - set_date = models.DateField() - set_start = models.DateTimeField() - set_end = models.DateTimeField() + set_start = models.DateTimeField(null=True) + set_end = models.DateTimeField(null=True) actual_start = 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) chart = models.TextField(max_length=1000, null=True) provider_approved = models.NullBooleanField(blank=True) diff --git a/caremyway/api/serializers.py b/caremyway/api/serializers.py index 26bf9aa..5a79ae0 100644 --- a/caremyway/api/serializers.py +++ b/caremyway/api/serializers.py @@ -185,8 +185,8 @@ class ShiftSerializer(serializers.ModelSerializer): return obj.price.uuid def get_amount(self, obj): - if obj.actual_end: - shift_length = obj.actual_end - obj.actual_start + if obj.approved_end: + shift_length = obj.approved_end - obj.approved_start return Decimal(shift_length.total_seconds()) / 3600 * obj.set_price else: return None @@ -196,8 +196,8 @@ class CShiftSerializer(ShiftSerializer): class Meta: 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') - read_only_fields = ('chart', 'price', 'set_price', 'set_date', 'actual_start', 'actual_end', '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', 'actual_start', 'actual_end', 'approved_date', 'provider_approved', 'deleted') def create(self, validated_data): user = self.context['request'].user @@ -220,7 +220,13 @@ class CShiftSerializer(ShiftSerializer): now = datetime.now(timezone.utc) 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 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: raise serializers.ValidationError("Shift ends before it starts.") if set_start > set_end - timedelta(hours=1): @@ -230,27 +236,58 @@ class CShiftSerializer(ShiftSerializer): # TODO: Make sure provider isn't double-booked????? - validated_data['set_date'] = set_start.date() validated_data['price'] = price return serializers.ModelSerializer.create(self, 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 instance.actual_start: - raise serializers.ValidationError("Can't update after check in.") - - # Reset approval on update - instance.provider_approved = None + if set_start and set_end: + if instance.actual_start: + raise serializers.ValidationError("Can't update after checkin.") + + 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.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.") - return super().update(instance, validated_data) + instance.save() + return instance class PShiftSerializer(ShiftSerializer): action = serializers.ChoiceField(write_only=True, allow_blank=True, choices=['checkin', 'checkout']) class Meta: model = Shift - fields = ('uuid', 'action', 'chart', 'price', 'set_price', 'set_date', 'set_start', 'set_end', 'actual_start', 'actual_end', 'amount', 'description', 'provider_approved', 'deleted') - read_only_fields = ('uuid', 'price', 'set_price', 'set_date', 'set_start', 'set_end', 'actual_start', 'actual_end', 'description', '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_start', 'set_end', 'actual_start', 'actual_end', 'approved_start', 'approved_end', 'approved_date', 'description', 'deleted') def update(self, instance, validated_data): provider_approved = validated_data['provider_approved'] diff --git a/caremyway/api/views.py b/caremyway/api/views.py index 2e114bf..28639b7 100644 --- a/caremyway/api/views.py +++ b/caremyway/api/views.py @@ -149,10 +149,6 @@ class CShiftViewSet(viewsets.ModelViewSet): def destroy(self, request, *args, **kwargs): 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.save() return Response(status=status.HTTP_204_NO_CONTENT) @@ -191,8 +187,8 @@ def gen_timesheets(user, payday, manage=None): .filter( Q(price__management__client__user__username=user) | Q(price__management__provider__user__username=user)) \ - .filter(set_date__gte=paystart) \ - .filter(set_date__lte=payend) \ + .filter(approved_date=paystart) \ + .filter(approved_date=payend) \ .order_by('price__management', 'actual_start') if manage: shifts = shifts.filter(price__management__uuid=manage) @@ -211,7 +207,7 @@ def gen_timesheets(user, payday, manage=None): for s in shifts: shift = {} - shift['date'] = s.set_date + shift['date'] = s.approved_date shift['start'] = s.actual_start shift['end'] = s.actual_end shift['hours'] = (shift['end'] - shift['start']).total_seconds() / 3600