bamboohr-cli 1.0.20 → 1.0.21-g0e48445.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.
- package/dist/index.js +98 -8
- 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).
|
|
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(
|
|
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-
|
|
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
|
-
|
|
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:
|
|
873
|
+
amount: total,
|
|
784
874
|
notes: note ? [{ from: "employee", note }] : void 0,
|
|
785
|
-
dates
|
|
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
|
|
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.
|
|
3
|
+
"version": "1.0.21-g0e48445.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-g0e48445.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": {
|