diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue index 87d9f429075cad7a46a80dd55a82e3d01ea642c2..c2b765cfdf368764771b6ea7ff71a7bef3e8b5c2 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue +++ b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue @@ -69,7 +69,10 @@ <DocumentationLoader /> </template> </infinite-scrolling-date-sorted-c-r-u-d-iterator> - <absence-creation-dialog :absence-reasons="absenceReasons" /> + <absence-creation-dialog + :absence-reasons="absenceReasons" + :affected-query="lastQuery" + /> </div> </template> diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue index 690b07972cd932a8d5eb0fd91609e0a662d829b0..e62c2ca4a22a314744a72a9feec45e439b9a1379 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue +++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue @@ -96,7 +96,10 @@ import permissionsMixin from "aleksis.core/mixins/permissions.js"; import mutateMixin from "aleksis.core/mixins/mutateMixin.js"; import { DateTime } from "luxon"; -import { createAbsencesForPersons } from "./absenceCreation.graphql"; +import { + clearAbsencesForPersons, + createAbsencesForPersons, +} from "./absenceCreation.graphql"; export default { name: "AbsenceCreationDialog", @@ -152,13 +155,17 @@ export default { confirm() { this.handleLoading(true); this.mutate( - createAbsencesForPersons, + this.absenceReason !== "present" + ? createAbsencesForPersons + : clearAbsencesForPersons, { persons: this.persons.map((p) => p.id), start: this.$toUTCISO(this.$parseISODate(this.startDate)), end: this.$toUTCISO(this.$parseISODate(this.endDate)), - comment: this.comment, - reason: this.absenceReason, + ...(this.absenceReason !== "present" && { comment: this.comment }), + ...(this.absenceReason !== "present" && { + reason: this.absenceReason, + }), }, (storedDocumentations, incomingStatuses) => { incomingStatuses.forEach((newStatus) => { @@ -172,7 +179,7 @@ export default { (part) => part.id === newStatus.id, ); - participationStatus.absenceReason = newStatus.absenceReason; + participationStatus.absenceReason = newStatus?.absenceReason; participationStatus.isOptimistic = newStatus.isOptimistic; }); diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationForm.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationForm.vue index 902a33f2582cc30a76d8145e619da432d0092a4c..1d392d83b7cdea91df0f037712a653b89f5f824b 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationForm.vue +++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationForm.vue @@ -55,6 +55,7 @@ <v-text-field :label="$t('forms.labels.comment')" :value="comment" + :disabled="absenceReason == 'present'" @input="$emit('comment', $event)" /> </v-row> @@ -62,6 +63,7 @@ <div aria-required="true"> <absence-reason-group-select :rules="$rules().required.build()" + allow-empty :value="absenceReason" :custom-absence-reasons="absenceReasons" @input="$emit('absence-reason', $event)" diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/absenceCreation.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/absences/absenceCreation.graphql index 5a520453f35062d49edd5eb82aca291384e2f739..f7f6de02e930ee81e2f9bd6c54cbd354e319de3b 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/absenceCreation.graphql +++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/absenceCreation.graphql @@ -59,3 +59,23 @@ mutation createAbsencesForPersons( } } } + +mutation clearAbsencesForPersons( + $persons: [ID]! + $start: DateTime! + $end: DateTime! +) { + clearAbsencesForPersons(persons: $persons, start: $start, end: $end) { + ok + items: participationStatuses { + id + isOptimistic + relatedDocumentation { + id + } + absenceReason { + id + } + } + } +} diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py index 2dd3deac4fbc03f3e85b4d791170f09eaf5054e3..6e8465376fad8816f65ab28204d60e367a97221b 100644 --- a/aleksis/apps/alsijil/models.py +++ b/aleksis/apps/alsijil/models.py @@ -498,6 +498,29 @@ class ParticipationStatus(CalendarEvent): return participation_statuses + @classmethod + def clear_absence_by_datetimes( + cls, person: Person, start: datetime, end: datetime + ) -> list["ParticipationStatus"]: + participation_statuses = [] + + events = cls.get_single_events( + start, + end, + None, + {"person": person}, + with_reference_object=True, + ) + + for event in events: + participation_status = event["REFERENCE_OBJECT"] + participation_status.absence_reason = None + participation_status.base_absence = None + participation_status.save() + participation_statuses.append(participation_status) + + return participation_statuses + def fill_from_kolego(self, kolego_absence: KolegoAbsence): """Take over data from a Kolego absence.""" self.base_absence = kolego_absence diff --git a/aleksis/apps/alsijil/schema/__init__.py b/aleksis/apps/alsijil/schema/__init__.py index 284d8da2d3a2570e57ae902b14302048eba7f3b8..f337be4f836c8f73383e952c88f79e229613e02a 100644 --- a/aleksis/apps/alsijil/schema/__init__.py +++ b/aleksis/apps/alsijil/schema/__init__.py @@ -24,6 +24,7 @@ from aleksis.core.util.core_helpers import ( from ..model_extensions import annotate_person_statistics_for_school_term from ..models import Documentation, ExtraMark, NewPersonalNote, ParticipationStatus from .absences import ( + AbsencesForPersonsClearMutation, AbsencesForPersonsCreateMutation, ) from .documentation import ( @@ -352,6 +353,7 @@ class Mutation(graphene.ObjectType): touch_documentation = TouchDocumentationMutation.Field() update_participation_statuses = ParticipationStatusBatchPatchMutation.Field() create_absences_for_persons = AbsencesForPersonsCreateMutation.Field() + clear_absences_for_persons = AbsencesForPersonsClearMutation.Field() extend_participation_statuses = ExtendParticipationStatusToAbsenceBatchMutation.Field() create_extra_marks = ExtraMarkBatchCreateMutation.Field() diff --git a/aleksis/apps/alsijil/schema/absences.py b/aleksis/apps/alsijil/schema/absences.py index eea8b075b3e1ba2a0e6011e4b7e33e6df4f09ed4..8a729f0a4ae10624c9522fb27a8a4166af78b40a 100644 --- a/aleksis/apps/alsijil/schema/absences.py +++ b/aleksis/apps/alsijil/schema/absences.py @@ -56,3 +56,44 @@ class AbsencesForPersonsCreateMutation(graphene.Mutation): return AbsencesForPersonsCreateMutation( ok=True, participation_statuses=participation_statuses ) + + +class AbsencesForPersonsClearMutation(graphene.Mutation): + class Arguments: + persons = graphene.List(graphene.ID, required=True) + start = graphene.DateTime(required=True) + end = graphene.DateTime(required=True) + + ok = graphene.Boolean() + participation_statuses = graphene.List(ParticipationStatusType) + + @classmethod + def mutate( + cls, + root, + info, + persons: list[str | int], + start: datetime.datetime, + end: datetime.datetime, + ): + participation_statuses = [] + + persons = Person.objects.filter(pk__in=persons) + + for person in persons: + if not info.context.user.has_perm("alsijil.register_absence_rule", person): + raise PermissionDenied() + + participation_statuses += ParticipationStatus.clear_absence_by_datetimes( + person=person, start=start, end=end + ) + + Absence.clear_or_extend_absences_in_timespan( + person=person, + datetime_start=start, + datetime_end=end, + ) + + return AbsencesForPersonsClearMutation( + ok=True, participation_statuses=participation_statuses + )