crowdtime-cli 0.20.0__tar.gz → 0.21.0__tar.gz
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.
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/PKG-INFO +1 -1
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/pyproject.toml +1 -1
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/__init__.py +1 -1
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/pto_cmd.py +3 -3
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/timesheet_cmd.py +74 -10
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/skills/crowdtime/SKILL.md +3 -1
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/skills/crowdtime/references/commands.md +21 -3
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/skills/crowdtime/references/pto.md +1 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/skills/crowdtime/references/workflows.md +6 -1
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/.gitignore +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/LICENSE +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/README.md +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/auth.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/client.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/__init__.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/ai_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/auth_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/billing_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/calendar_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/clients_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/config_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/expense_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/favorites_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/insights_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/invoice_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/log_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/org_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/payroll_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/projects_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/report_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/skill_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/tasks_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/team_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/timer_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/commands/version_cmd.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/config.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/formatters.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/main.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/models.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/oauth.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/resolvers.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/utils.py +0 -0
- {crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/version_check.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: crowdtime-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.0
|
|
4
4
|
Summary: AI-powered time tracking CLI — a modern, developer-friendly alternative to Harvest
|
|
5
5
|
Project-URL: Homepage, https://crowdtime.lat
|
|
6
6
|
Project-URL: Documentation, https://crowdtime.lat/docs
|
|
@@ -55,7 +55,7 @@ console = Console()
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
# Valid enum values mirrored from the contract.
|
|
58
|
-
_VALID_KINDS = {"vacation", "sick", "holiday", "personal", "other"}
|
|
58
|
+
_VALID_KINDS = {"vacation", "sick", "holiday", "personal", "birthday", "other"}
|
|
59
59
|
_VALID_STATUSES = {"pending", "approved", "rejected", "canceled"}
|
|
60
60
|
|
|
61
61
|
|
|
@@ -211,7 +211,7 @@ def request_cmd(
|
|
|
211
211
|
...,
|
|
212
212
|
help="Date or range: '2026-06-01', '2026-06-01:2026-06-05', 'today', 'next-monday'.",
|
|
213
213
|
),
|
|
214
|
-
kind: str = typer.Option("vacation", "--kind", "-k", help="vacation|sick|holiday|personal|other."),
|
|
214
|
+
kind: str = typer.Option("vacation", "--kind", "-k", help="vacation|sick|holiday|personal|birthday|other. Birthday: one day, once per calendar year."),
|
|
215
215
|
hours_per_day: Optional[str] = typer.Option(
|
|
216
216
|
None, "--hours-per-day",
|
|
217
217
|
help="Decimal hours. Empty/omitted = full day from membership capacity.",
|
|
@@ -256,7 +256,7 @@ def add_cmd(
|
|
|
256
256
|
...,
|
|
257
257
|
help="Date or range: '2026-06-01', '2026-06-01:2026-06-05', 'today', 'next-monday'.",
|
|
258
258
|
),
|
|
259
|
-
kind: str = typer.Option("vacation", "--kind", "-k", help="vacation|sick|holiday|personal|other."),
|
|
259
|
+
kind: str = typer.Option("vacation", "--kind", "-k", help="vacation|sick|holiday|personal|birthday|other. Birthday: one day, once per calendar year."),
|
|
260
260
|
hours_per_day: Optional[str] = typer.Option(
|
|
261
261
|
None, "--hours-per-day",
|
|
262
262
|
help="Decimal hours per day. Omit for full-day from membership.",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Timesheet commands: list, submit, approve, reject, recall, reopen, team overview."""
|
|
1
|
+
"""Timesheet commands: list, submit, remind, approve, reject, recall, reopen, team overview."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -246,17 +246,27 @@ def submit_timesheet(
|
|
|
246
246
|
period_end: str = typer.Option(
|
|
247
247
|
..., "--to", help="Period end date (YYYY-MM-DD or keyword)."
|
|
248
248
|
),
|
|
249
|
+
user: Optional[str] = typer.Option(
|
|
250
|
+
None, "--user", "-u",
|
|
251
|
+
help="Submit on someone's behalf (email or UUID). Reviewers only.",
|
|
252
|
+
),
|
|
249
253
|
force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation prompt."),
|
|
250
254
|
output_json: bool = typer.Option(False, "--json", help="Output as JSON."),
|
|
251
255
|
) -> None:
|
|
252
256
|
"""Submit a timesheet for approval.
|
|
253
257
|
|
|
254
|
-
Computes total hours from
|
|
258
|
+
Computes total hours from the time entries in the given period.
|
|
255
259
|
Shows a preview of hours before submitting (use --force to skip).
|
|
256
260
|
|
|
261
|
+
--user submits a week someone logged but never sent. It needs the same
|
|
262
|
+
authority as approving their whole timesheet (admin, their manager, or
|
|
263
|
+
running every project they logged on); the submission is then reviewed
|
|
264
|
+
as usual.
|
|
265
|
+
|
|
257
266
|
Examples:
|
|
258
267
|
ct timesheet submit --from 2026-03-09 --to 2026-03-15
|
|
259
268
|
ct timesheet submit --from monday --to friday --force
|
|
269
|
+
ct timesheet submit --from 2026-03-09 --to 2026-03-15 --user ana@acme.com
|
|
260
270
|
"""
|
|
261
271
|
try:
|
|
262
272
|
start = format_date(parse_date(period_start))
|
|
@@ -267,13 +277,23 @@ def submit_timesheet(
|
|
|
267
277
|
|
|
268
278
|
client = CrowdTimeClient(require_auth=True, require_org=True)
|
|
269
279
|
|
|
280
|
+
user_id = None
|
|
281
|
+
if user:
|
|
282
|
+
from .pto_cmd import _resolve_user_to_uuid
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
user_id = _resolve_user_to_uuid(client, user)
|
|
286
|
+
except APIError as e:
|
|
287
|
+
format_error(e.message)
|
|
288
|
+
raise typer.Exit(1)
|
|
289
|
+
|
|
270
290
|
# Preview: fetch entries for the period and show summary before confirming
|
|
271
291
|
if not force and not output_json:
|
|
272
292
|
try:
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
"
|
|
276
|
-
|
|
293
|
+
entry_params = {"date_from": start, "date_to": end}
|
|
294
|
+
if user_id:
|
|
295
|
+
entry_params["user"] = user_id
|
|
296
|
+
entries_data = client.get("/time-entries/", params=entry_params)
|
|
277
297
|
entries = extract_results(entries_data)
|
|
278
298
|
total_h = sum(Decimal(str(e.get("hours", 0))) for e in entries)
|
|
279
299
|
billable_h = sum(
|
|
@@ -296,10 +316,10 @@ def submit_timesheet(
|
|
|
296
316
|
pass # If preview fails, proceed to submit (server will validate)
|
|
297
317
|
|
|
298
318
|
try:
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
"
|
|
302
|
-
|
|
319
|
+
payload = {"period_start": start, "period_end": end}
|
|
320
|
+
if user_id:
|
|
321
|
+
payload["user_id"] = user_id
|
|
322
|
+
result = client.post("/timesheets/submit/", data=payload)
|
|
303
323
|
|
|
304
324
|
if output_json:
|
|
305
325
|
print_json(result)
|
|
@@ -316,6 +336,50 @@ def submit_timesheet(
|
|
|
316
336
|
raise typer.Exit(1)
|
|
317
337
|
|
|
318
338
|
|
|
339
|
+
@app.command("remind")
|
|
340
|
+
def remind_timesheet(
|
|
341
|
+
user: str = typer.Option(..., "--user", "-u", help="Who to remind (email or UUID)."),
|
|
342
|
+
period_start: str = typer.Option(
|
|
343
|
+
..., "--from", help="Period start date (YYYY-MM-DD or keyword)."
|
|
344
|
+
),
|
|
345
|
+
period_end: str = typer.Option(
|
|
346
|
+
..., "--to", help="Period end date (YYYY-MM-DD or keyword)."
|
|
347
|
+
),
|
|
348
|
+
output_json: bool = typer.Option(False, "--json", help="Output as JSON."),
|
|
349
|
+
) -> None:
|
|
350
|
+
"""Email someone a reminder to submit their timesheet (project_manager+).
|
|
351
|
+
|
|
352
|
+
For a week they haven't submitted. One reminder per person and period an hour.
|
|
353
|
+
|
|
354
|
+
Examples:
|
|
355
|
+
ct timesheet remind --user ana@acme.com --from 2026-03-09 --to 2026-03-15
|
|
356
|
+
"""
|
|
357
|
+
from .pto_cmd import _resolve_user_to_uuid
|
|
358
|
+
|
|
359
|
+
try:
|
|
360
|
+
start = format_date(parse_date(period_start))
|
|
361
|
+
end = format_date(parse_date(period_end))
|
|
362
|
+
except ValueError as e:
|
|
363
|
+
format_error(str(e))
|
|
364
|
+
raise typer.Exit(1)
|
|
365
|
+
|
|
366
|
+
client = CrowdTimeClient(require_auth=True, require_org=True)
|
|
367
|
+
try:
|
|
368
|
+
user_id = _resolve_user_to_uuid(client, user)
|
|
369
|
+
result = client.post("/timesheets/remind/", data={
|
|
370
|
+
"user_id": user_id,
|
|
371
|
+
"period_start": start,
|
|
372
|
+
"period_end": end,
|
|
373
|
+
})
|
|
374
|
+
if output_json:
|
|
375
|
+
print_json(result)
|
|
376
|
+
else:
|
|
377
|
+
format_success(f"Reminder sent to {user} for {start} → {end}.")
|
|
378
|
+
except APIError as e:
|
|
379
|
+
format_error(e.message)
|
|
380
|
+
raise typer.Exit(1)
|
|
381
|
+
|
|
382
|
+
|
|
319
383
|
@app.command("approve")
|
|
320
384
|
def approve_timesheet(
|
|
321
385
|
timesheet_id: str = typer.Argument(..., help="Timesheet ID to approve."),
|
|
@@ -300,6 +300,8 @@ ct timesheet list # List your timesheets
|
|
|
300
300
|
ct timesheet list --status submitted # Filter by status
|
|
301
301
|
ct timesheet submit --from 2026-03-10 --to 2026-03-16 # Submit full week
|
|
302
302
|
ct timesheet submit --from 2026-03-30 --to 2026-03-31 # Partial week (end-of-month split)
|
|
303
|
+
ct timesheet submit --from 2026-03-10 --to 2026-03-16 --user ana@acme.com # Submit a week someone logged but never sent (same authority as whole approve)
|
|
304
|
+
ct timesheet remind --user ana@acme.com --from 2026-03-10 --to 2026-03-16 # Email them a reminder to submit (project_manager+)
|
|
303
305
|
ct timesheet recall <id> # Recall submitted timesheet back to draft (--force to skip confirm)
|
|
304
306
|
ct timesheet approve <id> # Approve the whole timesheet (admin, direct manager, or PM of every project in it)
|
|
305
307
|
ct timesheet approve <id> --project <project-id> # Approve one project's portion (PM for that project)
|
|
@@ -424,7 +426,7 @@ ct log 0.25 --project argo --task BE --notes "Daily standup"
|
|
|
424
426
|
|
|
425
427
|
### Time Off (PTO)
|
|
426
428
|
|
|
427
|
-
Per-user time-off entries with a kind (vacation, sick, holiday, personal, other) and a status lifecycle (pending → approved/rejected; pending → canceled). Members submit requests that await approval, and their approvers are emailed automatically; project managers and above can also add PTO directly on behalf of someone in their scope (auto-approved).
|
|
429
|
+
Per-user time-off entries with a kind (vacation, sick, holiday, personal, birthday, other) and a status lifecycle (pending → approved/rejected; pending → canceled). Members submit requests that await approval, and their approvers are emailed automatically; project managers and above can also add PTO directly on behalf of someone in their scope (auto-approved).
|
|
428
430
|
|
|
429
431
|
> **IMPORTANT**: Never use magic routing (`ct "..."`) or `ct ai parse` for PTO operations. Always construct explicit `ct pto ...` commands.
|
|
430
432
|
> Full reference: `references/pto.md` and `references/commands.md` § PTO.
|
|
@@ -1735,13 +1735,14 @@ Lists your timesheets, plus — for managers and project managers — the timesh
|
|
|
1735
1735
|
### ct timesheet submit
|
|
1736
1736
|
|
|
1737
1737
|
```
|
|
1738
|
-
ct timesheet submit --from DATE --to DATE [--force/-f] [--json]
|
|
1738
|
+
ct timesheet submit --from DATE --to DATE [--user/-u USER] [--force/-f] [--json]
|
|
1739
1739
|
```
|
|
1740
1740
|
|
|
1741
1741
|
| Option | Type | Description |
|
|
1742
1742
|
|--------|------|-------------|
|
|
1743
1743
|
| `--from` | string (required) | Period start date |
|
|
1744
1744
|
| `--to` | string (required) | Period end date |
|
|
1745
|
+
| `--user`, `-u` | string | Submit on someone's behalf (email or UUID) |
|
|
1745
1746
|
| `--force`, `-f` | flag | Skip confirmation prompt |
|
|
1746
1747
|
| `--json` | flag | JSON output |
|
|
1747
1748
|
|
|
@@ -1749,6 +1750,23 @@ Submits a timesheet for the given period (any date range — not limited to full
|
|
|
1749
1750
|
|
|
1750
1751
|
A zero-hour period is rejected **unless every working day in it is covered by approved time off** — a full vacation week has no time entries by design and is submitted as-is. If someone was away and the submit is rejected, the fix is an approved PTO entry (`ct pto request`), not a fake time entry. Endpoint: `POST /timesheets/submit/`
|
|
1751
1752
|
|
|
1753
|
+
`--user` submits a week someone logged but never sent (body `user_id`). It needs the same authority as approving their whole timesheet — admin/owner, their direct manager, or running every project they logged on that period — otherwise `403`. The submission is recorded as theirs (the audit event names who sent it) and is then approved or rejected as usual. To nudge them to do it themselves, use `ct timesheet remind`.
|
|
1754
|
+
|
|
1755
|
+
### ct timesheet remind
|
|
1756
|
+
|
|
1757
|
+
```
|
|
1758
|
+
ct timesheet remind --user USER --from DATE --to DATE [--json]
|
|
1759
|
+
```
|
|
1760
|
+
|
|
1761
|
+
| Option | Type | Description |
|
|
1762
|
+
|--------|------|-------------|
|
|
1763
|
+
| `--user`, `-u` | string (required) | Who to remind (email or UUID) |
|
|
1764
|
+
| `--from` | string (required) | Period start date |
|
|
1765
|
+
| `--to` | string (required) | Period end date |
|
|
1766
|
+
| `--json` | flag | JSON output |
|
|
1767
|
+
|
|
1768
|
+
Emails the person a reminder to submit their timesheet for the period, naming who asked (project_manager+, anyone on your roster — `ct timesheet viewable-users`). Refused with `400` when a timesheet for the period is already submitted or approved, and with `429` when that person was reminded about the same period less than an hour ago. Endpoint: `POST /timesheets/remind/` → `202`.
|
|
1769
|
+
|
|
1752
1770
|
### ct timesheet approve
|
|
1753
1771
|
|
|
1754
1772
|
```
|
|
@@ -2740,7 +2758,7 @@ Submit a PTO request for yourself. Created with `status=pending` — must be app
|
|
|
2740
2758
|
| Argument/Option | Type | Required | Description |
|
|
2741
2759
|
|-----------------|------|----------|-------------|
|
|
2742
2760
|
| `<dates>` | string | yes | `YYYY-MM-DD` or `YYYY-MM-DD:YYYY-MM-DD` |
|
|
2743
|
-
| `--kind`, `-k` | string | no | `vacation` (default), `sick`, `holiday`, `personal`, `other` |
|
|
2761
|
+
| `--kind`, `-k` | string | no | `vacation` (default), `sick`, `holiday`, `personal`, `birthday` (one day, once per calendar year), `other` |
|
|
2744
2762
|
| `--hours-per-day`, `-h` | decimal | no | Hours/day. Empty = capacity-based default (see `references/pto.md`) |
|
|
2745
2763
|
| `--notes`, `-n` | string | no | Reason or notes |
|
|
2746
2764
|
| `--json` | flag | no | JSON output |
|
|
@@ -2786,7 +2804,7 @@ Add a PTO entry on behalf of another user (project manager and above). The entry
|
|
|
2786
2804
|
|-----------------|------|----------|-------------|
|
|
2787
2805
|
| `<user>` | string | yes | User email or UUID |
|
|
2788
2806
|
| `<dates>` | string | yes | `YYYY-MM-DD` or `YYYY-MM-DD:YYYY-MM-DD` |
|
|
2789
|
-
| `--kind`, `-k` | string | no | `vacation` (default), `sick`, `holiday`, `personal`, `other` |
|
|
2807
|
+
| `--kind`, `-k` | string | no | `vacation` (default), `sick`, `holiday`, `personal`, `birthday` (one day, once per calendar year), `other` |
|
|
2790
2808
|
| `--hours-per-day`, `-h` | decimal | no | Hours/day. Empty = capacity-based default |
|
|
2791
2809
|
| `--notes`, `-n` | string | no | Reason or context notes |
|
|
2792
2810
|
| `--json` | flag | no | JSON output |
|
{crowdtime_cli-0.20.0 → crowdtime_cli-0.21.0}/src/crowdtime_cli/skills/crowdtime/references/pto.md
RENAMED
|
@@ -25,6 +25,7 @@ A `TimeOffEntry` represents a block of time off for a single user within an orga
|
|
|
25
25
|
| `sick` | Sick Leave | Illness, medical appointment, recovery. |
|
|
26
26
|
| `holiday` | Public Holiday | National or regional public holiday observed by the org. |
|
|
27
27
|
| `personal` | Personal Day | Personal errands, family obligations, other non-vacation personal time. |
|
|
28
|
+
| `birthday` | Birthday | The person's birthday day off. **One day, once per calendar year** — a second one in the same year, or a multi-day range, is rejected. |
|
|
28
29
|
| `other` | Other | Anything that doesn't fit the above categories. |
|
|
29
30
|
|
|
30
31
|
If the user doesn't specify a kind, default to `vacation` but **confirm before submitting**.
|
|
@@ -634,6 +634,11 @@ ct timesheet reject <timesheet-id> --notes "Missing entries for Wednesday — pl
|
|
|
634
634
|
# Reject multiple timesheets at once
|
|
635
635
|
ct timesheet bulk-reject <id1> <id2> --notes "Incomplete entries for the week"
|
|
636
636
|
|
|
637
|
+
# Someone logged the week but never submitted ("No timesheet" in `ct timesheet team`)?
|
|
638
|
+
# There's nothing to reopen — nudge them, or submit it for them and then approve it
|
|
639
|
+
ct timesheet remind --user ana@acme.com --from 2026-03-09 --to 2026-03-15
|
|
640
|
+
ct timesheet submit --from 2026-03-09 --to 2026-03-15 --user ana@acme.com --force
|
|
641
|
+
|
|
637
642
|
# Approved it too early? Reopen — back to draft, entries unlocked, user fixes & resubmits
|
|
638
643
|
ct timesheet reopen <timesheet-id> --notes "Approved before Friday's entries were added"
|
|
639
644
|
ct timesheet reopen <timesheet-id> --notes "Wrong project on Tuesday" --force
|
|
@@ -1477,7 +1482,7 @@ Use this when a member wants to request time off. The request goes into `pending
|
|
|
1477
1482
|
|
|
1478
1483
|
**Before running any command, ask the user:**
|
|
1479
1484
|
1. What dates? (single day or range) — if they say "next week" or "a few days in June", ASK for the exact dates. Never assume.
|
|
1480
|
-
2. What kind? (vacation / sick / holiday / personal / other) — if not specified, default to `vacation` and confirm.
|
|
1485
|
+
2. What kind? (vacation / sick / holiday / personal / birthday / other) — if not specified, default to `vacation` and confirm. Birthday is a single day, once per calendar year.
|
|
1481
1486
|
3. Hours per day? — leave empty unless they want partial days (e.g. half-day sick leave).
|
|
1482
1487
|
4. Notes? — optional but useful for the manager.
|
|
1483
1488
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|