1 {-|
    2 
    3 A 'TimeLogEntry' is a clock-in, clock-out, or other directive in a timelog
    4 file (see timeclock.el or the command-line version). These can be
    5 converted to 'LedgerTransactions' and queried like a ledger.
    6 
    7 -}
    8 
    9 module Ledger.TimeLog
   10 where
   11 import Ledger.Utils
   12 import Ledger.Types
   13 import Ledger.Dates
   14 import Ledger.Commodity
   15 import Ledger.Amount
   16 import Ledger.LedgerTransaction
   17 
   18 instance Show TimeLogEntry where 
   19     show t = printf "%s %s %s" (show $ tlcode t) (show $ tldatetime t) (tlcomment t)
   20 
   21 instance Show TimeLogCode where 
   22     show SetBalance = "b"
   23     show SetRequiredHours = "h"
   24     show In = "i"
   25     show Out = "o"
   26     show FinalOut = "O"
   27 
   28 instance Read TimeLogCode where 
   29     readsPrec _ ('b' : xs) = [(SetBalance, xs)]
   30     readsPrec _ ('h' : xs) = [(SetRequiredHours, xs)]
   31     readsPrec _ ('i' : xs) = [(In, xs)]
   32     readsPrec _ ('o' : xs) = [(Out, xs)]
   33     readsPrec _ ('O' : xs) = [(FinalOut, xs)]
   34     readsPrec _ _ = []
   35 
   36 -- | Convert time log entries to ledger transactions. When there is no
   37 -- clockout, add one with the provided current time. Sessions crossing
   38 -- midnight are split into days to give accurate per-day totals.
   39 entriesFromTimeLogEntries :: LocalTime -> [TimeLogEntry] -> [LedgerTransaction]
   40 entriesFromTimeLogEntries _ [] = []
   41 entriesFromTimeLogEntries now [i]
   42     | odate > idate = [entryFromTimeLogInOut i o'] ++ entriesFromTimeLogEntries now [i',o]
   43     | otherwise = [entryFromTimeLogInOut i o]
   44     where
   45       o = TimeLogEntry Out end ""
   46       end = if itime > now then itime else now
   47       (itime,otime) = (tldatetime i,tldatetime o)
   48       (idate,odate) = (localDay itime,localDay otime)
   49       o' = o{tldatetime=itime{localDay=idate, localTimeOfDay=TimeOfDay 23 59 59}}
   50       i' = i{tldatetime=itime{localDay=addDays 1 idate, localTimeOfDay=midnight}}
   51 entriesFromTimeLogEntries now (i:o:rest)
   52     | odate > idate = [entryFromTimeLogInOut i o'] ++ entriesFromTimeLogEntries now (i':o:rest)
   53     | otherwise = [entryFromTimeLogInOut i o] ++ entriesFromTimeLogEntries now rest
   54     where
   55       (itime,otime) = (tldatetime i,tldatetime o)
   56       (idate,odate) = (localDay itime,localDay otime)
   57       o' = o{tldatetime=itime{localDay=idate, localTimeOfDay=TimeOfDay 23 59 59}}
   58       i' = i{tldatetime=itime{localDay=addDays 1 idate, localTimeOfDay=midnight}}
   59 
   60 -- | Convert a timelog clockin and clockout entry to an equivalent ledger
   61 -- entry, representing the time expenditure. Note this entry is  not balanced,
   62 -- since we omit the \"assets:time\" transaction for simpler output.
   63 entryFromTimeLogInOut :: TimeLogEntry -> TimeLogEntry -> LedgerTransaction
   64 entryFromTimeLogInOut i o
   65     | otime >= itime = t
   66     | otherwise = 
   67         error $ "clock-out time less than clock-in time in:\n" ++ showLedgerTransaction t
   68     where
   69       t = LedgerTransaction {
   70             ltdate         = idate,
   71             ltstatus       = True,
   72             ltcode         = "",
   73             ltdescription  = showtime itod ++ "-" ++ showtime otod,
   74             ltcomment      = "",
   75             ltpostings = ps,
   76             ltpreceding_comment_lines=""
   77           }
   78       showtime = take 5 . show
   79       acctname = tlcomment i
   80       itime    = tldatetime i
   81       otime    = tldatetime o
   82       itod     = localTimeOfDay itime
   83       otod     = localTimeOfDay otime
   84       idate    = localDay itime
   85       odate    = localDay otime
   86       hrs      = elapsedSeconds (toutc otime) (toutc itime) / 3600 where toutc = localTimeToUTC utc
   87       amount   = Mixed [hours hrs]
   88       ps       = [Posting False acctname amount "" RegularPosting
   89                  --,Posting "assets:time" (-amount) "" RegularPosting
   90                  ]