Every Monday, totals the start and finish times your team logs in a sheet and emails each person's hours to a manager or payroll.
Uses
Gmail
Google Sheets
Email a weekly summary of everyone's hours
spreadsheetUrl to blank.Set spreadsheetUrl to blank.entriesTab to Time entries.Set entriesTab to Time entries.summaryTab to Weekly summary.Set summaryTab to Weekly summary.managerEmail to blank.Set managerEmail to blank.daysToInclude to 7.Set daysToInclude to 7.today.Get today's date and save it as today.weekEnding.Get yesterday's date and save it as weekEnding.weekEnding readable as “November 2, 2026” and save it as weekEndingText.Make weekEnding readable as “November 2, 2026” and save it as weekEndingText.spreadsheetUrl. Save it as timesheet.Read the rows of spreadsheetUrl. Save it as timesheet.Date, Person, Start, End] where timesheet.headers doesn't contain item and save it as missingColumns.Keep each item in [Date, Person, Start, End] where timesheet.headers doesn't contain item and save it as missingColumns.missingColumns isn't emptyIf missingColumns isn't emptyYes
missingColumns with , and save it as missingList.Join missingColumns with , and save it as missingList.missingList. Row 1 needs Date, Person, Start and End, spelled exactly like that. Nothing was added to the summary or emailed.Stop and report a problem: The '@entriesTab' tab is missing these column headings: missingList. Row 1 needs Date, Person, Start and End, spelled exactly like that. Nothing was added to the summary or emailed.No
No steps
entries to timesheet.rows.Set entries to timesheet.rows.workedEntries to an empty list.Set workedEntries to an empty list.flaggedEntries to an empty list.Set flaggedEntries to an empty list.peopleSeen to an empty list.Set peopleSeen to an empty list.unreadableStore.Get the stored value 'timesheet_unreadable_rows'. Save it as unreadableStore.reportedBefore to an empty list.Set reportedBefore to an empty list.unreadableStore.found is yesIf unreadableStore.found is yesYes
reportedBefore to unreadableStore.value.Change reportedBefore to unreadableStore.value.No
No steps
unreadableNow to an empty list.Set unreadableNow to an empty list.entry in entries:For each entry in entries:entry.Date is blank and entry.Person is blank and entry.Start is blank and…If entry.Date is blank and entry.Person is blank and entry.Start is blank and…Yes
No
No steps
personLabel to Unnamed.Set personLabel to Unnamed.personErrorIf a step below fails, save the error as personErrorTry
entry.Person and save it as personLabel.Trim entry.Person and save it as personLabel.On error
personLabel to Unnamed.Change personLabel to Unnamed.personLabel is emptyIf personLabel is emptyYes
personLabel to Unnamed.Change personLabel to Unnamed.No
No steps
rowLabel to ?.Set rowLabel to ?.entry.row_number isn't emptyIf entry.row_number isn't emptyYes
rowLabel to entry.row_number.Change rowLabel to entry.row_number.No
No steps
dateReadable to false.Set dateReadable to false.inThisWeek to false.Set inThisWeek to false.entryHours to 0.Set entryHours to 0.entryUsable to false.Set entryUsable to false.problem to Date, Start or End is missing or is not written as YYYY-MM-DD and HH:MM.Set problem to Date, Start or End is missing or is not written as YYYY-MM-DD and HH:MM.entryErrorIf a step below fails, save the error as entryErrorTry
entry.Date and save it as entryDate.Trim entry.Date and save it as entryDate.entryDate to today and save it as entryAgeInDays.Count the days from entryDate to today and save it as entryAgeInDays.dateReadable to true.Change dateReadable to true.inThisWeek to entryAgeInDays is greater than or equal to 1 and entryAgeInDays is less than or equal to daysToInclude.Change inThisWeek to entryAgeInDays is greater than or equal to 1 and entryAgeInDays is less than or equal to daysToInclude.entryDate entry.Start to entryDate entry.End and save it as minutesWorked.Count the minutes from entryDate entry.Start to entryDate entry.End and save it as minutesWorked.minutesWorked is less than or equal to -600If minutesWorked is less than or equal to -600Yes
minutesWorked.Add 1440 to minutesWorked.No
No steps
minutesWorked divided by 60 to 2 decimal places and save it as entryHours.Round minutesWorked divided by 60 to 2 decimal places and save it as entryHours.entryUsable to true.Change entryUsable to true.On error
entryUsable to false.Change entryUsable to false.entryUsable is yes and entryHours is less than or equal to 0If entryUsable is yes and entryHours is less than or equal to 0Yes
entryUsable to false.Change entryUsable to false.problem to End is not later than Start.Change problem to End is not later than Start.No
No steps
includeEntry to inThisWeek.Set includeEntry to inThisWeek.dateReadable is noIf dateReadable is noYes
row @rowLabel: @entry.Person | @entry.Date | @entry.Start | @entry.End and save it as rowKey.Build the text row @rowLabel: @entry.Person | @entry.Date | @entry.Start | @entry.End and save it as rowKey.rowKey to the end of unreadableNow and save it as unreadableNow.Add rowKey to the end of unreadableNow and save it as unreadableNow.reportedBefore doesn't contain rowKeyIf reportedBefore doesn't contain rowKeyYes
includeEntry to true.Change includeEntry to true.No
No steps
No
No steps
includeEntry is yesIf includeEntry is yesYes
personLabel to the end of peopleSeen and save it as peopleSeen.Add personLabel to the end of peopleSeen and save it as peopleSeen.entryUsable is yesIf entryUsable is yesYes
workedEntries and save it as workedEntries.Add a record of person and hours to the end of workedEntries and save it as workedEntries.No
flaggedEntries and save it as flaggedEntries.Add a record of person, row, and problem to the end of flaggedEntries and save it as flaggedEntries.No
No steps
peopleSeen and save it as people.Remove duplicates from peopleSeen and save it as people.people is emptyIf people is emptyYes
managerEmail.Send an email to managerEmail.
Subject: Timesheet summary for the week ending weekEndingText: no entries foundNo time entries dated in the week ending weekEndingText were found on the '@entriesTab' tab, so there are no hours to report.
If your team did log hours that week, check that each row's Date is written as YYYY-MM-DD.weekEndingText, so nothing was added to the summary. Emailed managerEmail to say so.Finish the run: No time entries dated in the week ending weekEndingText, so nothing was added to the summary. Emailed managerEmail to say so.No
No steps
summaryLines to an empty list.Set summaryLines to an empty list.entriesToCheck to 0.Set entriesToCheck to 0.summaryTabProblem to blank.Set summaryTabProblem to blank.person in people:For each person in people:personHours to an empty list.Set personHours to an empty list.personProblems to an empty list.Set personProblems to an empty list.workedEntry in workedEntries:For each workedEntry in workedEntries:workedEntry.person is personIf workedEntry.person is personYes
workedEntry.hours to the end of personHours and save it as personHours.Add workedEntry.hours to the end of personHours and save it as personHours.No
No steps
flaggedEntry in flaggedEntries:For each flaggedEntry in flaggedEntries:flaggedEntry.person is personIf flaggedEntry.person is personYes
flaggedEntry.row - flaggedEntry.problem to the end of personProblems and save it as personProblems.Add row flaggedEntry.row - flaggedEntry.problem to the end of personProblems and save it as personProblems.No
No steps
personHours to 2 decimal places and save it as totalHours.Round the total of personHours to 2 decimal places and save it as totalHours.personProblems with ; and save it as problemText.Join personProblems with ; and save it as problemText.entriesToCheck to entriesToCheck plus the length of personProblems.Change entriesToCheck to entriesToCheck plus the length of personProblems.appendErrorIf a step below fails, save the error as appendErrorTry
summaryTab in spreadsheetUrl.Add a row to summaryTab in spreadsheetUrl.On error
summaryTabProblem to appendError.message.Change summaryTabProblem to appendError.message.summaryLine to person: totalHours hours from the length of personHours entries.Set summaryLine to person: totalHours hours from the length of personHours entries.personProblems isn't emptyIf personProblems isn't emptyYes
summaryLine to summaryLine - NEEDS ATTENTION: problemText.Change summaryLine to summaryLine - NEEDS ATTENTION: problemText.No
No steps
summaryLine to the end of summaryLines and save it as summaryLines.Add summaryLine to the end of summaryLines and save it as summaryLines.managerEmail.Send an email to managerEmail.
Subject: Timesheet summary for the week ending weekEndingTextSelf-reported hours for the week ending weekEndingText.
summaryLines joined by a new line
Entries needing attention: entriesToCheck
The full breakdown is on the '@summaryTab' tab of the timesheet.saveErrorIf a step below fails, save the error as saveErrorTry
unreadableNow.Set the stored value 'timesheet_unreadable_rows' to unreadableNow.On error
saveError.message), so they will be listed again next week.Stop and report a problem: The summary was emailed, but Lodol could not note which entries with an unreadable date it has reported (saveError.message), so they will be listed again next week.summaryTabProblem isn't blankIf summaryTabProblem isn't blankYes
managerEmail, but rows could not be added to the '@summaryTab' tab (summaryTabProblem). Check that the tab exists with that exact name.Stop and report a problem: The summary was emailed to managerEmail, but rows could not be added to the '@summaryTab' tab (summaryTabProblem). Check that the tab exists with that exact name.No
No steps
weekEndingText to managerEmail and added one row per person to the '@summaryTab' tab.Finish the run: Emailed the summary for the week ending weekEndingText to managerEmail and added one row per person to the '@summaryTab' tab.