+
+ #{pw}
+
+ _{MsgAdminUserPinPassNotIncluded}
+ $nothing
+ _{MsgAdminUserNoPassword}
+ |]
+ | otherwise = mempty
------------------------------
-- Decode MIME Encoded Word
diff --git a/src/Handler/Utils/Avs.hs b/src/Handler/Utils/Avs.hs
index 292fad0df..845874f64 100644
--- a/src/Handler/Utils/Avs.hs
+++ b/src/Handler/Utils/Avs.hs
@@ -381,7 +381,7 @@ updateAvsUserByADC newAvsDataContact@(AvsDataContact apid newAvsPersonInfo newAv
]
-- update company association & supervision
- Entity{entityKey=newCompanyId} <- upsertAvsCompany newAvsFirmInfo oldAvsFirmInfo
+ newCompanyEnt@Entity{entityKey=newCompanyId} <- upsertAvsCompany newAvsFirmInfo oldAvsFirmInfo
oldCompanyEnt <- getAvsCompany `traverseJoin` oldAvsFirmInfo
primaryCompanyId <- userCompanyCompany <<$>> getUserPrimaryCompany usrId
let oldCompanyId = entityKey <$> oldCompanyEnt
@@ -445,6 +445,7 @@ updateAvsUserByADC newAvsDataContact@(AvsDataContact apid newAvsPersonInfo newAv
-- newAPs <- count $ (UserSupervisorUser ==. usrId) : (UserSupervisorCompany ==. Just newCompanyId) : (UserSupervisorReason ~=. superReasonComDef)
-- when (oldAPs > 0 && newAPs <= 0) $ reportAdminProblem $ AdminProblemNewlyUnsupervised usrId oldCompanyId newCompanyId
-- return pst_up
+ upsertCompanySuperior newCompanyEnt newAvsFirmInfo oldAvsFirmInfo [usrId] -- ensure firmInfo superior is supervisor for this user
update usrId usr_up2 -- update user by company switch first (due to possible conflicts with usr_up2)
update usrId usr_up1 -- update user eventually
update uaId avs_ups -- update stored avsinfo for future updates
@@ -587,7 +588,7 @@ upsertAvsCompany :: AvsFirmInfo -> Maybe AvsFirmInfo -> DB (Entity Company)
upsertAvsCompany newAvsFirmInfo mbOldAvsFirmInfo = do
mbFirmEnt <- getAvsCompany newAvsFirmInfo -- primarily by AvsId, then Shorthand, then name
$logInfoS "AVS" [st|upsertAvsCompany: old #{tshow mbFirmEnt} new #{tshow newAvsFirmInfo}|]
- cmpEnt <- case (mbFirmEnt, mbOldAvsFirmInfo) of
+ case (mbFirmEnt, mbOldAvsFirmInfo) of
(Nothing, _) -> do -- insert new company, neither AvsId nor Shorthand exist in DB
afn <- if 0 < newAvsFirmInfo ^. _avsFirmFirmNo
then return $ newAvsFirmInfo ^. _avsFirmFirmNo
@@ -630,8 +631,6 @@ upsertAvsCompany newAvsFirmInfo mbOldAvsFirmInfo = do
_otherwise -> return res_cmp
$logInfoS "AVS" "Update company completed."
return res_cmp2
- void $ upsertCompanySuperior cmpEnt newAvsFirmInfo mbOldAvsFirmInfo -- ensure firmInfo superior is supervisor
- return cmpEnt
where
firmInfo2key =
CheckUpdate CompanyShorthand $ _avsFirmAbbreviation . from _CI -- Updating primary key works in principle thanks to OnUpdateCascade, but fails due to update get
@@ -644,9 +643,10 @@ upsertAvsCompany newAvsFirmInfo mbOldAvsFirmInfo = do
-- , CheckUpdate CompanyPrefersPostal _avsFirmPrefersPostal -- Guessing here is not useful, since postal preference is ignored anyway when there is only one option available
]
--- upsert company supervisor from AvsFirmEMailSuperior
-upsertCompanySuperior :: Entity Company -> AvsFirmInfo -> Maybe AvsFirmInfo -> DB () -- (Maybe UserId) possibly return superior, but currently not needed
-upsertCompanySuperior Entity{entityKey=cid, entityVal=Company{}} newAfi oldAfi = do
+
+-- | adjust superiors, assumes that CompanyUser exists for all usrs for given company; does not work otherwise
+upsertCompanySuperior :: Entity Company -> AvsFirmInfo -> Maybe AvsFirmInfo -> [UserId] -> DB () -- may return superior (Maybe UserId), but currently not needed
+upsertCompanySuperior Entity{entityKey=cid, entityVal=Company{}} newAfi oldAfi usrs =
let reasonSuperior = Just $ tshow SupervisorReasonAvsSuperior
getInsertUid eml = altM (guessUserByEmail $ stripCI eml) (catchAll2log' $ Just . entityKey <$> ldapLookupAndUpsert eml)
newAvsNo = newAfi ^. _avsFirmFirmNo
@@ -657,49 +657,55 @@ upsertCompanySuperior Entity{entityKey=cid, entityVal=Company{}} newAfi oldAfi =
getOldId = (guessUserByEmail . stripCI) `traverseJoin` mbOldEmail
unchangedCompany = oldAvsNo == Just newAvsNo
changedSuperior = mbSupEmail /= mbOldEmail -- beware, both could be Nothing
- mbSupId <- getSupId
- -- delete old superiors, if any
- when (unchangedCompany && changedSuperior) $
- deleteWhere $ mcons ((UserSupervisorSupervisor !=.) <$> mbSupId)
- [ UserSupervisorCompany ==. Just cid, UserSupervisorReason ==. reasonSuperior ]
- -- ensure superior supervision
- case mbSupId of
- Just supId -> do
- -- ensure association between company and superior at equal-to-top priority
- prio <- getCompanyUserMaxPrio supId
- void $ insertUnique (UserCompany supId cid False False prio True reasonSuperior) -- superior is not a supervisor, do not change existing user company associations
- -- ensure all company associates are irregularly supervised by the superior
- E.insertSelectWithConflict UniqueUserSupervisor
- (do
- usr <- E.from $ E.table @UserCompany
- E.where_ $ usr E.^. UserCompanyCompany E.==. E.val cid
- -- E.&&. E.notExists (do -- restrict to primary company only
- -- othr <- E.from $ E.table @UserCompany
- -- E.where_ $ othr E.^. UserCompanyPriority E.>. usr E.^. UserCompanyPriority
- -- E.&&. othr E.^. UserCompanyUser E.==. usr E.^. UserCompanyUser
- -- E.&&. othr E.^. UserCompanyCompany E.!=. E.val cid -- redundant due to > above, but likely performance improving
- -- )
- return $ UserSupervisor
- E.<# E.val supId
- E.<&> (usr E.^. UserCompanyUser)
- E.<&> E.false
- E.<&> E.justVal cid
- E.<&> E.val reasonSuperior
- )
- (\_old _new -> [] -- do not change exisitng supervision
- -- [ UserSupervisorCompany E.=. new E.^. UserSupervisorCompany
- -- , UserSupervisorReason E.=. new E.^. UserSupervisorReason
- -- , UserSupervisorRerouteNotifications E.=. new E.^. UserSupervisorRerouteNotifications
- -- ]
- )
- when (unchangedCompany && changedSuperior) $ do
- oldSupId <- getOldId
- reportAdminProblem $ AdminProblemCompanySuperiorChange supId cid oldSupId
- Nothing ->
- when (unchangedCompany && changedSuperior) $ do
- oldSupId <- getOldId
- reportAdminProblem $ AdminProblemCompanySuperiorNotFound mbSupEmail cid oldSupId
+ -- 1. not unchangedCompany: do not delete, but ensure that superior supervision is set, since it could be a just a single user company change
+ -- 2. unchangedCompany && not changedSuperior: superior must already been set, short-circuit
+ -- 3. unchangedCompany && changedSuperior: update superior for all users
+ in unless (unchangedCompany && not changedSuperior) $ do -- do nothing if (unchangedCompany && not changedSuperior).
+ mbSupId <- getSupId
+ -- delete old superiors, if any
+ when (unchangedCompany && changedSuperior) $
+ deleteWhere $ mcons ((UserSupervisorSupervisor !=.) <$> mbSupId)
+ [ UserSupervisorCompany ==. Just cid, UserSupervisorReason ==. reasonSuperior ]
+ unless unchangedCompany $
+ deleteWhere [ UserSupervisorReason ==. reasonSuperior, UserSupervisorUser <-. usrs ]
+ -- ensure superior supervision
+ case mbSupId of
+ Just supId -> do
+ -- ensure association between company and superior at equal-to-top priority
+ prio <- getCompanyUserMaxPrio supId
+ void $ insertUnique (UserCompany supId cid False False prio True reasonSuperior) -- superior is not a supervisor, do not change existing user company associations
+ -- ensure all company associates are irregularly supervised by the superior
+ E.insertSelectWithConflict UniqueUserSupervisor
+ (do
+ usr <- E.from $ E.table @UserCompany
+ E.where_ $ usr E.^. UserCompanyCompany E.==. E.val cid
+ -- E.&&. E.notExists (do -- restrict to primary company only
+ -- othr <- E.from $ E.table @UserCompany
+ -- E.where_ $ othr E.^. UserCompanyPriority E.>. usr E.^. UserCompanyPriority
+ -- E.&&. othr E.^. UserCompanyUser E.==. usr E.^. UserCompanyUser
+ -- E.&&. othr E.^. UserCompanyCompany E.!=. E.val cid -- redundant due to > above, but likely performance improving
+ -- )
+ return $ UserSupervisor
+ E.<# E.val supId
+ E.<&> (usr E.^. UserCompanyUser)
+ E.<&> E.false
+ E.<&> E.justVal cid
+ E.<&> E.val reasonSuperior
+ )
+ (\_old _new -> [] -- do not change exisitng supervision
+ -- [ UserSupervisorCompany E.=. new E.^. UserSupervisorCompany
+ -- , UserSupervisorReason E.=. new E.^. UserSupervisorReason
+ -- , UserSupervisorRerouteNotifications E.=. new E.^. UserSupervisorRerouteNotifications
+ -- ]
+ )
+ when (unchangedCompany && changedSuperior) $ do
+ oldSupId <- getOldId
+ reportAdminProblem $ AdminProblemCompanySuperiorChange supId cid oldSupId
+ Nothing ->
+ when (unchangedCompany && changedSuperior) $ do
+ oldSupId <- getOldId
+ reportAdminProblem $ AdminProblemCompanySuperiorNotFound mbSupEmail cid oldSupId
queueAvsUpdateByUID :: (MonoFoldable mono, UserId ~ Element mono) => mono -> Maybe Day -> DB Int64
queueAvsUpdateByUID uids = queueAvsUpdateAux (E.table @User) (E.^. UserId) (\usr -> usr E.^. UserId `E.in_` E.vals uids)
diff --git a/src/Jobs/Handler/SynchroniseAvs.hs b/src/Jobs/Handler/SynchroniseAvs.hs
index 1a3ed10ab..5651f9558 100644
--- a/src/Jobs/Handler/SynchroniseAvs.hs
+++ b/src/Jobs/Handler/SynchroniseAvs.hs
@@ -14,7 +14,7 @@ import Import
import qualified Data.Text as Text
import qualified Data.Set as Set
-import qualified Data.Map as Map
+-- import qualified Data.Map as Map
import qualified Data.Conduit.List as C
import Database.Esqueleto.Experimental ((:&)(..))
@@ -151,7 +151,7 @@ dispatchJobSynchroniseAvsLicences = JobHandlerException $ do -- when (synchLevel
procLic aLic up apids
| n <- Set.size apids, n > 0 =
let subtype = Text.cons (bool '↧' '↥' up) $ Text.singleton $ licence2char aLic
- logit errm = runDB $ logInterface' "AVS" subtype False (isJust errm) (Just n) (fromMaybe "Automatic synch" errm)
+ logit errm = runDB $ logInterface' "AVS" subtype False (isNothing errm) (Just n) (fromMaybe "Automatic synch" errm)
catchAllAvs = flip catch (\err -> logit (Just $ tshow (err :: SomeException)) >> return (-1))
in if NTop (Just n) <= NTop maxChanges
then do
@@ -163,7 +163,7 @@ dispatchJobSynchroniseAvsLicences = JobHandlerException $ do -- when (synchLevel
(AvsLicenceDifferences{..}, rsChanged) <- retrieveDifferingLicences
-- prevent automatic changes to users blocked with certain reasons and with currently being associated with multiple companies
- multiFirmBlocks <- ifNothingM reasonFilter mempty $ \reasons -> do
+ reasonFltrdIds <- ifNothingM reasonFilter mempty $ \reasons -> do
now <- liftIO getCurrentTime
firmBlocks <- runDBRead $ E.select $ do
(uavs :& _qualUser :& qblock) <- E.from $ E.table @UserAvs
@@ -172,16 +172,14 @@ dispatchJobSynchroniseAvsLicences = JobHandlerException $ do -- when (synchLevel
qualUser E.^. QualificationUserId E.==. qblock E.^. QualificationUserBlockQualificationUser
E.&&. qblock `isLatestBlockBefore'` E.val now)
E.where_ $ (qblock E.^. QualificationUserBlockReason E.~*. E.val reasons)
- E.&&. uavs E.^. UserAvsPersonId `E.in_` E.vals (avsLicenceDiffRevokeAll `Set.union` avsLicenceDiffRevokeRollfeld)
- E.&&. E.not_ (qblock E.^. QualificationUserBlockUnblock)
+ E.&&. uavs E.^. UserAvsPersonId `E.in_` E.vals (Set.unions [avsLicenceDiffRevokeAll, avsLicenceDiffRevokeRollfeld, avsLicenceDiffGrantVorfeld, avsLicenceDiffGrantRollfeld])
return $ uavs E.^. UserAvsPersonId
- firmBlockData <- lookupAvsUsers $ Set.fromList $ map E.unValue firmBlocks -- may throw, but we need to abort then
- return $ Map.keysSet $ Map.filter hasMultipleFirms firmBlockData
+ return $ Set.fromList $ map E.unValue firmBlocks
let fltrIds
| synchLevel >= 5 = id
- | synchLevel >= 3 = flip Set.difference multiFirmBlocks
- | otherwise = flip Set.difference $ multiFirmBlocks `Set.union` rsChanged
+ | synchLevel >= 3 = flip Set.difference reasonFltrdIds
+ | otherwise = flip Set.difference $ reasonFltrdIds `Set.union` rsChanged
when (synchLevel >= 1) $ procLic AvsNoLicence False $ fltrIds avsLicenceDiffRevokeAll --revoke Vorfeld and maybe also Rollfeld
when (synchLevel >= 2) $ procLic AvsLicenceVorfeld True $ fltrIds avsLicenceDiffGrantVorfeld --grant Vorfeld
diff --git a/src/Model/Types/Avs.hs b/src/Model/Types/Avs.hs
index 0c50360be..26c0aad49 100644
--- a/src/Model/Types/Avs.hs
+++ b/src/Model/Types/Avs.hs
@@ -501,9 +501,11 @@ deriveJSON defaultOptions
} ''AvsDataPerson
-}
+{- Did not work as intended! Verify, if needed again.
hasMultipleFirms :: AvsDataPerson -> Bool
hasMultipleFirms AvsDataPerson{avsPersonPersonCards=crds} =
1 < Set.size (Set.filter isJust $ Set.map avsDataFirm crds)
+-}
data AvsPersonLicence = AvsPersonLicence
{ avsLicenceRampLicence :: AvsLicence
diff --git a/src/Model/Types/Misc.hs b/src/Model/Types/Misc.hs
index c830bd0f5..91538ff04 100644
--- a/src/Model/Types/Misc.hs
+++ b/src/Model/Types/Misc.hs
@@ -69,7 +69,7 @@ data SupervisorReason
-- so do not change values here without a proper migration
instance Show SupervisorReason where
show SupervisorReasonCompanyDefault = "Firmenstandard"
- show SupervisorReasonAvsSuperior = "Vorgesetzer"
+ show SupervisorReasonAvsSuperior = "Vorgesetzter"
show SupervisorReasonUnknown = "Unbekannt"
diff --git a/src/Utils.hs b/src/Utils.hs
index ceac5a618..201fd54de 100644
--- a/src/Utils.hs
+++ b/src/Utils.hs
@@ -813,6 +813,19 @@ checkAsc :: Ord a => [a] -> Bool
checkAsc (x:r@(y:_)) = x<=y && checkAsc r
checkAsc _ = True
+-- return a part of a list between two given elements, if it exists
+listBracket :: Eq a => (a,a) -> [a] -> Maybe [a]
+listBracket _ [] = Nothing
+listBracket b@(s,e) (h:t)
+ | s == h = listUntil [] t
+ | otherwise = listBracket b t
+ where
+ listUntil _ [] = Nothing
+ listUntil l1 (h1:t1)
+ | e == h1 = Just $ reverse l1
+ | otherwise = listUntil (h1:l1) t1
+
+
----------
-- Sets --
----------
diff --git a/templates/avs.hamlet b/templates/avs.hamlet
index f3c84153f..d52e32446 100644
--- a/templates/avs.hamlet
+++ b/templates/avs.hamlet
@@ -35,6 +35,9 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
$maybe answer <- mbQryLic
^{answer}
+ $maybe autodiffs <- mbAutoDiffs
+
+ #{autodiffs}
diff --git a/templates/i18n/avs-synchronisation/de-de-formal.hamlet b/templates/i18n/avs-synchronisation/de-de-formal.hamlet
index 0c5117f01..502789afc 100644
--- a/templates/i18n/avs-synchronisation/de-de-formal.hamlet
+++ b/templates/i18n/avs-synchronisation/de-de-formal.hamlet
@@ -50,27 +50,27 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later
$if notNull avsLicenceSynchTimes
- Automatische AVS Fahrlizen Sychronisation
+ Automatische AVS Fahrlizenzen Sychronisation
-
Uhrzeiten Synchronisation
-
- Werktags, weniger Minuten nach folgenden vollen Stunden: #{tshow avsLicenceSynchTimes}
+ Werktags, wenige Minuten nach folgenden vollen Stunden: #{tshow avsLicenceSynchTimes}
-
Synchronisationslevel
-
- #{avsLicenceSynchLevel} #
- $case avsLicenceSynchLevel
- $of 1
- Nur Vorfeld-Fahrberechtigungen entziehen
- $of 2
- Vorfeld-Fahrberechtigungen entziehen und gewähren
- $of 3
- Vorfeld-Fahrberechtigungen entziehen und gewähren, #
- so wie Rollfeld-Fahrberechtigungen zu Vorfeld-Fahrberechtigungen herabstufen
- $of _
- Vorfeld- und Rollfeld-Fahrberechtigungen entziehen und gewähren
+ #{avsLicenceSynchLevel}: #
+ $case avsLicenceSynchLevel
+ $of 1
+ Nur Vorfeld-Fahrberechtigungen entziehen
+ $of 2
+ Vorfeld-Fahrberechtigungen entziehen und gewähren
+ $of 3
+ Vorfeld-Fahrberechtigungen entziehen und gewähren, #
+ so wie Rollfeld-Fahrberechtigungen zu Vorfeld-Fahrberechtigungen herabstufen
+ $of _
+ Vorfeld- und Rollfeld-Fahrberechtigungen entziehen und gewähren
$maybe reasons <- avsLicenceSynchReasonFilter
-
Ausnahmen
@@ -80,4 +80,4 @@ $if notNull avsLicenceSynchTimes
-
Maximal Änderungen
-
- Keine Synchronisation durchführen, wenn es mehr als #{maxChange} Änderungen pro Level wären
+ Keine Synchronisation eines Levels durchführen, welches mehr als #{maxChange} Änderungen hätte
diff --git a/templates/i18n/avs-synchronisation/en-eu.hamlet b/templates/i18n/avs-synchronisation/en-eu.hamlet
index 0eba07f77..06bb9561f 100644
--- a/templates/i18n/avs-synchronisation/en-eu.hamlet
+++ b/templates/i18n/avs-synchronisation/en-eu.hamlet
@@ -60,16 +60,16 @@ $if notNull avsLicenceSynchTimes
-
Synchronisation level
-
- #{avsLicenceSynchLevel} #
- $case avsLicenceSynchLevel
- $of 1
- Revoke apron driving licences only
- $of 2
- Grant and revoke apron driving licences only
- $of 3
- Grant and revoke apron driving licences and downgrade maneuvering area licences to apron driving licences
- $of _
- Grant and revoke all driving licences automatically
+ #{avsLicenceSynchLevel}: #
+ $case avsLicenceSynchLevel
+ $of 1
+ Revoke apron driving licences only
+ $of 2
+ Grant and revoke apron driving licences only
+ $of 3
+ Grant and revoke apron driving licences and downgrade maneuvering area licences to apron driving licences
+ $of _
+ Grant and revoke all driving licences automatically
$maybe reasons <- avsLicenceSynchReasonFilter
-
Exemptions
@@ -79,4 +79,4 @@ $if notNull avsLicenceSynchTimes
-
Max changes
-
- Do not synchronize a licence if the number of changes exceeds #{maxChange}
+ Do not synchronize a licence level if the number of changes exceeds #{maxChange}
diff --git a/test/Database/Fill.hs b/test/Database/Fill.hs
index 268c56c97..6827257e6 100644
--- a/test/Database/Fill.hs
+++ b/test/Database/Fill.hs
@@ -113,10 +113,10 @@ fillDb = do
, userMobile = Nothing
, userCompanyPersonalNumber = Just "00000"
, userCompanyDepartment = Nothing
- , userPinPassword = Nothing
+ , userPinPassword = Just "1234.5"
, userPostAddress = Just $ markdownToStoredMarkup ("Büro 127 \nMathematisches Institut der Ludwig-Maximilians-Universität München \nTheresienstr. 39 \nD-80333 München"::Text)
, userPostLastUpdate = Nothing
- , userPrefersPostal = True
+ , userPrefersPostal = False
, userExamOfficeGetSynced = userDefaultExamOfficeGetSynced
, userExamOfficeGetLabels = userDefaultExamOfficeGetLabels
}
@@ -202,7 +202,7 @@ fillDb = do
, userPinPassword = Nothing
, userPostAddress = Nothing
, userPostLastUpdate = Nothing
- , userPrefersPostal = True
+ , userPrefersPostal = False
, userExamOfficeGetSynced = userDefaultExamOfficeGetSynced
, userExamOfficeGetLabels = userDefaultExamOfficeGetLabels
}
@@ -766,7 +766,7 @@ fillDb = do
void . insert' $ QualificationUser jost qid_r (n_day 99) (n_day $ -11) (n_day $ -222) True (n_day' $ -9) -- TODO: better dates!
void . insert' $ QualificationUser jost qid_l (n_day 999) (n_day $ -111) (n_day $ -2222) True (n_day' $ -9) -- TODO: better dates!
void . insert' $ QualificationUser jost qid_rp (n_day 999) (n_day $ -111) (n_day $ -2222) True (n_day' $ -9) -- TODO: better dates!
- qfkleen <- insert' $ QualificationUser gkleen qid_f (n_day 33) (n_day $ -4) (n_day $ -20) True (n_day' $ -9)
+ qfkleen <- insert' $ QualificationUser gkleen qid_f (n_day 10) (n_day $ -40) (n_day $ -120) True (n_day' $ -20)
void . insert $ QualificationUserBlock qfkleen False (n_day' 1) "Future block" (Just svaupel)
void . insert' $ QualificationUser maxMuster qid_f (n_day 0) (n_day $ -2) (n_day $ -8) False (n_day' $ -1)
qfvaupel <- insert' $ QualificationUser svaupel qid_f (n_day 2) (n_day $ -1) (n_day $ -2) True (n_day' $ -9)