Merge branch 'fradrive/cr3'

This commit is contained in:
Steffen Jost 2024-05-23 18:19:08 +02:00
commit b77e9e1d1c
11 changed files with 515 additions and 2173 deletions

View File

@ -25,7 +25,8 @@ import qualified Database.Esqueleto.Legacy as E
import qualified Database.Esqueleto.Utils as E
import Database.Esqueleto.Utils.TH
import Utils.Print
import Utils.Print hiding (LetterRenewQualificationF)
import Utils.Print.RenewQualification
import qualified Data.Aeson as Aeson
-- import qualified Data.Text as Text
@ -82,7 +83,7 @@ lrqf2letter LRQF{..}
usr <- getUser lrqfUser
rcvr <- mapM getUser lrqfSuper
now <- liftIO getCurrentTime
let letter = LetterRenewQualificationF
let letter = LetterRenewQualification
{ lmsLogin = lrqfIdent
, lmsPin = lrqfPin
, qualHolderID = usr ^. _entityKey

View File

@ -565,16 +565,16 @@ getAvsCompany afi =
in firstJustM $
bcons (compAvsId > 0)
( getBy $ UniqueCompanyAvsId compAvsId )
[ getEntity $ CompanyKey compShorthand
, getBy $ UniqueCompanyName compName
[ getBy $ UniqueCompanyName compName
, getEntity $ CompanyKey compShorthand
]
-- | insert a company from AVS firm info or update an existing one based on previous values
upsertAvsCompany :: AvsFirmInfo -> Maybe AvsFirmInfo -> DB (Entity Company)
upsertAvsCompany newAvsFirmInfo mbOldAvsFirmInfo = do
mbFirmEnt <- getAvsCompany newAvsFirmInfo
mbFirmEnt <- getAvsCompany newAvsFirmInfo -- primarily by AvsId, then Shorthand, then name
case (mbFirmEnt, mbOldAvsFirmInfo) of
(Nothing, _) -> do -- insert new company
(Nothing, _) -> do -- insert new company, neither AvsId, Shorthand or Name are known to exist
let upd = flip updateRecord newAvsFirmInfo
dmy = Company -- mostly dummy, values are actually prodcued through firmInfo2company below for consistency
{ companyName = newAvsFirmInfo ^. _avsFirmFirm . from _CI
@ -583,32 +583,36 @@ upsertAvsCompany newAvsFirmInfo mbOldAvsFirmInfo = do
, companyPrefersPostal = True
, companyPostAddress = newAvsFirmInfo ^. _avsFirmPostAddress
, companyEmail = newAvsFirmInfo ^? _avsFirmPrimaryEmail . _Just . from _CI
}
newCmp <- insertEntity $ foldl' upd dmy $ firmInfo2key : firmInfo2company
}
newCmp <- insertEntity $ foldl' upd dmy $ firmInfo2key : firmInfo2companyUniques <> firmInfo2company
reportAdminProblem $ AdminProblemNewCompany $ entityKey newCmp
return newCmp
(Just Entity{entityKey=firmid, entityVal=firm}, oldAvsFirmInfo) -> do -- possibly update existing company, if isJust oldAvsFirmInfo and changed occurred
let cmp_ups = mapMaybe (mkUpdate' firm newAvsFirmInfo oldAvsFirmInfo) firmInfo2company
key_ups = mkUpdate' firm newAvsFirmInfo oldAvsFirmInfo firmInfo2key
res_cmp <- updateGetEntity firmid cmp_ups
let cmp_ups = mapMaybe (mkUpdate' firm newAvsFirmInfo oldAvsFirmInfo) firmInfo2company
key_ups = mkUpdate' firm newAvsFirmInfo oldAvsFirmInfo firmInfo2key
uniq_ups <- maybeMapM (mkUpdateCheckUnique' firm newAvsFirmInfo oldAvsFirmInfo) firmInfo2companyUniques
res_cmp <- updateGetEntity firmid $ cmp_ups <> uniq_ups
case key_ups of
Nothing -> return res_cmp
Just key_up -> do
let uniq_cmp = UniqueCompanyAvsId $ res_cmp ^. _entityVal . _companyAvsId
let compId = res_cmp ^. _entityVal . _companyAvsId
uniq_cmp = if compId > 0 then UniqueCompanyAvsId compId
else UniqueCompanyName $ res_cmp ^. _entityVal . _companyName
updateBy uniq_cmp [key_up] -- this is ok, since we have OnUpdateCascade on all CompanyId entries
maybeM (return res_cmp) return $ getBy uniq_cmp
where
firmInfo2key =
firmInfo2key =
CheckUpdate CompanyShorthand $ _avsFirmAbbreviation . from _CI -- Updating primary key works in principle thanks to OnUpdateCascade, but fails due to update get
firmInfo2companyUniques =
[ CheckUpdate CompanyName $ _avsFirmFirm . from _CI -- Updating unique turned out to be problematic, who would have thought!
, CheckUpdate CompanyAvsId _avsFirmFirmNo -- Updating unique turned out to be problematic, who would have thought!
]
firmInfo2company =
[ CheckUpdate CompanyName $ _avsFirmFirm . from _CI
, CheckUpdate CompanyAvsId _avsFirmFirmNo -- Updating unique might be problematic
-- , CheckUpdate CompanyPrefersPostal _avsFirmPrefersPostal -- Guessing here is not useful, since postal preference is ignored anyway when there is only one option available
, CheckUpdate CompanyPostAddress _avsFirmPostAddress
[ CheckUpdate CompanyPostAddress _avsFirmPostAddress
, CheckUpdate CompanyEmail $ _avsFirmPrimaryEmail . _Just . from _CI . re _Just
-- , CheckUpdate CompanyPrefersPostal _avsFirmPrefersPostal -- Guessing here is not useful, since postal preference is ignored anyway when there is only one option available
]

View File

@ -364,4 +364,28 @@ updateRecord :: PersistEntity record => record -> iraw -> CheckUpdate record ira
updateRecord ent new (CheckUpdate up l) =
let newval = new ^. l
lensRec = fieldLensVal up
in ent & lensRec .~ newval
in ent & lensRec .~ newval
-- | like mkUpdate' but only returns the update if the new value would be unique
-- mkUpdateCheckUnique' :: PersistEntity record => record -> iraw -> Maybe iraw -> CheckUpdate record iraw -> DB (Maybe (Update record))
mkUpdateCheckUnique' :: (MonadIO m, PersistQueryRead backend, PersistEntity record, PersistEntityBackend record ~ BaseBackend backend)
=> record -> a -> Maybe a -> CheckUpdate record a -> ReaderT backend m (Maybe (Update record))
mkUpdateCheckUnique' ent new Nothing (CheckUpdate up l)
| let newval = new ^. l
, let entval = ent ^. fieldLensVal up
, newval /= entval
= do
newval_exists <- exists [up ==. newval]
return $ toMaybe (not newval_exists) (up =. newval)
mkUpdateCheckUnique' ent new (Just old) (CheckUpdate up l)
| let newval = new ^. l
, let oldval = old ^. l
, let entval = ent ^. fieldLensVal up
, newval /= entval
, oldval == entval
= do
newval_exists <- exists [up ==. newval]
return $ toMaybe (not newval_exists) (up =. newval)
mkUpdateCheckUnique' _ _ _ _ = return Nothing

View File

@ -23,6 +23,7 @@ module Utils.Print
-- , MDLetter
, SomeLetter(..)
, LetterRenewQualificationF(..)
-- , LetterRenewQualification(..)
, LetterExpireQualification(..)
-- , LetterCourseCertificate()
, makeCourseCertificates
@ -59,7 +60,8 @@ import Jobs.Handler.SendNotification.Utils
import Utils.Print.Instances ()
import Utils.Print.Letters
import Utils.Print.SomeLetter
import Utils.Print.RenewQualification
import Utils.Print.RenewQualificationF
import Utils.Print.RenewQualification()
import Utils.Print.ExpireQualification
import Utils.Print.CourseCertificate

View File

@ -129,6 +129,7 @@ defWriterOpts t = def { P.writerExtensions = P.pandocExtensions, P.writerTemplat
data LetterKind = Din5008 -- scrlttr2: Standard postal letter with address field, expects peprinted FraportLogo
| PinLetter -- Like Din5008, but for special paper with a protected pin field
| PinNew -- New Variant for Pin Letters for R. TODO: Remove/rename/replace PinLetter
| Plain -- scrartcl: Empty, expects empty paper with no preprints
| PlainLogo -- Like plain, but expects to be printed on paper with Logo
-- | Logo -- Like plain, but prints Fraport Logo in the upper right corner
@ -139,15 +140,18 @@ templateLatex =
let
tDin5008 = decodeUtf8 $(Data.FileEmbed.embedFile "templates/letter/din5008.latex")
tPinLetter = decodeUtf8 $(Data.FileEmbed.embedFile "templates/letter/din5008with_pin.latex")
tPinNew = decodeUtf8 $(Data.FileEmbed.embedFile "templates/letter/din5008with_pin_new.latex")
tPlain = decodeUtf8 $(Data.FileEmbed.embedFile "templates/letter/plain_article.latex")
in \case
PinLetter -> tPinLetter
PinNew -> tPinNew
Din5008 -> tDin5008
PlainLogo -> tPlain
Plain -> tPlain
paperKind :: LetterKind -> Text -- Muss genau 5 Zeichen haben!
paperKind PinLetter = "a4pin" -- Pin-Brief
paperKind PinNew = "a4pin" -- Pin-Brief
paperKind Plain = "a4wht" -- Ohne Logo
paperKind Din5008 = "a4log" -- Mit Logo
paperKind PlainLogo = "a4log"

View File

@ -19,7 +19,7 @@ import Utils.Print.Letters
import Handler.Utils.Widgets (nameHtml) -- , nameHtml')
data LetterRenewQualificationF = LetterRenewQualificationF
data LetterRenewQualification = LetterRenewQualification
{ lmsLogin :: LmsIdent
, lmsPin :: Text
, qualHolderID :: UserId
@ -37,30 +37,31 @@ data LetterRenewQualificationF = LetterRenewQualificationF
-- this datatype is specific to this letter only, and just to avoid code duplication for derived data or constants
data LetterRenewQualificationFData = LetterRenewQualificationFData { lmsUrl, lmsUrlLogin, lmsIdent :: Text }
data LetterRenewQualificationData = LetterRenewQualificationData { lmsUrl, lmsUrlLogin, lmsIdent :: Text }
deriving (Eq, Show)
letterRenewalQualificationFData :: LetterRenewQualificationF -> LetterRenewQualificationFData
letterRenewalQualificationFData LetterRenewQualificationF{lmsLogin} = LetterRenewQualificationFData{..}
letterRenewalQualificationFData :: LetterRenewQualification -> LetterRenewQualificationData
letterRenewalQualificationFData LetterRenewQualification{lmsLogin} = LetterRenewQualificationData{..}
where
lmsUrl = "https://drive.fraport.de"
lmsUrlLogin = lmsUrl <> "/?login=" <> lmsIdent
lmsUrl = "drive.fraport.de"
lmsUrlLogin = "https://" <> lmsUrl <> "/?login=" <> lmsIdent
lmsIdent = getLmsIdent lmsLogin
instance MDLetter LetterRenewQualificationF where
instance MDLetter LetterRenewQualification where
encryptPDFfor _ = PasswordUnderling
getLetterKind _ = PinLetter
getLetterKind _ = PinNew
getLetterEnvelope _ = 'f' -- maybe 'q' (Char.toLower . fst) $ Text.uncons (qualShort l)
getTemplate _ = decodeUtf8 $(Data.FileEmbed.embedFile "templates/letter/fraport_renewal.md")
getTemplate _ = decodeUtf8 $(Data.FileEmbed.embedFile "templates/letter/fraport_renewal_new.md")
getMailSubject l = SomeMessage $ MsgMailSubjectQualificationRenewal $ qualShort l
getMailBody l@LetterRenewQualificationF{..} = Just $ \DateTimeFormatter{ format } ->
let LetterRenewQualificationFData{..} = letterRenewalQualificationFData l
getMailBody l@LetterRenewQualification{..} = Just $ \DateTimeFormatter{ format } ->
let LetterRenewQualificationData{..} = letterRenewalQualificationFData l
in $(ihamletFile "templates/mail/body/qualificationRenewal.hamlet")
letterMeta l@LetterRenewQualificationF{..} DateTimeFormatter{ format } lang Entity{entityKey=rcvrId, entityVal=User{userDisplayName}} =
let LetterRenewQualificationFData{..} = letterRenewalQualificationFData l
letterMeta l@LetterRenewQualification{..} DateTimeFormatter{ format } lang Entity{entityKey=rcvrId, entityVal=User{userDisplayName}} =
let LetterRenewQualificationData{..} = letterRenewalQualificationFData l
isSupervised = rcvrId /= qualHolderID
newExpire = addDays (fromIntegral $ fromMaybe 0 qualDuration) qualExpiry
in mkMeta $
guardMonoid isSupervised
[ toMeta "supervisor" userDisplayName
@ -79,10 +80,15 @@ instance MDLetter LetterRenewQualificationF where
, mbMeta "validduration" (show <$> qualDuration)
, toMeta "url-text" lmsUrl
, toMeta "url" lmsUrlLogin
, toMeta "notice" [ [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 #{format SelFormatDate newExpire}. Wir empfehlen die Schulung zeitnah durchzuführen. Sollte bis zum Ablaufdatum das E-Learning nicht erfolgreich abgeschlossen sein oder der Test nach 5 Versuchen nicht bestanden werden, muss zur Wiedererlangung der Fahrberechtigung #{qualShort} ein Grundkurs #{qualName} bei der Fahrerausbildung absolviert werden.|]
, "Benötigen Sie die Fahrberechtigung nicht mehr, informieren Sie bitte die Fahrerausbildung."::Text
, "(Please contact us if you prefer letters in English.)"
]
, toMeta "de-subject" [st|Verlängerung Fahrberechtigung #{qualShort} (#{qualName})|]
, toMeta "en-subject" [st|Renewal of driving licence #{qualShort} (#{qualName})|]
] -- TODO use [st|some simple text with interpolation|]
]
getPJId LetterRenewQualificationF{..} =
getPJId LetterRenewQualification{..} =
PrintJobIdentification
{ pjiName = bool "Renewal" "Renewal Reminder" isReminder
, pjiApcAcknowledge = "lms-" <> getLmsIdent lmsLogin

View File

@ -0,0 +1,100 @@
-- SPDX-FileCopyrightText: 2023 Steffen Jost <jost@tcs.ifi.lmu.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}
module Utils.Print.RenewQualificationF where
import Import
import Text.Hamlet
-- import Data.Char as Char
-- import qualified Data.Text as Text
import qualified Data.CaseInsensitive as CI
import Data.FileEmbed (embedFile)
import Utils.Print.Letters
import Handler.Utils.Widgets (nameHtml) -- , nameHtml')
data LetterRenewQualificationF = LetterRenewQualificationF
{ lmsLogin :: LmsIdent
, lmsPin :: Text
, qualHolderID :: UserId
, qualHolderDN :: UserDisplayName
, qualHolderSN :: UserSurname
, qualExpiry :: Day
, qualId :: QualificationId
, qualName :: Text
, qualShort :: Text
, qualSchool :: SchoolId
, qualDuration :: Maybe Int
, isReminder :: Bool
}
deriving (Eq, Show)
-- this datatype is specific to this letter only, and just to avoid code duplication for derived data or constants
data LetterRenewQualificationFData = LetterRenewQualificationFData { lmsUrl, lmsUrlLogin, lmsIdent :: Text }
deriving (Eq, Show)
letterRenewalQualificationFData :: LetterRenewQualificationF -> LetterRenewQualificationFData
letterRenewalQualificationFData LetterRenewQualificationF{lmsLogin} = LetterRenewQualificationFData{..}
where
lmsUrl = "https://drive.fraport.de"
lmsUrlLogin = lmsUrl <> "/?login=" <> lmsIdent
lmsIdent = getLmsIdent lmsLogin
instance MDLetter LetterRenewQualificationF where
encryptPDFfor _ = PasswordUnderling
getLetterKind _ = PinLetter
getLetterEnvelope _ = 'f' -- maybe 'q' (Char.toLower . fst) $ Text.uncons (qualShort l)
getTemplate _ = decodeUtf8 $(Data.FileEmbed.embedFile "templates/letter/fraport_renewal.md")
getMailSubject l = SomeMessage $ MsgMailSubjectQualificationRenewal $ qualShort l
getMailBody l@LetterRenewQualificationF{..} = Just $ \DateTimeFormatter{ format } ->
let LetterRenewQualificationFData{..} = letterRenewalQualificationFData l
in $(ihamletFile "templates/mail/body/qualificationRenewal.hamlet")
letterMeta l@LetterRenewQualificationF{..} DateTimeFormatter{ format } lang Entity{entityKey=rcvrId, entityVal=User{userDisplayName}} =
let LetterRenewQualificationFData{..} = letterRenewalQualificationFData l
isSupervised = rcvrId /= qualHolderID
in mkMeta $
guardMonoid isSupervised
[ toMeta "supervisor" userDisplayName
, toMeta "de-opening" ("Sehr geehrte Damen und Herren,"::Text)
, toMeta "en-opening" ("Dear Sir or Madam,"::Text)
] <>
guardMonoid isReminder
[ toMeta "reminder" ("reminder"::Text)
] <>
[ toMeta "lang" lang
, toMeta "login" lmsIdent
, toMeta "pin" lmsPin
, toMeta "examinee" qualHolderDN
, toMeta "subject-meta" qualHolderDN
, toMeta "expiry" (format SelFormatDate qualExpiry)
, mbMeta "validduration" (show <$> qualDuration)
, toMeta "url-text" lmsUrl
, toMeta "url" lmsUrlLogin
]
getPJId LetterRenewQualificationF{..} =
PrintJobIdentification
{ pjiName = bool "Renewal" "Renewal Reminder" isReminder
, pjiApcAcknowledge = "lms-" <> getLmsIdent lmsLogin
, pjiRecipient = Nothing -- to be filled later
, pjiSender = Nothing
, pjiCourse = Nothing
, pjiQualification = Just qualId
, pjiLmsUser = Just lmsLogin
, pjiFileName = "renew_" <> CI.original (unSchoolKey qualSchool) <> "-" <> qualShort <> "_" <> qualHolderSN
-- let nameRecipient = abbrvName <$> recipient
-- nameSender = abbrvName <$> sender
-- nameCourse = CI.original . courseShorthand <$> course
-- nameQuali = CI.original . qualificationShorthand <$> quali
-- in .. = T.replace " " "-" (T.intercalate "_" . catMaybes $ [Just printJobName, nameQuali, nameCourse, nameSender, nameRecipient])
}

View File

@ -0,0 +1,206 @@
%Based upon https://github.com/benedictdudel/pandoc-letter-din5008
\documentclass[
paper=A4,
foldmarks=BTm, % show foldmarks top, middle, bottom
foldmarks=false, % don't print foldmarks
fromalign=left, % letter head on the right
fromphone=true, % show phone number
fromemail=true, % show email
fromlogo=false, % don't show logo in letter head
version=last, % latest version of KOMA letter
pagenumber=botright, % show pagenumbers on bottom right
firstfoot=false % first-page footer
]{scrlttr2}
\PassOptionsToPackage{hyphens}{url}
\PassOptionsToPackage{unicode$for(hyperrefoptions)$,$hyperrefoptions$$endfor$}{hyperref}
\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
\IfFileExists{bookmark.sty}{\usepackage{bookmark}}{\usepackage{hyperref}}
\hypersetup{
$if(subject-meta)$
pdfsubject={$subject-meta$},
$endif$
$if(author-meta)$
pdfauthor={$author-meta$},
$endif$
$if(lang)$
pdflang={$lang$},
$endif$
$if(is-de)$
$if(de-subject)$
pdftitle={$de-subject$},
$endif$
$else$
$if(en-subject)$
pdftitle={$en-subject$},
$endif$
$endif$
$if(apc-ident)$
pdfkeywords={$apc-ident$},
$endif$
}
\usepackage{url}
\usepackage{iftex}
%\usepackage[ngerman]{babel}
$if(lang)$
\ifLuaTeX
\usepackage[bidi=basic]{babel}
\else
\usepackage[bidi=default]{babel}
\fi
\babelprovide[main,import]{$babel-lang$}
$for(babel-otherlangs)$
\babelprovide[import]{$babel-otherlangs$}
$endfor$
% get rid of language-specific shorthands (see #6817):
\let\LanguageShortHands\languageshorthands
\def\languageshorthands#1{}
$endif$
\ifLuaTeX
\usepackage{selnolig} % disable illegal ligatures
\fi
\usepackage[sfdefault]{roboto}
\ifPDFTeX
\usepackage[$if(fontenc)$$fontenc$$else$T1$endif$]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{textcomp} % provide euro and other symbols
% \usepackage{DejaVuSansMono} % better monofont
\else
% if luatex or xetex
\usepackage{fontspec}
% \setmonofont{DejaVu Sans Mono}
\fi
\renewcommand{\familydefault}{\sfdefault}
$if(mathspec)$
\ifXeTeX
\usepackage{mathspec}
\else
\usepackage{unicode-math}
\fi
$else$
\usepackage{unicode-math}
$endif$
%\usepackage[a4paper, bottom=8cm, top=3cm]{geometry} %%% THIS HAD NO EFFECT AT ALL
\usepackage{parskip}% might be useful for pandoc tightlist
\usepackage{graphics}
\usepackage{xcolor}
\usepackage{booktabs}
\usepackage{longtable}
\usepackage[right]{eurosym}
\usepackage{enumitem}
\makeatletter
\setplength{firstheadvpos}{1.8cm}
\setplength{toaddrvpos}{5.5cm}
\setlength{\@tempskipa}{-1.2cm}%
\@addtoplength{toaddrheight}{\@tempskipa}
\makeatother
\setlength{\oddsidemargin}{\useplength{toaddrhpos}}
\addtolength{\oddsidemargin}{-1in}
\setlength{\textwidth}{\useplength{firstheadwidth}}
\usepackage[absolute,quiet,overlay]{textpos}%,showboxes
\setlength{\TPHorizModule}{1mm}
\setlength{\TPVertModule}{1mm}
\providecommand{\tightlist}{%
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
\begin{document}%
\setkomavar{fromname}{$author$}%
\renewcommand*{\raggedsignature}{\raggedright}%
\setkomavar{fromaddress}{%
$for(return-address)$%
$return-address$$sep$\\
$endfor$
}
\setkomavar{fromphone}{$phone$}
\setkomavar{fromemail}{$email$}
%if there is a handwritten signature
%\setkomavar{signature}{$author$}
%if there is no handwritten signature
\setkomavar{signature}{}
\setplength{sigbeforevskip}{-\baselineskip}
\setkomavar{date}{$date$}
\setkomavar{place}{$place$}
$if(is-de)$
\setkomavar{subject}{$de-subject$}
$else$
\setkomavar{subject}{$en-subject$}
$endif$
\begin{letter}{%
$for(address)$
$address$$sep$\\
$endfor$
}
$if(apc-ident)$
\begin{textblock}{200}(5,5)%hpos,vpos
\textcolor{white!0}{$apc-ident$}%
\end{textblock}%
$endif$
$if(is-de)$
\opening{$de-opening$}
$else$
\opening{$en-opening$}
$endif$
\begin{textblock}{65}(84,232)%hpos,vpos
\textcolor{black!39}{
\begin{labeling}{Password:}%Achtung! Die Position des Logins muss sprachunabhängig immer an der gleichen Position sein, sonst kannn die Rückmeldung der Druckerei den Ident nicht mehr identifizieren!
$if(is-de)$
\item[Benutzer:] \texttt{$login$}
\item[Passwort:] \texttt{$pin$}
$else$
\item[User:] \texttt{$login$}
\item[Password:] \texttt{$pin$}
$endif$
\end{labeling}
~}
\end{textblock}
$body$
$if(is-de)$
\closing{$de-closing$}
$else$
\closing{$en-closing$}
$endif$
%\ps $postskriptum$
$if(encludes)$
\setkomavar*{enclseparator}{Anlage}
\encl{$encludes$}
$endif$
$if(notice)$
\begin{textblock}{170}(20,258)%hpos,vpos
\scriptsize
\textbf{Hinweise für den Schulungsteilnehmer:}
\newline
$for(notice)$
$notice$
$sep$\newline
$endfor$
\end{textblock}
$endif$
\end{letter}
\end{document}

View File

@ -86,6 +86,8 @@ $else$
$endif$
Die Durchführung des Lernprogramms und des Abschlusstests dauert etwa 2,5h.
This is the new version
Fahrberechtigungsinhaber
: $examinee$

View File

@ -0,0 +1,130 @@
---
### Metadaten, welche hier eingestellt werden:
# Absender
de-subject: 'Verlängerung Fahrberechtigung "F" (Vorfeldführerschein)'
en-subject: Renewal of apron driving license
author: Fraport AG - Fahrerausbildung (AVN-AR)
phone: +49 69 690-30306
email: fahrerausbildung@fraport.de
place: Frankfurt am Main
return-address:
- 60547 Frankfurt
de-opening: Liebe Fahrberechtigungsinhaber,
en-opening: Dear driver,
de-closing: |
Mit freundlichen Grüßen,
Ihre Fraport Fahrerausbildung
en-closing: |
With kind regards,
Your Fraport Driver Training
encludes:
hyperrefoptions: hidelinks
### Metadaten, welche automatisch ersetzt werden:
url-text: 'drive.fraport.de'
url: 'https://drive.fraport.de'
date: 11.11.1111
expiry: 00.00.0000
lang: de-de
is-de: true
login: 123456
pin: abcdef
paper: pin
# Emfpänger
examinee: P. Rüfling
address:
- E. M. Pfänger
- Musterfirma GmbH
- Musterstraße 11
- 12345 Musterstadt
...
$if(titleblock)$
$titleblock$
$endif$
$for(header-includes)$
$header-includes$
$endfor$
$for(include-before)$
$include-before$
$endfor$
$if(is-de)$
<!-- deutsche Version des Briefes -->
um die Rollfeldfahrberechtigung von \textbf{$examinee$} zu erhalten, benötigen wir bis zum $date$ den Nachweis, dass die theoretische und praktische flughafenspezifische Rollfeld Recurrent Schulung der Fraport AG gemäß Verordnung der Europäische Union Nr. 139/2014 absolviert wurde.
Die Online-Schulung der Fraport AG ist erreichbar unter folgendem Link:
[$url-text$]($url$)
Der erforderliche Benutzername und das Passwort für die Fraport Online-Schulung finden Sie untenstehend. Die Weitergabe der persönlichen Benutzerdaten an Dritte ist untersagt. Ausschließlich Sie sind berechtigt, die Benutzerdaten an den Schulungsteilnehmer auszuhändigen.
Für die Absolvierung der Schulungsmaßnahme werden 1-2 Stunden benötigt. Der Abschluss der Schulung wird automatisch an das System der Fahrerausbildung übermittelt.
Nach erfolgreichem Abschluss der Online-Schulung muss \textbf{$examinee$} sich von Ihrer Firma zum praktischen Teil der Schulung einplanen lassen. Im Rahmen der 3--4-stündigen praktischen Auffrischung erfolgen Funkübungen sowie die Durchführung einer Übungsfahrt mit Prüfungscharakter im Start-/Landebahnsystem.
$else$
<!-- englische Version des Briefes -->
$if(reminder)$
this is a last **reminder**: as of $date$,
$if(supervisor)$
$examinee$ has
$else$
you have
$endif$
not yet completed the below detailed elearning.
The qualification will expire automatically,
if the elearning is not concluded in time!
$else$
$if(supervisor)$
the apron diving license of $examinee$
$else$
your apron diving license
$endif$
is about to expire soon.
$endif$
The validity will be extended
$if(validduration)$
by $validduration$ months
$endif$
by successfully participating in
an elearning.
$if(supervisor)$
Supervisors are kindly requested to forward the login data
below confidentially to the examinee.
$else$
Please use the login data from the protected area below.
$endif$
Reserve 2.5h for the entire e-learning, including the exam.
Examinee
: $examinee$
Expiry
: $expiry$
E-learning website
: [$url-text$]($url$)
If the apron driving license expires before completing this e-learning,
$if(supervisor)$
the examinee has to participate in a basic training course again to regain
to regain the apron driving licence.
$else$
you have to participate in a basic training course again to regain
your apron driving licence.
$endif$
Please contact the Fraport driving school team, if you need any assistance.
(Kontaktieren Sie uns bitte, wenn Sie zukünftige Briefe in deutscher Sprache bevorzugen.)
$endif$

File diff suppressed because it is too large Load Diff