@mpurdon/mcp-freshbooks 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Time entry tools: list and create.
3
+ *
4
+ * list_time_entries — returns a day-by-day breakdown of what's tracked for a
5
+ * period, including which weekdays have no entries yet.
6
+ *
7
+ * create_time_entry — adds a single time entry for a given date, project,
8
+ * service, and duration. Uses Toronto (America/Toronto) as the local timezone
9
+ * since that's where PurdonMoi Inc operates.
10
+ */
11
+ import { z } from "zod";
12
+ // ── Constants ─────────────────────────────────────────────────────────────────
13
+ const TRAJECTOR_CLIENT_ID = 688912;
14
+ // ── Input schemas ─────────────────────────────────────────────────────────────
15
+ export const ListTimeEntriesInput = z
16
+ .object({
17
+ start_date: z
18
+ .string()
19
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
20
+ .describe("YYYY-MM-DD"),
21
+ end_date: z
22
+ .string()
23
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
24
+ .describe("YYYY-MM-DD"),
25
+ client_id: z
26
+ .number()
27
+ .int()
28
+ .positive()
29
+ .optional()
30
+ .default(TRAJECTOR_CLIENT_ID)
31
+ .describe("FreshBooks client ID (defaults to Trajector 688912)"),
32
+ })
33
+ .strict();
34
+ export const CreateTimeEntryInput = z
35
+ .object({
36
+ date: z
37
+ .string()
38
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
39
+ .describe("YYYY-MM-DD — the day the work was done"),
40
+ duration_hours: z
41
+ .number()
42
+ .positive()
43
+ .max(24)
44
+ .describe("Hours worked, e.g. 8 or 8.5"),
45
+ project_id: z
46
+ .number()
47
+ .int()
48
+ .positive()
49
+ .describe("FreshBooks project ID (visible in list_time_entries output)"),
50
+ service_id: z
51
+ .number()
52
+ .int()
53
+ .positive()
54
+ .describe("FreshBooks service ID (visible in list_time_entries output)"),
55
+ note: z
56
+ .string()
57
+ .max(2000)
58
+ .optional()
59
+ .describe("Optional work note / activity description"),
60
+ start_hour: z
61
+ .number()
62
+ .int()
63
+ .min(0)
64
+ .max(23)
65
+ .optional()
66
+ .default(9)
67
+ .describe("Local hour to start (24-hour, Toronto time). Defaults to 9 (9 AM)."),
68
+ client_id: z
69
+ .number()
70
+ .int()
71
+ .positive()
72
+ .optional()
73
+ .default(TRAJECTOR_CLIENT_ID)
74
+ .describe("FreshBooks client ID (defaults to Trajector 688912)"),
75
+ })
76
+ .strict();
77
+ // ── Helpers ───────────────────────────────────────────────────────────────────
78
+ /**
79
+ * Returns the UTC offset for Toronto (America/Toronto) on a given YYYY-MM-DD date.
80
+ * EDT = UTC−4 (approx. 2nd Sunday in March → 1st Sunday in November)
81
+ * EST = UTC−5 (rest of year)
82
+ */
83
+ function torontoUtcOffsetHours(ymd) {
84
+ const [year, month, day] = ymd.split("-").map(Number);
85
+ const d = new Date(Date.UTC(year, month - 1, day));
86
+ // 2nd Sunday in March (DST starts at 2:00 AM local)
87
+ const marchDst = new Date(Date.UTC(year, 2, 8)); // earliest possible: Mar 8
88
+ marchDst.setUTCDate(8 + ((7 - marchDst.getUTCDay()) % 7));
89
+ // 1st Sunday in November (DST ends at 2:00 AM local)
90
+ const novDst = new Date(Date.UTC(year, 10, 1)); // earliest possible: Nov 1
91
+ novDst.setUTCDate(1 + ((7 - novDst.getUTCDay()) % 7));
92
+ return d >= marchDst && d < novDst ? -4 : -5;
93
+ }
94
+ /** Returns all weekdays (Mon–Fri) between startDate and endDate inclusive. */
95
+ function weekdaysBetween(startDate, endDate) {
96
+ const result = [];
97
+ const [sy, sm, sd] = startDate.split("-").map(Number);
98
+ const [ey, em, ed] = endDate.split("-").map(Number);
99
+ const cur = new Date(Date.UTC(sy, sm - 1, sd));
100
+ const end = new Date(Date.UTC(ey, em - 1, ed));
101
+ while (cur <= end) {
102
+ const dow = cur.getUTCDay(); // 0=Sun, 6=Sat
103
+ if (dow !== 0 && dow !== 6) {
104
+ const y = cur.getUTCFullYear();
105
+ const m = String(cur.getUTCMonth() + 1).padStart(2, "0");
106
+ const d = String(cur.getUTCDate()).padStart(2, "0");
107
+ result.push(`${y}-${m}-${d}`);
108
+ }
109
+ cur.setUTCDate(cur.getUTCDate() + 1);
110
+ }
111
+ return result;
112
+ }
113
+ // ── FreshBooks data layer ─────────────────────────────────────────────────────
114
+ async function fetchProjectNames(client) {
115
+ const data = await client.request("GET", `/projects/business/${client.businessId}/projects`, {
116
+ query: { active: "true", client_id: TRAJECTOR_CLIENT_ID },
117
+ });
118
+ const projects = new Map();
119
+ const services = new Map();
120
+ for (const p of data.projects ?? []) {
121
+ projects.set(p.id, p.title);
122
+ for (const s of p.services ?? []) {
123
+ services.set(s.id, s.name);
124
+ }
125
+ }
126
+ return { projects, services };
127
+ }
128
+ export async function listTimeEntries(client, input) {
129
+ const [raw, names] = await Promise.all([
130
+ client.request("GET", `/timetracking/business/${client.businessId}/time_entries`, {
131
+ query: {
132
+ client_id: input.client_id,
133
+ started_from: `${input.start_date}T00:00:00Z`,
134
+ started_to: `${input.end_date}T23:59:59Z`,
135
+ per_page: 100,
136
+ },
137
+ }),
138
+ fetchProjectNames(client),
139
+ ]);
140
+ const entries = (raw.time_entries ?? [])
141
+ .filter((e) => e.active !== false)
142
+ .sort((a, b) => a.started_at.localeCompare(b.started_at) || a.id - b.id)
143
+ .map((e) => ({
144
+ id: e.id,
145
+ date: e.local_started_at.slice(0, 10),
146
+ duration_hours: Math.round((e.duration / 3600) * 100) / 100,
147
+ project_id: e.project_id,
148
+ project_name: names.projects.get(e.project_id) ?? `Project ${e.project_id}`,
149
+ service_id: e.service_id,
150
+ service_name: names.services.get(e.service_id) ?? `Service ${e.service_id}`,
151
+ note: e.note ?? null,
152
+ }));
153
+ // Aggregate by date
154
+ const byDate = {};
155
+ let totalHours = 0;
156
+ for (const e of entries) {
157
+ if (!byDate[e.date])
158
+ byDate[e.date] = { hours: 0, entry_count: 0 };
159
+ byDate[e.date].hours += e.duration_hours;
160
+ byDate[e.date].entry_count += 1;
161
+ totalHours += e.duration_hours;
162
+ }
163
+ const daysWithEntries = new Set(entries.map((e) => e.date));
164
+ const allWeekdays = weekdaysBetween(input.start_date, input.end_date);
165
+ const missingWeekdays = allWeekdays.filter((d) => !daysWithEntries.has(d));
166
+ return {
167
+ entries,
168
+ by_date: byDate,
169
+ total_hours: Math.round(totalHours * 100) / 100,
170
+ missing_weekdays: missingWeekdays,
171
+ period: { start: input.start_date, end: input.end_date },
172
+ };
173
+ }
174
+ export async function createTimeEntry(client, input) {
175
+ const durationSeconds = Math.round(input.duration_hours * 3600);
176
+ const startHour = input.start_hour ?? 9;
177
+ const offset = torontoUtcOffsetHours(input.date);
178
+ const utcHour = startHour - offset; // e.g. 9 AM EDT → 13:00 UTC
179
+ const localStartedAt = `${input.date}T${String(startHour).padStart(2, "0")}:00:00`;
180
+ const startedAt = `${input.date}T${String(utcHour).padStart(2, "0")}:00:00Z`;
181
+ const body = {
182
+ time_entry: {
183
+ client_id: input.client_id,
184
+ project_id: input.project_id,
185
+ service_id: input.service_id,
186
+ duration: durationSeconds,
187
+ local_started_at: localStartedAt,
188
+ started_at: startedAt,
189
+ note: input.note ?? null,
190
+ is_logged: true,
191
+ },
192
+ };
193
+ const raw = await client.request("POST", `/timetracking/business/${client.businessId}/time_entries`, { body });
194
+ return {
195
+ id: raw.time_entry.id,
196
+ date: input.date,
197
+ duration_hours: input.duration_hours,
198
+ project_id: input.project_id,
199
+ service_id: input.service_id,
200
+ local_started_at: localStartedAt,
201
+ started_at: startedAt,
202
+ };
203
+ }
204
+ //# sourceMappingURL=time_entries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time_entries.js","sourceRoot":"","sources":["../../src/tools/time_entries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,395 @@
1
+ /**
2
+ * generate_timesheet tool — fetches FreshBooks time entries for a date range
3
+ * and writes a Consultant-Bi-Weekly-Timesheet Excel file to
4
+ * ~/Documents/trajector/timesheets/, matching the existing file format exactly.
5
+ */
6
+ import path from "node:path";
7
+ import os from "node:os";
8
+ import fs from "node:fs/promises";
9
+ import { z } from "zod";
10
+ import ExcelJS from "exceljs";
11
+ // ── Constants ─────────────────────────────────────────────────────────────────
12
+ const TRAJECTOR_CLIENT_ID = 688912;
13
+ const OUTPUT_DIR = path.join(os.homedir(), "Documents", "trajector", "timesheets");
14
+ const SIGNATURE_PATH = path.join(os.homedir(), "Projects", "python", "freshbooks", "signature.png");
15
+ const DEFAULT_NOTE = "Core Services: Architecture, Planning, Development, Continous Improvement, Documentation";
16
+ // ── Input schema ──────────────────────────────────────────────────────────────
17
+ export const GenerateTimesheetInput = z
18
+ .object({
19
+ start_date: z
20
+ .string()
21
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
22
+ .describe("YYYY-MM-DD"),
23
+ end_date: z
24
+ .string()
25
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
26
+ .describe("YYYY-MM-DD"),
27
+ })
28
+ .strict();
29
+ // ── Helpers ───────────────────────────────────────────────────────────────────
30
+ function secondsToHHMM(s) {
31
+ const h = Math.floor(s / 3600);
32
+ const m = Math.floor((s % 3600) / 60);
33
+ return `${h}:${m.toString().padStart(2, "0")}`;
34
+ }
35
+ /** Convert minutes-from-midnight to "HH:MM AM/PM" */
36
+ function minutesToTimeStr(totalMinutes) {
37
+ const h = Math.floor(totalMinutes / 60);
38
+ const m = totalMinutes % 60;
39
+ const ampm = h >= 12 ? "PM" : "AM";
40
+ const hour = h % 12 || 12;
41
+ return `${hour.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")} ${ampm}`;
42
+ }
43
+ // ── FreshBooks data layer ─────────────────────────────────────────────────────
44
+ async function fetchProjects(client) {
45
+ const data = await client.request("GET", `/projects/business/${client.businessId}/projects`, {
46
+ query: { active: "true", client_id: TRAJECTOR_CLIENT_ID },
47
+ });
48
+ const map = new Map();
49
+ for (const p of data.projects ?? []) {
50
+ const svcMap = new Map();
51
+ for (const s of p.services ?? [])
52
+ svcMap.set(s.id, s.name);
53
+ map.set(p.id, { title: p.title, services: svcMap });
54
+ }
55
+ return map;
56
+ }
57
+ async function buildRows(client, startDate, endDate) {
58
+ const data = await client.request("GET", `/timetracking/business/${client.businessId}/time_entries`, {
59
+ query: {
60
+ client_id: TRAJECTOR_CLIENT_ID,
61
+ started_from: `${startDate}T00:00:00Z`,
62
+ started_to: `${endDate}T23:59:59Z`,
63
+ per_page: 100,
64
+ },
65
+ });
66
+ const projects = await fetchProjects(client);
67
+ // Sort by UTC start then id — matches the Python script
68
+ const raw = (data.time_entries ?? [])
69
+ .filter((e) => e.active !== false)
70
+ .sort((a, b) => a.started_at.localeCompare(b.started_at) || a.id - b.id);
71
+ const rows = [];
72
+ let currentDay = "";
73
+ let currentMinutes = 0; // minutes from midnight
74
+ for (const entry of raw) {
75
+ // Use the local date (Toronto) so the day grouping is correct
76
+ const day = entry.local_started_at.slice(0, 10);
77
+ if (day !== currentDay) {
78
+ currentDay = day;
79
+ currentMinutes = 9 * 60; // 9:00 AM — same as Python script
80
+ }
81
+ const durationMins = Math.floor(entry.duration / 60);
82
+ const endMinutes = currentMinutes + durationMins;
83
+ const project = projects.get(entry.project_id);
84
+ const service = project?.services.get(entry.service_id) ?? "Unknown Service";
85
+ rows.push({
86
+ dateRecorded: day,
87
+ startTime: minutesToTimeStr(currentMinutes),
88
+ endTime: minutesToTimeStr(endMinutes),
89
+ duration: entry.duration,
90
+ service,
91
+ activity: entry.note ?? DEFAULT_NOTE,
92
+ });
93
+ currentMinutes = endMinutes;
94
+ }
95
+ return rows;
96
+ }
97
+ // ── Excel generation ──────────────────────────────────────────────────────────
98
+ async function buildWorkbook(rows, startDate, endDate) {
99
+ const wb = new ExcelJS.Workbook();
100
+ const ws = wb.addWorksheet("Timesheet - Consultant");
101
+ ws.views = [{ showGridLines: false }];
102
+ // Column widths B–G (1-indexed columns 2–7)
103
+ [11, 22, 81, 14, 14, 36].forEach((w, i) => {
104
+ ws.getColumn(i + 2).width = w;
105
+ });
106
+ // Row heights for header block
107
+ const headerHeights = {
108
+ 2: 26,
109
+ 3: 13,
110
+ 4: 26,
111
+ 5: 26,
112
+ 6: 13,
113
+ 7: 13,
114
+ 8: 26,
115
+ };
116
+ for (const [r, h] of Object.entries(headerHeights)) {
117
+ ws.getRow(Number(r)).height = h;
118
+ }
119
+ // ── Style constants ────────────────────────────────────────────────────────
120
+ const thin = { style: "thin", color: { argb: "FF000000" } };
121
+ const thick = { style: "medium", color: { argb: "FF000000" } };
122
+ // Border combos
123
+ const thinAll = {
124
+ top: thin,
125
+ left: thin,
126
+ right: thin,
127
+ bottom: thin,
128
+ };
129
+ const thickL = { left: thick };
130
+ const thickR = { right: thick };
131
+ const thickLT = {
132
+ top: thin,
133
+ left: thick,
134
+ right: thin,
135
+ bottom: thin,
136
+ };
137
+ const thickRT = {
138
+ top: thin,
139
+ left: thin,
140
+ right: thick,
141
+ bottom: thin,
142
+ };
143
+ const thickBL = { left: thick, bottom: thick };
144
+ const thickBR = { right: thick, bottom: thick };
145
+ const thickBot = { bottom: thick };
146
+ // Fonts
147
+ const fVerdana = { name: "Verdana", size: 10 };
148
+ const fVerdanaBold = {
149
+ name: "Verdana",
150
+ size: 10,
151
+ bold: true,
152
+ };
153
+ const fWarning = {
154
+ name: "Arial",
155
+ size: 10,
156
+ bold: true,
157
+ color: { argb: "FFFF0000" },
158
+ };
159
+ const fLarge = { name: "Verdana", size: 11 };
160
+ const fLargeBold = {
161
+ name: "Verdana",
162
+ size: 11,
163
+ bold: true,
164
+ };
165
+ const fTitle = {
166
+ name: "Verdana",
167
+ size: 14,
168
+ bold: true,
169
+ };
170
+ // Alignments (exceljs uses "middle" where openpyxl uses "center" for vertical)
171
+ const aCenter = {
172
+ horizontal: "center",
173
+ vertical: "middle",
174
+ };
175
+ const aGeneral = {
176
+ horizontal: "left",
177
+ vertical: "middle",
178
+ };
179
+ const aRight = {
180
+ horizontal: "right",
181
+ vertical: "middle",
182
+ };
183
+ const aRBot = {
184
+ horizontal: "right",
185
+ vertical: "bottom",
186
+ };
187
+ const aMidH = { horizontal: "center" };
188
+ // Fills
189
+ const blueFill = {
190
+ type: "pattern",
191
+ pattern: "solid",
192
+ fgColor: { argb: "0099CCFF" },
193
+ };
194
+ const greyFill = {
195
+ type: "pattern",
196
+ pattern: "solid",
197
+ fgColor: { argb: "00F0F0F0" },
198
+ };
199
+ // Helper: apply styles to a cell
200
+ function style(ref, opts) {
201
+ const cell = typeof ref === "string" ? ws.getCell(ref) : ref;
202
+ if (opts.value !== undefined)
203
+ cell.value = opts.value;
204
+ if (opts.font)
205
+ cell.font = opts.font;
206
+ if (opts.alignment)
207
+ cell.alignment = opts.alignment;
208
+ if (opts.fill)
209
+ cell.fill = opts.fill;
210
+ if (opts.border)
211
+ cell.border = opts.border;
212
+ return cell;
213
+ }
214
+ // ── Title ─────────────────────────────────────────────────────────────────
215
+ ws.mergeCells("B2:G2");
216
+ style("B2", {
217
+ value: "CONTRACTOR TIME SHEET",
218
+ font: fTitle,
219
+ alignment: aCenter,
220
+ fill: blueFill,
221
+ });
222
+ // ── Info block ────────────────────────────────────────────────────────────
223
+ style("C4", {
224
+ value: "Contractor Name:",
225
+ font: fLargeBold,
226
+ alignment: aMidH,
227
+ border: thinAll,
228
+ });
229
+ style("D4", { value: "Matthew Purdon", font: fLarge, border: thinAll });
230
+ style("C5", {
231
+ value: "Client Name:",
232
+ font: fLargeBold,
233
+ alignment: aMidH,
234
+ border: thinAll,
235
+ });
236
+ style("D5", { value: "Trajector Medical", font: fLarge, border: thinAll });
237
+ ws.mergeCells("E5:F5");
238
+ style("E5", {
239
+ value: "Time Card Date Range",
240
+ font: fLargeBold,
241
+ alignment: aRBot,
242
+ });
243
+ style("G5", {
244
+ value: `${startDate} - ${endDate}`,
245
+ font: fLarge,
246
+ fill: greyFill,
247
+ });
248
+ style("E7", {
249
+ value: "NOTE: Place a space between Start Time / End Time ie. 8 AM = 8:00 AM",
250
+ font: fWarning,
251
+ });
252
+ // Thick left/right borders for header rows 1–7
253
+ for (let r = 1; r <= 7; r++) {
254
+ const lc = ws.getCell(r, 2);
255
+ const rc = ws.getCell(r, 7);
256
+ lc.border = { ...lc.border, left: thick };
257
+ rc.border = { ...rc.border, right: thick };
258
+ }
259
+ // ── Column headers (row 8) ────────────────────────────────────────────────
260
+ const headers = [
261
+ "Date",
262
+ "Task/Project",
263
+ "Description",
264
+ "Start Time",
265
+ "End Time",
266
+ "Total Hours",
267
+ ];
268
+ headers.forEach((h, i) => {
269
+ const col = i + 2;
270
+ const cell = ws.getCell(8, col);
271
+ cell.value = h;
272
+ cell.font = fLargeBold;
273
+ cell.alignment = aCenter;
274
+ cell.border = thinAll;
275
+ if (col === 4)
276
+ cell.fill = greyFill; // Description column gets grey
277
+ });
278
+ ws.getCell(8, 2).border = thickLT;
279
+ ws.getCell(8, 7).border = thickRT;
280
+ // ── Data rows ─────────────────────────────────────────────────────────────
281
+ let totalDuration = 0;
282
+ let prevDate = "";
283
+ let rowNum = 9;
284
+ for (const [i, row] of rows.entries()) {
285
+ rowNum = 9 + i;
286
+ ws.getRow(rowNum).height = 21;
287
+ if (row.dateRecorded !== prevDate) {
288
+ style(ws.getCell(rowNum, 2), {
289
+ value: row.dateRecorded,
290
+ font: fVerdana,
291
+ alignment: aRight,
292
+ });
293
+ prevDate = row.dateRecorded;
294
+ }
295
+ ws.getCell(rowNum, 2).border = thickLT;
296
+ style(ws.getCell(rowNum, 3), {
297
+ value: row.service,
298
+ font: fVerdana,
299
+ alignment: aGeneral,
300
+ border: thinAll,
301
+ });
302
+ style(ws.getCell(rowNum, 4), {
303
+ value: row.activity,
304
+ font: fVerdana,
305
+ alignment: aGeneral,
306
+ border: thinAll,
307
+ fill: greyFill,
308
+ });
309
+ style(ws.getCell(rowNum, 5), {
310
+ value: row.startTime,
311
+ font: fVerdana,
312
+ alignment: aCenter,
313
+ border: thinAll,
314
+ });
315
+ style(ws.getCell(rowNum, 6), {
316
+ value: row.endTime,
317
+ font: fVerdana,
318
+ alignment: aCenter,
319
+ border: thinAll,
320
+ });
321
+ style(ws.getCell(rowNum, 7), {
322
+ value: secondsToHHMM(row.duration),
323
+ font: fVerdana,
324
+ alignment: aCenter,
325
+ border: thickRT,
326
+ });
327
+ totalDuration += row.duration;
328
+ }
329
+ // ── Footer ────────────────────────────────────────────────────────────────
330
+ rowNum += 1;
331
+ ws.getCell(rowNum, 2).border = thickL;
332
+ ws.getCell(rowNum, 7).border = thickR;
333
+ rowNum += 1;
334
+ const sigRow = rowNum;
335
+ style(ws.getCell(rowNum, 6), {
336
+ value: "Total Hours",
337
+ font: fLargeBold,
338
+ alignment: aRight,
339
+ });
340
+ style(ws.getCell(rowNum, 7), {
341
+ value: secondsToHHMM(totalDuration),
342
+ font: fVerdanaBold,
343
+ alignment: aCenter,
344
+ fill: blueFill,
345
+ });
346
+ style(ws.getCell(rowNum, 3), {
347
+ value: "Contractor Signature:",
348
+ font: fLargeBold,
349
+ alignment: aRight,
350
+ });
351
+ ws.getCell(rowNum, 2).border = thickL;
352
+ ws.getCell(rowNum, 7).border = thickR;
353
+ // Signature image — placed at column D, one row above the signature label
354
+ const sigExists = await fs
355
+ .access(SIGNATURE_PATH)
356
+ .then(() => true)
357
+ .catch(() => false);
358
+ if (sigExists) {
359
+ const imgId = wb.addImage({ filename: SIGNATURE_PATH, extension: "png" });
360
+ ws.addImage(imgId, {
361
+ tl: { col: 3, row: sigRow - 2 }, // col D (0-based: D=3), row above
362
+ ext: { width: 112, height: 48 },
363
+ });
364
+ }
365
+ rowNum += 1;
366
+ ws.getCell(rowNum, 2).border = thickBL;
367
+ ws.getCell(rowNum, 7).border = thickBR;
368
+ for (let c = 3; c <= 6; c++) {
369
+ ws.getCell(rowNum, c).border = thickBot;
370
+ }
371
+ return wb;
372
+ }
373
+ // ── Tool handler ──────────────────────────────────────────────────────────────
374
+ export async function generateTimesheet(client, input) {
375
+ const rows = await buildRows(client, input.start_date, input.end_date);
376
+ if (rows.length === 0) {
377
+ throw new Error(`No time entries found for ${input.start_date} – ${input.end_date}.`);
378
+ }
379
+ const wb = await buildWorkbook(rows, input.start_date, input.end_date);
380
+ // Filename convention: end_date + 1 day (matches the Python script)
381
+ const endDt = new Date(`${input.end_date}T12:00:00Z`);
382
+ endDt.setUTCDate(endDt.getUTCDate() + 1);
383
+ const sendDate = endDt.toISOString().slice(0, 10);
384
+ const filename = `Consultant-Bi-Weekly-Timesheet ${sendDate}.xlsx`;
385
+ await fs.mkdir(OUTPUT_DIR, { recursive: true });
386
+ const outPath = path.join(OUTPUT_DIR, filename);
387
+ await wb.xlsx.writeFile(outPath);
388
+ const totalSec = rows.reduce((s, r) => s + r.duration, 0);
389
+ return {
390
+ file: outPath,
391
+ entries: rows.length,
392
+ total_hours: secondsToHHMM(totalSec),
393
+ };
394
+ }
395
+ //# sourceMappingURL=timesheet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timesheet.js","sourceRoot":"","sources":["../../src/tools/timesheet.ts"],"names":[],"mappings":"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"}