diff --git a/src/onegov/pas/collections/attendence.py b/src/onegov/pas/collections/attendence.py index 2f46780362..6ae4544d84 100644 --- a/src/onegov/pas/collections/attendence.py +++ b/src/onegov/pas/collections/attendence.py @@ -161,13 +161,10 @@ def for_commission_president( (Attendence.commission_id.in_(active_commission_ids)) ) - def view_for_parliamentarian( + def query_for_current_user( self, request: PasRequest ) -> list[Attendence]: - """ - Returns filtered attendances based on user role and permissions. - This encapsulates the filtering logic previously in the view. - """ + """Returns attendances filtered by the current user's role.""" user = request.current_user if not request.is_parliamentarian: diff --git a/src/onegov/pas/layouts/attendence.py b/src/onegov/pas/layouts/attendence.py index f6817cf7d9..49161ad79d 100644 --- a/src/onegov/pas/layouts/attendence.py +++ b/src/onegov/pas/layouts/attendence.py @@ -120,6 +120,27 @@ def breadcrumbs(self) -> list[Link]: @cached_property def editbar_links(self) -> list[Link] | None: + if self.model.bulk_edit_id: + name = ( + 'edit-plenary-bulk-attendences' + if self.model.type == 'plenary' + else 'edit-commission-bulk-attendences' + ) + if self.request.is_admin or ( + self.request.is_parliamentarian + and self.request.current_parliamentarian + and str(self.request.current_parliamentarian.id) + == str(self.model.parliamentarian_id) + ): + return [ + Link( + text=_('Edit bulk'), + url=self.request.link(self.model, name), + attrs={'class': 'edit-link'}, + ) + ] + return None + if self.request.is_admin: return [ Link( diff --git a/src/onegov/pas/locale/de_CH/LC_MESSAGES/onegov.pas.po b/src/onegov/pas/locale/de_CH/LC_MESSAGES/onegov.pas.po index 3594fabb3b..4269e99863 100644 --- a/src/onegov/pas/locale/de_CH/LC_MESSAGES/onegov.pas.po +++ b/src/onegov/pas/locale/de_CH/LC_MESSAGES/onegov.pas.po @@ -395,6 +395,9 @@ msgstr "Massenbuchung Plenarsitzung" msgid "Commission session (bulk)" msgstr "Massenbuchung Kommissionssitzung" +msgid "Edit bulk" +msgstr "Massenbuchung bearbeiten" + msgid "Edit" msgstr "Bearbeiten" @@ -589,12 +592,15 @@ msgstr "Dauer" msgid "No meeting defined yet." msgstr "Noch keine Sitzung erfasst." -msgid "Bulk edits" -msgstr "Massenbuchungen" +msgid "Session date" +msgstr "Sitzungsdatum" msgid "edited" msgstr "bearbeitet" +msgid "Bulk edits" +msgstr "Massenbuchungen" + msgid "Timestamp" msgstr "Zeitpunkt" @@ -908,8 +914,8 @@ msgstr "Sitzung hinzugefügt" msgid "You do not have permission to edit plenary sessions." msgstr "Sie haben keine Berechtigung, Plenarsitzungen zu bearbeiten." -msgid "Edit plenary session" -msgstr "Plenarsitzung bearbeiten" +msgid "Edit bulk: plenary session" +msgstr "Massenbuchung: Plenarsitzung bearbeiten" msgid "Edited attendences" msgstr "Anwesenheiten bearbeitet" @@ -924,8 +930,8 @@ msgid "Delete Attendencess" msgstr "Anwesenheiten löschen" #, python-format -msgid "Edit ${type}" -msgstr "${type} bearbeiten" +msgid "Edit bulk: ${type}" +msgstr "Massenbuchung: ${type} bearbeiten" #, python-format msgid "Cannot edit attendance - abschluss already set for: ${names}" @@ -950,6 +956,9 @@ msgstr "" msgid "Your changes were saved" msgstr "Änderungen gespeichert" +msgid "Cannot delete individual bulk attendance." +msgstr "Einzelne Massenbuchung kann nicht gelöscht werden." + msgid "Cannot delete attendance in closed settlement run." msgstr "" "Anwesenheit in geschlossenem Abrechnungslauf kann nicht gelöscht werden." @@ -1096,12 +1105,33 @@ msgstr "" "Geschlossener Abrechnungslauf kann nicht gelöscht werden. Bitte zuerst " "öffnen." +#~ msgid "Edit plenary session" +#~ msgstr "Plenarsitzung bearbeiten" + +#, python-format +#~ msgid "Edit ${type}" +#~ msgstr "${type} bearbeiten" + +#~ msgid "User Account Sync" +#~ msgstr "Benutzerkonto-Synchronisation" + +#~ msgid "Synced" +#~ msgstr "Synchronisiert" + +#~ msgid "Skipped" +#~ msgstr "Übersprungen" + +#~ msgid "Created" +#~ msgstr "Erstellt" + +#~ msgid "New accounts" +#~ msgstr "Neue Konten" + #~ msgid "No president or vice president found" #~ msgstr "Kein Präsident oder Vizepräsident gefunden" #~ msgid "President: ${name} (CHF ${amount})" #~ msgstr "Präsident/in: ${name} (CHF ${amount})" - #~ msgid "Vice president: ${name} (CHF ${amount})" #~ msgstr "Vizepräsident/in: ${name} (CHF ${amount})" diff --git a/src/onegov/pas/templates/attendences.pt b/src/onegov/pas/templates/attendences.pt index 988e7594d6..cfd23ae822 100644 --- a/src/onegov/pas/templates/attendences.pt +++ b/src/onegov/pas/templates/attendences.pt @@ -9,7 +9,7 @@ - + @@ -18,13 +18,19 @@ - + - + +
DateSession date Duration Parliamentarian Type
${layout.format_date(attendence.date, 'date')}${layout.format_date(attendence.date, 'date')} +
+ edited: ${layout.format_date(attendence.modified, 'date')} +
+
${(attendence.duration/60)} h ${attendence.parliamentarian.title} ${attendence.type_label}
${attendence.commission.title}
diff --git a/src/onegov/pas/views/attendence.py b/src/onegov/pas/views/attendence.py index 07e5d94623..ff8cedb4c2 100644 --- a/src/onegov/pas/views/attendence.py +++ b/src/onegov/pas/views/attendence.py @@ -1,11 +1,8 @@ from __future__ import annotations -from itertools import groupby -from operator import attrgetter +from collections import defaultdict import uuid -from more_itertools import flatten - from onegov.core.elements import BackLink, Confirm, Intercooler, Link from onegov.core.security import Private from onegov.pas import _ @@ -50,41 +47,23 @@ def view_attendences( layout = AttendenceCollectionLayout(self, request) - # Apply role-based filtering, then re-sort for bulk edit grouping - filtered_attendences = self.view_for_parliamentarian(request) - bulk_edit_attendences = sorted( - filtered_attendences, - key=lambda x: (str(x.bulk_edit_id) if x.bulk_edit_id else '', - x.created or x.modified), - reverse=True - ) + attendences = self.query_for_current_user(request) - bulk_edit_groups = [ - sorted(group, key=attrgetter('created', 'modified'), reverse=True) - for bulk_edit_id, group in groupby( - bulk_edit_attendences, key=attrgetter('bulk_edit_id')) - ] + groups: dict[str, list[Attendence]] = defaultdict(list) + for a in attendences: + if a.bulk_edit_id: + groups[str(a.bulk_edit_id)].append(a) - non_null_groups = [g for g in bulk_edit_groups if getattr( - g[0], 'bulk_edit_id', None) is not None] - null_groups = [g for g in bulk_edit_groups if getattr( - g[0], 'bulk_edit_id', None) is None] - - non_null_groups.sort( - key=lambda group: max( # type: ignore - (attendence.modified or attendence.created - for attendence in group), - default=None - ), - reverse=True + bulk_edit_groups = sorted( + groups.values(), + key=lambda g: max(a.modified or a.created for a in g), + reverse=True, ) - bulk_edit_groups = non_null_groups + null_groups - return { 'add_link': request.link(self, name='new'), 'layout': layout, - 'attendences': list(flatten(bulk_edit_groups)), + 'attendences': attendences, 'title': layout.title, 'bulk_edit_groups': bulk_edit_groups } @@ -290,7 +269,7 @@ def edit_plenary_bulk_attendence( request.alert(error) return { 'layout': AttendenceCollectionLayout(self, request), - 'title': _('Edit plenary session'), + 'title': _('Edit bulk: plenary session'), 'form': form, 'form_width': 'large' } @@ -366,7 +345,7 @@ def edit_plenary_bulk_attendence( return { 'layout': layout, - 'title': _('Edit plenary session'), + 'title': _('Edit bulk: plenary session'), 'form': form, 'form_width': 'large' } @@ -387,7 +366,7 @@ def edit_commission_bulk_attendence( request.include('custom') type_label = request.translate(self.type_label) - title = _('Edit ${type}', mapping={'type': type_label}) + title = _('Edit bulk: ${type}', mapping={'type': type_label}) if form.submitted(request): if form.date.data: @@ -616,6 +595,14 @@ def edit_attendence( form: AttendenceForm ) -> RenderData | Response: + if self.bulk_edit_id: + name = ( + 'edit-plenary-bulk-attendences' + if self.type == 'plenary' + else 'edit-commission-bulk-attendences' + ) + return request.redirect(request.link(self, name)) + if form.submitted(request): if form.date.data: if error := validate_attendance_date( @@ -682,6 +669,10 @@ def delete_attendence( request.assert_valid_csrf_token() + if self.bulk_edit_id: + request.alert(_('Cannot delete individual bulk attendance.')) + return + # Check if attendance is in a closed settlement run settlement_run = request.session.query(SettlementRun).filter( SettlementRun.start <= self.date, diff --git a/src/onegov/pas/views/templates/parliamentarian_settlement_pdf.css b/src/onegov/pas/views/templates/parliamentarian_settlement_pdf.css index fa25a12056..a382ead9c6 100644 --- a/src/onegov/pas/views/templates/parliamentarian_settlement_pdf.css +++ b/src/onegov/pas/views/templates/parliamentarian_settlement_pdf.css @@ -18,15 +18,15 @@ body { font-size: 7pt; text-decoration: underline; margin-left: 0; - margin-bottom: 0; + margin-top: -0.3cm; + margin-bottom: 0.1cm; } .address { margin-left: 0; - margin-top: -0.3cm; margin-bottom: 0.5cm; font-size: 8pt; - line-height: 1.4; + line-height: 1.2; } .date {