Skip to content
Snippets Groups Projects
Commit 69e52c72 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Merge branch '287-absence-creation-form-should-support-datetimes' into 'master'

Resolve "Absence creation form should support datetimes"

Closes #287

See merge request !392
parents 344a0374 dd632b2b
No related branches found
No related tags found
1 merge request!392Resolve "Absence creation form should support datetimes"
Pipeline #192402 failed
...@@ -93,6 +93,7 @@ import SecondaryActionButton from "aleksis.core/components/generic/buttons/Secon ...@@ -93,6 +93,7 @@ import SecondaryActionButton from "aleksis.core/components/generic/buttons/Secon
import loadingMixin from "aleksis.core/mixins/loadingMixin.js"; import loadingMixin from "aleksis.core/mixins/loadingMixin.js";
import permissionsMixin from "aleksis.core/mixins/permissions.js"; import permissionsMixin from "aleksis.core/mixins/permissions.js";
import mutateMixin from "aleksis.core/mixins/mutateMixin.js"; import mutateMixin from "aleksis.core/mixins/mutateMixin.js";
import { DateTime } from "luxon";
import { createAbsencesForPersons } from "./absenceCreation.graphql"; import { createAbsencesForPersons } from "./absenceCreation.graphql";
...@@ -122,6 +123,7 @@ export default { ...@@ -122,6 +123,7 @@ export default {
}, },
mounted() { mounted() {
this.addPermissions(["alsijil.view_register_absence_rule"]); this.addPermissions(["alsijil.view_register_absence_rule"]);
this.clearForm();
}, },
methods: { methods: {
cancel() { cancel() {
...@@ -131,8 +133,12 @@ export default { ...@@ -131,8 +133,12 @@ export default {
}, },
clearForm() { clearForm() {
this.persons = []; this.persons = [];
this.startDate = ""; this.startDate = DateTime.now()
this.endDate = ""; .startOf("day")
.toISO({ suppressSeconds: true });
this.endDate = DateTime.now()
.endOf("day")
.toISO({ suppressSeconds: true });
this.comment = ""; this.comment = "";
this.absenceReason = ""; this.absenceReason = "";
}, },
...@@ -142,8 +148,8 @@ export default { ...@@ -142,8 +148,8 @@ export default {
createAbsencesForPersons, createAbsencesForPersons,
{ {
persons: this.persons.map((p) => p.id), persons: this.persons.map((p) => p.id),
start: this.startDate, start: this.$toUTCISO(this.$parseISODate(this.startDate)),
end: this.endDate, end: this.$toUTCISO(this.$parseISODate(this.endDate)),
comment: this.comment, comment: this.comment,
reason: this.absenceReason, reason: this.absenceReason,
}, },
......
...@@ -26,9 +26,10 @@ ...@@ -26,9 +26,10 @@
<v-row> <v-row>
<v-col cols="12" :sm="6" class="pl-0"> <v-col cols="12" :sm="6" class="pl-0">
<div aria-required="true"> <div aria-required="true">
<date-field <date-time-field
:label="$t('forms.labels.start')" :label="$t('forms.labels.start')"
:max="endDate" :max-date="endDate"
:max-time="maxStartTime"
:rules="$rules().required.build()" :rules="$rules().required.build()"
:value="startDate" :value="startDate"
@input="$emit('start-date', $event)" @input="$emit('start-date', $event)"
...@@ -37,9 +38,10 @@ ...@@ -37,9 +38,10 @@
</v-col> </v-col>
<v-col cols="12" :sm="6" class="pr-0"> <v-col cols="12" :sm="6" class="pr-0">
<div aria-required="true"> <div aria-required="true">
<date-field <date-time-field
:label="$t('forms.labels.end')" :label="$t('forms.labels.end')"
:min="startDate" :min-date="startDate"
:min-time="minEndTime"
:rules="$rules().required.build()" :rules="$rules().required.build()"
:value="endDate" :value="endDate"
@input="$emit('end-date', $event)" @input="$emit('end-date', $event)"
...@@ -69,15 +71,16 @@ ...@@ -69,15 +71,16 @@
<script> <script>
import AbsenceReasonGroupSelect from "aleksis.apps.kolego/components/AbsenceReasonGroupSelect.vue"; import AbsenceReasonGroupSelect from "aleksis.apps.kolego/components/AbsenceReasonGroupSelect.vue";
import DateField from "aleksis.core/components/generic/forms/DateField.vue"; import DateTimeField from "aleksis.core/components/generic/forms/DateTimeField.vue";
import { persons } from "./absenceCreation.graphql"; import { persons } from "./absenceCreation.graphql";
import formRulesMixin from "aleksis.core/mixins/formRulesMixin.js"; import formRulesMixin from "aleksis.core/mixins/formRulesMixin.js";
import { DateTime } from "luxon";
export default { export default {
name: "AbsenceCreationForm", name: "AbsenceCreationForm",
components: { components: {
AbsenceReasonGroupSelect, AbsenceReasonGroupSelect,
DateField, DateTimeField,
}, },
mixins: [formRulesMixin], mixins: [formRulesMixin],
emits: [ emits: [
...@@ -113,5 +116,25 @@ export default { ...@@ -113,5 +116,25 @@ export default {
required: true, required: true,
}, },
}, },
computed: {
maxStartTime() {
// Only if on the same day
const start = DateTime.fromISO(this.startDate);
const end = DateTime.fromISO(this.endDate);
if (start.day !== end.day) return;
return end.minus({ minutes: 5 }).toFormat("HH:mm");
},
minEndTime() {
// Only if on the same day
const start = DateTime.fromISO(this.startDate);
const end = DateTime.fromISO(this.endDate);
if (start.day !== end.day) return;
return start.plus({ minutes: 5 }).toFormat("HH:mm");
},
},
}; };
</script> </script>
...@@ -6,7 +6,7 @@ query persons { ...@@ -6,7 +6,7 @@ query persons {
} }
} }
query lessonsForPersons($persons: [ID]!, $start: Date!, $end: Date!) { query lessonsForPersons($persons: [ID]!, $start: DateTime!, $end: DateTime!) {
items: lessonsForPersons(persons: $persons, start: $start, end: $end) { items: lessonsForPersons(persons: $persons, start: $start, end: $end) {
id id
lessons { lessons {
...@@ -31,8 +31,8 @@ query lessonsForPersons($persons: [ID]!, $start: Date!, $end: Date!) { ...@@ -31,8 +31,8 @@ query lessonsForPersons($persons: [ID]!, $start: Date!, $end: Date!) {
# Use absencesInputType? # Use absencesInputType?
mutation createAbsencesForPersons( mutation createAbsencesForPersons(
$persons: [ID]! $persons: [ID]!
$start: Date! $start: DateTime!
$end: Date! $end: DateTime!
$comment: String $comment: String
$reason: ID! $reason: ID!
) { ) {
......
...@@ -11,7 +11,7 @@ from django.utils.translation import gettext as _ ...@@ -11,7 +11,7 @@ from django.utils.translation import gettext as _
from calendarweek import CalendarWeek from calendarweek import CalendarWeek
from aleksis.apps.chronos.managers import DateRangeQuerySetMixin from aleksis.apps.chronos.managers import DateRangeQuerySetMixin
from aleksis.core.managers import AlekSISBaseManagerWithoutMigrations, PolymorphicBaseManager from aleksis.core.managers import AlekSISBaseManagerWithoutMigrations, RecurrencePolymorphicManager
if TYPE_CHECKING: if TYPE_CHECKING:
from aleksis.core.models import Group from aleksis.core.models import Group
...@@ -189,7 +189,7 @@ class GroupRoleAssignmentQuerySet(DateRangeQuerySetMixin, QuerySet): ...@@ -189,7 +189,7 @@ class GroupRoleAssignmentQuerySet(DateRangeQuerySetMixin, QuerySet):
return self.filter(Q(groups=group) | Q(groups__child_groups=group)) return self.filter(Q(groups=group) | Q(groups__child_groups=group))
class DocumentationManager(PolymorphicBaseManager): class DocumentationManager(RecurrencePolymorphicManager):
"""Manager adding specific methods to documentations.""" """Manager adding specific methods to documentations."""
def get_queryset(self): def get_queryset(self):
...@@ -205,9 +205,7 @@ class DocumentationManager(PolymorphicBaseManager): ...@@ -205,9 +205,7 @@ class DocumentationManager(PolymorphicBaseManager):
) )
class ParticipationStatusManager(PolymorphicBaseManager): class ParticipationStatusManager(RecurrencePolymorphicManager):
"""Manager adding specific methods to participation statuses.""" """Manager adding specific methods to participation statuses."""
def get_queryset(self): pass
"""Ensure often used related data are loaded as well."""
return super().get_queryset().select_related("person", "absence_reason", "base_absence")
...@@ -63,8 +63,8 @@ class Query(graphene.ObjectType): ...@@ -63,8 +63,8 @@ class Query(graphene.ObjectType):
lessons_for_persons = graphene.List( lessons_for_persons = graphene.List(
LessonsForPersonType, LessonsForPersonType,
persons=graphene.List(graphene.ID, required=True), persons=graphene.List(graphene.ID, required=True),
start=graphene.Date(required=True), start=graphene.DateTime(required=True),
end=graphene.Date(required=True), end=graphene.DateTime(required=True),
) )
extra_marks = FilterOrderList(ExtraMarkType) extra_marks = FilterOrderList(ExtraMarkType)
...@@ -213,8 +213,8 @@ class Query(graphene.ObjectType): ...@@ -213,8 +213,8 @@ class Query(graphene.ObjectType):
for person in persons: for person in persons:
docs, dummies = Documentation.get_documentations_for_person( docs, dummies = Documentation.get_documentations_for_person(
person, person,
datetime.combine(start, datetime.min.time()), start,
datetime.combine(end, datetime.max.time()), end,
) )
lessons_for_person.append(LessonsForPersonType(id=person, lessons=docs + dummies)) lessons_for_person.append(LessonsForPersonType(id=person, lessons=docs + dummies))
......
from datetime import datetime import datetime
from typing import List
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db.models import Q
import graphene import graphene
...@@ -14,8 +16,8 @@ from .participation_status import ParticipationStatusType ...@@ -14,8 +16,8 @@ from .participation_status import ParticipationStatusType
class AbsencesForPersonsCreateMutation(graphene.Mutation): class AbsencesForPersonsCreateMutation(graphene.Mutation):
class Arguments: class Arguments:
persons = graphene.List(graphene.ID, required=True) persons = graphene.List(graphene.ID, required=True)
start = graphene.Date(required=True) start = graphene.DateTime(required=True)
end = graphene.Date(required=True) end = graphene.DateTime(required=True)
comment = graphene.String(required=False) comment = graphene.String(required=False)
reason = graphene.ID(required=True) reason = graphene.ID(required=True)
...@@ -23,7 +25,16 @@ class AbsencesForPersonsCreateMutation(graphene.Mutation): ...@@ -23,7 +25,16 @@ class AbsencesForPersonsCreateMutation(graphene.Mutation):
participation_statuses = graphene.List(ParticipationStatusType) participation_statuses = graphene.List(ParticipationStatusType)
@classmethod @classmethod
def mutate(cls, root, info, persons, start, end, comment, reason): # noqa def mutate(
cls,
root,
info,
persons: List[str | int],
start: datetime.datetime,
end: datetime.datetime,
comment: str,
reason: str | int,
):
participation_statuses = [] participation_statuses = []
persons = Person.objects.filter(pk__in=persons) persons = Person.objects.filter(pk__in=persons)
...@@ -31,17 +42,30 @@ class AbsencesForPersonsCreateMutation(graphene.Mutation): ...@@ -31,17 +42,30 @@ class AbsencesForPersonsCreateMutation(graphene.Mutation):
for person in persons: for person in persons:
if not info.context.user.has_perm("alsijil.register_absence_rule", person): if not info.context.user.has_perm("alsijil.register_absence_rule", person):
raise PermissionDenied() raise PermissionDenied()
kolego_absence, __ = Absence.objects.get_or_create(
date_start=start, # Check if there is an existing absence with overlapping datetime
date_end=end, absences = Absence.objects.filter(
Q(datetime_start__lte=start) | Q(date_start__lte=start.date()),
Q(datetime_end__gte=end) | Q(date_end__gte=end.date()),
reason_id=reason, reason_id=reason,
person=person, person=person,
defaults={"comment": comment},
) )
if len(absences) > 0:
kolego_absence = absences.first()
else:
# Check for same times and create otherwise
kolego_absence, __ = Absence.objects.get_or_create(
datetime_start=start,
datetime_end=end,
reason_id=reason,
person=person,
defaults={"comment": comment},
)
events = ParticipationStatus.get_single_events( events = ParticipationStatus.get_single_events(
datetime.combine(start, datetime.min.time()), start,
datetime.combine(end, datetime.max.time()), end,
None, None,
{"person": person}, {"person": person},
with_reference_object=True, with_reference_object=True,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment