bamboohr-cli 1.0.20 → 1.0.21-gd16d691.1

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.
Files changed (2) hide show
  1. package/dist/index.js +98 -8
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -756,16 +756,79 @@ function balance(parent) {
756
756
  }
757
757
 
758
758
  // src/commands/timeoff/create.ts
759
- import { Option as Option4 } from "commander";
759
+ import { InvalidArgumentError as InvalidArgumentError4, Option as Option4 } from "commander";
760
+
761
+ // src/utils/timeoff-dates.ts
762
+ import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
763
+ var WEEKDAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
764
+ var YMD = /^\d{4}-\d{2}-\d{2}$/;
765
+ function parseDatesJson(raw) {
766
+ let parsed;
767
+ try {
768
+ parsed = JSON.parse(raw);
769
+ } catch {
770
+ throw new InvalidArgumentError3(
771
+ `--dates is not valid JSON. Expected an array like '[{"ymd":"2026-03-21","amount":0.5}]'.`
772
+ );
773
+ }
774
+ const isEntry = (d) => typeof d === "object" && d !== null && typeof d.ymd === "string" && YMD.test(d.ymd) && typeof d.amount === "number" && Number.isFinite(d.amount) && d.amount >= 0;
775
+ if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every(isEntry)) {
776
+ throw new InvalidArgumentError3(
777
+ '--dates must be a non-empty JSON array of {"ymd":"YYYY-MM-DD","amount":<non-negative number>} objects.'
778
+ );
779
+ }
780
+ return parsed.map((d) => ({ ymd: d.ymd, amount: d.amount }));
781
+ }
782
+ function parseYmd(value, label) {
783
+ if (!YMD.test(value)) {
784
+ throw new InvalidArgumentError3(`${label} must be a YYYY-MM-DD date, got "${value}".`);
785
+ }
786
+ const date = /* @__PURE__ */ new Date(`${value}T00:00:00Z`);
787
+ if (Number.isNaN(date.getTime())) {
788
+ throw new InvalidArgumentError3(`${label} is not a valid date: "${value}".`);
789
+ }
790
+ return date;
791
+ }
792
+ function toYmd(date) {
793
+ return date.toISOString().slice(0, 10);
794
+ }
795
+ function buildTimeOffDates(params) {
796
+ const { start, end, units, defaultHours, holidays = [] } = params;
797
+ const startDate = parseYmd(start, "--start-date");
798
+ const endDate = parseYmd(end, "--end-date");
799
+ if (endDate < startDate) {
800
+ throw new InvalidArgumentError3(`--end-date ${end} is before --start-date ${start}.`);
801
+ }
802
+ const hoursByName = new Map(defaultHours.map((entry) => [entry.name, Number(entry.amount)]));
803
+ const fallbackHours = hoursByName.get("default") ?? 8;
804
+ const dates = [];
805
+ for (const day = new Date(startDate); day <= endDate; day.setUTCDate(day.getUTCDate() + 1)) {
806
+ const ymd = toYmd(day);
807
+ const workingHours = hoursByName.get(WEEKDAY_NAMES[day.getUTCDay()]) ?? fallbackHours;
808
+ const isHoliday = holidays.some((h) => ymd >= h.start && ymd <= h.end);
809
+ const working = workingHours > 0 && !isHoliday;
810
+ dates.push({ ymd, amount: working ? units === "hours" ? workingHours : 1 : 0 });
811
+ }
812
+ return dates;
813
+ }
814
+
815
+ // src/commands/timeoff/create.ts
760
816
  function create(parent) {
761
- const cmd = parent.command("create [employeeId]").description("Create a time off request (defaults to current user)").requiredOption("--start-date <date>", "Start date (YYYY-MM-DD)").requiredOption("--end-date <date>", "End date (YYYY-MM-DD)").requiredOption("--type-id <id>", 'Time off type ID (use "timeoff types" to find IDs)', positiveInt).requiredOption("--amount <amount>", "Total amount of time off (in days or hours depending on type)", parseFloat).addOption(
817
+ const cmd = parent.command("create [employeeId]").description("Create a time off request (defaults to current user)").requiredOption("--start-date <date>", "Start date (YYYY-MM-DD)").requiredOption("--end-date <date>", "End date (YYYY-MM-DD)").requiredOption("--type-id <id>", 'Time off type ID (use "timeoff types" to find IDs)', positiveInt).addOption(
762
818
  new Option4("--status <status>", "Request status").choices(["requested", "approved"]).default("requested")
763
- ).option("--dates <json>", `Per-day amounts as JSON array, e.g. '[{"ymd":"2026-03-21","amount":4}]' for half-day`);
819
+ ).option(
820
+ "--dates <json>",
821
+ `Per-day amounts as JSON array, e.g. '[{"ymd":"2026-03-21","amount":0.5}]' for a half day. When omitted, generated automatically: 1 day (or the weekday's working hours) per working day, 0 on weekends and company holidays`
822
+ );
764
823
  textOrFileOption(cmd, "note", { description: "Note to include with the request" });
765
824
  examples(cmd, [
766
- "--start-date 2026-04-01 --end-date 2026-04-01 --type-id 83 --amount 1",
825
+ ["--start-date 2026-04-06 --end-date 2026-04-10 --type-id 83", "a full week off, spread per day automatically"],
767
826
  [
768
- '504 --start-date 2026-04-01 --end-date 2026-04-01 --type-id 83 --amount 1 --note "Doctor appointment"',
827
+ `--start-date 2026-04-01 --end-date 2026-04-01 --type-id 83 --dates '[{"ymd":"2026-04-01","amount":0.5}]'`,
828
+ "a half day"
829
+ ],
830
+ [
831
+ '504 --start-date 2026-04-01 --end-date 2026-04-01 --type-id 83 --note "Doctor appointment"',
769
832
  "for another employee"
770
833
  ]
771
834
  ]);
@@ -774,15 +837,42 @@ function create(parent) {
774
837
  const client = getClient();
775
838
  const id = await resolveEmployeeId(client, employeeId);
776
839
  const note = resolveTextOrFile(opts, "note", { required: false });
840
+ let dates;
841
+ if (opts.dates) {
842
+ dates = parseDatesJson(opts.dates);
843
+ } else {
844
+ const [types2, whosOut2] = await Promise.all([
845
+ getTimeOffTypes(client),
846
+ client.timeOff.getWhosOut({ start: opts.startDate, end: opts.endDate })
847
+ ]);
848
+ const type = types2.timeOffTypes.find((t) => Number(t.id) === opts.typeId);
849
+ if (!type) {
850
+ throw new InvalidArgumentError4(`Unknown time off type ID ${opts.typeId}. Run "timeoff types" to list IDs.`);
851
+ }
852
+ const holidays = whosOut2.filter((e) => e.type === "holiday").map((e) => ({ start: e.start, end: e.end }));
853
+ dates = buildTimeOffDates({
854
+ start: opts.startDate,
855
+ end: opts.endDate,
856
+ units: type.units,
857
+ defaultHours: types2.defaultHours,
858
+ holidays
859
+ });
860
+ }
861
+ const total = dates.reduce((sum, d) => sum + d.amount, 0);
862
+ if (total <= 0) {
863
+ throw new InvalidArgumentError4(
864
+ opts.dates ? "The per-day amounts in --dates sum to 0; a request must claim more than 0 days/hours." : "The requested range contains no working days (weekends/company holidays only). Adjust the dates or pass --dates explicitly."
865
+ );
866
+ }
777
867
  const result = await client.timeOff.createRequest({
778
868
  employeeId: id,
779
869
  status: opts.status,
780
870
  start: opts.startDate,
781
871
  end: opts.endDate,
782
872
  timeOffTypeId: opts.typeId,
783
- amount: opts.amount,
873
+ amount: total,
784
874
  notes: note ? [{ from: "employee", note }] : void 0,
785
- dates: opts.dates ? JSON.parse(opts.dates) : void 0
875
+ dates
786
876
  });
787
877
  output(
788
878
  result ? stripNullish(result) : { created: true, employeeId: id, start: opts.startDate, end: opts.endDate }
@@ -889,7 +979,7 @@ function registerTimeoffCommands(program) {
889
979
  const timeoff = program.command("timeoff").description("Time off operations");
890
980
  examples(timeoff, [
891
981
  "types --requestable",
892
- "create 504 --start-date 2026-04-01 --end-date 2026-04-01 --type-id 83 --amount 1",
982
+ "create 504 --start-date 2026-04-01 --end-date 2026-04-01 --type-id 83",
893
983
  ["requests --status requested --start-date 2026-01-01 --end-date 2026-12-31", "pending requests in date range"],
894
984
  "balance 504",
895
985
  "whos-out",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bamboohr-cli",
3
- "version": "1.0.20",
3
+ "version": "1.0.21-gd16d691.1",
4
4
  "publish": true,
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "dependencies": {
14
14
  "commander": "^13.1.0",
15
- "bamboohr-client": "1.0.24"
15
+ "bamboohr-client": "1.0.24-gd16d691.1"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/node": "24.10.4",
@@ -22,8 +22,8 @@
22
22
  "tsx": "^4.19.2",
23
23
  "typescript": "^5.7.2",
24
24
  "vitest": "^4.0.16",
25
- "config-eslint": "0.0.0",
26
25
  "cli-utils": "1.0.0",
26
+ "config-eslint": "0.0.0",
27
27
  "config-typescript": "0.0.0"
28
28
  },
29
29
  "engines": {