diff --git a/messages/uniworx/categories/qualification/de-de-formal.msg b/messages/uniworx/categories/qualification/de-de-formal.msg index bd5b82662..fd17f8e21 100644 --- a/messages/uniworx/categories/qualification/de-de-formal.msg +++ b/messages/uniworx/categories/qualification/de-de-formal.msg @@ -16,6 +16,9 @@ QualificationRefreshReminder: 2. Erinnerung QualificationRefreshReminderTooltip: Optionaler Zeitraum vor Ablauf zur Versendung einer zweiten Erinnerung per Brief oder Email mit identischen Zugangsdaten, sofern in diesem Zeitraum vor Ablauf noch keine Ablaufbenachrichtigung versendet wurde. QualificationElearningStart: Wird das E‑Learning automatisch gestartet? QualificationElearningRenew: Verlängert ein erfolgreiches E‑Learning die Qualifikation automatisch um die reguläre Gültigkeitsdauer? +QualificationElearningLimit: Ist die Anzahl der E‑Learning Versuche limitiert? +QualificationElearningLimitMax n@Int: Maximal #{n} Versuche +QualificationElearningNoLimit: Nicht limitiert QualificationExpiryNotification: Ungültigkeitsbenachrichtigung? QualificationExpiryNotificationTooltip: Nutzer werden benachrichtigt, wenn die Qualifikation ungültig wird, sofern der jeweilige Nutzer in seinen Benutzereinstellungen diese Art Benachrichtigung aktiviert hat. TableQualificationCountActive: Aktive @@ -54,6 +57,7 @@ TableLmsEmail: E‑Mail TableLmsIdent: E‑Learning Benutzer TableLmsElearning: E‑Learning TableLmsElearningRenews: Automatische Verlängerung +TableLmsElearningLimit: Maximale Versuche TableLmsPin: E‑Learning Passwort TableLmsResetPin: E‑Learning Passwort zurücksetzen? TableLmsDatePin: E‑Learning Passwort erstellt diff --git a/messages/uniworx/categories/qualification/en-eu.msg b/messages/uniworx/categories/qualification/en-eu.msg index a3dd39375..f0436c4e4 100644 --- a/messages/uniworx/categories/qualification/en-eu.msg +++ b/messages/uniworx/categories/qualification/en-eu.msg @@ -16,6 +16,9 @@ QualificationRefreshReminder: 2. Reminder QualificationRefreshReminderTooltip: Optional period before expiry to send a second notification by post or email once more, provided that no renewal notification was sent in this period before expiry. QualificationElearningStart: Is e‑learning automatically started? QualificationElearningRenew: Does successful e‑learning automatically extend a qualification by the default validity period? +QualificationElearningLimit: Is the number of e‑learning attempts limited? +QualificationElearningLimitMax n: #{n} attempts maximum +QualificationElearningNoLimit: No limit QualificationExpiryNotification: Invalidity notification? QualificationExpiryNotificationTooltip: Qualification holder are notfied upon invalidity, provided they have activated such notification in their user settings. TableQualificationCountActive: Active @@ -55,6 +58,7 @@ TableLmsIdent: E‑learning user TableLmsPin: E‑learning password TableLmsElearning: E‑learning TableLmsElearningRenews: Automatic renewal +TableLmsElearningLimit: Max attempts TableLmsResetPin: Reset E‑learning password? TableLmsDatePin: E‑learning password created TableLmsDate: Date diff --git a/models/lms.model b/models/lms.model index dd1606611..0d4b1e49d 100644 --- a/models/lms.model +++ b/models/lms.model @@ -13,7 +13,8 @@ Qualification refreshWithin CalendarDiffDays Maybe -- notify users about renewal within this number of month/days before expiry; to be used with addGregorianDurationClip refreshReminder CalendarDiffDays Maybe -- send a second notification about renewal within this number of month/days before expiry elearningStart Bool -- automatically schedule e-refresher - elearningRenews Bool default=true -- successful E-learing automatically increases validity automatically by validDuration + elearningRenews Bool default=true -- successful e-learing automatically increases validity automatically by validDuration + elearningLimit Int Maybe defualt=5 -- limit of e-learning attempts, currently only for informative purposes, as it is enforced by LMS only lmsReuses QualificationId Maybe -- if set, lms is also included within the given qualification's lms, but only for direct routes. AuditDuration is used from this Qualification instead. expiryNotification Bool default=true -- should expiryNotification be generated for this qualification? avsLicence AvsLicence Maybe -- if set, valid QualificationUsers are synchronized to AVS as a driving licence diff --git a/src/Handler/Admin/Test.hs b/src/Handler/Admin/Test.hs index 6b4836105..86e96ddcc 100644 --- a/src/Handler/Admin/Test.hs +++ b/src/Handler/Admin/Test.hs @@ -352,6 +352,7 @@ getAdminTestPdfR = do , qualSchool = qual ^. _qualificationSchool , qualDuration = qual ^. _qualificationValidDuration , qualRenewAuto = qual ^. _qualificationElearningRenews + , qualELimit = qual ^. _qualificationElearningLimit , isReminder = False } apcIdent <- letterApcIdent letter encRecipient now diff --git a/src/Handler/LMS.hs b/src/Handler/LMS.hs index 69a77b68f..46d7e008e 100644 --- a/src/Handler/LMS.hs +++ b/src/Handler/LMS.hs @@ -155,7 +155,11 @@ mkLmsAllTable isAdmin lmsDeletionDays = do foldMap (textCell . formatCalendarDiffDays . fromMonths) . view (resultAllQualification . _qualificationAuditDuration) , sortable (Just "qelearning") (i18nCell MsgTableLmsElearning & cellTooltip MsgQualificationElearningStart) $ tickmarkCell . view (resultAllQualification . _qualificationElearningStart) - , sortable Nothing (i18nCell MsgTableQualificationLmsReuses & cellTooltip MsgTableQualificationLmsReusesTooltip) + , sortable (Just "qel-renew") (i18nCell MsgTableLmsElearningRenews & cellTooltip MsgQualificationElearningRenew) + $ tickmarkCell . view (resultAllQualification . _qualificationElearningRenews) + , sortable (Just "qel-limit") (i18nCell MsgTableLmsElearningLimit & cellTooltip MsgQualificationElearningLimit) + $ cellMaybe numCell . view (resultAllQualification . _qualificationElearningLimit) + , sortable (Just "qel-reuse") (i18nCell MsgTableQualificationLmsReuses & cellTooltip MsgTableQualificationLmsReusesTooltip) $ \(view (resultAllQualification . _qualificationLmsReuses) -> reuseQid) -> maybeCell reuseQid qualificationIdShortCell , sortable Nothing (i18nCell MsgTableQualificationIsAvsLicence & cellTooltip MsgTableQualificationIsAvsLicenceTooltip) $ \(view (resultAllQualification . _qualificationAvsLicence) -> licence) -> maybeCell licence $ textCell . T.singleton . licence2char @@ -177,6 +181,9 @@ mkLmsAllTable isAdmin lmsDeletionDays = do , singletonMap "qshort" $ SortColumn (E.^. QualificationShorthand) , singletonMap "qname" $ SortColumn (E.^. QualificationName) , singletonMap "qelearning" $ SortColumn (E.^. QualificationElearningStart) + , singletonMap "qel-renew" $ SortColumn (E.^. QualificationElearningRenews) + , singletonMap "qel-limit" $ SortColumn (E.^. QualificationElearningLimit) + , singletonMap "qel-reuse" $ SortColumn (E.^. QualificationLmsReuses) ] dbtFilter = mconcat [ diff --git a/src/Handler/PrintCenter.hs b/src/Handler/PrintCenter.hs index 0f9ccc9d3..99e2433b4 100644 --- a/src/Handler/PrintCenter.hs +++ b/src/Handler/PrintCenter.hs @@ -95,6 +95,7 @@ lrqf2letter LRQF{..} , qualSchool = lrqfQuali ^. _qualificationSchool , qualDuration = lrqfQuali ^. _qualificationValidDuration , qualRenewAuto = lrqfQuali ^. _qualificationElearningRenews + , qualELimit = lrqfQuali ^. _qualificationElearningLimit , isReminder = lrqfReminder } return (fromMaybe usr rcvr, SomeLetter letter) diff --git a/src/Handler/Qualification.hs b/src/Handler/Qualification.hs index ba3bbce8b..8b4cf4118 100644 --- a/src/Handler/Qualification.hs +++ b/src/Handler/Qualification.hs @@ -102,10 +102,10 @@ mkQualificationAllTable isAdmin = do foldMap (textCell . formatCalendarDiffDays ) . view (resultAllQualification . _qualificationRefreshReminder) , sortable (Just "qelearning") (i18nCell MsgTableLmsElearning & cellTooltip MsgQualificationElearningStart) $ tickmarkCell . view (resultAllQualification . _qualificationElearningStart) - , sortable (Just "qelearrenew") (i18nCell MsgTableLmsElearningRenews & cellTooltip MsgQualificationElearningRenew) - $ tickmarkCell . view (resultAllQualification . _qualificationElearningRenews) , sortable (Just "noteexpiry") (i18nCell MsgQualificationExpiryNotification & cellTooltip MsgQualificationExpiryNotificationTooltip) $ tickmarkCell . view (resultAllQualification . _qualificationExpiryNotification) + -- , sortable (Just "qelearrenew") (i18nCell MsgTableLmsElearningRenews & cellTooltip MsgQualificationElearningRenew) + -- $ tickmarkCell . view (resultAllQualification . _qualificationElearningRenews) -- , sortable Nothing (i18nCell MsgTableQualificationLmsReuses & cellTooltip MsgTableQualificationLmsReusesTooltip) -- $ \(view (resultAllQualification . _qualificationLmsReuses) -> reuseQid) -> maybeCell reuseQid qualificationIdShortCell , sortable Nothing (i18nCell MsgTableQualificationIsAvsLicence & cellTooltip MsgTableQualificationIsAvsLicenceTooltip) diff --git a/src/Jobs/Handler/SendNotification/Qualification.hs b/src/Jobs/Handler/SendNotification/Qualification.hs index b94204ad7..a03383d9b 100644 --- a/src/Jobs/Handler/SendNotification/Qualification.hs +++ b/src/Jobs/Handler/SendNotification/Qualification.hs @@ -110,6 +110,7 @@ dispatchNotificationQualificationRenewal nQualification nReminder jRecipient = d , qualSchool = qualificationSchool , qualDuration = qualificationValidDuration , qualRenewAuto = qualificationElearningRenews + , qualELimit = qualificationElearningLimit , isReminder = nReminder } $logInfoS "LMS" $ "Notify " <> tshow encRecipient <> " for renewal of qualification " <> qname diff --git a/src/Utils/Print/RenewQualification.hs b/src/Utils/Print/RenewQualification.hs index c8e935a8f..48ffa3a17 100644 --- a/src/Utils/Print/RenewQualification.hs +++ b/src/Utils/Print/RenewQualification.hs @@ -20,41 +20,40 @@ import Handler.Utils.Widgets (nameHtml) -- , nameHtml') import Handler.Utils.Qualification (computeNewValidDate) -defaultNotice :: Bool -> Lang -> Text -> Text -> Text -> [Text] -defaultNotice renewAuto l qualName qualShort newExpire - | isDe l, renewAuto - = [ [st|Ein Zertifikat für Ihre Unterlagen kann nur direkt nach dem erfolgreichen Test erstellt werden. - Das Zertifikat wird auf die Benutzerkennung ausgestellt. Zusammen mit diesem Schreiben können Sie Ihrem Arbeitgeber zeigen, dass Sie bestanden haben. - Bei erfolgreichem Abschluss der Schulung verlängert sich das Ablaufdatum automatisch auf den #{newExpire}. - Wir empfehlen die Schulung zeitnah durchzuführen. - Sollte bis zum Ablaufdatum das E-Learning nicht innerhalb von 5 Versuchen erfolgreich abgeschlossen sein, muss zur Wiedererlangung der Fahrberechtigung „#{qualShort}“ ein Grundkurs #{qualName} bei der Fraport Fahrerausbildung absolviert werden.|] - , "Benötigen Sie die Fahrberechtigung nicht mehr, informieren Sie bitte die Fraport Fahrerausbildung." - , "(Please contact us if you prefer letters in English.)" - ] - | isDe l - = [ [st|Ein Zertifikat für Ihre Unterlagen kann nur direkt nach dem erfolgreichen Test erstellt werden. - Das Zertifikat wird auf die Benutzerkennung ausgestellt. Zusammen mit diesem Schreiben können Sie Ihrem Arbeitgeber zeigen, dass Sie bestanden haben. - Wir empfehlen die Schulung zeitnah durchzuführen. - Sollte bis zum Ablaufdatum das E-Learning und der Praxisteil nicht erfolgreich abgeschlossen sein, muss zur Wiedererlangung der Fahrberechtigung „#{qualShort}“ ein Grundkurs #{qualName} bei der Fraport Fahrerausbildung absolviert werden.|] - , "Benötigen Sie die Fahrberechtigung nicht mehr, informieren Sie bitte die Fraport Fahrerausbildung." - ] - | renewAuto - = [ [st|A certificate for your records can only be generated immediately after a successful test. - The certificate will be issued for the user login. The certificate and this letter may then prove that you have passed. - Upon successful completion of the training, the expiry date will automatically be extended until #{newExpire}. - We recommend completing the training as soon as possible. - The licence irrevocably expires, if the e-learning is not successfully completed within 5 attempts by the expiry date. In this case, regaining licence "#{qualShort}" requires the completing of a normal training course #{qualName} again, as if no prior experience existed.|] - , "Please inform us, if this driving licence is no longer required." - , "(Kontaktieren Sie uns bitte, um zukünftige Briefe von uns in deutscher Sprache zu erhalten.)" - ] - | otherwise - = [ [st|A certificate for your records can only be generated immediately after a successful test. - The certificate will be issued for the user login. The certificate and this letter may then prove that you have passed. - We recommend completing the training as soon as possible. - The licence irrevocably expires, if the e-learning is not successfully completed within 5 attempts by the expiry date. In this case, regaining licence "#{qualShort}" requires the completing of a normal training course #{qualName} again, as if no prior experience existed.|] - , "Please inform us, if this driving licence is no longer required." - , "(Kontaktieren Sie uns bitte, um zukünftige Briefe von uns in deutscher Sprache zu erhalten.)" - ] +defaultNotice :: Lang -> Bool -> Maybe Int -> Text -> Text -> Text -> [Text] +defaultNotice l renewAuto elimit qualName qualShort newExpire = + [intro <> renewal <> bequick <> outro, still_needed, switch_lang] -- list of separate paragraphs + where + intro :: Text + | isDe l = [st|Ein Zertifikat für Ihre Unterlagen kann nur direkt nach dem erfolgreichen Test erstellt werden. + Das Zertifikat wird auf die Benutzerkennung ausgestellt. Zusammen mit diesem Schreiben können Sie Ihrem Arbeitgeber zeigen, dass Sie bestanden haben. |] + | otherwise = [st|A certificate for your records can only be generated immediately after a successful test. + The certificate will be issued for the user login. The certificate and this letter may then prove that you have passed. |] + renewal :: Text + | not renewAuto = mempty + | isDe l = [st|Bei erfolgreichem Abschluss der Schulung verlängert sich das Ablaufdatum automatisch auf den #{newExpire}. |] + | otherwise = [st|Upon successful completion of the training, the expiry date will automatically be extended until #{newExpire}. |] + bequick :: Text + | isDe l = "Wir empfehlen die Schulung zeitnah durchzuführen. " + | otherwise = "We recommend completing the training as soon as possible. " + limit :: Text + | Just n <- elimit, n > 0, isDe l = [st|innerhalb von #{n} Versuchen |] + | Just n <- elimit, n > 0 = [st|within #{n} attempts |] + | otherwise = mempty + praxis :: Text + | renewAuto = mempty + | isDe l = "der Praxisteil und " + | otherwise = "the practical part and " + outro :: Text + | isDe l = [st|Sollte bis zum Ablaufdatum #{praxis}das E-Learning nicht #{limit}erfolgreich abgeschlossen sein, muss zur Wiedererlangung der Fahrberechtigung „#{qualShort}“ ein Grundkurs #{qualName} bei der Fraport Fahrerausbildung absolviert werden.|] + | otherwise = [st|The licence irrevocably expires, if #{praxis}the e-learning is not successfully completed #{limit}by the expiry date. In this case, regaining licence "#{qualShort}" requires the completing of a normal training course #{qualName} again, as if no prior experience existed.|] + still_needed :: Text + | isDe l = "Benötigen Sie die Fahrberechtigung nicht mehr, informieren Sie bitte die Fraport Fahrerausbildung." + | otherwise = "Please inform us, if this driving licence is no longer required." + switch_lang :: Text + | isDe l = "(Please contact us if you prefer letters in English.)" + | otherwise = "(Kontaktieren Sie uns bitte, um zukünftige Briefe von uns in deutscher Sprache zu erhalten.)" + isAnyDrivingLicence :: Text -> Maybe Text -- isAnyDrivingLicence = firstJust (Text.stripSuffix "führerschein") . Text.words . Text.replace "-" " " . Text.replace "+" "" @@ -101,6 +100,7 @@ data LetterRenewQualification = LetterRenewQualification , qualSchool :: SchoolId , qualDuration :: Maybe Int , qualRenewAuto :: Bool + , qualELimit :: Maybe Int , isReminder :: Bool } deriving (Eq, Show) @@ -153,7 +153,7 @@ instance MDLetter LetterRenewQualification where , mbMeta "validduration" (show <$> qualDuration) , toMeta "url-text" lmsUrl , toMeta "url" lmsUrlPassword -- ok for PDF, since it contains the PIN already - , toMeta "notice" $ defaultNotice qualRenewAuto lang qualName qualShort $ format SelFormatDate newExpire + , toMeta "notice" $ defaultNotice lang qualRenewAuto qualELimit qualName qualShort $ format SelFormatDate newExpire , toMeta "de-subject" [st|Verlängerung Fahrberechtigung „#{qualShort}“ (#{qualName})|] , toMeta "en-subject" [st|Renewal of driving licence "#{qualShort}" (#{qualName})|] , toMeta "de-opening" $ bool [st|Guten Tag #{qualHolderDN},|] [st|Guten Tag #{userDisplayName},|] isSupervised diff --git a/templates/lms.hamlet b/templates/lms.hamlet index 40f52e1ea..a6db19331 100644 --- a/templates/lms.hamlet +++ b/templates/lms.hamlet @@ -51,16 +51,32 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later $if drd > 0 _{MsgDays (fromIntegral drd)} - $maybe lqre <- lmsQualiReused -
#{icon IconNotificationError} _{MsgLmsErrorNoRefreshElearning} - -^{lmsTable} + +
+ #{icon IconNotificationError} + _{MsgLmsErrorNoRefreshElearning} + +
#{icon IconNotificationError}
_{MsgLmsErrorNoRefreshElearning}
-
- Berechtigung zum Führen eines Fahrzeuges auf dem gesamten Rollfeld.|]
let l_descr = Just $ htmlToStoredMarkup [shamlet| für unhabilitierte|]
- qid_f <- insert' $ Qualification avn "F" "Vorfeldführerschein" f_descr (Just 24) (Just 8) (Just $ CalendarDiffDays 0 60) (Just $ CalendarDiffDays 0 14) True True Nothing True (Just AvsLicenceVorfeld) $ Just "F4466"
- qid_r <- insert' $ Qualification avn "R" "Rollfeldführerschein" r_descr (Just 12) (Just 6) (Just $ CalendarDiffDays 2 3) Nothing False False Nothing False (Just AvsLicenceRollfeld) $ Just "R2801"
- qid_rp <- insert' $ Qualification avn "R+" "Rollfeldführerschein-Plus" r_descr (Just 12) (Just 4) (Just $ CalendarDiffDays 2 3) Nothing False False (Just qid_r) False (Just AvsLicenceRollfeld) $ Just "R2802"
- qid_l <- insert' $ Qualification ifi "L" "Lehrbefähigung" l_descr Nothing (Just 6) Nothing Nothing True False Nothing True Nothing Nothing
+ qid_f <- insert' $ Qualification avn "F" "Vorfeldführerschein" f_descr (Just 24) (Just 8) (Just $ CalendarDiffDays 0 60) (Just $ CalendarDiffDays 0 14) True True (Just 5) Nothing True (Just AvsLicenceVorfeld) $ Just "F4466"
+ qid_r <- insert' $ Qualification avn "R" "Rollfeldführerschein" r_descr (Just 12) (Just 6) (Just $ CalendarDiffDays 2 3) Nothing False False Nothing Nothing False (Just AvsLicenceRollfeld) $ Just "R2801"
+ qid_rp <- insert' $ Qualification avn "R+" "Rollfeldführerschein-Plus" r_descr (Just 12) (Just 4) (Just $ CalendarDiffDays 2 3) Nothing False False Nothing (Just qid_r) False (Just AvsLicenceRollfeld) $ Just "R2802"
+ qid_l <- insert' $ Qualification ifi "L" "Lehrbefähigung" l_descr Nothing (Just 6) Nothing Nothing True False Nothing Nothing True Nothing Nothing
qfjost <- insert' $ QualificationUser jost qid_f (n_day 11) (n_day $ -1) (n_day $ -22) True (n_day' $ -9) -- TODO: better dates!
void . insert $ QualificationUserBlock qfjost False (n_day' $ -6) "First block" (Just svaupel)
void . insert $ QualificationUserBlock qfjost True (n_day' $ -5) "Second unblock" (Just gkleen)