diff --git a/hledger-lib/Hledger/Data/Timeclock.hs b/hledger-lib/Hledger/Data/Timeclock.hs index 9507408de..e4a356dda 100644 --- a/hledger-lib/Hledger/Data/Timeclock.hs +++ b/hledger-lib/Hledger/Data/Timeclock.hs @@ -106,21 +106,28 @@ pairClockEntries (entry : rest) actives sessions (idate, odate) = (localDay itime, localDay otime) omidnight = entry {tldatetime = itime {localDay = idate, localTimeOfDay = TimeOfDay 23 59 59}} imidnight = inentry {tldatetime = itime {localDay = addDays 1 idate, localTimeOfDay = midnight}} - (sessions', actives', rest') = - if odate > idate - then (Session {in' = inentry, out = omidnight} : sessions, imidnight : newactive, entry : rest) - else (Session {in' = inentry, out = entry} : sessions, newactive, rest) - l = show $ unPos $ sourceLine $ tlsourcepos entry - c = unPos $ sourceColumn $ tlsourcepos entry - inentries = - if any (\e -> tlaccount e == tlaccount entry) actives - then error' $ printf - "%s:\n%s\n%s\n\nEncountered clockin entry for session \"%s\" that is already active." - (sourcePosPretty $ tlsourcepos entry) - (l ++ " | " ++ show entry) - (replicate (length l) ' ' ++ " |" ++ replicate c ' ' ++ "^") - (tlaccount entry) - else entry : actives + (sessions', actives', rest') + | odate > idate = (Session {in' = inentry, out = omidnight} : sessions, imidnight : newactive, entry : rest) + | otherwise = (Session {in' = inentry, out = entry} : sessions, newactive, rest) + inentries = case filter ((== tlaccount entry) . tlaccount) actives of + [] -> entry : actives + activesinthisacct -> error' $ T.unpack $ makeTimeClockErrorExcerpt entry $ T.unlines $ [ + "Simultaneous sessions with the same account name are not supported." + ,"This clockin overlaps with:" + ] <> map (flip makeTimeClockErrorExcerpt "") activesinthisacct + -- XXX better to show full session(s) + -- <> map T.show (filter ((`elem` activesinthisacct).in') sessions) + +makeTimeClockErrorExcerpt :: TimeclockEntry -> T.Text -> T.Text +makeTimeClockErrorExcerpt e@TimeclockEntry{tlsourcepos=pos} msg = T.unlines [ + T.pack (sourcePosPretty pos) <> ":" + ,l <> " | " <> T.show e + -- ,T.replicate (T.length l) " " <> " |" -- <> T.replicate c " " <> "^") + ,"" + ] <> msg + where + l = T.show $ unPos $ sourceLine $ tlsourcepos e + -- c = unPos $ sourceColumn $ tlsourcepos e -- | Convert time log entries to journal transactions, allowing multiple clocked-in sessions at once. -- This is the new, default behaviour. diff --git a/hledger/hledger.m4.md b/hledger/hledger.m4.md index 6e71502d7..b911193a7 100644 --- a/hledger/hledger.m4.md +++ b/hledger/hledger.m4.md @@ -4804,6 +4804,9 @@ $ hledger -f t.timeclock print ``` +Note, you can have overlapping sessions (multiple sessions open simultaneously), but they must have different account names. +Overlapping sessions with the same account name are currently not supported currently. + Here is a [sample.timeclock](https://raw.github.com/simonmichael/hledger/master/examples/sample.timeclock) to download and some queries to try: diff --git a/hledger/test/errors/tcorderedactions.test b/hledger/test/errors/tcorderedactions.test index b6d768539..e79648341 100644 --- a/hledger/test/errors/tcorderedactions.test +++ b/hledger/test/errors/tcorderedactions.test @@ -1,9 +1,9 @@ $$$ hledger check -f tcorderedactions.timeclock >>>2 /Error: .*tcorderedactions.timeclock:8:1: 8 \| i 2022-01-01 00:01:00 a - \| \^ -Encountered clockin entry for session "a" that is already active. +Simultaneous sessions with the same account name are not supported. +This clockin overlaps with: / >>>= 1 diff --git a/hledger/test/timeclock.test b/hledger/test/timeclock.test index a65fbfc41..7a872eb8d 100644 --- a/hledger/test/timeclock.test +++ b/hledger/test/timeclock.test @@ -192,6 +192,17 @@ $ hledger -f timeclock:- print >= +# ** 14. Sessions with the same account name may not overlap (#2417). +< +i 2024-01-01 13:00:00 a +o 2024-01-01 15:00:00 +i 2024-01-01 14:00:00 a +o 2024-01-01 15:00:00 + +$ hledger -f timeclock:- print +>2 /Simultaneous sessions with the same account name are not supported./ +>=!0 + # ** OLD: ## multi-day sessions get a new transaction for each day