boondmanager-mcp-server 2.16.0 → 2.18.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.
- package/README.md +57 -4
- package/dist/config/access-policy.d.ts.map +1 -1
- package/dist/config/access-policy.js +6 -18
- package/dist/config/access-policy.js.map +1 -1
- package/dist/config/dictionary-overrides.d.ts.map +1 -1
- package/dist/config/dictionary-overrides.js +2 -12
- package/dist/config/dictionary-overrides.js.map +1 -1
- package/dist/config/env.d.ts +63 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +116 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/profiles.d.ts +22 -8
- package/dist/config/profiles.d.ts.map +1 -1
- package/dist/config/profiles.js +48 -4
- package/dist/config/profiles.js.map +1 -1
- package/dist/config/transport-kind.d.ts +8 -0
- package/dist/config/transport-kind.d.ts.map +1 -0
- package/dist/config/transport-kind.js +13 -0
- package/dist/config/transport-kind.js.map +1 -0
- package/dist/constants.d.ts +20 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +32 -1
- package/dist/constants.js.map +1 -1
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +6 -1
- package/dist/icons.js.map +1 -1
- package/dist/index.js +32 -20
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.d.ts.map +1 -1
- package/dist/instructions.js +6 -6
- package/dist/instructions.js.map +1 -1
- package/dist/prompts/index.d.ts +19 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +495 -25
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/periods.d.ts +45 -0
- package/dist/prompts/periods.d.ts.map +1 -0
- package/dist/prompts/periods.js +154 -0
- package/dist/prompts/periods.js.map +1 -0
- package/dist/resources/index.d.ts +14 -0
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +285 -0
- package/dist/resources/index.js.map +1 -1
- package/dist/resources/templates.d.ts +1 -1
- package/dist/resources/templates.d.ts.map +1 -1
- package/dist/resources/templates.js +2 -0
- package/dist/resources/templates.js.map +1 -1
- package/dist/schema-dialect.js.map +1 -1
- package/dist/schemas/filter-aliases.d.ts +1 -1
- package/dist/schemas/filter-aliases.d.ts.map +1 -1
- package/dist/schemas/filter-aliases.js +101 -2
- package/dist/schemas/filter-aliases.js.map +1 -1
- package/dist/schemas/index.d.ts +1045 -163
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +979 -330
- package/dist/schemas/index.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -3
- package/dist/server.js.map +1 -1
- package/dist/services/boond-client.d.ts +29 -213
- package/dist/services/boond-client.d.ts.map +1 -1
- package/dist/services/boond-client.js +29 -1168
- package/dist/services/boond-client.js.map +1 -1
- package/dist/services/dictionary.d.ts +27 -3
- package/dist/services/dictionary.d.ts.map +1 -1
- package/dist/services/dictionary.js +60 -23
- package/dist/services/dictionary.js.map +1 -1
- package/dist/services/document-text.d.ts +32 -0
- package/dist/services/document-text.d.ts.map +1 -0
- package/dist/services/document-text.js +120 -0
- package/dist/services/document-text.js.map +1 -0
- package/dist/services/format/detail.d.ts +16 -0
- package/dist/services/format/detail.d.ts.map +1 -0
- package/dist/services/format/detail.js +36 -0
- package/dist/services/format/detail.js.map +1 -0
- package/dist/services/format/html.d.ts +19 -0
- package/dist/services/format/html.d.ts.map +1 -0
- package/dist/services/format/html.js +87 -0
- package/dist/services/format/html.js.map +1 -0
- package/dist/services/format/list.d.ts +3 -0
- package/dist/services/format/list.d.ts.map +1 -0
- package/dist/services/format/list.js +44 -0
- package/dist/services/format/list.js.map +1 -0
- package/dist/services/format/summary.d.ts +23 -0
- package/dist/services/format/summary.d.ts.map +1 -0
- package/dist/services/format/summary.js +194 -0
- package/dist/services/format/summary.js.map +1 -0
- package/dist/services/format/tab.d.ts +9 -0
- package/dist/services/format/tab.d.ts.map +1 -0
- package/dist/services/format/tab.js +32 -0
- package/dist/services/format/tab.js.map +1 -0
- package/dist/services/http/auth.d.ts +44 -0
- package/dist/services/http/auth.d.ts.map +1 -0
- package/dist/services/http/auth.js +146 -0
- package/dist/services/http/auth.js.map +1 -0
- package/dist/services/http/download.d.ts +60 -0
- package/dist/services/http/download.d.ts.map +1 -0
- package/dist/services/http/download.js +160 -0
- package/dist/services/http/download.js.map +1 -0
- package/dist/services/http/errors.d.ts +45 -0
- package/dist/services/http/errors.d.ts.map +1 -0
- package/dist/services/http/errors.js +188 -0
- package/dist/services/http/errors.js.map +1 -0
- package/dist/services/http/rate-limit.d.ts +40 -0
- package/dist/services/http/rate-limit.d.ts.map +1 -0
- package/dist/services/http/rate-limit.js +92 -0
- package/dist/services/http/rate-limit.js.map +1 -0
- package/dist/services/http/retry.d.ts +46 -0
- package/dist/services/http/retry.d.ts.map +1 -0
- package/dist/services/http/retry.js +101 -0
- package/dist/services/http/retry.js.map +1 -0
- package/dist/services/http/transport.d.ts +85 -0
- package/dist/services/http/transport.d.ts.map +1 -0
- package/dist/services/http/transport.js +283 -0
- package/dist/services/http/transport.js.map +1 -0
- package/dist/services/logger.d.ts +53 -2
- package/dist/services/logger.d.ts.map +1 -1
- package/dist/services/logger.js +82 -21
- package/dist/services/logger.js.map +1 -1
- package/dist/services/oauth.d.ts +12 -0
- package/dist/services/oauth.d.ts.map +1 -1
- package/dist/services/oauth.js +21 -8
- package/dist/services/oauth.js.map +1 -1
- package/dist/services/rate-limiter.d.ts +9 -2
- package/dist/services/rate-limiter.d.ts.map +1 -1
- package/dist/services/rate-limiter.js +18 -6
- package/dist/services/rate-limiter.js.map +1 -1
- package/dist/services/request-context.d.ts +47 -0
- package/dist/services/request-context.d.ts.map +1 -0
- package/dist/services/request-context.js +112 -0
- package/dist/services/request-context.js.map +1 -0
- package/dist/services/search.d.ts +26 -0
- package/dist/services/search.d.ts.map +1 -0
- package/dist/services/search.js +98 -0
- package/dist/services/search.js.map +1 -0
- package/dist/services/update-checker.d.ts.map +1 -1
- package/dist/services/update-checker.js +15 -8
- package/dist/services/update-checker.js.map +1 -1
- package/dist/services/upload-source.d.ts +31 -0
- package/dist/services/upload-source.d.ts.map +1 -0
- package/dist/services/upload-source.js +180 -0
- package/dist/services/upload-source.js.map +1 -0
- package/dist/tools/absences.d.ts +9 -0
- package/dist/tools/absences.d.ts.map +1 -1
- package/dist/tools/absences.js +65 -20
- package/dist/tools/absences.js.map +1 -1
- package/dist/tools/actions.d.ts.map +1 -1
- package/dist/tools/actions.js +11 -27
- package/dist/tools/actions.js.map +1 -1
- package/dist/tools/advantages.d.ts +2 -0
- package/dist/tools/advantages.d.ts.map +1 -1
- package/dist/tools/advantages.js +66 -13
- package/dist/tools/advantages.js.map +1 -1
- package/dist/tools/alerts.d.ts +18 -0
- package/dist/tools/alerts.d.ts.map +1 -0
- package/dist/tools/alerts.js +82 -0
- package/dist/tools/alerts.js.map +1 -0
- package/dist/tools/application.d.ts +2 -1
- package/dist/tools/application.d.ts.map +1 -1
- package/dist/tools/application.js +9 -3
- package/dist/tools/application.js.map +1 -1
- package/dist/tools/candidates.d.ts +7 -0
- package/dist/tools/candidates.d.ts.map +1 -1
- package/dist/tools/candidates.js +96 -1
- package/dist/tools/candidates.js.map +1 -1
- package/dist/tools/contacts.d.ts.map +1 -1
- package/dist/tools/contacts.js +3 -7
- package/dist/tools/contacts.js.map +1 -1
- package/dist/tools/contracts.d.ts +22 -0
- package/dist/tools/contracts.d.ts.map +1 -1
- package/dist/tools/contracts.js +185 -52
- package/dist/tools/contracts.js.map +1 -1
- package/dist/tools/crud-factory.d.ts +33 -2
- package/dist/tools/crud-factory.d.ts.map +1 -1
- package/dist/tools/crud-factory.js +45 -17
- package/dist/tools/crud-factory.js.map +1 -1
- package/dist/tools/deliveries.d.ts +3 -0
- package/dist/tools/deliveries.d.ts.map +1 -1
- package/dist/tools/deliveries.js +50 -94
- package/dist/tools/deliveries.js.map +1 -1
- package/dist/tools/description-builders.d.ts +0 -1
- package/dist/tools/description-builders.d.ts.map +1 -1
- package/dist/tools/description-builders.js +47 -4
- package/dist/tools/description-builders.js.map +1 -1
- package/dist/tools/documents.d.ts.map +1 -1
- package/dist/tools/documents.js +113 -27
- package/dist/tools/documents.js.map +1 -1
- package/dist/tools/expenses.d.ts.map +1 -1
- package/dist/tools/expenses.js +2 -11
- package/dist/tools/expenses.js.map +1 -1
- package/dist/tools/find.d.ts +61 -0
- package/dist/tools/find.d.ts.map +1 -0
- package/dist/tools/find.js +221 -0
- package/dist/tools/find.js.map +1 -0
- package/dist/tools/flags.d.ts +5 -0
- package/dist/tools/flags.d.ts.map +1 -1
- package/dist/tools/flags.js +116 -1
- package/dist/tools/flags.js.map +1 -1
- package/dist/tools/forms.d.ts +5 -0
- package/dist/tools/forms.d.ts.map +1 -0
- package/dist/tools/forms.js +64 -0
- package/dist/tools/forms.js.map +1 -0
- package/dist/tools/groupments.d.ts +5 -0
- package/dist/tools/groupments.d.ts.map +1 -0
- package/dist/tools/groupments.js +68 -0
- package/dist/tools/groupments.js.map +1 -0
- package/dist/tools/inactivities.d.ts +5 -0
- package/dist/tools/inactivities.d.ts.map +1 -0
- package/dist/tools/inactivities.js +59 -0
- package/dist/tools/inactivities.js.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/invoices.d.ts.map +1 -1
- package/dist/tools/invoices.js +27 -2
- package/dist/tools/invoices.js.map +1 -1
- package/dist/tools/linked-entity-filters.d.ts +48 -0
- package/dist/tools/linked-entity-filters.d.ts.map +1 -0
- package/dist/tools/linked-entity-filters.js +64 -0
- package/dist/tools/linked-entity-filters.js.map +1 -0
- package/dist/tools/notifications.d.ts.map +1 -1
- package/dist/tools/notifications.js +0 -5
- package/dist/tools/notifications.js.map +1 -1
- package/dist/tools/orders.d.ts.map +1 -1
- package/dist/tools/orders.js +33 -4
- package/dist/tools/orders.js.map +1 -1
- package/dist/tools/parameter-disclosure.js.map +1 -1
- package/dist/tools/payments.d.ts +3 -0
- package/dist/tools/payments.d.ts.map +1 -1
- package/dist/tools/payments.js +43 -109
- package/dist/tools/payments.js.map +1 -1
- package/dist/tools/positionings.d.ts.map +1 -1
- package/dist/tools/positionings.js +9 -39
- package/dist/tools/positionings.js.map +1 -1
- package/dist/tools/projects.d.ts.map +1 -1
- package/dist/tools/projects.js +5 -12
- package/dist/tools/projects.js.map +1 -1
- package/dist/tools/provider-invoices.d.ts +2 -0
- package/dist/tools/provider-invoices.d.ts.map +1 -1
- package/dist/tools/provider-invoices.js +30 -111
- package/dist/tools/provider-invoices.js.map +1 -1
- package/dist/tools/purchases.d.ts +2 -0
- package/dist/tools/purchases.d.ts.map +1 -1
- package/dist/tools/purchases.js +50 -104
- package/dist/tools/purchases.js.map +1 -1
- package/dist/tools/registration-decorators.d.ts +28 -0
- package/dist/tools/registration-decorators.d.ts.map +1 -1
- package/dist/tools/registration-decorators.js +107 -0
- package/dist/tools/registration-decorators.js.map +1 -1
- package/dist/tools/resources.d.ts.map +1 -1
- package/dist/tools/resources.js +27 -5
- package/dist/tools/resources.js.map +1 -1
- package/dist/tools/rights.d.ts +101 -0
- package/dist/tools/rights.d.ts.map +1 -0
- package/dist/tools/rights.js +75 -0
- package/dist/tools/rights.js.map +1 -0
- package/dist/tools/tab-tools.js +3 -3
- package/dist/tools/tab-tools.js.map +1 -1
- package/dist/tools/timesheets.d.ts +29 -0
- package/dist/tools/timesheets.d.ts.map +1 -1
- package/dist/tools/timesheets.js +196 -126
- package/dist/tools/timesheets.js.map +1 -1
- package/dist/tools/todolists.d.ts +2 -0
- package/dist/tools/todolists.d.ts.map +1 -1
- package/dist/tools/todolists.js +64 -1
- package/dist/tools/todolists.js.map +1 -1
- package/dist/tools/usage-guidance.d.ts.map +1 -1
- package/dist/tools/usage-guidance.js +12 -3
- package/dist/tools/usage-guidance.js.map +1 -1
- package/dist/tools/validations.d.ts +9 -0
- package/dist/tools/validations.d.ts.map +1 -1
- package/dist/tools/validations.js +86 -9
- package/dist/tools/validations.js.map +1 -1
- package/dist/tools/workflows.js +1 -1
- package/dist/tools/workflows.js.map +1 -1
- package/dist/transports/http.d.ts +116 -0
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +422 -92
- package/dist/transports/http.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/manifest.json +2 -2
- package/package.json +6 -5
package/dist/schemas/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, MAX_SEARCH_PAGE } from "../constants.js";
|
|
2
|
+
import { DEFAULT_PAGE_SIZE, INLINE_UPLOAD_MAX_BYTES, MAX_PAGE_SIZE, MAX_SEARCH_PAGE } from "../constants.js";
|
|
3
3
|
import { appendOverridesToDescription, resolveLabel } from "../config/dictionary-overrides.js";
|
|
4
4
|
// Client-side projection, shared by every search schema. Declared before
|
|
5
5
|
// `SearchSchema` because that schema embeds it (a `const` referenced during
|
|
@@ -10,7 +10,7 @@ import { appendOverridesToDescription, resolveLabel } from "../config/dictionary
|
|
|
10
10
|
export const fieldsField = z
|
|
11
11
|
.array(z.string())
|
|
12
12
|
.optional()
|
|
13
|
-
.describe("Projection : attributs
|
|
13
|
+
.describe("Projection : attributs ou relations (type#id) par résultat (ex: ['title','dependsOn']). " +
|
|
14
14
|
"Absent = résumé standard. Noms inconnus ignorés.");
|
|
15
15
|
// ---- Reusable filter field helpers ----
|
|
16
16
|
// IMPORTANT: input field names below MUST match the BoondManager API query parameter names
|
|
@@ -24,7 +24,7 @@ export const fieldsField = z
|
|
|
24
24
|
// ("Too big: expected number to be <=100") says what was refused but not what
|
|
25
25
|
// to do instead, and a model that hit the page ceiling should refine its
|
|
26
26
|
// filters rather than retry one page lower.
|
|
27
|
-
const pageField = z
|
|
27
|
+
export const pageField = z
|
|
28
28
|
.number()
|
|
29
29
|
.int()
|
|
30
30
|
.min(1)
|
|
@@ -33,15 +33,27 @@ const pageField = z
|
|
|
33
33
|
})
|
|
34
34
|
.default(1)
|
|
35
35
|
.describe(`Numéro de page (défaut: 1, max: ${MAX_SEARCH_PAGE})`);
|
|
36
|
-
const pageSizeField = z
|
|
36
|
+
export const pageSizeField = z
|
|
37
37
|
.number()
|
|
38
38
|
.int()
|
|
39
39
|
.min(1)
|
|
40
40
|
.max(MAX_PAGE_SIZE, { error: `pageSize > ${MAX_PAGE_SIZE} refusé : maximum ${MAX_PAGE_SIZE} résultats par page.` })
|
|
41
41
|
.default(DEFAULT_PAGE_SIZE)
|
|
42
42
|
.describe(`Nombre de résultats par page (max: ${MAX_PAGE_SIZE}, défaut: ${DEFAULT_PAGE_SIZE})`);
|
|
43
|
-
const sortField = z.string().optional().describe("Champ de tri (ex: lastName, firstName, updateDate)");
|
|
44
|
-
const orderField = z.enum(["asc", "desc"]).optional().describe("Ordre de tri (asc/desc)");
|
|
43
|
+
export const sortField = z.string().optional().describe("Champ de tri (ex: lastName, firstName, updateDate)");
|
|
44
|
+
export const orderField = z.enum(["asc", "desc"]).optional().describe("Ordre de tri (asc/desc)");
|
|
45
|
+
/** `page` / `pageSize` / `fields` — every paginated search. */
|
|
46
|
+
export const paginationShape = {
|
|
47
|
+
page: pageField,
|
|
48
|
+
pageSize: pageSizeField,
|
|
49
|
+
fields: fieldsField,
|
|
50
|
+
};
|
|
51
|
+
/** `sort` / `order` + pagination — the six entity searches that accept sorting. */
|
|
52
|
+
export const sortedPaginationShape = {
|
|
53
|
+
sort: sortField,
|
|
54
|
+
order: orderField,
|
|
55
|
+
...paginationShape,
|
|
56
|
+
};
|
|
45
57
|
// Integer-id filters take ids, and a model that passes labels ("actif",
|
|
46
58
|
// "Acme") gets an invalid_type issue. Point it at the resolution path in the
|
|
47
59
|
// message itself rather than making it re-read the field description — but at
|
|
@@ -61,9 +73,7 @@ const strArray = (doc) => z.array(z.string()).optional().describe(doc);
|
|
|
61
73
|
export const SearchSchema = z
|
|
62
74
|
.object({
|
|
63
75
|
keywords: z.string().optional().describe("Mots-clés de recherche (nom, email, compétences...)"),
|
|
64
|
-
|
|
65
|
-
pageSize: pageSizeField,
|
|
66
|
-
fields: fieldsField,
|
|
76
|
+
...paginationShape,
|
|
67
77
|
})
|
|
68
78
|
.strict();
|
|
69
79
|
// Shared `tools` (technologies/skills) filter. Centralised so every entity
|
|
@@ -106,6 +116,90 @@ const endDateField = z
|
|
|
106
116
|
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
107
117
|
.optional()
|
|
108
118
|
.describe("Date de fin (YYYY-MM-DD), à utiliser avec `period`.");
|
|
119
|
+
// ---- Shared filters of the finance / activity searches (issue #248) ----
|
|
120
|
+
// `/invoices`, `/orders`, `/actions`, `/times-reports`, `/absences-reports`,
|
|
121
|
+
// `/deliveries-groupments` and `/payments` all declare the RAML trait
|
|
122
|
+
// `searchable`, so they take the six `perimeter*` filters — verified live on
|
|
123
|
+
// 2026-09-26 (`perimeterDynamic=data` reduced every one of them). The
|
|
124
|
+
// vocabulary below is per endpoint and was read from each `search.raml`.
|
|
125
|
+
/** `periodDynamic` — the relative windows the RAML lists on every dated search (no `startDate` / `endDate` needed). */
|
|
126
|
+
const periodDynamicField = z
|
|
127
|
+
.enum([
|
|
128
|
+
"today",
|
|
129
|
+
"yesterday",
|
|
130
|
+
"tomorrow",
|
|
131
|
+
"thisWeek",
|
|
132
|
+
"lastWeek",
|
|
133
|
+
"nextWeek",
|
|
134
|
+
"thisMonth",
|
|
135
|
+
"lastMonth",
|
|
136
|
+
"nextMonth",
|
|
137
|
+
"thisTrimester",
|
|
138
|
+
"lastTrimester",
|
|
139
|
+
"nextTrimester",
|
|
140
|
+
"thisSemester",
|
|
141
|
+
"lastSemester",
|
|
142
|
+
"nextSemester",
|
|
143
|
+
"thisYear",
|
|
144
|
+
"lastYear",
|
|
145
|
+
"nextYear",
|
|
146
|
+
"thisFiscalYear",
|
|
147
|
+
"lastFiscalYear",
|
|
148
|
+
"nextFiscalYear",
|
|
149
|
+
"untilToday",
|
|
150
|
+
])
|
|
151
|
+
.optional()
|
|
152
|
+
.describe("Fenêtre relative appliquée à `period` (à la place de `startDate` / `endDate`) : today, thisWeek, thisMonth, lastMonth, thisYear, untilToday…");
|
|
153
|
+
const flagsField = entityIdArray("IDs de drapeaux (flags) — `boond_flags_search`.");
|
|
154
|
+
/** The validation workflow of a CRA / absence report is a string state, not a dictionary id (#250). */
|
|
155
|
+
const validationStatesField = z
|
|
156
|
+
.array(z.enum(["waitingForValidation", "validated", "rejected"]))
|
|
157
|
+
.optional()
|
|
158
|
+
.describe("États du workflow de validation : waitingForValidation (à valider), validated, rejected.");
|
|
159
|
+
const resourceTypesField = intArray("IDs de types de ressource — `boond://dictionary/typeOf/resources`.");
|
|
160
|
+
const paymentMethodsField = intArray("IDs de modes de paiement — `boond://dictionary/paymentMethods`.");
|
|
161
|
+
const projectTypesFilterField = intArray("IDs de types de projet — `boond://dictionary/typeOf/projects`.");
|
|
162
|
+
const companiesFilterField = entityIdArray("IDs de sociétés (filtre API natif, équivalent de `companyId` pour plusieurs sociétés).");
|
|
163
|
+
// ---- Shared shapes (issue #240) ----
|
|
164
|
+
// Composed by spread into the entity search schemas so a change to one field
|
|
165
|
+
// (a `.describe()`, the page-ceiling message) propagates everywhere. Before,
|
|
166
|
+
// the six perimeter fields were copied six times, `shields` five times and the
|
|
167
|
+
// `page`/`pageSize` pair ~16 times — ten of those copies had already lost the
|
|
168
|
+
// `MAX_SEARCH_PAGE` explanation `pageField` carries.
|
|
169
|
+
/** The six RAML-trait `searchable` perimeter filters, in API order. */
|
|
170
|
+
export const perimeterShape = {
|
|
171
|
+
perimeterManagers: perimeterManagersField,
|
|
172
|
+
perimeterAgencies: perimeterAgenciesField,
|
|
173
|
+
perimeterPoles: perimeterPolesField,
|
|
174
|
+
perimeterBusinessUnits: perimeterBusinessUnitsField,
|
|
175
|
+
perimeterDynamic: perimeterDynamicField,
|
|
176
|
+
narrowPerimeter: narrowPerimeterField,
|
|
177
|
+
};
|
|
178
|
+
/** Completeness level of the conditional fields (RAML trait `shields`). */
|
|
179
|
+
export const shieldsField = z
|
|
180
|
+
.array(z.enum(["uncomplete", "minimum", "complete"]))
|
|
181
|
+
.optional()
|
|
182
|
+
.describe("Niveau de complétude des champs conditionnels.");
|
|
183
|
+
/** `keywordsType` values shared by the two people endpoints (resources, candidates). */
|
|
184
|
+
export const peopleKeywordsTypeEnum = z.enum([
|
|
185
|
+
"resumeTd",
|
|
186
|
+
"lastName",
|
|
187
|
+
"firstName",
|
|
188
|
+
"fullName",
|
|
189
|
+
"strictFullName",
|
|
190
|
+
"emails",
|
|
191
|
+
"title",
|
|
192
|
+
"titleSkills",
|
|
193
|
+
"phones",
|
|
194
|
+
"reference",
|
|
195
|
+
"resume",
|
|
196
|
+
"td",
|
|
197
|
+
]);
|
|
198
|
+
/** `perimeterManagersType` — candidates and opportunities only. */
|
|
199
|
+
const perimeterManagersTypeField = z
|
|
200
|
+
.enum(["main", "hr"])
|
|
201
|
+
.optional()
|
|
202
|
+
.describe("Type de responsable visé par `perimeterManagers`: 'main' (Main Manager) ou 'hr' (HR Manager).");
|
|
109
203
|
// ---- Resource search schema (collaborateurs internes) ----
|
|
110
204
|
// Source: https://doc.boondmanager.com/api-externe/raml-build/resources/resources/search.raml
|
|
111
205
|
export const ResourceSearchSchema = z
|
|
@@ -115,30 +209,11 @@ export const ResourceSearchSchema = z
|
|
|
115
209
|
.optional()
|
|
116
210
|
.describe("Mots-clés (par défaut, recherche dans CV + dossier technique). Pour cibler un champ précis, " +
|
|
117
211
|
"fournir aussi `keywordsType` (ex: lastName, firstName, fullName, emails, title, titleSkills, phones, reference)."),
|
|
118
|
-
keywordsType:
|
|
119
|
-
.enum([
|
|
120
|
-
"resumeTd",
|
|
121
|
-
"lastName",
|
|
122
|
-
"firstName",
|
|
123
|
-
"fullName",
|
|
124
|
-
"strictFullName",
|
|
125
|
-
"emails",
|
|
126
|
-
"title",
|
|
127
|
-
"titleSkills",
|
|
128
|
-
"phones",
|
|
129
|
-
"reference",
|
|
130
|
-
"resume",
|
|
131
|
-
"td",
|
|
132
|
-
])
|
|
212
|
+
keywordsType: peopleKeywordsTypeEnum
|
|
133
213
|
.optional()
|
|
134
214
|
.describe("Champ ciblé par `keywords`. Défaut: 'resumeTd' (CV + dossier technique). " +
|
|
135
215
|
"Pour 'fullName' utiliser `keywords = 'NOM#PRENOM'`."),
|
|
136
|
-
|
|
137
|
-
perimeterAgencies: perimeterAgenciesField,
|
|
138
|
-
perimeterPoles: perimeterPolesField,
|
|
139
|
-
perimeterBusinessUnits: perimeterBusinessUnitsField,
|
|
140
|
-
perimeterDynamic: perimeterDynamicField,
|
|
141
|
-
narrowPerimeter: narrowPerimeterField,
|
|
216
|
+
...perimeterShape,
|
|
142
217
|
resourceStates: intArray("IDs d'états de ressource (dictionnaire setting.state.resource via boond_application_dictionary)."),
|
|
143
218
|
excludeResourceStates: intArray("IDs d'états de ressource à EXCLURE."),
|
|
144
219
|
resourceTypes: intArray("IDs de types de ressource (dictionnaire setting.typeOf.resource)."),
|
|
@@ -178,15 +253,8 @@ export const ResourceSearchSchema = z
|
|
|
178
253
|
.optional()
|
|
179
254
|
.describe("Rayon en km pour la recherche géographique (5-200). Requis si `coordinates` ou `location` est fourni."),
|
|
180
255
|
excludeManager: z.boolean().optional().describe("Si true, ne retourne que les ressources sans compte manager."),
|
|
181
|
-
shields:
|
|
182
|
-
|
|
183
|
-
.optional()
|
|
184
|
-
.describe("Niveau de complétude des champs conditionnels."),
|
|
185
|
-
sort: sortField,
|
|
186
|
-
order: orderField,
|
|
187
|
-
page: pageField,
|
|
188
|
-
pageSize: pageSizeField,
|
|
189
|
-
fields: fieldsField,
|
|
256
|
+
shields: shieldsField,
|
|
257
|
+
...sortedPaginationShape,
|
|
190
258
|
})
|
|
191
259
|
.strict();
|
|
192
260
|
// ---- Candidate search schema ----
|
|
@@ -197,33 +265,9 @@ export const CandidateSearchSchema = z
|
|
|
197
265
|
.string()
|
|
198
266
|
.optional()
|
|
199
267
|
.describe("Mots-clés (par défaut, recherche dans CV + dossier technique). Combinable avec `keywordsType`."),
|
|
200
|
-
keywordsType:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"lastName",
|
|
204
|
-
"firstName",
|
|
205
|
-
"fullName",
|
|
206
|
-
"strictFullName",
|
|
207
|
-
"emails",
|
|
208
|
-
"title",
|
|
209
|
-
"titleSkills",
|
|
210
|
-
"phones",
|
|
211
|
-
"reference",
|
|
212
|
-
"resume",
|
|
213
|
-
"td",
|
|
214
|
-
])
|
|
215
|
-
.optional()
|
|
216
|
-
.describe("Champ ciblé par `keywords` (défaut: 'resumeTd')."),
|
|
217
|
-
perimeterManagers: perimeterManagersField,
|
|
218
|
-
perimeterAgencies: perimeterAgenciesField,
|
|
219
|
-
perimeterPoles: perimeterPolesField,
|
|
220
|
-
perimeterBusinessUnits: perimeterBusinessUnitsField,
|
|
221
|
-
perimeterDynamic: perimeterDynamicField,
|
|
222
|
-
perimeterManagersType: z
|
|
223
|
-
.enum(["main", "hr"])
|
|
224
|
-
.optional()
|
|
225
|
-
.describe("Type de responsable visé par `perimeterManagers`: 'main' (Main Manager) ou 'hr' (HR Manager)."),
|
|
226
|
-
narrowPerimeter: narrowPerimeterField,
|
|
268
|
+
keywordsType: peopleKeywordsTypeEnum.optional().describe("Champ ciblé par `keywords` (défaut: 'resumeTd')."),
|
|
269
|
+
...perimeterShape,
|
|
270
|
+
perimeterManagersType: perimeterManagersTypeField,
|
|
227
271
|
candidateStates: intArray("IDs d'états de candidat (dictionnaire setting.state.candidate via boond_application_dictionary)."),
|
|
228
272
|
candidateTypes: intArray("IDs de types de candidat (dictionnaire setting.typeOf.resource)."),
|
|
229
273
|
contractTypes: intArray("IDs de types de contrat recherchés (dictionnaire setting.typeOf.contract)."),
|
|
@@ -249,15 +293,8 @@ export const CandidateSearchSchema = z
|
|
|
249
293
|
coordinates: z.string().optional().describe("Coordonnées GPS 'lat,lon'. Requiert `geoDistance`."),
|
|
250
294
|
location: z.string().optional().describe("Adresse texte. Requiert `geoDistance`."),
|
|
251
295
|
geoDistance: z.number().int().min(5).max(200).optional().describe("Rayon km (5-200)."),
|
|
252
|
-
shields:
|
|
253
|
-
|
|
254
|
-
.optional()
|
|
255
|
-
.describe("Niveau de complétude."),
|
|
256
|
-
sort: sortField,
|
|
257
|
-
order: orderField,
|
|
258
|
-
page: pageField,
|
|
259
|
-
pageSize: pageSizeField,
|
|
260
|
-
fields: fieldsField,
|
|
296
|
+
shields: shieldsField,
|
|
297
|
+
...sortedPaginationShape,
|
|
261
298
|
})
|
|
262
299
|
.strict();
|
|
263
300
|
// ---- Contact search schema ----
|
|
@@ -284,12 +321,7 @@ export const ContactSearchSchema = z
|
|
|
284
321
|
.optional()
|
|
285
322
|
.describe("Champ ciblé. Défaut: 'default'. Pour 'fullName' utiliser `keywords = 'NOM#PRENOM'`. " +
|
|
286
323
|
"Pour 'companyFullName' utiliser `keywords = 'CSOCid#NOM#PRENOM'`."),
|
|
287
|
-
|
|
288
|
-
perimeterAgencies: perimeterAgenciesField,
|
|
289
|
-
perimeterPoles: perimeterPolesField,
|
|
290
|
-
perimeterBusinessUnits: perimeterBusinessUnitsField,
|
|
291
|
-
perimeterDynamic: perimeterDynamicField,
|
|
292
|
-
narrowPerimeter: narrowPerimeterField,
|
|
324
|
+
...perimeterShape,
|
|
293
325
|
states: intArray("IDs d'états de contact (dictionnaire setting.state.contact)."),
|
|
294
326
|
companyStates: intArray("IDs d'états des sociétés rattachées (dictionnaire setting.state.company)."),
|
|
295
327
|
typesOf: intArray("IDs de types de contact (dictionnaire setting.typeOf.contact). " +
|
|
@@ -309,15 +341,8 @@ export const ContactSearchSchema = z
|
|
|
309
341
|
endDate: endDateField,
|
|
310
342
|
completeness: strArray("Filtre par complétude des champs au format `fieldId:mode` (fieldId: email/phone/socialNetworks ; " +
|
|
311
343
|
"mode: empty/filled). Logique OU par défaut, '#AND#' en 1er pour ET. Ex: ['email:empty','phone:empty']."),
|
|
312
|
-
shields:
|
|
313
|
-
|
|
314
|
-
.optional()
|
|
315
|
-
.describe("Niveau de complétude."),
|
|
316
|
-
sort: sortField,
|
|
317
|
-
order: orderField,
|
|
318
|
-
page: pageField,
|
|
319
|
-
pageSize: pageSizeField,
|
|
320
|
-
fields: fieldsField,
|
|
344
|
+
shields: shieldsField,
|
|
345
|
+
...sortedPaginationShape,
|
|
321
346
|
})
|
|
322
347
|
.strict();
|
|
323
348
|
// ---- Company search schema ----
|
|
@@ -332,12 +357,7 @@ export const CompanySearchSchema = z
|
|
|
332
357
|
.enum(["default", "name", "phones", "emails", "socialNetworks"])
|
|
333
358
|
.optional()
|
|
334
359
|
.describe("Champ ciblé par `keywords`. Défaut: 'default'."),
|
|
335
|
-
|
|
336
|
-
perimeterAgencies: perimeterAgenciesField,
|
|
337
|
-
perimeterPoles: perimeterPolesField,
|
|
338
|
-
perimeterBusinessUnits: perimeterBusinessUnitsField,
|
|
339
|
-
perimeterDynamic: perimeterDynamicField,
|
|
340
|
-
narrowPerimeter: narrowPerimeterField,
|
|
360
|
+
...perimeterShape,
|
|
341
361
|
states: intArray("IDs d'états de société (dictionnaire setting.state.company)."),
|
|
342
362
|
expertiseAreas: strArray("IDs de domaines d'expertise (dictionnaire setting.expertiseArea)."),
|
|
343
363
|
origins: strArray("IDs d'origines (dictionnaire setting.origin)."),
|
|
@@ -350,15 +370,8 @@ export const CompanySearchSchema = z
|
|
|
350
370
|
"À combiner avec `startDate` + `endDate`."),
|
|
351
371
|
startDate: startDateField,
|
|
352
372
|
endDate: endDateField,
|
|
353
|
-
shields:
|
|
354
|
-
|
|
355
|
-
.optional()
|
|
356
|
-
.describe("Niveau de complétude."),
|
|
357
|
-
sort: sortField,
|
|
358
|
-
order: orderField,
|
|
359
|
-
page: pageField,
|
|
360
|
-
pageSize: pageSizeField,
|
|
361
|
-
fields: fieldsField,
|
|
373
|
+
shields: shieldsField,
|
|
374
|
+
...sortedPaginationShape,
|
|
362
375
|
})
|
|
363
376
|
.strict();
|
|
364
377
|
// ---- Opportunity search schema ----
|
|
@@ -371,16 +384,8 @@ export const OpportunitySearchSchema = z
|
|
|
371
384
|
.describe("Mots-clés. Pour cibler par ID préfixé : 'AOnnn' (opportunité), 'CSOCnnn' (société), " +
|
|
372
385
|
"'CCONnnn' (contact), 'CANDnnn' (candidat), 'COMPnnn' (ressource), 'PRODnnn' (produit). " +
|
|
373
386
|
"Sinon recherche plein texte sur titre/société."),
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
perimeterPoles: perimeterPolesField,
|
|
377
|
-
perimeterBusinessUnits: perimeterBusinessUnitsField,
|
|
378
|
-
perimeterDynamic: perimeterDynamicField,
|
|
379
|
-
perimeterManagersType: z
|
|
380
|
-
.enum(["main", "hr"])
|
|
381
|
-
.optional()
|
|
382
|
-
.describe("Type de responsable visé par `perimeterManagers` (main/hr)."),
|
|
383
|
-
narrowPerimeter: narrowPerimeterField,
|
|
387
|
+
...perimeterShape,
|
|
388
|
+
perimeterManagersType: perimeterManagersTypeField,
|
|
384
389
|
opportunityStates: intArray("IDs d'états d'opportunité (dictionnaire setting.state.opportunity)."),
|
|
385
390
|
opportunityTypes: strArray("IDs de types d'opportunité (dictionnaire setting.typeOf.project)."),
|
|
386
391
|
positioningStates: strArray("IDs d'états de positionnement, ou 'none' pour les opportunités sans positionnement."),
|
|
@@ -399,15 +404,8 @@ export const OpportunitySearchSchema = z
|
|
|
399
404
|
"À combiner avec `startDate` + `endDate`."),
|
|
400
405
|
startDate: startDateField,
|
|
401
406
|
endDate: endDateField,
|
|
402
|
-
shields:
|
|
403
|
-
|
|
404
|
-
.optional()
|
|
405
|
-
.describe("Niveau de complétude."),
|
|
406
|
-
sort: sortField,
|
|
407
|
-
order: orderField,
|
|
408
|
-
page: pageField,
|
|
409
|
-
pageSize: pageSizeField,
|
|
410
|
-
fields: fieldsField,
|
|
407
|
+
shields: shieldsField,
|
|
408
|
+
...sortedPaginationShape,
|
|
411
409
|
})
|
|
412
410
|
.strict();
|
|
413
411
|
// ---- Project search schema ----
|
|
@@ -419,12 +417,7 @@ export const ProjectSearchSchema = z
|
|
|
419
417
|
.optional()
|
|
420
418
|
.describe("Mots-clés. Pour cibler par ID préfixé : 'PRJnnn' (projet), 'CSOCnnn' (société), " +
|
|
421
419
|
"'CCONnnn' (contact), 'AOnnn' (opportunité), 'COMPnnn' (ressource), 'CTRnnn' (contrat)."),
|
|
422
|
-
|
|
423
|
-
perimeterAgencies: perimeterAgenciesField,
|
|
424
|
-
perimeterPoles: perimeterPolesField,
|
|
425
|
-
perimeterBusinessUnits: perimeterBusinessUnitsField,
|
|
426
|
-
perimeterDynamic: perimeterDynamicField,
|
|
427
|
-
narrowPerimeter: narrowPerimeterField,
|
|
420
|
+
...perimeterShape,
|
|
428
421
|
projectStates: intArray("IDs d'états de projet (dictionnaire setting.state.project)."),
|
|
429
422
|
projectTypes: intArray("IDs de types de projet (dictionnaire setting.typeOf.project)."),
|
|
430
423
|
companies: entityIdArray("IDs de sociétés clientes : projets rattachés à ces sociétés."),
|
|
@@ -438,11 +431,7 @@ export const ProjectSearchSchema = z
|
|
|
438
431
|
"'hasAdditionalDataOrPurchase'. À combiner avec `startDate` + `endDate`."),
|
|
439
432
|
startDate: startDateField,
|
|
440
433
|
endDate: endDateField,
|
|
441
|
-
|
|
442
|
-
order: orderField,
|
|
443
|
-
page: pageField,
|
|
444
|
-
pageSize: pageSizeField,
|
|
445
|
-
fields: fieldsField,
|
|
434
|
+
...sortedPaginationShape,
|
|
446
435
|
})
|
|
447
436
|
.strict();
|
|
448
437
|
// BoondManager entity ids are numeric. Constraining the schema to digits
|
|
@@ -470,6 +459,11 @@ export const IdSchema = z
|
|
|
470
459
|
// the id still can't smuggle a path segment, `..`, `?` or `#` into the API
|
|
471
460
|
// path. Letters are allowed in both cases because Boond derives the suffix
|
|
472
461
|
// from camelCase parent types (`administrativeFile`).
|
|
462
|
+
/** `mode` of `boond_documents_get` (issue #263): extracted text by default, the raw bytes on demand. */
|
|
463
|
+
export const DocumentGetModeSchema = z
|
|
464
|
+
.enum(["text", "raw"])
|
|
465
|
+
.default("text")
|
|
466
|
+
.describe("text (défaut) : PDF et DOCX renvoyés en texte extrait côté serveur, images en contenu `image` ; raw : le fichier tel quel en ressource embarquée (base64).");
|
|
473
467
|
export const DocumentIdSchema = z
|
|
474
468
|
.object({
|
|
475
469
|
id: z
|
|
@@ -478,6 +472,7 @@ export const DocumentIdSchema = z
|
|
|
478
472
|
.describe("Identifiant du document, tel qu'exposé par les relations d'entités (ex. 123_resume)"),
|
|
479
473
|
})
|
|
480
474
|
.strict();
|
|
475
|
+
export const DocumentGetSchema = DocumentIdSchema.extend({ mode: DocumentGetModeSchema }).strict();
|
|
481
476
|
// ID + tab param schema
|
|
482
477
|
export const IdTabSchema = z
|
|
483
478
|
.object({
|
|
@@ -520,13 +515,8 @@ export const CandidateCreateSchema = z
|
|
|
520
515
|
.strict();
|
|
521
516
|
export const PositioningUpdateSchema = z
|
|
522
517
|
.object({
|
|
523
|
-
id:
|
|
524
|
-
state:
|
|
525
|
-
.number()
|
|
526
|
-
.int()
|
|
527
|
-
.min(0)
|
|
528
|
-
.optional()
|
|
529
|
-
.describe("\u00c9tat du positionnement : ID num\u00e9rique du dictionnaire setting.state.positioning"),
|
|
518
|
+
id: EntityIdSchema.describe("ID du positionnement \u00e0 modifier"),
|
|
519
|
+
state: stateField("positioning", "État du positionnement : ID numérique de `boond://dictionary/states/positionings`"),
|
|
530
520
|
stateReasonTypeOf: z
|
|
531
521
|
.number()
|
|
532
522
|
.int()
|
|
@@ -540,7 +530,7 @@ export const PositioningUpdateSchema = z
|
|
|
540
530
|
.strict();
|
|
541
531
|
export const CandidateUpdateSchema = z
|
|
542
532
|
.object({
|
|
543
|
-
id:
|
|
533
|
+
id: EntityIdSchema.describe("ID du candidat à modifier"),
|
|
544
534
|
firstName: z.string().optional().describe("Prénom"),
|
|
545
535
|
lastName: z.string().optional().describe("Nom"),
|
|
546
536
|
email1: z.string().email().optional().describe("Email principal"),
|
|
@@ -551,6 +541,24 @@ export const CandidateUpdateSchema = z
|
|
|
551
541
|
state: stateField("candidate", "État du candidat"),
|
|
552
542
|
mainSkills: z.string().optional().describe("Compétences principales"),
|
|
553
543
|
note: z.string().optional().describe("Notes"),
|
|
544
|
+
address: z.string().optional().describe("Adresse (rue et numéro)"),
|
|
545
|
+
postcode: z.string().optional().describe("Code postal"),
|
|
546
|
+
town: z.string().optional().describe("Ville, telle que stockée par l'onglet Information (`town`)"),
|
|
547
|
+
availability: z
|
|
548
|
+
.number()
|
|
549
|
+
.int()
|
|
550
|
+
.optional()
|
|
551
|
+
.describe("Disponibilité : ID numérique du dictionnaire `setting.availability`"),
|
|
552
|
+
mobilityAreas: z
|
|
553
|
+
.array(z.string())
|
|
554
|
+
.optional()
|
|
555
|
+
.describe("Zones de mobilité : IDs du dictionnaire `setting.mobilityArea` (ex: 'mondeeuropebelgique'). " +
|
|
556
|
+
"Remplace la liste existante : relire l'onglet Information et fusionner avant d'appeler."),
|
|
557
|
+
globalEvaluation: z
|
|
558
|
+
.string()
|
|
559
|
+
.optional()
|
|
560
|
+
.describe("Évaluation globale : ID du dictionnaire `setting.evaluation` (ex: 'a', 'b', 'c', 'd')"),
|
|
561
|
+
informationComments: z.string().optional().describe("Commentaires de l'onglet Information"),
|
|
554
562
|
})
|
|
555
563
|
.strict();
|
|
556
564
|
// ---- Resource schemas ----
|
|
@@ -569,7 +577,7 @@ export const ResourceCreateSchema = z
|
|
|
569
577
|
.strict();
|
|
570
578
|
export const ResourceUpdateSchema = z
|
|
571
579
|
.object({
|
|
572
|
-
id:
|
|
580
|
+
id: EntityIdSchema.describe("ID de la ressource à modifier"),
|
|
573
581
|
firstName: z.string().optional().describe("Prénom"),
|
|
574
582
|
lastName: z.string().optional().describe("Nom"),
|
|
575
583
|
email1: z.string().email().optional().describe("Email principal"),
|
|
@@ -600,7 +608,7 @@ const technicalDataLanguageItemSchema = z
|
|
|
600
608
|
.strict();
|
|
601
609
|
export const ResourceTechnicalDataUpdateSchema = z
|
|
602
610
|
.object({
|
|
603
|
-
id:
|
|
611
|
+
id: EntityIdSchema.describe("ID de la ressource dont le dossier technique est mis à jour."),
|
|
604
612
|
mode: z
|
|
605
613
|
.enum(["merge", "replace"])
|
|
606
614
|
.default("merge")
|
|
@@ -620,6 +628,64 @@ export const ResourceTechnicalDataUpdateSchema = z
|
|
|
620
628
|
.describe("Diplômes (texte libre, ex: 'DUT Informatique - IUT Bordeaux (2016)')."),
|
|
621
629
|
})
|
|
622
630
|
.strict();
|
|
631
|
+
// ---- Candidate technical-data (DT) write schema ----
|
|
632
|
+
// Same payload as the resource DT (PUT /candidates/{id}/technical-data, type=candidate).
|
|
633
|
+
// Scalars experience/training and the area lists take dictionary IDs, not free text.
|
|
634
|
+
export const CandidateTechnicalDataUpdateSchema = z
|
|
635
|
+
.object({
|
|
636
|
+
id: EntityIdSchema.describe("ID du candidat dont le dossier technique est mis à jour."),
|
|
637
|
+
mode: z
|
|
638
|
+
.enum(["merge", "replace"])
|
|
639
|
+
.default("merge")
|
|
640
|
+
.describe("'merge' (défaut) enrichit le DT sans rien écraser. 'replace' remplace intégralement chaque champ fourni."),
|
|
641
|
+
title: z.string().optional().describe("Titre / poste."),
|
|
642
|
+
summary: z.string().optional().describe("Résumé / synthèse du parcours (Profile Summary)."),
|
|
643
|
+
skills: z.string().optional().describe("Compétences libres, séparées par virgule."),
|
|
644
|
+
experience: z
|
|
645
|
+
.number()
|
|
646
|
+
.int()
|
|
647
|
+
.min(0)
|
|
648
|
+
.optional()
|
|
649
|
+
.describe("Tranche d'expérience : ID numérique du dictionnaire `setting.experience`."),
|
|
650
|
+
training: z.string().optional().describe("Niveau de formation : ID du dictionnaire `setting.training`."),
|
|
651
|
+
expertiseAreas: z
|
|
652
|
+
.array(z.string())
|
|
653
|
+
.optional()
|
|
654
|
+
.describe("Domaines d'expertise : IDs du dictionnaire `setting.expertiseArea`."),
|
|
655
|
+
activityAreas: z
|
|
656
|
+
.array(z.string())
|
|
657
|
+
.optional()
|
|
658
|
+
.describe("Secteurs / profils : IDs du dictionnaire `setting.activityArea` (options des groupes)."),
|
|
659
|
+
tools: z.array(technicalDataToolItemSchema).optional().describe("Outils maîtrisés avec niveau (1-4)."),
|
|
660
|
+
languages: z.array(technicalDataLanguageItemSchema).optional().describe("Langues parlées avec niveau."),
|
|
661
|
+
diplomas: z
|
|
662
|
+
.array(z.string())
|
|
663
|
+
.optional()
|
|
664
|
+
.describe("Diplômes (texte libre, ex: '2021 - Ingénieur Civil - UCLouvain')."),
|
|
665
|
+
})
|
|
666
|
+
.strict();
|
|
667
|
+
// ---- Candidate administrative write schema ----
|
|
668
|
+
// PUT /candidates/{id}/administrative, type=candidate. Attribute names mirror the GET payload.
|
|
669
|
+
const salaryRangeSchema = z
|
|
670
|
+
.object({
|
|
671
|
+
min: z.number().min(0).describe("Borne basse"),
|
|
672
|
+
max: z.number().min(0).describe("Borne haute"),
|
|
673
|
+
})
|
|
674
|
+
.strict();
|
|
675
|
+
export const CandidateAdministrativeUpdateSchema = z
|
|
676
|
+
.object({
|
|
677
|
+
id: EntityIdSchema.describe("ID du candidat."),
|
|
678
|
+
nationality: z.string().optional().describe("Nationalité (texte, ex: 'Belgique')."),
|
|
679
|
+
desiredSalary: salaryRangeSchema.optional().describe("Salaire souhaité (fourchette min / max)."),
|
|
680
|
+
actualSalary: z.number().min(0).optional().describe("Salaire actuel."),
|
|
681
|
+
desiredContract: z
|
|
682
|
+
.number()
|
|
683
|
+
.int()
|
|
684
|
+
.optional()
|
|
685
|
+
.describe("Contrat souhaité : ID du dictionnaire `setting.typeOf.contract` (-1 = non renseigné)."),
|
|
686
|
+
administrativeComments: z.string().optional().describe("Commentaires administratifs."),
|
|
687
|
+
})
|
|
688
|
+
.strict();
|
|
623
689
|
// ---- Reference (DT experience) schemas ----
|
|
624
690
|
// Validated live against the BoondManager API on 2026-05-20: references are NOT a
|
|
625
691
|
// standalone REST entity. They're sub-objects embedded in the resource's DT (the
|
|
@@ -646,7 +712,7 @@ const referenceYear = z.coerce
|
|
|
646
712
|
.describe("Année (ex: 2024). Accepte int ou string '2024'.");
|
|
647
713
|
export const ReferenceCreateSchema = z
|
|
648
714
|
.object({
|
|
649
|
-
resourceId:
|
|
715
|
+
resourceId: EntityIdSchema.describe("ID de la ressource à laquelle rattacher la référence."),
|
|
650
716
|
title: z.string().min(1).describe("Intitulé du poste."),
|
|
651
717
|
company: z.string().min(1).describe("Société / employeur."),
|
|
652
718
|
description: z
|
|
@@ -663,11 +729,8 @@ export const ReferenceCreateSchema = z
|
|
|
663
729
|
.strict();
|
|
664
730
|
export const ReferenceUpdateSchema = z
|
|
665
731
|
.object({
|
|
666
|
-
resourceId:
|
|
667
|
-
|
|
668
|
-
.min(1)
|
|
669
|
-
.describe("ID de la ressource portant la référence (les references sont embarquées dans le DT)."),
|
|
670
|
-
referenceId: z.string().min(1).describe("ID de la référence à modifier."),
|
|
732
|
+
resourceId: EntityIdSchema.describe("ID de la ressource portant la référence (les references sont embarquées dans le DT)."),
|
|
733
|
+
referenceId: EntityIdSchema.describe("ID de la référence à modifier."),
|
|
671
734
|
title: z.string().optional().describe("Intitulé du poste."),
|
|
672
735
|
company: z.string().optional().describe("Société / employeur."),
|
|
673
736
|
description: z.string().optional().describe("Description / missions / réalisations."),
|
|
@@ -681,11 +744,8 @@ export const ReferenceUpdateSchema = z
|
|
|
681
744
|
.strict();
|
|
682
745
|
export const ReferenceIdSchema = z
|
|
683
746
|
.object({
|
|
684
|
-
resourceId:
|
|
685
|
-
|
|
686
|
-
.min(1)
|
|
687
|
-
.describe("ID de la ressource portant la référence (les references sont embarquées dans le DT)."),
|
|
688
|
-
referenceId: z.string().min(1).describe("ID de la référence à supprimer."),
|
|
747
|
+
resourceId: EntityIdSchema.describe("ID de la ressource portant la référence (les references sont embarquées dans le DT)."),
|
|
748
|
+
referenceId: EntityIdSchema.describe("ID de la référence à supprimer."),
|
|
689
749
|
})
|
|
690
750
|
.strict();
|
|
691
751
|
// ---- Contact schemas ----
|
|
@@ -698,13 +758,13 @@ export const ContactCreateSchema = z
|
|
|
698
758
|
city: z.string().optional().describe("Ville"),
|
|
699
759
|
country: z.string().optional().describe("Pays"),
|
|
700
760
|
title: z.string().optional().describe("Titre / fonction"),
|
|
701
|
-
companyId:
|
|
761
|
+
companyId: EntityIdSchema.optional().describe("ID de la société associée"),
|
|
702
762
|
note: z.string().optional().describe("Notes"),
|
|
703
763
|
})
|
|
704
764
|
.strict();
|
|
705
765
|
export const ContactUpdateSchema = z
|
|
706
766
|
.object({
|
|
707
|
-
id:
|
|
767
|
+
id: EntityIdSchema.describe("ID du contact à modifier"),
|
|
708
768
|
firstName: z.string().optional().describe("Prénom"),
|
|
709
769
|
lastName: z.string().optional().describe("Nom"),
|
|
710
770
|
email1: z.string().email().optional().describe("Email"),
|
|
@@ -730,7 +790,7 @@ export const CompanyCreateSchema = z
|
|
|
730
790
|
.strict();
|
|
731
791
|
export const CompanyUpdateSchema = z
|
|
732
792
|
.object({
|
|
733
|
-
id:
|
|
793
|
+
id: EntityIdSchema.describe("ID de la société à modifier"),
|
|
734
794
|
name: z.string().optional().describe("Nom"),
|
|
735
795
|
email1: z.string().email().optional().describe("Email"),
|
|
736
796
|
phone1: z.string().optional().describe("Téléphone"),
|
|
@@ -759,8 +819,8 @@ const opportunityWritableShape = {
|
|
|
759
819
|
.min(1)
|
|
760
820
|
.optional()
|
|
761
821
|
.describe("Type d'opportunité : ID numérique du dictionnaire setting.typeOf.project (ex: 1, 3), via boond_application_dictionary"),
|
|
762
|
-
companyId:
|
|
763
|
-
contactId:
|
|
822
|
+
companyId: EntityIdSchema.optional().describe("ID de la société cliente (relation company)"),
|
|
823
|
+
contactId: EntityIdSchema.optional().describe("ID du contact associé (relation contact)"),
|
|
764
824
|
state: stateField("opportunity", "État de l'opportunité"),
|
|
765
825
|
startDate: z.string().optional().describe("Date de début prévue (YYYY-MM-DD ou 'immediate')"),
|
|
766
826
|
endDate: z.string().optional().describe("Date de fin prévue (YYYY-MM-DD)"),
|
|
@@ -779,13 +839,10 @@ const opportunityWritableShape = {
|
|
|
779
839
|
.optional()
|
|
780
840
|
.describe("Domaine d'expertise : ID du dictionnaire setting.expertiseArea (via boond_application_dictionary)"),
|
|
781
841
|
turnoverEstimatedExcludingTax: z.coerce.number().optional().describe("Chiffre d'affaires estimé HT (montant)"),
|
|
782
|
-
poleId:
|
|
783
|
-
hrManagerId:
|
|
784
|
-
mainManagerId:
|
|
785
|
-
|
|
786
|
-
.optional()
|
|
787
|
-
.describe("ID de la ressource responsable principal / commercial (relation mainManager)"),
|
|
788
|
-
agencyId: z.string().optional().describe("ID de l'agence (relation agency)"),
|
|
842
|
+
poleId: EntityIdSchema.optional().describe("ID du pôle (relation pole)"),
|
|
843
|
+
hrManagerId: EntityIdSchema.optional().describe("ID de la ressource responsable RH (relation hrManager)"),
|
|
844
|
+
mainManagerId: EntityIdSchema.optional().describe("ID de la ressource responsable principal / commercial (relation mainManager)"),
|
|
845
|
+
agencyId: EntityIdSchema.optional().describe("ID de l'agence (relation agency)"),
|
|
789
846
|
};
|
|
790
847
|
export const OpportunityCreateSchema = z
|
|
791
848
|
.object({
|
|
@@ -795,22 +852,35 @@ export const OpportunityCreateSchema = z
|
|
|
795
852
|
.strict();
|
|
796
853
|
export const OpportunityUpdateSchema = z
|
|
797
854
|
.object({
|
|
798
|
-
id:
|
|
855
|
+
id: EntityIdSchema.describe("ID de l'opportunité à modifier"),
|
|
799
856
|
name: z.string().optional().describe("Nom / titre"),
|
|
800
857
|
...opportunityWritableShape,
|
|
801
858
|
})
|
|
802
859
|
.strict();
|
|
803
860
|
// ---- Action schemas ----
|
|
861
|
+
// Source: resources/actions/search.raml (filters verified live, issue #248).
|
|
804
862
|
export const ActionSearchSchema = z
|
|
805
863
|
.object({
|
|
806
864
|
keywords: z.string().optional().describe("Mots-clés de recherche"),
|
|
807
|
-
candidateId:
|
|
808
|
-
resourceId:
|
|
809
|
-
contactId:
|
|
810
|
-
companyId:
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
865
|
+
candidateId: EntityIdSchema.optional().describe("Filtrer par ID candidat (référence keywords CAND<id>)"),
|
|
866
|
+
resourceId: EntityIdSchema.optional().describe("Filtrer par ID ressource (référence keywords COMP<id>)"),
|
|
867
|
+
contactId: EntityIdSchema.optional().describe("Filtrer par ID contact (référence keywords CCON<id>)"),
|
|
868
|
+
companyId: EntityIdSchema.optional().describe("Filtrer par ID société (référence keywords CSOC<id>)"),
|
|
869
|
+
opportunityId: EntityIdSchema.optional().describe("Filtrer par ID opportunité (référence keywords AO<id>)"),
|
|
870
|
+
projectId: EntityIdSchema.optional().describe("Filtrer par ID projet (référence keywords PRJ<id>)"),
|
|
871
|
+
orderId: EntityIdSchema.optional().describe("Filtrer par ID bon de commande (référence keywords BDC<id>)"),
|
|
872
|
+
invoiceId: EntityIdSchema.optional().describe("Filtrer par ID facture (référence keywords FACT<id>)"),
|
|
873
|
+
actionTypes: intArray("IDs de types d'action — `boond://dictionary/actions/<entité>` (les types sont déclarés par entité de rattachement)."),
|
|
874
|
+
period: z
|
|
875
|
+
.enum(["started", "created", "updated"])
|
|
876
|
+
.optional()
|
|
877
|
+
.describe("Champ de date borné par `startDate` / `endDate` : started (date de l'action), created, updated."),
|
|
878
|
+
periodDynamic: periodDynamicField,
|
|
879
|
+
startDate: startDateField,
|
|
880
|
+
endDate: endDateField,
|
|
881
|
+
flags: flagsField,
|
|
882
|
+
...perimeterShape,
|
|
883
|
+
...sortedPaginationShape,
|
|
814
884
|
})
|
|
815
885
|
.strict();
|
|
816
886
|
// POST /actions requires a polymorphic `dependsOn` relationship (the entity the
|
|
@@ -831,16 +901,13 @@ export const ActionCreateSchema = z
|
|
|
831
901
|
.optional()
|
|
832
902
|
.describe("Date de début au format ISO 8601 avec timezone (ex: 2026-06-05T10:00:00+0200)"),
|
|
833
903
|
endDate: z.string().regex(actionDateTimeRegex).optional().describe("Date de fin (même format que startDate)"),
|
|
834
|
-
candidateId:
|
|
835
|
-
resourceId:
|
|
836
|
-
contactId:
|
|
837
|
-
opportunityId:
|
|
838
|
-
projectId:
|
|
839
|
-
companyId:
|
|
840
|
-
positioningId:
|
|
841
|
-
.string()
|
|
842
|
-
.optional()
|
|
843
|
-
.describe("ID du positionnement à lier à l'action (relation positioning). Requis par l'API pour les types d'action liés aux positionnements (ex. RQ) — sans lui, erreur 422 « 1002 - Wrong or missing attribute (/data/relationships/positioning) »."),
|
|
904
|
+
candidateId: EntityIdSchema.optional().describe("ID du candidat auquel rattacher l'action (dependsOn)"),
|
|
905
|
+
resourceId: EntityIdSchema.optional().describe("ID de la ressource à laquelle rattacher l'action (dependsOn)"),
|
|
906
|
+
contactId: EntityIdSchema.optional().describe("ID du contact auquel rattacher l'action (dependsOn)"),
|
|
907
|
+
opportunityId: EntityIdSchema.optional().describe("ID de l'opportunité à laquelle rattacher l'action (dependsOn)"),
|
|
908
|
+
projectId: EntityIdSchema.optional().describe("ID du projet auquel rattacher l'action (dependsOn)"),
|
|
909
|
+
companyId: EntityIdSchema.optional().describe("ID de la société associée (uniquement en complément d'un contactId)"),
|
|
910
|
+
positioningId: EntityIdSchema.optional().describe("ID du positionnement à lier à l'action (relation positioning). Requis par l'API pour les types d'action liés aux positionnements (ex. RQ) — sans lui, erreur 422 « 1002 - Wrong or missing attribute (/data/relationships/positioning) »."),
|
|
844
911
|
})
|
|
845
912
|
.strict();
|
|
846
913
|
// PUT /actions/{id} (PATCH renvoie 405). Mise à jour partielle des seuls
|
|
@@ -850,7 +917,7 @@ export const ActionCreateSchema = z
|
|
|
850
917
|
// de résolution de libellé, faute d'entité `dependsOn` connue sur l'update).
|
|
851
918
|
export const ActionUpdateSchema = z
|
|
852
919
|
.object({
|
|
853
|
-
id:
|
|
920
|
+
id: EntityIdSchema.describe("ID de l'action à modifier"),
|
|
854
921
|
typeOf: z.coerce
|
|
855
922
|
.number()
|
|
856
923
|
.int()
|
|
@@ -870,7 +937,7 @@ export const ActionUpdateSchema = z
|
|
|
870
937
|
// ---- Timesheet schemas ----
|
|
871
938
|
export const ResourceTimesheetSchema = z
|
|
872
939
|
.object({
|
|
873
|
-
resourceId:
|
|
940
|
+
resourceId: EntityIdSchema.describe("ID de la ressource"),
|
|
874
941
|
month: z.number().int().min(1).max(12).optional().describe("Mois (1-12). Si omis, mois courant."),
|
|
875
942
|
year: z.number().int().min(2000).optional().describe("Année (ex: 2025). Si omis, année courante."),
|
|
876
943
|
})
|
|
@@ -889,28 +956,103 @@ export const TimesheetSearchSchema = z
|
|
|
889
956
|
.regex(/^\d{4}-\d{2}$/)
|
|
890
957
|
.describe("Mois de fin au format YYYY-MM (ex: '2025-03'). Requis."),
|
|
891
958
|
keywords: z.string().optional().describe("Mots-clés (préfixes 'TPS', 'COMP'...)."),
|
|
892
|
-
|
|
893
|
-
|
|
959
|
+
resourceId: EntityIdSchema.optional().describe("Filtrer par ID ressource (référence keywords COMP<id>)"),
|
|
960
|
+
resourceTypes: resourceTypesField,
|
|
961
|
+
validationStates: validationStatesField,
|
|
962
|
+
closed: z.boolean().optional().describe("true = CRA clôturés uniquement, false = non clôturés."),
|
|
963
|
+
...perimeterShape,
|
|
964
|
+
...sortedPaginationShape,
|
|
965
|
+
})
|
|
966
|
+
.strict();
|
|
967
|
+
// ---- Timesheet (CRA) write schemas (issue #249) ----
|
|
968
|
+
//
|
|
969
|
+
// A times report is a monthly container per resource (`term` + `resource`)
|
|
970
|
+
// whose lines live in `regularTimes[]` / `exceptionalTimes[]` — the exact
|
|
971
|
+
// twin of the expenses report (#179). The line shape below is the one the API
|
|
972
|
+
// *returns* (`GET /times-reports/{id}`, `models.time` in the dictionary, and
|
|
973
|
+
// the `plannedTimes` of `/times-reports/default`): `startDate`, `duration`,
|
|
974
|
+
// `workUnitType.reference`, `project`, `delivery`, `batch`. `state` is moved by
|
|
975
|
+
// the validation workflow (`validated`, `waitingForValidation`…) and is not a
|
|
976
|
+
// write field. See CLAUDE.md → *Timesheets* for what was and was not verified
|
|
977
|
+
// against the live API.
|
|
978
|
+
export const TimesheetLineSchema = z
|
|
979
|
+
.object({
|
|
980
|
+
startDate: z
|
|
981
|
+
.string()
|
|
982
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
983
|
+
.describe("Jour saisi (YYYY-MM-DD). Doit tomber dans le mois `term` du CRA."),
|
|
984
|
+
duration: z
|
|
985
|
+
.number()
|
|
986
|
+
.positive()
|
|
987
|
+
.default(1)
|
|
988
|
+
.describe("Durée en unités d'œuvre (jour par défaut : 1 = journée, 0.5 = demi-journée)."),
|
|
989
|
+
workUnitTypeReference: z
|
|
894
990
|
.number()
|
|
895
991
|
.int()
|
|
896
|
-
.
|
|
897
|
-
.
|
|
898
|
-
|
|
899
|
-
|
|
992
|
+
.default(1)
|
|
993
|
+
.describe("Code `reference` du type d'unité d'œuvre — voir `boond_timesheets_default` (`workUnitTypesAllowed` de la ressource). " +
|
|
994
|
+
"1 = « Normale » (production) sur les tenants observés ; les absences (congés, RTT, maladie…) ont leurs propres codes."),
|
|
995
|
+
projectId: EntityIdSchema.optional().describe("ID du projet imputé. Requis pour une activité de production ; absent pour une absence. Couples autorisés : `boond_timesheets_default`."),
|
|
996
|
+
deliveryId: EntityIdSchema.optional().describe("ID de la prestation (delivery) imputée. Requis avec `projectId` pour la production — voir `boond_timesheets_default`."),
|
|
997
|
+
batchId: EntityIdSchema.optional().describe("ID du lot. Absent = aucun lot."),
|
|
998
|
+
})
|
|
999
|
+
.strict();
|
|
1000
|
+
export const TimesheetCreateSchema = z
|
|
1001
|
+
.object({
|
|
1002
|
+
resourceId: EntityIdSchema.describe("ID de la ressource (le collaborateur dont c'est le CRA)."),
|
|
1003
|
+
agencyId: EntityIdSchema.optional().describe("ID de l'agence — voir `boond_timesheets_default`. Déduite de la ressource si omise."),
|
|
1004
|
+
term: z
|
|
1005
|
+
.string()
|
|
1006
|
+
.regex(/^\d{4}-\d{2}$/)
|
|
1007
|
+
.describe("Mois du CRA (YYYY-MM). Un CRA = un mois × une ressource ; l'API ne déduplique pas."),
|
|
1008
|
+
informationComments: z.string().optional().describe("Commentaires du CRA."),
|
|
1009
|
+
regularTimes: z
|
|
1010
|
+
.array(TimesheetLineSchema)
|
|
1011
|
+
.optional()
|
|
1012
|
+
.describe("Lignes d'activité normale (production et absences). Omettre pour créer un CRA vide."),
|
|
1013
|
+
exceptionalTimes: z
|
|
1014
|
+
.array(TimesheetLineSchema)
|
|
1015
|
+
.optional()
|
|
1016
|
+
.describe("Lignes d'activité exceptionnelle (interventions, astreintes) — types `exceptionalTime` / `exceptionalCalendar`."),
|
|
1017
|
+
})
|
|
1018
|
+
.strict();
|
|
1019
|
+
export const TimesheetUpdateSchema = z
|
|
1020
|
+
.object({
|
|
1021
|
+
id: EntityIdSchema.describe("ID du CRA à modifier"),
|
|
1022
|
+
informationComments: z.string().optional().describe("Commentaires"),
|
|
1023
|
+
closed: z.boolean().optional().describe("Clôturer le CRA"),
|
|
1024
|
+
regularTimes: z
|
|
1025
|
+
.array(TimesheetLineSchema)
|
|
1026
|
+
.optional()
|
|
1027
|
+
.describe("⚠️ Remplace TOUTES les lignes d'activité normale — envoyer la liste complète (relire le CRA avec `boond_timesheets_get` et fusionner), pas seulement les ajouts."),
|
|
1028
|
+
exceptionalTimes: z
|
|
1029
|
+
.array(TimesheetLineSchema)
|
|
1030
|
+
.optional()
|
|
1031
|
+
.describe("⚠️ Remplace TOUTES les lignes d'activité exceptionnelle — même règle que `regularTimes`."),
|
|
1032
|
+
})
|
|
1033
|
+
.strict();
|
|
1034
|
+
export const TimesheetDefaultSchema = z
|
|
1035
|
+
.object({
|
|
1036
|
+
resourceId: EntityIdSchema.describe("ID de la ressource"),
|
|
1037
|
+
term: z
|
|
1038
|
+
.string()
|
|
1039
|
+
.regex(/^\d{4}-\d{2}$/)
|
|
1040
|
+
.describe("Mois du CRA (YYYY-MM)"),
|
|
1041
|
+
agencyId: EntityIdSchema.optional().describe("ID de l'agence, si la ressource en a plusieurs"),
|
|
900
1042
|
})
|
|
901
1043
|
.strict();
|
|
902
1044
|
export const TimesheetGetSchema = z
|
|
903
1045
|
.object({
|
|
904
|
-
id:
|
|
1046
|
+
id: EntityIdSchema.describe("Identifiant unique de la feuille de temps"),
|
|
905
1047
|
})
|
|
906
1048
|
.strict();
|
|
907
1049
|
// ---- Project schemas ----
|
|
908
1050
|
export const ProjectCreateSchema = z
|
|
909
1051
|
.object({
|
|
910
1052
|
name: z.string().min(1).describe("Nom du projet / mission"),
|
|
911
|
-
companyId:
|
|
912
|
-
contactId:
|
|
913
|
-
opportunityId:
|
|
1053
|
+
companyId: EntityIdSchema.optional().describe("ID de la société cliente"),
|
|
1054
|
+
contactId: EntityIdSchema.optional().describe("ID du contact associé"),
|
|
1055
|
+
opportunityId: EntityIdSchema.optional().describe("ID de l'opportunité liée"),
|
|
914
1056
|
typeOf: z.number().int().optional().describe("Type de projet (ID du dictionnaire setting.typeOf.project)"),
|
|
915
1057
|
state: stateField("project", "État du projet (0=en cours, 1=terminé, 2=archivé...)"),
|
|
916
1058
|
startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
|
|
@@ -920,7 +1062,7 @@ export const ProjectCreateSchema = z
|
|
|
920
1062
|
.strict();
|
|
921
1063
|
export const ProjectUpdateSchema = z
|
|
922
1064
|
.object({
|
|
923
|
-
id:
|
|
1065
|
+
id: EntityIdSchema.describe("ID du projet à modifier"),
|
|
924
1066
|
name: z.string().optional().describe("Nom du projet"),
|
|
925
1067
|
typeOf: z.number().int().optional().describe("Type de projet (ID du dictionnaire setting.typeOf.project)"),
|
|
926
1068
|
state: stateField("project", "État du projet"),
|
|
@@ -930,13 +1072,29 @@ export const ProjectUpdateSchema = z
|
|
|
930
1072
|
})
|
|
931
1073
|
.strict();
|
|
932
1074
|
// ---- Invoice schemas ----
|
|
1075
|
+
// API pattern is `^|YYYY-MM-DD$`: an empty string clears the date.
|
|
1076
|
+
const performedPaymentDateField = z
|
|
1077
|
+
.union([z.string().regex(/^\d{4}-\d{2}-\d{2}$/), z.literal("")])
|
|
1078
|
+
.optional()
|
|
1079
|
+
.describe("Date de règlement effectif (YYYY-MM-DD, `\"\"` pour l'effacer). À envoyer dans le même appel que le passage de `state` à l'état « payée » : sans elle, BoondManager date le règlement du jour du changement d'état. Si la date est encore écrasée, rappeler `boond_invoices_update` avec ce seul champ.");
|
|
1080
|
+
const invoicePaymentMethodField = z
|
|
1081
|
+
.number()
|
|
1082
|
+
.int()
|
|
1083
|
+
.min(-1)
|
|
1084
|
+
.optional()
|
|
1085
|
+
.describe("Mode de règlement : ID de `boond://dictionary/paymentMethods`");
|
|
933
1086
|
export const InvoiceCreateSchema = z
|
|
934
1087
|
.object({
|
|
935
1088
|
reference: z.string().optional().describe("Référence de la facture"),
|
|
936
|
-
orderId:
|
|
937
|
-
state:
|
|
1089
|
+
orderId: EntityIdSchema.optional().describe("ID du bon de commande associé"),
|
|
1090
|
+
state: stateField("invoice", "État de la facture : ID de `boond://dictionary/states/invoices` (ex: 0 = Création, 1 = Transmis au client)"),
|
|
938
1091
|
invoiceDate: z.string().optional().describe("Date de facturation (YYYY-MM-DD)"),
|
|
939
|
-
expectedPaymentDate: z
|
|
1092
|
+
expectedPaymentDate: z
|
|
1093
|
+
.string()
|
|
1094
|
+
.optional()
|
|
1095
|
+
.describe("Date d'échéance (YYYY-MM-DD) — pas la date de règlement, voir `performedPaymentDate`"),
|
|
1096
|
+
performedPaymentDate: performedPaymentDateField,
|
|
1097
|
+
paymentMethod: invoicePaymentMethodField,
|
|
940
1098
|
amountExcludingTax: z.number().optional().describe("Montant HT"),
|
|
941
1099
|
taxRate: z.number().optional().describe("Taux de TVA (%)"),
|
|
942
1100
|
invoiceRecords: z.array(z.record(z.string(), z.unknown())).optional().describe("Lignes de facture Boond"),
|
|
@@ -946,11 +1104,16 @@ export const InvoiceCreateSchema = z
|
|
|
946
1104
|
.strict();
|
|
947
1105
|
export const InvoiceUpdateSchema = z
|
|
948
1106
|
.object({
|
|
949
|
-
id:
|
|
1107
|
+
id: EntityIdSchema.describe("ID de la facture à modifier"),
|
|
950
1108
|
reference: z.string().optional().describe("Référence de la facture"),
|
|
951
|
-
state:
|
|
1109
|
+
state: stateField("invoice", "État de la facture : ID de `boond://dictionary/states/invoices` (ex: 0 = Création, 1 = Transmis au client)"),
|
|
952
1110
|
invoiceDate: z.string().optional().describe("Date de facturation (YYYY-MM-DD)"),
|
|
953
|
-
expectedPaymentDate: z
|
|
1111
|
+
expectedPaymentDate: z
|
|
1112
|
+
.string()
|
|
1113
|
+
.optional()
|
|
1114
|
+
.describe("Date d'échéance (YYYY-MM-DD) — pas la date de règlement, voir `performedPaymentDate`"),
|
|
1115
|
+
performedPaymentDate: performedPaymentDateField,
|
|
1116
|
+
paymentMethod: invoicePaymentMethodField,
|
|
954
1117
|
amountExcludingTax: z.number().optional().describe("Montant HT"),
|
|
955
1118
|
taxRate: z.number().optional().describe("Taux de TVA (%)"),
|
|
956
1119
|
invoiceRecords: z.array(z.record(z.string(), z.unknown())).optional().describe("Lignes de facture Boond"),
|
|
@@ -958,29 +1121,42 @@ export const InvoiceUpdateSchema = z
|
|
|
958
1121
|
note: z.string().optional().describe("Notes"),
|
|
959
1122
|
})
|
|
960
1123
|
.strict();
|
|
1124
|
+
// Source: resources/invoices/search.raml (filters verified live, issue #248).
|
|
961
1125
|
export const InvoiceSearchSchema = z
|
|
962
1126
|
.object({
|
|
963
1127
|
keywords: z.string().optional().describe("Mots-clés de recherche (référence, société...)"),
|
|
964
|
-
companyId:
|
|
965
|
-
projectId:
|
|
966
|
-
|
|
967
|
-
|
|
1128
|
+
companyId: EntityIdSchema.optional().describe("Filtrer par ID société (référence keywords CSOC<id>)"),
|
|
1129
|
+
projectId: EntityIdSchema.optional().describe("Filtrer par ID projet (référence keywords PRJ<id>)"),
|
|
1130
|
+
contactId: EntityIdSchema.optional().describe("Filtrer par ID contact (référence keywords CCON<id>)"),
|
|
1131
|
+
orderId: EntityIdSchema.optional().describe("Filtrer par ID bon de commande (référence keywords BDC<id>)"),
|
|
1132
|
+
states: intArray("IDs d'états de facture — `boond://dictionary/states/invoices` (ex. impayées = tous les états sauf « payée »)."),
|
|
1133
|
+
projectTypes: projectTypesFilterField,
|
|
1134
|
+
paymentMethods: paymentMethodsField,
|
|
1135
|
+
companies: companiesFilterField,
|
|
1136
|
+
closed: z
|
|
1137
|
+
.boolean()
|
|
1138
|
+
.optional()
|
|
1139
|
+
.describe("true = factures / avoirs clôturés uniquement, false = non clôturés uniquement."),
|
|
1140
|
+
creditNote: z.boolean().optional().describe("true = avoirs uniquement, false = factures uniquement."),
|
|
968
1141
|
period: z
|
|
969
|
-
.
|
|
1142
|
+
.enum(["created", "updated", "expectedPayment", "performedPayment", "period"])
|
|
970
1143
|
.optional()
|
|
971
|
-
.describe("
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1144
|
+
.describe("Champ de date borné par `startDate` / `endDate` : created, updated, expectedPayment (échéance), performedPayment (règlement), period (période facturée, défaut)."),
|
|
1145
|
+
periodDynamic: periodDynamicField,
|
|
1146
|
+
startDate: startDateField,
|
|
1147
|
+
endDate: endDateField,
|
|
1148
|
+
flags: flagsField,
|
|
1149
|
+
...perimeterShape,
|
|
1150
|
+
...sortedPaginationShape,
|
|
975
1151
|
})
|
|
976
1152
|
.strict();
|
|
977
1153
|
// ---- Order schemas (Bons de commande) ----
|
|
978
1154
|
export const OrderCreateSchema = z
|
|
979
1155
|
.object({
|
|
980
1156
|
reference: z.string().optional().describe("Référence du bon de commande"),
|
|
981
|
-
companyId:
|
|
982
|
-
projectId:
|
|
983
|
-
state:
|
|
1157
|
+
companyId: EntityIdSchema.optional().describe("ID de la société"),
|
|
1158
|
+
projectId: EntityIdSchema.optional().describe("ID du projet associé"),
|
|
1159
|
+
state: stateField("order", "État du bon de commande : ID de `boond://dictionary/states/orders`"),
|
|
984
1160
|
orderDate: z.string().optional().describe("Date du bon de commande (YYYY-MM-DD)"),
|
|
985
1161
|
startDate: z.string().optional().describe("Date de début couverte (YYYY-MM-DD)"),
|
|
986
1162
|
endDate: z.string().optional().describe("Date de fin couverte (YYYY-MM-DD)"),
|
|
@@ -992,9 +1168,9 @@ export const OrderCreateSchema = z
|
|
|
992
1168
|
.strict();
|
|
993
1169
|
export const OrderUpdateSchema = z
|
|
994
1170
|
.object({
|
|
995
|
-
id:
|
|
1171
|
+
id: EntityIdSchema.describe("ID du bon de commande à modifier"),
|
|
996
1172
|
reference: z.string().optional().describe("Référence"),
|
|
997
|
-
state:
|
|
1173
|
+
state: stateField("order", "État du bon de commande : ID de `boond://dictionary/states/orders`"),
|
|
998
1174
|
orderDate: z.string().optional().describe("Date (YYYY-MM-DD)"),
|
|
999
1175
|
startDate: z.string().optional().describe("Date de début couverte (YYYY-MM-DD)"),
|
|
1000
1176
|
endDate: z.string().optional().describe("Date de fin couverte (YYYY-MM-DD)"),
|
|
@@ -1004,61 +1180,197 @@ export const OrderUpdateSchema = z
|
|
|
1004
1180
|
note: z.string().optional().describe("Notes"),
|
|
1005
1181
|
})
|
|
1006
1182
|
.strict();
|
|
1183
|
+
// Source: resources/orders/search.raml (filters verified live, issue #248).
|
|
1007
1184
|
export const OrderSearchSchema = z
|
|
1008
1185
|
.object({
|
|
1009
1186
|
keywords: z.string().optional().describe("Mots-clés de recherche"),
|
|
1010
|
-
companyId:
|
|
1011
|
-
projectId:
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1187
|
+
companyId: EntityIdSchema.optional().describe("Filtrer par ID société (référence keywords CSOC<id>)"),
|
|
1188
|
+
projectId: EntityIdSchema.optional().describe("Filtrer par ID projet (référence keywords PRJ<id>)"),
|
|
1189
|
+
contactId: EntityIdSchema.optional().describe("Filtrer par ID contact (référence keywords CCON<id>)"),
|
|
1190
|
+
states: intArray("IDs d'états de bon de commande — `boond://dictionary/states/orders`."),
|
|
1191
|
+
projectTypes: projectTypesFilterField,
|
|
1192
|
+
paymentMethods: paymentMethodsField,
|
|
1193
|
+
companies: companiesFilterField,
|
|
1194
|
+
customerAgreement: z.boolean().optional().describe("true = accord client reçu uniquement, false = sans accord."),
|
|
1195
|
+
exceededOrderedTurnover: z
|
|
1196
|
+
.boolean()
|
|
1197
|
+
.optional()
|
|
1198
|
+
.describe("true = commandes dont le facturé dépasse le montant commandé."),
|
|
1199
|
+
period: z
|
|
1200
|
+
.enum(["created", "updated", "period"])
|
|
1201
|
+
.optional()
|
|
1202
|
+
.describe("Champ de date borné par `startDate` / `endDate` : created, updated, period (période couverte)."),
|
|
1203
|
+
periodDynamic: periodDynamicField,
|
|
1204
|
+
startDate: startDateField,
|
|
1205
|
+
endDate: endDateField,
|
|
1206
|
+
flags: flagsField,
|
|
1207
|
+
...perimeterShape,
|
|
1208
|
+
...sortedPaginationShape,
|
|
1015
1209
|
})
|
|
1016
1210
|
.strict();
|
|
1017
1211
|
// ---- Delivery schemas (Livraisons / CRA) ----
|
|
1212
|
+
// Source: resources/deliveriesGroupments/search.raml — the list route of the
|
|
1213
|
+
// deliveries domain (filters verified live, issue #248).
|
|
1018
1214
|
export const DeliverySearchSchema = z
|
|
1019
1215
|
.object({
|
|
1020
1216
|
keywords: z.string().optional().describe("Mots-clés de recherche"),
|
|
1021
|
-
projectId:
|
|
1022
|
-
companyId:
|
|
1217
|
+
projectId: EntityIdSchema.optional().describe("Filtrer par ID projet (référence keywords PRJ<id>)"),
|
|
1218
|
+
companyId: EntityIdSchema.optional().describe("Filtrer par ID société (référence keywords CSOC<id>)"),
|
|
1219
|
+
resourceId: EntityIdSchema.optional().describe("Filtrer par ID ressource (référence keywords COMP<id>)"),
|
|
1220
|
+
opportunityId: EntityIdSchema.optional().describe("Filtrer par ID opportunité (référence keywords AO<id>)"),
|
|
1221
|
+
projectTypes: projectTypesFilterField,
|
|
1222
|
+
projectStates: intArray("IDs d'états de projet — `boond://dictionary/states/projects`."),
|
|
1223
|
+
deliveryStates: intArray("IDs d'états de prestation — `boond://dictionary/states/deliveries`."),
|
|
1224
|
+
expertiseAreas: strArray("Domaines d'expertise — `boond://dictionary/expertiseAreas`."),
|
|
1225
|
+
transferType: z
|
|
1226
|
+
.enum(["master", "slave", "none", "notSlave", "notMaster", "missing"])
|
|
1227
|
+
.optional()
|
|
1228
|
+
.describe("Filtre sur les transferts de prestation (master / slave / none / notSlave / notMaster / missing)."),
|
|
1229
|
+
period: z
|
|
1230
|
+
.enum(["started", "updated", "stopped", "projectRunning", "running", "hasAdditionalDataOrPurchase"])
|
|
1231
|
+
.optional()
|
|
1232
|
+
.describe("Champ de date borné par `startDate` / `endDate` : started, stopped, updated, running (prestations en cours sur la fenêtre), projectRunning (projet en cours)."),
|
|
1233
|
+
periodDynamic: periodDynamicField,
|
|
1234
|
+
startDate: startDateField,
|
|
1235
|
+
endDate: endDateField,
|
|
1236
|
+
companies: companiesFilterField,
|
|
1237
|
+
flags: flagsField,
|
|
1238
|
+
...perimeterShape,
|
|
1239
|
+
...sortedPaginationShape,
|
|
1240
|
+
})
|
|
1241
|
+
.strict();
|
|
1242
|
+
export const DeliveryCreateSchema = z
|
|
1243
|
+
.object({
|
|
1244
|
+
projectId: EntityIdSchema.describe("ID du projet"),
|
|
1245
|
+
resourceId: EntityIdSchema.describe("ID de la ressource portée par la prestation"),
|
|
1246
|
+
title: z.string().optional().describe("Titre de la prestation/livraison"),
|
|
1247
|
+
typeOf: z.number().int().optional().describe("Type de prestation : ID de `boond://dictionary/typeOf/deliveries`"),
|
|
1248
|
+
state: stateField("delivery", "État de la prestation : ID de `boond://dictionary/states/deliveries`"),
|
|
1023
1249
|
startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
|
|
1024
1250
|
endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1251
|
+
quantity: z.number().optional().describe("Nombre de jours / quantité"),
|
|
1252
|
+
unitPrice: z.number().optional().describe("Prix journalier HT"),
|
|
1253
|
+
averageDailyCost: z.number().optional().describe("Coût journalier moyen"),
|
|
1254
|
+
forceAverageDailyPriceExcludingTax: z.boolean().optional().describe("Forcer le prix journalier HT"),
|
|
1255
|
+
note: z.string().optional().describe("Notes, mappées vers informationComments"),
|
|
1256
|
+
})
|
|
1257
|
+
.strict();
|
|
1258
|
+
// PUT /deliveries/{id} — attributes only, the project / resource attachment
|
|
1259
|
+
// is not moved by an update (issue #252; write path not exercised, see CLAUDE.md).
|
|
1260
|
+
export const DeliveryUpdateSchema = z
|
|
1261
|
+
.object({
|
|
1262
|
+
id: EntityIdSchema.describe("ID de la prestation à modifier"),
|
|
1263
|
+
title: z.string().optional().describe("Titre de la prestation"),
|
|
1264
|
+
typeOf: z.number().int().optional().describe("Type de prestation : ID de `boond://dictionary/typeOf/deliveries`"),
|
|
1265
|
+
state: stateField("delivery", "État de la prestation : ID de `boond://dictionary/states/deliveries`"),
|
|
1266
|
+
startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
|
|
1267
|
+
endDate: z
|
|
1268
|
+
.string()
|
|
1269
|
+
.optional()
|
|
1270
|
+
.describe("Date de fin (YYYY-MM-DD) — prolonger une prestation = repousser cette date"),
|
|
1271
|
+
quantity: z.number().optional().describe("Nombre de jours / quantité (`numberOfDaysInvoicedOrQuantity`)"),
|
|
1272
|
+
unitPrice: z.number().optional().describe("Prix journalier HT (`averageDailyPriceExcludingTax`, forcé)"),
|
|
1273
|
+
averageDailyCost: z.number().optional().describe("Coût journalier moyen"),
|
|
1274
|
+
note: z.string().optional().describe("Notes, mappées vers informationComments"),
|
|
1028
1275
|
})
|
|
1029
1276
|
.strict();
|
|
1030
1277
|
// ---- Absence schemas ----
|
|
1278
|
+
/** `GET /absences-reports/default?resource=&agency=` (issue #257) — the absence types are published there only. */
|
|
1279
|
+
export const AbsenceDefaultSchema = z
|
|
1280
|
+
.object({
|
|
1281
|
+
resourceId: EntityIdSchema.describe("ID de la ressource (requis par l'API)."),
|
|
1282
|
+
agencyId: EntityIdSchema.optional().describe("ID de l'agence — déduite de la ressource si omise."),
|
|
1283
|
+
})
|
|
1284
|
+
.strict();
|
|
1031
1285
|
export const AbsenceCreateSchema = z
|
|
1032
1286
|
.object({
|
|
1033
|
-
resourceId:
|
|
1034
|
-
typeOf: z
|
|
1287
|
+
resourceId: EntityIdSchema.describe("ID de la ressource en absence"),
|
|
1288
|
+
typeOf: z
|
|
1289
|
+
.string()
|
|
1290
|
+
.min(1)
|
|
1291
|
+
.describe("Libellé de l'absence (congé payé, RTT, maladie, sans solde...) — porté par `title` ; le type effectif est `workUnitTypeReference`."),
|
|
1035
1292
|
startDate: z.string().min(1).describe("Date de début (YYYY-MM-DD)"),
|
|
1036
1293
|
endDate: z.string().min(1).describe("Date de fin (YYYY-MM-DD)"),
|
|
1037
1294
|
duration: z.number().optional().describe("Durée en jours ; calculée automatiquement si absente"),
|
|
1038
|
-
workUnitTypeReference: z
|
|
1295
|
+
workUnitTypeReference: z
|
|
1296
|
+
.number()
|
|
1297
|
+
.int()
|
|
1298
|
+
.min(1)
|
|
1299
|
+
.optional()
|
|
1300
|
+
.describe("Code `reference` du type d'absence, publié par `boond_absences_default` (`workUnitTypesAllowed` de la ressource : RTT, maladie, congés payés…) — nulle part ailleurs. Défaut 1 (à confirmer avec le default)."),
|
|
1039
1301
|
absencesPeriods: z.array(z.record(z.string(), z.unknown())).optional().describe("Périodes d'absence Boond brutes"),
|
|
1040
|
-
state
|
|
1302
|
+
// No `state` (issue #250): on `/absences-reports` it is a validation-workflow
|
|
1303
|
+
// string (`waitingForValidation`, `validated`…), not a dictionary integer,
|
|
1304
|
+
// and it is moved by the workflow — same finding as `/expenses-reports`.
|
|
1041
1305
|
note: z.string().optional().describe("Commentaire / motif"),
|
|
1042
1306
|
})
|
|
1043
1307
|
.strict();
|
|
1044
1308
|
export const AbsenceUpdateSchema = z
|
|
1045
1309
|
.object({
|
|
1046
|
-
id:
|
|
1310
|
+
id: EntityIdSchema.describe("ID de l'absence à modifier"),
|
|
1047
1311
|
startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
|
|
1048
1312
|
endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
|
|
1049
|
-
state: z.number().int().optional().describe("État de la demande"),
|
|
1050
1313
|
note: z.string().optional().describe("Commentaire / motif"),
|
|
1051
1314
|
})
|
|
1052
1315
|
.strict();
|
|
1053
1316
|
export const AbsenceSearchSchema = z
|
|
1054
1317
|
.object({
|
|
1055
1318
|
keywords: z.string().optional().describe("Mots-clés de recherche"),
|
|
1056
|
-
resourceId:
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1319
|
+
resourceId: EntityIdSchema.optional().describe("Filtrer par ID ressource (référence keywords COMP<id>)"),
|
|
1320
|
+
// Both are REQUIRED by `GET /absences-reports` (422 `1017 - Missing required
|
|
1321
|
+
// attribute` otherwise — caught by scripts/smoke-live.mjs, issue #244).
|
|
1322
|
+
// Declaring them optional made the tool fail on its most natural call.
|
|
1323
|
+
startMonth: z
|
|
1324
|
+
.string()
|
|
1325
|
+
.regex(/^\d{4}-\d{2}$/, "Format attendu : YYYY-MM")
|
|
1326
|
+
.describe("Mois de début de période (YYYY-MM) — obligatoire pour l'API"),
|
|
1327
|
+
endMonth: z
|
|
1328
|
+
.string()
|
|
1329
|
+
.regex(/^\d{4}-\d{2}$/, "Format attendu : YYYY-MM")
|
|
1330
|
+
.describe("Mois de fin de période (YYYY-MM) — obligatoire pour l'API"),
|
|
1331
|
+
resourceTypes: resourceTypesField,
|
|
1332
|
+
validationStates: validationStatesField,
|
|
1333
|
+
...perimeterShape,
|
|
1334
|
+
...sortedPaginationShape,
|
|
1335
|
+
})
|
|
1336
|
+
.strict();
|
|
1337
|
+
// ---- Contract search (issue #253) ----
|
|
1338
|
+
// There is no collection GET on `/contracts` (WAF 403 page, the RAML only
|
|
1339
|
+
// documents `post`) and no `/resources/{id}/contracts` tab (404) — probed live
|
|
1340
|
+
// on 2026-09-26. A resource's contracts are only reachable through
|
|
1341
|
+
// `/resources/{id}/administrative` (`included` of type `contract`). This schema
|
|
1342
|
+
// therefore drives a *composed* search: the resource filters select the
|
|
1343
|
+
// resources to scan, the contract filters are applied server-side to what
|
|
1344
|
+
// their administrative tabs return.
|
|
1345
|
+
export const ContractSearchSchema = z
|
|
1346
|
+
.object({
|
|
1347
|
+
resourceId: EntityIdSchema.optional().describe("ID d'une ressource : ne lit que ses contrats (aucune recherche de ressources)."),
|
|
1348
|
+
keywords: z.string().optional().describe("Mots-clés de la recherche de ressources (nom, prénom…)."),
|
|
1349
|
+
resourceStates: intArray("IDs d'états de ressource à parcourir — `boond://dictionary/states/resources`."),
|
|
1350
|
+
resourceTypes: resourceTypesField,
|
|
1351
|
+
...perimeterShape,
|
|
1352
|
+
contractTypes: intArray("IDs de types de contrat à retenir — `boond://dictionary/typeOf/contracts` (CDI, CDD…)."),
|
|
1353
|
+
period: z
|
|
1354
|
+
.enum(["running", "ending", "starting", "probationEnding"])
|
|
1355
|
+
.optional()
|
|
1356
|
+
.describe("Fenêtre `startDate` / `endDate` appliquée côté serveur : running (contrat en cours sur la fenêtre), ending (fin de contrat dans la fenêtre), starting (début dans la fenêtre), probationEnding (fin de période d'essai — `probationEndDate` ou `renewalProbationEndDate` — dans la fenêtre)."),
|
|
1357
|
+
startDate: startDateField,
|
|
1358
|
+
endDate: endDateField,
|
|
1359
|
+
...paginationShape,
|
|
1360
|
+
})
|
|
1361
|
+
.strict();
|
|
1362
|
+
// ---- Validation decision (issue #251) ----
|
|
1363
|
+
// `PUT /validations/{id}` with the workflow state the RAML enumerates on
|
|
1364
|
+
// `validationStates` (`validated` | `rejected`). No RAML documents the write;
|
|
1365
|
+
// the route follows the pattern of every other single-entity PUT and has NOT
|
|
1366
|
+
// been exercised (production tenant) — see CLAUDE.md → *Validations*.
|
|
1367
|
+
export const ValidationDecisionSchema = z
|
|
1368
|
+
.object({
|
|
1369
|
+
id: EntityIdSchema.describe("ID de la validation (ligne de `boond_validations_search`), pas celui du CRA / de la note / de l'absence."),
|
|
1370
|
+
decision: z
|
|
1371
|
+
.enum(["validate", "reject"])
|
|
1372
|
+
.describe("validate → `validated` ; reject → `rejected` (confirmation demandée à l'utilisateur si le client la supporte)."),
|
|
1373
|
+
reason: z.string().optional().describe("Motif, recommandé sur un refus — transmis dans `reason`."),
|
|
1062
1374
|
})
|
|
1063
1375
|
.strict();
|
|
1064
1376
|
// ---- Expense schemas (Notes de frais) ----
|
|
@@ -1128,15 +1440,9 @@ export const ExpenseLineSchema = z
|
|
|
1128
1440
|
.describe("`true` pour un frais kilométrique : renseigner `numberOfKilometers` et omettre `expenseTypeReference`."),
|
|
1129
1441
|
numberOfKilometers: z.number().optional().describe("Nombre de kilomètres (frais kilométrique uniquement)."),
|
|
1130
1442
|
activityType: z.enum(EXPENSE_ACTIVITY_TYPES).default("production").describe("Type d'activité rattachée au frais."),
|
|
1131
|
-
projectId:
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
.describe("ID du projet à imputer. Obligatoire — les couples (projet, prestation) autorisés sont donnés par `boond_expenses_default`."),
|
|
1135
|
-
deliveryId: z
|
|
1136
|
-
.string()
|
|
1137
|
-
.min(1)
|
|
1138
|
-
.describe("ID de la prestation (delivery) à imputer. Obligatoire — voir `boond_expenses_default`."),
|
|
1139
|
-
batchId: z.string().optional().describe("ID du lot. Absent = aucun lot."),
|
|
1443
|
+
projectId: EntityIdSchema.describe("ID du projet à imputer. Obligatoire — les couples (projet, prestation) autorisés sont donnés par `boond_expenses_default`."),
|
|
1444
|
+
deliveryId: EntityIdSchema.describe("ID de la prestation (delivery) à imputer. Obligatoire — voir `boond_expenses_default`."),
|
|
1445
|
+
batchId: EntityIdSchema.optional().describe("ID du lot. Absent = aucun lot."),
|
|
1140
1446
|
reinvoiced: z.boolean().default(false).describe("Frais refacturable au client."),
|
|
1141
1447
|
currency: z.number().int().default(0).describe("ID de devise (`setting.currency`, 0 = EUR)."),
|
|
1142
1448
|
exchangeRate: z.number().default(1).describe("Taux de change vers la devise agence."),
|
|
@@ -1150,11 +1456,8 @@ export const ExpenseLineSchema = z
|
|
|
1150
1456
|
.strict();
|
|
1151
1457
|
export const ExpenseCreateSchema = z
|
|
1152
1458
|
.object({
|
|
1153
|
-
resourceId:
|
|
1154
|
-
agencyId:
|
|
1155
|
-
.string()
|
|
1156
|
-
.optional()
|
|
1157
|
-
.describe("ID de l'agence — voir `boond_expenses_default`. Déduit de la ressource si omis."),
|
|
1459
|
+
resourceId: EntityIdSchema.describe("ID de la ressource (le collaborateur qui a engagé les frais)."),
|
|
1460
|
+
agencyId: EntityIdSchema.optional().describe("ID de l'agence — voir `boond_expenses_default`. Déduit de la ressource si omis."),
|
|
1158
1461
|
term: z
|
|
1159
1462
|
.string()
|
|
1160
1463
|
.regex(/^\d{4}-\d{2}$/)
|
|
@@ -1176,7 +1479,7 @@ export const ExpenseCreateSchema = z
|
|
|
1176
1479
|
.strict();
|
|
1177
1480
|
export const ExpenseUpdateSchema = z
|
|
1178
1481
|
.object({
|
|
1179
|
-
id:
|
|
1482
|
+
id: EntityIdSchema.describe("ID de la note de frais à modifier"),
|
|
1180
1483
|
exchangeRateAgency: z.number().optional().describe("Taux de change agence"),
|
|
1181
1484
|
currencyAgency: z.number().int().optional().describe("ID de devise de l'agence"),
|
|
1182
1485
|
informationComments: z.string().optional().describe("Commentaires"),
|
|
@@ -1192,24 +1495,22 @@ export const ExpenseUpdateSchema = z
|
|
|
1192
1495
|
.strict();
|
|
1193
1496
|
export const ExpenseDefaultSchema = z
|
|
1194
1497
|
.object({
|
|
1195
|
-
resourceId:
|
|
1498
|
+
resourceId: EntityIdSchema.describe("ID de la ressource"),
|
|
1196
1499
|
term: z
|
|
1197
1500
|
.string()
|
|
1198
1501
|
.regex(/^\d{4}-\d{2}$/)
|
|
1199
1502
|
.describe("Mois ciblé (YYYY-MM)"),
|
|
1200
|
-
agencyId:
|
|
1503
|
+
agencyId: EntityIdSchema.optional().describe("ID de l'agence (optionnel — déduit de la ressource)"),
|
|
1201
1504
|
})
|
|
1202
1505
|
.strict();
|
|
1203
1506
|
export const ExpenseSearchSchema = z
|
|
1204
1507
|
.object({
|
|
1205
1508
|
keywords: z.string().optional().describe("Mots-clés de recherche"),
|
|
1206
|
-
resourceId:
|
|
1207
|
-
projectId:
|
|
1509
|
+
resourceId: EntityIdSchema.optional().describe("Filtrer par ID ressource (référence keywords COMP<id>)"),
|
|
1510
|
+
projectId: EntityIdSchema.optional().describe("Filtrer par ID projet (référence keywords PRJ<id>)"),
|
|
1208
1511
|
startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
|
|
1209
1512
|
endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
|
|
1210
|
-
|
|
1211
|
-
pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE).describe("Résultats par page"),
|
|
1212
|
-
fields: fieldsField,
|
|
1513
|
+
...paginationShape,
|
|
1213
1514
|
})
|
|
1214
1515
|
.strict();
|
|
1215
1516
|
// ---- Product schemas ----
|
|
@@ -1225,7 +1526,7 @@ export const ProductCreateSchema = z
|
|
|
1225
1526
|
.strict();
|
|
1226
1527
|
export const ProductUpdateSchema = z
|
|
1227
1528
|
.object({
|
|
1228
|
-
id:
|
|
1529
|
+
id: EntityIdSchema.describe("ID du produit à modifier"),
|
|
1229
1530
|
name: z.string().optional().describe("Nom du produit"),
|
|
1230
1531
|
reference: z.string().optional().describe("Référence"),
|
|
1231
1532
|
unitPrice: z.number().optional().describe("Prix unitaire HT"),
|
|
@@ -1237,11 +1538,11 @@ export const ProductUpdateSchema = z
|
|
|
1237
1538
|
// ---- Positioning schemas ----
|
|
1238
1539
|
export const PositioningCreateSchema = z
|
|
1239
1540
|
.object({
|
|
1240
|
-
candidateId:
|
|
1241
|
-
resourceId:
|
|
1242
|
-
projectId:
|
|
1243
|
-
opportunityId:
|
|
1244
|
-
state:
|
|
1541
|
+
candidateId: EntityIdSchema.optional().describe("ID du candidat positionné"),
|
|
1542
|
+
resourceId: EntityIdSchema.optional().describe("ID de la ressource positionnée"),
|
|
1543
|
+
projectId: EntityIdSchema.optional().describe("ID du projet"),
|
|
1544
|
+
opportunityId: EntityIdSchema.optional().describe("ID de l'opportunité"),
|
|
1545
|
+
state: stateField("positioning", "État du positionnement : ID de `boond://dictionary/states/positionings` (`won` rattache le positionnement à un projet)"),
|
|
1245
1546
|
startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
|
|
1246
1547
|
endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
|
|
1247
1548
|
note: z.string().optional().describe("Notes / commentaires"),
|
|
@@ -1253,61 +1554,403 @@ export const PositioningSearchSchema = z
|
|
|
1253
1554
|
.string()
|
|
1254
1555
|
.optional()
|
|
1255
1556
|
.describe("Mots-clés de recherche. L'API y accepte aussi des références d'entités (AO<id>, CAND<id>, COMP<id>...) — les filtres *Id ci-dessous sont convertis automatiquement en de telles références."),
|
|
1256
|
-
candidateId:
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1557
|
+
candidateId: EntityIdSchema.optional().describe("Filtrer par ID candidat (envoyé à l'API comme référence keywords CAND<id>)"),
|
|
1558
|
+
resourceId: EntityIdSchema.optional().describe("Filtrer par ID ressource (envoyé à l'API comme référence keywords COMP<id>)"),
|
|
1559
|
+
opportunityId: EntityIdSchema.optional().describe("Filtrer par ID opportunité (envoyé à l'API comme référence keywords AO<id>)"),
|
|
1560
|
+
companyId: EntityIdSchema.optional().describe("Filtrer par ID société (envoyé à l'API comme référence keywords CSOC<id>)"),
|
|
1561
|
+
contactId: EntityIdSchema.optional().describe("Filtrer par ID contact (envoyé à l'API comme référence keywords CCON<id>)"),
|
|
1562
|
+
productId: EntityIdSchema.optional().describe("Filtrer par ID produit (envoyé à l'API comme référence keywords PROD<id>)"),
|
|
1563
|
+
...paginationShape,
|
|
1564
|
+
})
|
|
1565
|
+
.strict();
|
|
1566
|
+
// ---- Payment schemas ----
|
|
1567
|
+
// Source: resources/payments/search.raml (filters verified live, issue #248).
|
|
1568
|
+
export const PaymentSearchSchema = z
|
|
1569
|
+
.object({
|
|
1570
|
+
keywords: z.string().optional().describe("Mots-clés de recherche"),
|
|
1571
|
+
purchaseId: EntityIdSchema.optional().describe("Filtrer par ID achat (référence keywords ACH<id>)"),
|
|
1572
|
+
companyId: EntityIdSchema.optional().describe("Filtrer par ID société (référence keywords CSOC<id>)"),
|
|
1573
|
+
projectId: EntityIdSchema.optional().describe("Filtrer par ID projet (référence keywords PRJ<id>)"),
|
|
1574
|
+
resourceId: EntityIdSchema.optional().describe("Filtrer par ID ressource (référence keywords COMP<id>)"),
|
|
1575
|
+
contactId: EntityIdSchema.optional().describe("Filtrer par ID contact (référence keywords CCON<id>)"),
|
|
1576
|
+
paymentStates: intArray("IDs d'états de paiement — `boond://dictionary/states/payments`."),
|
|
1577
|
+
paymentMethods: paymentMethodsField,
|
|
1578
|
+
purchaseTypes: intArray("IDs de types d'achat — `boond://dictionary/typeOf/purchases`."),
|
|
1579
|
+
subscriptionTypes: intArray("IDs de types d'abonnement (`setting.typeOf.subscription` via `boond_application_dictionary`)."),
|
|
1580
|
+
period: z
|
|
1581
|
+
.enum(["created", "updated", "createdPurchase", "subscription", "expected", "performed", "billing"])
|
|
1270
1582
|
.optional()
|
|
1271
|
-
.describe("
|
|
1272
|
-
|
|
1273
|
-
|
|
1583
|
+
.describe("Champ de date borné par `startDate` / `endDate` : expected (échéance), performed (règlement effectif), created, updated, createdPurchase, subscription, billing."),
|
|
1584
|
+
periodDynamic: periodDynamicField,
|
|
1585
|
+
startDate: startDateField,
|
|
1586
|
+
endDate: endDateField,
|
|
1587
|
+
excludeProviderInvoice: z
|
|
1588
|
+
.boolean()
|
|
1274
1589
|
.optional()
|
|
1275
|
-
.describe("
|
|
1276
|
-
|
|
1277
|
-
|
|
1590
|
+
.describe("true = exclure les paiements adossés à une facture fournisseur."),
|
|
1591
|
+
deliveryPurchases: z.boolean().optional().describe("true = paiements des achats liés à une prestation uniquement."),
|
|
1592
|
+
flags: flagsField,
|
|
1593
|
+
...perimeterShape,
|
|
1594
|
+
...sortedPaginationShape,
|
|
1595
|
+
})
|
|
1596
|
+
.strict();
|
|
1597
|
+
export const PaymentCreateSchema = z
|
|
1598
|
+
.object({
|
|
1599
|
+
purchaseId: EntityIdSchema.describe("ID de l'achat réglé"),
|
|
1600
|
+
paymentDate: z.string().optional().describe("Date du paiement (YYYY-MM-DD), mappée vers date"),
|
|
1601
|
+
performedDate: z.string().optional().describe("Date de paiement effectif (YYYY-MM-DD)"),
|
|
1602
|
+
expectedDate: z.string().optional().describe("Date de paiement attendu (YYYY-MM-DD)"),
|
|
1603
|
+
startDate: z.string().optional().describe("Date de début couverte (YYYY-MM-DD)"),
|
|
1604
|
+
endDate: z.string().optional().describe("Date de fin couverte (YYYY-MM-DD)"),
|
|
1605
|
+
amount: z.number().optional().describe("Montant HT du paiement, mappé vers amountExcludingTax"),
|
|
1606
|
+
amountExcludingTax: z.number().optional().describe("Montant HT du paiement"),
|
|
1607
|
+
state: stateField("payment", "État du paiement : ID de `boond://dictionary/states/payments`"),
|
|
1608
|
+
paymentMethod: z.number().int().optional().describe("Mode de paiement : ID de `boond://dictionary/paymentMethods`"),
|
|
1609
|
+
taxRates: z.array(z.number()).optional().describe("Taux de taxes Boond"),
|
|
1610
|
+
reference: z.string().optional().describe("Référence bancaire ou règlement"),
|
|
1611
|
+
note: z.string().optional().describe("Note interne, mappée vers informationComments"),
|
|
1612
|
+
})
|
|
1613
|
+
.strict();
|
|
1614
|
+
// PUT /payments/{id} — issue #252 (write path not exercised, see CLAUDE.md).
|
|
1615
|
+
export const PaymentUpdateSchema = z
|
|
1616
|
+
.object({
|
|
1617
|
+
id: EntityIdSchema.describe("ID du paiement à modifier"),
|
|
1618
|
+
paymentDate: z.string().optional().describe("Date du paiement (YYYY-MM-DD), mappée vers date"),
|
|
1619
|
+
performedDate: z.string().optional().describe("Date de paiement effectif (YYYY-MM-DD) — régulariser un règlement"),
|
|
1620
|
+
expectedDate: z.string().optional().describe("Date de paiement attendu (YYYY-MM-DD)"),
|
|
1621
|
+
startDate: z.string().optional().describe("Date de début couverte (YYYY-MM-DD)"),
|
|
1622
|
+
endDate: z.string().optional().describe("Date de fin couverte (YYYY-MM-DD)"),
|
|
1623
|
+
amount: z.number().optional().describe("Montant HT, mappé vers amountExcludingTax"),
|
|
1624
|
+
amountExcludingTax: z.number().optional().describe("Montant HT"),
|
|
1625
|
+
amountIncludingTax: z.number().optional().describe("Montant TTC"),
|
|
1626
|
+
state: stateField("payment", "État du paiement : ID de `boond://dictionary/states/payments`"),
|
|
1627
|
+
paymentMethod: z.number().int().optional().describe("Mode de paiement : ID de `boond://dictionary/paymentMethods`"),
|
|
1628
|
+
taxRates: z.array(z.number()).optional().describe("Taux de taxes Boond"),
|
|
1629
|
+
reference: z.string().optional().describe("Référence bancaire ou règlement (`number`)"),
|
|
1630
|
+
note: z.string().optional().describe("Note interne, mappée vers informationComments"),
|
|
1631
|
+
})
|
|
1632
|
+
.strict();
|
|
1633
|
+
// ---- Purchase schemas (achats / sous-traitance) ----
|
|
1634
|
+
export const PurchaseSearchSchema = z
|
|
1635
|
+
.object({
|
|
1636
|
+
keywords: z.string().optional().describe("Mots-clés de recherche"),
|
|
1637
|
+
companyId: EntityIdSchema.optional().describe("Filtrer par ID société (référence keywords CSOC<id>)"),
|
|
1638
|
+
projectId: EntityIdSchema.optional().describe("Filtrer par ID projet (référence keywords PRJ<id>)"),
|
|
1639
|
+
...paginationShape,
|
|
1640
|
+
})
|
|
1641
|
+
.strict();
|
|
1642
|
+
export const PurchaseCreateSchema = z
|
|
1643
|
+
.object({
|
|
1644
|
+
title: z.string().optional().describe("Titre de l'achat/sous-traitance"),
|
|
1645
|
+
companyId: EntityIdSchema.optional().describe("ID de la société fournisseur"),
|
|
1646
|
+
contactId: EntityIdSchema.optional().describe("ID du contact fournisseur"),
|
|
1647
|
+
projectId: EntityIdSchema.optional().describe("ID du projet associé"),
|
|
1648
|
+
state: stateField("purchase", "État de l'achat : ID de `boond://dictionary/states/purchases`"),
|
|
1649
|
+
startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
|
|
1650
|
+
endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
|
|
1651
|
+
note: z.string().optional().describe("Notes / commentaires"),
|
|
1652
|
+
})
|
|
1653
|
+
.strict();
|
|
1654
|
+
// PUT /purchases/{id}/information (RAML `purchases/information.raml`, issue #252).
|
|
1655
|
+
export const PurchaseUpdateSchema = z
|
|
1656
|
+
.object({
|
|
1657
|
+
id: EntityIdSchema.describe("ID de l'achat à modifier"),
|
|
1658
|
+
title: z.string().optional().describe("Titre de l'achat/sous-traitance"),
|
|
1659
|
+
typeOf: z.number().int().optional().describe("Type d'achat : ID de `boond://dictionary/typeOf/purchases`"),
|
|
1660
|
+
state: stateField("purchase", "État de l'achat : ID de `boond://dictionary/states/purchases`"),
|
|
1661
|
+
companyId: EntityIdSchema.optional().describe("ID de la société fournisseur"),
|
|
1662
|
+
contactId: EntityIdSchema.optional().describe("ID du contact fournisseur"),
|
|
1663
|
+
projectId: EntityIdSchema.optional().describe("ID du projet associé"),
|
|
1664
|
+
startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
|
|
1665
|
+
endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
|
|
1666
|
+
quantity: z.number().optional().describe("Quantité"),
|
|
1667
|
+
amountExcludingTax: z.number().optional().describe("Montant unitaire HT"),
|
|
1668
|
+
taxRate: z.number().optional().describe("Taux de TVA (%) — `boond://dictionary/taxRates`"),
|
|
1669
|
+
paymentMethod: z.number().int().optional().describe("Mode de paiement : ID de `boond://dictionary/paymentMethods`"),
|
|
1670
|
+
paymentTerm: z
|
|
1671
|
+
.number()
|
|
1672
|
+
.int()
|
|
1278
1673
|
.optional()
|
|
1279
|
-
.describe("
|
|
1280
|
-
|
|
1281
|
-
pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE).describe("Résultats par page"),
|
|
1282
|
-
fields: fieldsField,
|
|
1674
|
+
.describe("Condition de paiement : ID de `boond://dictionary/paymentTerms`"),
|
|
1675
|
+
note: z.string().optional().describe("Notes / commentaires (`informationComments`)"),
|
|
1283
1676
|
})
|
|
1284
1677
|
.strict();
|
|
1285
|
-
// ----
|
|
1286
|
-
export const
|
|
1678
|
+
// ---- Provider invoice schemas (factures fournisseur) ----
|
|
1679
|
+
export const ProviderInvoiceSearchSchema = z
|
|
1287
1680
|
.object({
|
|
1288
1681
|
keywords: z.string().optional().describe("Mots-clés de recherche"),
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1682
|
+
companyId: EntityIdSchema.optional().describe("Filtrer par ID société fournisseur (référence keywords CSOC<id>)"),
|
|
1683
|
+
resourceId: EntityIdSchema.optional().describe("Filtrer par ID ressource (référence keywords COMP<id>)"),
|
|
1684
|
+
...paginationShape,
|
|
1685
|
+
})
|
|
1686
|
+
.strict();
|
|
1687
|
+
export const ProviderInvoiceCreateSchema = z
|
|
1688
|
+
.object({
|
|
1689
|
+
reference: z.string().min(1).describe("Référence de la facture fournisseur"),
|
|
1690
|
+
resourceId: EntityIdSchema.describe("ID de la ressource portée par la facture fournisseur"),
|
|
1691
|
+
companyId: EntityIdSchema.optional().describe("ID de la société fournisseur, mappé vers providerCompany"),
|
|
1692
|
+
contactId: EntityIdSchema.optional().describe("ID du contact fournisseur, mappé vers providerContact"),
|
|
1693
|
+
invoiceDate: z.string().optional().describe("Date de facture (YYYY-MM-DD)"),
|
|
1694
|
+
startDate: z.string().min(1).describe("Date de début de période (YYYY-MM-DD)"),
|
|
1695
|
+
endDate: z.string().min(1).describe("Date de fin de période (YYYY-MM-DD)"),
|
|
1696
|
+
amountExcludingTax: z.number().optional().describe("Montant HT"),
|
|
1697
|
+
amountIncludingTax: z.number().optional().describe("Montant TTC"),
|
|
1698
|
+
currency: z.number().optional().describe("Devise Boond"),
|
|
1699
|
+
exchangeRate: z.number().optional().describe("Taux de change"),
|
|
1700
|
+
currencyAgency: z.number().optional().describe("Devise agence"),
|
|
1701
|
+
exchangeRateAgency: z.number().optional().describe("Taux de change agence"),
|
|
1702
|
+
state: stateField("providerinvoice", "État de la facture fournisseur : ID de `boond://dictionary/states/provider-invoices`"),
|
|
1703
|
+
})
|
|
1704
|
+
.strict();
|
|
1705
|
+
// PUT /provider-invoices/{id} — issue #252 (write path not exercised, see CLAUDE.md).
|
|
1706
|
+
export const ProviderInvoiceUpdateSchema = z
|
|
1707
|
+
.object({
|
|
1708
|
+
id: EntityIdSchema.describe("ID de la facture fournisseur à modifier"),
|
|
1709
|
+
reference: z.string().optional().describe("Référence de la facture fournisseur"),
|
|
1710
|
+
invoiceDate: z.string().optional().describe("Date de facture (YYYY-MM-DD)"),
|
|
1711
|
+
startDate: z.string().optional().describe("Date de début de période (YYYY-MM-DD)"),
|
|
1712
|
+
endDate: z.string().optional().describe("Date de fin de période (YYYY-MM-DD)"),
|
|
1713
|
+
dueDate: z.string().optional().describe("Date d'échéance (YYYY-MM-DD)"),
|
|
1714
|
+
paidDate: z.string().optional().describe("Date de règlement (YYYY-MM-DD) — marquer la facture payée"),
|
|
1715
|
+
amountExcludingTax: z.number().optional().describe("Montant HT"),
|
|
1716
|
+
amountIncludingTax: z.number().optional().describe("Montant TTC"),
|
|
1717
|
+
state: stateField("providerinvoice", "État de la facture fournisseur : ID de `boond://dictionary/states/provider-invoices`"),
|
|
1718
|
+
})
|
|
1719
|
+
.strict();
|
|
1720
|
+
// ---- Contract schemas ----
|
|
1721
|
+
export const ContractCreateSchema = z
|
|
1722
|
+
.object({
|
|
1723
|
+
resourceId: EntityIdSchema.optional().describe("ID de la ressource associée"),
|
|
1724
|
+
typeOf: z
|
|
1725
|
+
.number()
|
|
1726
|
+
.int()
|
|
1727
|
+
.optional()
|
|
1728
|
+
.describe("Type de contrat : ID entier du dictionnaire `setting.typeOf.contract` (CDI, CDD, freelance…), via `boond_application_dictionary`"),
|
|
1294
1729
|
startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
|
|
1295
1730
|
endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1731
|
+
note: z.string().optional().describe("Notes / commentaires (`informationComments`)"),
|
|
1732
|
+
})
|
|
1733
|
+
.strict();
|
|
1734
|
+
// PUT /contracts/{id} — issue #252 (write path not exercised, see CLAUDE.md).
|
|
1735
|
+
export const ContractUpdateSchema = z
|
|
1736
|
+
.object({
|
|
1737
|
+
id: EntityIdSchema.describe("ID du contrat à modifier"),
|
|
1738
|
+
typeOf: z.number().int().optional().describe("Type de contrat : ID de `boond://dictionary/typeOf/contracts`"),
|
|
1739
|
+
startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
|
|
1740
|
+
endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD) — prolonger un CDD = repousser cette date"),
|
|
1741
|
+
endReason: z.number().int().optional().describe("Motif de fin : ID de `boond://dictionary/contractEndReasons`"),
|
|
1742
|
+
probationEndDate: z.string().optional().describe("Fin de période d'essai initiale (YYYY-MM-DD)"),
|
|
1743
|
+
renewalProbationEndDate: z.string().optional().describe("Fin de période d'essai renouvelée (YYYY-MM-DD)"),
|
|
1744
|
+
monthlySalary: z.number().optional().describe("Salaire mensuel"),
|
|
1745
|
+
annualSalary: z.number().optional().describe("Salaire annuel"),
|
|
1746
|
+
numberOfWorkingDays: z.number().optional().describe("Nombre de jours ouvrés annuel"),
|
|
1747
|
+
note: z.string().optional().describe("Notes / commentaires (`informationComments`)"),
|
|
1299
1748
|
})
|
|
1300
1749
|
.strict();
|
|
1301
1750
|
// ---- Advantage schemas ----
|
|
1751
|
+
// `GET /advantages` does not exist (the RAML only has POST there, and the live
|
|
1752
|
+
// API answers with a WAF 403 page); advantages are listed per resource via
|
|
1753
|
+
// `GET /resources/{id}/advantages` (issue #247). Hence a required `resourceId`.
|
|
1302
1754
|
export const AdvantageSearchSchema = z
|
|
1303
1755
|
.object({
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1756
|
+
resourceId: EntityIdSchema.describe("ID de la ressource dont on liste les avantages (route /resources/{id}/advantages)"),
|
|
1757
|
+
advantageTypes: strArray("Types d'avantage à conserver, au format `<reference>_<agencyId>` (référence du type + ID d'agence, ex: '2_1')."),
|
|
1758
|
+
...paginationShape,
|
|
1759
|
+
})
|
|
1760
|
+
.strict();
|
|
1761
|
+
// ---- Flags, attached flags, tasks, todolists, advantages writes (issue #254) ----
|
|
1762
|
+
/** Entities that carry `attachedFlags.raml` (11) — the query-parameter name of `DELETE /attached-flags` is the entity's singular. */
|
|
1763
|
+
export const ATTACHED_FLAG_ENTITIES = [
|
|
1764
|
+
"candidate",
|
|
1765
|
+
"resource",
|
|
1766
|
+
"contact",
|
|
1767
|
+
"company",
|
|
1768
|
+
"opportunity",
|
|
1769
|
+
"project",
|
|
1770
|
+
"order",
|
|
1771
|
+
"product",
|
|
1772
|
+
"purchase",
|
|
1773
|
+
"action",
|
|
1774
|
+
"positioning",
|
|
1775
|
+
"invoice",
|
|
1776
|
+
];
|
|
1777
|
+
/** Entities that carry `tasks.raml` (14). */
|
|
1778
|
+
export const TASK_ENTITIES = [
|
|
1779
|
+
"candidate",
|
|
1780
|
+
"resource",
|
|
1781
|
+
"contact",
|
|
1782
|
+
"company",
|
|
1783
|
+
"opportunity",
|
|
1784
|
+
"project",
|
|
1785
|
+
"order",
|
|
1786
|
+
"product",
|
|
1787
|
+
"purchase",
|
|
1788
|
+
"positioning",
|
|
1789
|
+
"invoice",
|
|
1790
|
+
"contract",
|
|
1791
|
+
"delivery",
|
|
1792
|
+
"payment",
|
|
1793
|
+
"form",
|
|
1794
|
+
];
|
|
1795
|
+
export const FlagCreateSchema = z
|
|
1796
|
+
.object({
|
|
1797
|
+
name: z.string().min(1).describe("Libellé du drapeau (ex. « vivier Java Q4 »)."),
|
|
1798
|
+
mainManagerId: EntityIdSchema.optional().describe("ID de la ressource responsable du drapeau — l'utilisateur courant si omis."),
|
|
1799
|
+
})
|
|
1800
|
+
.strict();
|
|
1801
|
+
export const AttachedFlagsListSchema = z
|
|
1802
|
+
.object({
|
|
1803
|
+
entity: z.enum(ATTACHED_FLAG_ENTITIES).describe("Type de l'enregistrement."),
|
|
1804
|
+
id: EntityIdSchema.describe("ID numérique de l'enregistrement."),
|
|
1805
|
+
})
|
|
1806
|
+
.strict();
|
|
1807
|
+
export const AttachedFlagSchema = AttachedFlagsListSchema.extend({
|
|
1808
|
+
flagId: EntityIdSchema.describe("ID du drapeau (`boond_flags_search`)."),
|
|
1809
|
+
}).strict();
|
|
1810
|
+
export const TasksGetSchema = z
|
|
1811
|
+
.object({
|
|
1812
|
+
entity: z.enum(TASK_ENTITIES).describe("Type de l'enregistrement."),
|
|
1813
|
+
id: EntityIdSchema.describe("ID numérique de l'enregistrement."),
|
|
1814
|
+
})
|
|
1815
|
+
.strict();
|
|
1816
|
+
const TodolistTaskSchema = z
|
|
1817
|
+
.object({
|
|
1818
|
+
description: z.string().min(1).describe("Intitulé de la tâche."),
|
|
1819
|
+
row: z.number().int().min(0).optional().describe("Ordre d'affichage."),
|
|
1820
|
+
state: z.number().int().optional().describe("État (0 = à faire par défaut)."),
|
|
1821
|
+
})
|
|
1822
|
+
.strict();
|
|
1823
|
+
// POST /todolists from `models.todolist` (title, state, profile, profileTypesOf,
|
|
1824
|
+
// profileStates, agencies, tasks) — the RAML documents no body; not exercised.
|
|
1825
|
+
export const TodolistCreateSchema = z
|
|
1826
|
+
.object({
|
|
1827
|
+
title: z.string().min(1).describe("Titre de la todolist."),
|
|
1828
|
+
profile: z
|
|
1829
|
+
.string()
|
|
1830
|
+
.optional()
|
|
1831
|
+
.describe("Fiche sur laquelle la liste s'applique (ex. candidate, resource, opportunity…)."),
|
|
1832
|
+
profileTypesOf: z.array(z.number().int()).optional().describe("Types de fiche concernés (IDs de dictionnaire)."),
|
|
1833
|
+
profileStates: z.array(z.number().int()).optional().describe("États de fiche concernés (IDs de dictionnaire)."),
|
|
1834
|
+
agencyIds: z.array(EntityIdSchema).optional().describe("Agences concernées (IDs)."),
|
|
1835
|
+
tasks: z.array(TodolistTaskSchema).min(1).describe("Tâches de la liste, dans l'ordre."),
|
|
1836
|
+
})
|
|
1837
|
+
.strict();
|
|
1838
|
+
/** `GET /advantages/default?resource=&contract=&project=&delivery=` (RAML `advantages/default.raml`). */
|
|
1839
|
+
export const AdvantageDefaultSchema = z
|
|
1840
|
+
.object({
|
|
1841
|
+
resourceId: EntityIdSchema.describe("ID de la ressource bénéficiaire."),
|
|
1842
|
+
contractId: EntityIdSchema.optional().describe("ID du contrat de rattachement."),
|
|
1843
|
+
projectId: EntityIdSchema.optional().describe("ID du projet (avantage refacturable)."),
|
|
1844
|
+
deliveryId: EntityIdSchema.optional().describe("ID de la prestation."),
|
|
1309
1845
|
})
|
|
1310
1846
|
.strict();
|
|
1847
|
+
// POST /advantages from `models.advantage`; the RAML documents no body — not exercised.
|
|
1848
|
+
export const AdvantageCreateSchema = z
|
|
1849
|
+
.object({
|
|
1850
|
+
resourceId: EntityIdSchema.describe("ID de la ressource bénéficiaire (relation `resource`, requise)."),
|
|
1851
|
+
advantageType: z
|
|
1852
|
+
.union([z.number().int(), z.string().min(1)])
|
|
1853
|
+
.describe("Type d'avantage : `reference` ou `<reference>_<agencyId>` tel que publié par `boond_advantages_default` (`advantageTypes`)."),
|
|
1854
|
+
date: z
|
|
1855
|
+
.string()
|
|
1856
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
1857
|
+
.describe("Date de l'avantage (YYYY-MM-DD)."),
|
|
1858
|
+
returnDate: z
|
|
1859
|
+
.string()
|
|
1860
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
1861
|
+
.optional()
|
|
1862
|
+
.describe("Date de restitution (véhicule, matériel)."),
|
|
1863
|
+
quantity: z.number().optional().describe("Quantité (ex. nombre de tickets)."),
|
|
1864
|
+
participationAmount: z.number().optional().describe("Participation salarié."),
|
|
1865
|
+
employeeAmount: z.number().optional().describe("Montant versé au salarié."),
|
|
1866
|
+
agencyAmount: z.number().optional().describe("Charges / coefficient de charge côté agence."),
|
|
1867
|
+
contractId: EntityIdSchema.optional().describe("ID du contrat de rattachement."),
|
|
1868
|
+
projectId: EntityIdSchema.optional().describe("ID du projet à refacturer."),
|
|
1869
|
+
deliveryId: EntityIdSchema.optional().describe("ID de la prestation."),
|
|
1870
|
+
note: z.string().optional().describe("Commentaires (`informationComments`)."),
|
|
1871
|
+
})
|
|
1872
|
+
.strict();
|
|
1873
|
+
// ---- Inactivities, forms, groupments (issue #256) ----
|
|
1874
|
+
// All three collections document `post` only (no list GET), plus `default`
|
|
1875
|
+
// and `rights`; bodies come from `models.*` in the dictionary and were not
|
|
1876
|
+
// exercised (production tenant). Detail reads follow the `/{collection}/{id}`
|
|
1877
|
+
// pattern of every other entity.
|
|
1878
|
+
const isoDate = (doc) => z
|
|
1879
|
+
.string()
|
|
1880
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
1881
|
+
.describe(doc);
|
|
1882
|
+
export const InactivityDefaultSchema = z
|
|
1883
|
+
.object({
|
|
1884
|
+
resourceId: EntityIdSchema.describe("ID de la ressource (requis par `GET /inactivities/default`)."),
|
|
1885
|
+
})
|
|
1886
|
+
.strict();
|
|
1887
|
+
export const InactivityCreateSchema = z
|
|
1888
|
+
.object({
|
|
1889
|
+
resourceId: EntityIdSchema.describe("ID de la ressource en intercontrat / inactivité (relation `resource`, requise)."),
|
|
1890
|
+
title: z.string().optional().describe("Intitulé (ex. « Intercontrat », « Formation interne »)."),
|
|
1891
|
+
inactivityType: z
|
|
1892
|
+
.union([z.number().int(), z.string().min(1)])
|
|
1893
|
+
.optional()
|
|
1894
|
+
.describe("Type d'inactivité tel que publié par `boond_inactivities_default`."),
|
|
1895
|
+
startDate: isoDate("Date de début (YYYY-MM-DD)."),
|
|
1896
|
+
endDate: isoDate("Date de fin (YYYY-MM-DD)."),
|
|
1897
|
+
numberOfDaysInvoicedOrQuantity: z.number().optional().describe("Nombre de jours de la période."),
|
|
1898
|
+
averageDailyCost: z.number().optional().describe("Coût journalier moyen retenu."),
|
|
1899
|
+
contractId: EntityIdSchema.optional().describe("ID du contrat de rattachement."),
|
|
1900
|
+
note: z.string().optional().describe("Commentaires (`informationComments`)."),
|
|
1901
|
+
})
|
|
1902
|
+
.strict();
|
|
1903
|
+
export const FormDefaultSchema = z
|
|
1904
|
+
.object({
|
|
1905
|
+
templateId: EntityIdSchema.describe("ID du modèle de formulaire (`template`)."),
|
|
1906
|
+
resourceId: EntityIdSchema.describe("ID de l'entité visée par le formulaire (`resource`)."),
|
|
1907
|
+
})
|
|
1908
|
+
.strict();
|
|
1909
|
+
export const FormCreateSchema = z
|
|
1910
|
+
.object({
|
|
1911
|
+
templateId: EntityIdSchema.describe("ID du modèle de formulaire (relation `template`, requise)."),
|
|
1912
|
+
resourceId: EntityIdSchema.optional().describe("Ressource visée (relation `dependsOn`, type resource)."),
|
|
1913
|
+
candidateId: EntityIdSchema.optional().describe("Candidat visé (relation `dependsOn`, type candidate)."),
|
|
1914
|
+
validatorId: EntityIdSchema.optional().describe("Ressource qui validera le formulaire (`validator`)."),
|
|
1915
|
+
recipientId: EntityIdSchema.optional().describe("Ressource destinataire (`recipient`)."),
|
|
1916
|
+
validateDate: isoDate("Date de validation souhaitée (YYYY-MM-DD).").optional(),
|
|
1917
|
+
remindDate: isoDate("Date de rappel (YYYY-MM-DD).").optional(),
|
|
1918
|
+
})
|
|
1919
|
+
.strict();
|
|
1920
|
+
export const GroupmentDefaultSchema = z
|
|
1921
|
+
.object({
|
|
1922
|
+
projectId: EntityIdSchema.describe("ID du projet (requis par `GET /groupments/default`)."),
|
|
1923
|
+
})
|
|
1924
|
+
.strict();
|
|
1925
|
+
const groupmentWritableShape = {
|
|
1926
|
+
title: z.string().optional().describe("Titre du regroupement."),
|
|
1927
|
+
startDate: isoDate("Date de début (YYYY-MM-DD).").optional(),
|
|
1928
|
+
endDate: isoDate("Date de fin (YYYY-MM-DD).").optional(),
|
|
1929
|
+
averageDailyPriceExcludingTax: z.number().optional().describe("Prix journalier HT du regroupement."),
|
|
1930
|
+
forceAverageDailyPriceExcludingTax: z.boolean().optional().describe("Forcer le prix journalier."),
|
|
1931
|
+
averageDailyCost: z.number().optional().describe("Coût journalier moyen."),
|
|
1932
|
+
numberOfDaysInvoicedOrQuantity: z.number().optional().describe("Jours / quantité facturés."),
|
|
1933
|
+
numberOfDaysFree: z.number().optional().describe("Jours offerts."),
|
|
1934
|
+
deliveryIds: z.array(EntityIdSchema).optional().describe("Prestations regroupées (relation `deliveries`)."),
|
|
1935
|
+
note: z.string().optional().describe("Commentaires (`informationComments`)."),
|
|
1936
|
+
};
|
|
1937
|
+
export const GroupmentCreateSchema = z
|
|
1938
|
+
.object({
|
|
1939
|
+
projectId: EntityIdSchema.describe("ID du projet (relation `project`, requise)."),
|
|
1940
|
+
...groupmentWritableShape,
|
|
1941
|
+
})
|
|
1942
|
+
.strict();
|
|
1943
|
+
export const GroupmentUpdateSchema = z
|
|
1944
|
+
.object({
|
|
1945
|
+
id: EntityIdSchema.describe("ID du regroupement à modifier."),
|
|
1946
|
+
...groupmentWritableShape,
|
|
1947
|
+
})
|
|
1948
|
+
.strict();
|
|
1949
|
+
// ---- Alerts (issue #255) ----
|
|
1950
|
+
// `GET /alerts` ("Search alerts for user dashboard") documents no parameter at
|
|
1951
|
+
// all — not even the searchable / paginable traits — so the tool takes only
|
|
1952
|
+
// the client-side projection.
|
|
1953
|
+
export const AlertSearchSchema = z.object({ fields: fieldsField }).strict();
|
|
1311
1954
|
// ---- Application schemas ----
|
|
1312
1955
|
// ---- Validation schemas ----
|
|
1313
1956
|
// `/validations` requires `startMonth` + `endMonth` (YYYY-MM). Optional filters
|
|
@@ -1339,15 +1982,7 @@ export const ValidationSearchSchema = z
|
|
|
1339
1982
|
.optional()
|
|
1340
1983
|
.describe("États de validation."),
|
|
1341
1984
|
validationAlerts: z.boolean().optional().describe("Filtrer sur les validations avec alertes."),
|
|
1342
|
-
|
|
1343
|
-
pageSize: z
|
|
1344
|
-
.number()
|
|
1345
|
-
.int()
|
|
1346
|
-
.min(1)
|
|
1347
|
-
.max(MAX_PAGE_SIZE)
|
|
1348
|
-
.default(DEFAULT_PAGE_SIZE)
|
|
1349
|
-
.describe(`Nombre de résultats par page (max: ${MAX_PAGE_SIZE}, défaut: ${DEFAULT_PAGE_SIZE})`),
|
|
1350
|
-
fields: fieldsField,
|
|
1985
|
+
...paginationShape,
|
|
1351
1986
|
})
|
|
1352
1987
|
.strict();
|
|
1353
1988
|
// ---- Notification schemas ----
|
|
@@ -1364,15 +1999,7 @@ export const NotificationSearchSchema = z
|
|
|
1364
1999
|
.array(z.string())
|
|
1365
2000
|
.optional()
|
|
1366
2001
|
.describe("Types de modules parents (ex: 'contract', 'global', 'project'...)."),
|
|
1367
|
-
|
|
1368
|
-
pageSize: z
|
|
1369
|
-
.number()
|
|
1370
|
-
.int()
|
|
1371
|
-
.min(1)
|
|
1372
|
-
.max(MAX_PAGE_SIZE)
|
|
1373
|
-
.default(DEFAULT_PAGE_SIZE)
|
|
1374
|
-
.describe(`Nombre de résultats par page (max: ${MAX_PAGE_SIZE}, défaut: ${DEFAULT_PAGE_SIZE})`),
|
|
1375
|
-
fields: fieldsField,
|
|
2002
|
+
...paginationShape,
|
|
1376
2003
|
})
|
|
1377
2004
|
.strict();
|
|
1378
2005
|
// ---- Reporting schemas ----
|
|
@@ -1546,14 +2173,16 @@ export const DictionaryGetSchema = z
|
|
|
1546
2173
|
dictionaryType: z
|
|
1547
2174
|
.string()
|
|
1548
2175
|
.min(1)
|
|
1549
|
-
.describe("
|
|
2176
|
+
.describe("Chemin dotté relatif à `data` de /application/dictionary (ex: setting.state.resource, setting.typeOf.project, setting.action.candidate, setting.tool, country, languages). La forme « states/resources » (slash) n'est pas valide."),
|
|
1550
2177
|
})
|
|
1551
2178
|
.strict();
|
|
1552
2179
|
// ---- Documents ----
|
|
1553
2180
|
// Source: https://doc.boondmanager.com/api-externe/raml-build/resources/documents/search.raml
|
|
1554
|
-
//
|
|
1555
|
-
//
|
|
1556
|
-
//
|
|
2181
|
+
// Trois sources, exactement une par appel (vérifié par le handler, un JSON
|
|
2182
|
+
// Schema ne sait pas l'exprimer lisiblement) : `fileUrl` (BoondManager
|
|
2183
|
+
// télécharge lui-même), `filePath` (fichier local, stdio uniquement, désactivé
|
|
2184
|
+
// tant que `BOOND_MCP_UPLOAD_DIRS` n'est pas défini) et `fileContent` + `fileName`
|
|
2185
|
+
// (base64 inline, plafonné). Garde-fous : `services/upload-source.ts`.
|
|
1557
2186
|
export const DocumentParentTypes = [
|
|
1558
2187
|
"action",
|
|
1559
2188
|
"resourceResume",
|
|
@@ -1589,7 +2218,27 @@ export const DocumentCreateSchema = z
|
|
|
1589
2218
|
fileUrl: z
|
|
1590
2219
|
.string()
|
|
1591
2220
|
.url()
|
|
2221
|
+
.optional()
|
|
1592
2222
|
.describe("URL (https) du fichier à téléverser — BoondManager télécharge le fichier depuis cette URL."),
|
|
2223
|
+
filePath: z
|
|
2224
|
+
.string()
|
|
2225
|
+
.min(1)
|
|
2226
|
+
.max(4096)
|
|
2227
|
+
.optional()
|
|
2228
|
+
.describe("Chemin absolu d'un fichier local, lu par le serveur MCP (transport stdio uniquement, sous un répertoire " +
|
|
2229
|
+
"de BOOND_MCP_UPLOAD_DIRS)."),
|
|
2230
|
+
fileContent: z
|
|
2231
|
+
.string()
|
|
2232
|
+
.min(1)
|
|
2233
|
+
.max(Math.ceil(INLINE_UPLOAD_MAX_BYTES / 3) * 4 + 256)
|
|
2234
|
+
.optional()
|
|
2235
|
+
.describe("Contenu du fichier en base64 (préfixe data: URI accepté), petits fichiers uniquement. Exige fileName."),
|
|
2236
|
+
fileName: z
|
|
2237
|
+
.string()
|
|
2238
|
+
.min(1)
|
|
2239
|
+
.max(255)
|
|
2240
|
+
.optional()
|
|
2241
|
+
.describe("Nom du fichier avec son extension (ex. cv.pdf), requis avec fileContent."),
|
|
1593
2242
|
parsing: z
|
|
1594
2243
|
.boolean()
|
|
1595
2244
|
.optional()
|