chore(letter): add number of tries as qualification property

This commit is contained in:
Steffen Jost 2024-07-08 14:22:54 +02:00
parent a97c3a5c9d
commit 3def8ca916
12 changed files with 94 additions and 52 deletions

View File

@ -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 ELearning automatisch gestartet?
QualificationElearningRenew: Verlängert ein erfolgreiches ELearning die Qualifikation automatisch um die reguläre Gültigkeitsdauer?
QualificationElearningLimit: Ist die Anzahl der ELearning 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: EMail
TableLmsIdent: ELearning Benutzer
TableLmsElearning: ELearning
TableLmsElearningRenews: Automatische Verlängerung
TableLmsElearningLimit: Maximale Versuche
TableLmsPin: ELearning Passwort
TableLmsResetPin: ELearning Passwort zurücksetzen?
TableLmsDatePin: ELearning Passwort erstellt

View File

@ -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 elearning automatically started?
QualificationElearningRenew: Does successful elearning automatically extend a qualification by the default validity period?
QualificationElearningLimit: Is the number of elearning 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: Elearning user
TableLmsPin: Elearning password
TableLmsElearning: Elearning
TableLmsElearningRenews: Automatic renewal
TableLmsElearningLimit: Max attempts
TableLmsResetPin: Reset Elearning password?
TableLmsDatePin: Elearning password created
TableLmsDate: Date

View File

@ -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

View File

@ -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

View File

@ -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
[

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -51,16 +51,32 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
$if drd > 0
_{MsgDays (fromIntegral drd)}
$maybe lqre <- lmsQualiReused
<dt .deflist__dt>_{MsgTableQualificationLmsReusesTooltip}
<dd .deflist__dd>^{simpleLink (citext2widget (qualificationName lqre)) (QualificationR (qualificationSchool lqre) (qualificationShorthand lqre))}
<dt .deflist__dt>_{MsgQualificationElearningStart}
<dd .deflist__dd>#{boolSymbol (qualificationElearningStart quali)}
$if (qualificationElearningStart quali) && isNothing (qualificationRefreshWithin quali)
<p>
#{icon IconNotificationError}
_{MsgLmsErrorNoRefreshElearning}
^{lmsTable}
<dt .deflist__dt>_{MsgQualificationElearningRenew}
<dd .deflist__dd>#{boolSymbol (qualificationElearningRenews quali)}
$if (qualificationElearningRenews quali) && isNothing (qualificationValidDuration quali)
<p>
#{icon IconNotificationError}
_{MsgLmsErrorNoRefreshElearning}
<dt .deflist__dt>_{MsgQualificationElearningLimit}
<dd .deflist__dd>
$maybe n <- qualificationElearningLimit quali
_{MsgQualificationElearningLimitMax n}
$nothing
_{MsgQualificationElearningNoLimit}
$maybe lqre <- lmsQualiReused
<dt .deflist__dt>_{MsgTableQualificationLmsReusesTooltip}
<dd .deflist__dd>^{simpleLink (citext2widget (qualificationName lqre)) (QualificationR (qualificationSchool lqre) (qualificationShorthand lqre))}
<section>
^{lmsTable}

View File

@ -68,7 +68,14 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
<p>
#{icon IconNotificationError}
_{MsgLmsErrorNoRefreshElearning}
<section>
<dt .deflist__dt>_{MsgQualificationElearningLimit}
<dd .deflist__dd>
$maybe n <- qualificationElearningLimit quali
_{MsgQualificationElearningLimitMax n}
$nothing
_{MsgQualificationElearningNoLimit}
<section>
^{qualificationTable}

View File

@ -753,10 +753,10 @@ fillDb = do
let r_descr = Just $ htmlToStoredMarkup [shamlet|<p>Berechtigung zum Führen eines Fahrzeuges auf dem gesamten Rollfeld.|]
let l_descr = Just $ htmlToStoredMarkup [shamlet|<p>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)