boondmanager-mcp-server 1.7.4 → 1.7.5

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.
@@ -1,18 +1,42 @@
1
1
  import { z } from "zod";
2
2
  import { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, MAX_SEARCH_PAGE } from "../constants.js";
3
3
  // Common search schema
4
- export const SearchSchema = z.object({
4
+ export const SearchSchema = z
5
+ .object({
5
6
  keywords: z.string().optional().describe("Mots-clés de recherche (nom, email, compétences...)"),
6
- page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (défaut: 1, max: ${MAX_SEARCH_PAGE})`),
7
- pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE)
7
+ page: z
8
+ .number()
9
+ .int()
10
+ .min(1)
11
+ .max(MAX_SEARCH_PAGE)
12
+ .default(1)
13
+ .describe(`Numéro de page (défaut: 1, max: ${MAX_SEARCH_PAGE})`),
14
+ pageSize: z
15
+ .number()
16
+ .int()
17
+ .min(1)
18
+ .max(MAX_PAGE_SIZE)
19
+ .default(DEFAULT_PAGE_SIZE)
8
20
  .describe(`Nombre de résultats par page (max: ${MAX_PAGE_SIZE}, défaut: ${DEFAULT_PAGE_SIZE})`),
9
- }).strict();
21
+ })
22
+ .strict();
10
23
  // ---- Reusable filter field helpers ----
11
24
  // IMPORTANT: input field names below MUST match the BoondManager API query parameter names
12
25
  // exactly (e.g., perimeterManagers, resourceStates, opportunityStates). buildSearchQuery
13
26
  // passes them through as `key[]=value` for arrays. See https://doc.boondmanager.com/api-externe/
14
- const pageField = z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (défaut: 1, max: ${MAX_SEARCH_PAGE})`);
15
- const pageSizeField = z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE)
27
+ const pageField = z
28
+ .number()
29
+ .int()
30
+ .min(1)
31
+ .max(MAX_SEARCH_PAGE)
32
+ .default(1)
33
+ .describe(`Numéro de page (défaut: 1, max: ${MAX_SEARCH_PAGE})`);
34
+ const pageSizeField = z
35
+ .number()
36
+ .int()
37
+ .min(1)
38
+ .max(MAX_PAGE_SIZE)
39
+ .default(DEFAULT_PAGE_SIZE)
16
40
  .describe(`Nombre de résultats par page (max: ${MAX_PAGE_SIZE}, défaut: ${DEFAULT_PAGE_SIZE})`);
17
41
  const sortField = z.string().optional().describe("Champ de tri (ex: lastName, firstName, updateDate)");
18
42
  const orderField = z.enum(["asc", "desc"]).optional().describe("Ordre de tri (asc/desc)");
@@ -20,29 +44,58 @@ const intArray = (doc) => z.array(z.number().int()).optional().describe(doc);
20
44
  const strArray = (doc) => z.array(z.string()).optional().describe(doc);
21
45
  // Shared "perimeter" filters available on every entity search (from RAML trait `searchable`).
22
46
  // These are the CORRECT filters for "my team / my agency / my N-1" — NOT the old `mainManagers`.
23
- const perimeterManagersField = intArray("IDs des managers (ressources). Conserve les entités dont le responsable est l'un de ces managers. "
24
- + "Pour 'mon équipe / N-1 d'une personne X', passer [X_id]. Obtenir son propre ID via boond_application_current_user.");
47
+ const perimeterManagersField = intArray("IDs des managers (ressources). Conserve les entités dont le responsable est l'un de ces managers. " +
48
+ "Pour 'mon équipe / N-1 d'une personne X', passer [X_id]. Obtenir son propre ID via boond_application_current_user.");
25
49
  const perimeterAgenciesField = intArray("IDs d'agences. Conserve les entités dont le responsable appartient à ces agences.");
26
50
  const perimeterPolesField = intArray("IDs de pôles. Conserve les entités dont le responsable appartient à ces pôles.");
27
51
  const perimeterBusinessUnitsField = intArray("IDs de business units. Conserve les entités dont le responsable appartient à ces BU.");
28
- const perimeterDynamicField = z.array(z.enum(["data", "agencies", "poles", "businessUnits", "managers"])).optional().describe("Périmètre dynamique relatif à l'utilisateur courant (raccourci sans avoir à connaître son propre ID). "
29
- + "Valeurs : 'data' (mes propres données), 'managers' (mon équipe / mes N-1), 'agencies' (mes agences), "
30
- + "'poles' (mes pôles), 'businessUnits' (mes BU). Combinable.");
31
- const narrowPerimeterField = z.boolean().optional().describe("Si true, jointure ET entre les filtres `perimeter*` (au lieu de OU par défaut).");
32
- const startDateField = z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
52
+ const perimeterDynamicField = z
53
+ .array(z.enum(["data", "agencies", "poles", "businessUnits", "managers"]))
54
+ .optional()
55
+ .describe("Périmètre dynamique relatif à l'utilisateur courant (raccourci sans avoir à connaître son propre ID). " +
56
+ "Valeurs : 'data' (mes propres données), 'managers' (mon équipe / mes N-1), 'agencies' (mes agences), " +
57
+ "'poles' (mes pôles), 'businessUnits' (mes BU). Combinable.");
58
+ const narrowPerimeterField = z
59
+ .boolean()
60
+ .optional()
61
+ .describe("Si true, jointure ET entre les filtres `perimeter*` (au lieu de OU par défaut).");
62
+ const startDateField = z
63
+ .string()
64
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
65
+ .optional()
33
66
  .describe("Date de début (YYYY-MM-DD), à utiliser avec `period`.");
34
- const endDateField = z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
67
+ const endDateField = z
68
+ .string()
69
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
70
+ .optional()
35
71
  .describe("Date de fin (YYYY-MM-DD), à utiliser avec `period`.");
36
72
  // ---- Resource search schema (collaborateurs internes) ----
37
73
  // Source: https://doc.boondmanager.com/api-externe/raml-build/resources/resources/search.raml
38
- export const ResourceSearchSchema = z.object({
39
- keywords: z.string().optional().describe("Mots-clés (par défaut, recherche dans CV + dossier technique). Pour cibler un champ précis, "
40
- + "fournir aussi `keywordsType` (ex: lastName, firstName, fullName, emails, title, titleSkills, phones, reference)."),
41
- keywordsType: z.enum([
42
- "resumeTd", "lastName", "firstName", "fullName", "strictFullName",
43
- "emails", "title", "titleSkills", "phones", "reference", "resume", "td",
44
- ]).optional().describe("Champ ciblé par `keywords`. Défaut: 'resumeTd' (CV + dossier technique). "
45
- + "Pour 'fullName' utiliser `keywords = 'NOM#PRENOM'`."),
74
+ export const ResourceSearchSchema = z
75
+ .object({
76
+ keywords: z
77
+ .string()
78
+ .optional()
79
+ .describe("Mots-clés (par défaut, recherche dans CV + dossier technique). Pour cibler un champ précis, " +
80
+ "fournir aussi `keywordsType` (ex: lastName, firstName, fullName, emails, title, titleSkills, phones, reference)."),
81
+ keywordsType: z
82
+ .enum([
83
+ "resumeTd",
84
+ "lastName",
85
+ "firstName",
86
+ "fullName",
87
+ "strictFullName",
88
+ "emails",
89
+ "title",
90
+ "titleSkills",
91
+ "phones",
92
+ "reference",
93
+ "resume",
94
+ "td",
95
+ ])
96
+ .optional()
97
+ .describe("Champ ciblé par `keywords`. Défaut: 'resumeTd' (CV + dossier technique). " +
98
+ "Pour 'fullName' utiliser `keywords = 'NOM#PRENOM'`."),
46
99
  perimeterManagers: perimeterManagersField,
47
100
  perimeterAgencies: perimeterAgenciesField,
48
101
  perimeterPoles: perimeterPolesField,
@@ -55,46 +108,84 @@ export const ResourceSearchSchema = z.object({
55
108
  excludeResourceTypes: intArray("IDs de types de ressource à EXCLURE."),
56
109
  activityAreas: strArray("IDs de secteurs d'activité (dictionnaire setting.activityArea)."),
57
110
  expertiseAreas: strArray("IDs de domaines d'expertise (dictionnaire setting.expertiseArea)."),
58
- tools: strArray("IDs d'outils/technos (dictionnaire setting.tool). Logique OU par défaut. "
59
- + "Pour ET, ajouter '#AND#' en 1er élément: tools=['#AND#','12','34']."),
111
+ tools: strArray("IDs d'outils/technos (dictionnaire setting.tool). Logique OU par défaut. " +
112
+ "Pour ET, ajouter '#AND#' en 1er élément: tools=['#AND#','12','34']."),
60
113
  experiences: intArray("IDs de niveaux d'expérience (dictionnaire setting.experience)."),
61
114
  trainings: strArray("IDs de formations (dictionnaire setting.training)."),
62
115
  mobilityAreas: strArray("IDs de zones de mobilité (dictionnaire setting.mobilityArea)."),
63
116
  languages: strArray("Langues parlées au format `langueId|niveauId` (dictionnaires setting.languageSpoken et setting.languageLevel). Ex: ['anglais|courant']."),
64
117
  flags: intArray("IDs de tags (drapeaux) attachés à la ressource."),
65
- period: z.string().optional().describe("Champ temporel pour filtrer par période. Valeurs courantes: 'available' (disponibilité), "
66
- + "'working' (en mission hors interne), 'workingAll', 'absent', 'idle', 'hired', 'left', "
67
- + "'employed', 'unemployed', 'updated', 'arrival', 'birthday', 'seniority', 'present', "
68
- + "'noAction'/'withActions'/'withoutActions'/'withAbsences'/'withoutAbsences'. "
69
- + "À combiner avec `startDate` + `endDate`."),
118
+ period: z
119
+ .string()
120
+ .optional()
121
+ .describe("Champ temporel pour filtrer par période. Valeurs courantes: 'available' (disponibilité), " +
122
+ "'working' (en mission hors interne), 'workingAll', 'absent', 'idle', 'hired', 'left', " +
123
+ "'employed', 'unemployed', 'updated', 'arrival', 'birthday', 'seniority', 'present', " +
124
+ "'noAction'/'withActions'/'withoutActions'/'withAbsences'/'withoutAbsences'. " +
125
+ "À combiner avec `startDate` + `endDate`."),
70
126
  startDate: startDateField,
71
127
  endDate: endDateField,
72
128
  providerCompanies: intArray("IDs de sociétés sous-traitantes (filtre pour ressources externes)."),
73
- coordinates: z.string().optional().describe("Coordonnées GPS 'latitude,longitude' pour recherche géographique. À combiner avec `geoDistance`."),
74
- location: z.string().optional().describe("Adresse texte (ville, etc.) pour recherche géographique. À combiner avec `geoDistance`."),
75
- geoDistance: z.number().int().min(5).max(200).optional().describe("Rayon en km pour la recherche géographique (5-200). Requis si `coordinates` ou `location` est fourni."),
129
+ coordinates: z
130
+ .string()
131
+ .optional()
132
+ .describe("Coordonnées GPS 'latitude,longitude' pour recherche géographique. À combiner avec `geoDistance`."),
133
+ location: z
134
+ .string()
135
+ .optional()
136
+ .describe("Adresse texte (ville, etc.) pour recherche géographique. À combiner avec `geoDistance`."),
137
+ geoDistance: z
138
+ .number()
139
+ .int()
140
+ .min(5)
141
+ .max(200)
142
+ .optional()
143
+ .describe("Rayon en km pour la recherche géographique (5-200). Requis si `coordinates` ou `location` est fourni."),
76
144
  excludeManager: z.boolean().optional().describe("Si true, ne retourne que les ressources sans compte manager."),
77
- shields: z.array(z.enum(["uncomplete", "minimum", "complete"])).optional()
145
+ shields: z
146
+ .array(z.enum(["uncomplete", "minimum", "complete"]))
147
+ .optional()
78
148
  .describe("Niveau de complétude des champs conditionnels."),
79
149
  sort: sortField,
80
150
  order: orderField,
81
151
  page: pageField,
82
152
  pageSize: pageSizeField,
83
- }).strict();
153
+ })
154
+ .strict();
84
155
  // ---- Candidate search schema ----
85
156
  // Source: https://doc.boondmanager.com/api-externe/raml-build/resources/candidates/search.raml
86
- export const CandidateSearchSchema = z.object({
87
- keywords: z.string().optional().describe("Mots-clés (par défaut, recherche dans CV + dossier technique). Combinable avec `keywordsType`."),
88
- keywordsType: z.enum([
89
- "resumeTd", "lastName", "firstName", "fullName", "strictFullName",
90
- "emails", "title", "titleSkills", "phones", "reference", "resume", "td",
91
- ]).optional().describe("Champ ciblé par `keywords` (défaut: 'resumeTd')."),
157
+ export const CandidateSearchSchema = z
158
+ .object({
159
+ keywords: z
160
+ .string()
161
+ .optional()
162
+ .describe("Mots-clés (par défaut, recherche dans CV + dossier technique). Combinable avec `keywordsType`."),
163
+ keywordsType: z
164
+ .enum([
165
+ "resumeTd",
166
+ "lastName",
167
+ "firstName",
168
+ "fullName",
169
+ "strictFullName",
170
+ "emails",
171
+ "title",
172
+ "titleSkills",
173
+ "phones",
174
+ "reference",
175
+ "resume",
176
+ "td",
177
+ ])
178
+ .optional()
179
+ .describe("Champ ciblé par `keywords` (défaut: 'resumeTd')."),
92
180
  perimeterManagers: perimeterManagersField,
93
181
  perimeterAgencies: perimeterAgenciesField,
94
182
  perimeterPoles: perimeterPolesField,
95
183
  perimeterBusinessUnits: perimeterBusinessUnitsField,
96
184
  perimeterDynamic: perimeterDynamicField,
97
- perimeterManagersType: z.enum(["main", "hr"]).optional().describe("Type de responsable visé par `perimeterManagers`: 'main' (Main Manager) ou 'hr' (HR Manager)."),
185
+ perimeterManagersType: z
186
+ .enum(["main", "hr"])
187
+ .optional()
188
+ .describe("Type de responsable visé par `perimeterManagers`: 'main' (Main Manager) ou 'hr' (HR Manager)."),
98
189
  narrowPerimeter: narrowPerimeterField,
99
190
  candidateStates: intArray("IDs d'états de candidat (dictionnaire setting.state.candidate via boond_application_dictionary)."),
100
191
  candidateTypes: intArray("IDs de types de candidat (dictionnaire setting.typeOf.resource)."),
@@ -110,31 +201,51 @@ export const CandidateSearchSchema = z.object({
110
201
  evaluations: strArray("IDs d'évaluations."),
111
202
  sources: strArray("IDs de sources de recrutement (dictionnaire setting.source)."),
112
203
  flags: intArray("IDs de tags."),
113
- period: z.string().optional().describe("Filtre temporel : 'created', 'updated', 'available', "
114
- + "'noAction'/'withActions'/'withoutActions'. À combiner avec `startDate` + `endDate`."),
204
+ period: z
205
+ .string()
206
+ .optional()
207
+ .describe("Filtre temporel : 'created', 'updated', 'available', " +
208
+ "'noAction'/'withActions'/'withoutActions'. À combiner avec `startDate` + `endDate`."),
115
209
  startDate: startDateField,
116
210
  endDate: endDateField,
117
211
  providerCompanies: intArray("IDs de sociétés sous-traitantes."),
118
212
  coordinates: z.string().optional().describe("Coordonnées GPS 'lat,lon'. Requiert `geoDistance`."),
119
213
  location: z.string().optional().describe("Adresse texte. Requiert `geoDistance`."),
120
214
  geoDistance: z.number().int().min(5).max(200).optional().describe("Rayon km (5-200)."),
121
- shields: z.array(z.enum(["uncomplete", "minimum", "complete"])).optional()
215
+ shields: z
216
+ .array(z.enum(["uncomplete", "minimum", "complete"]))
217
+ .optional()
122
218
  .describe("Niveau de complétude."),
123
219
  sort: sortField,
124
220
  order: orderField,
125
221
  page: pageField,
126
222
  pageSize: pageSizeField,
127
- }).strict();
223
+ })
224
+ .strict();
128
225
  // ---- Contact search schema ----
129
226
  // Source: https://doc.boondmanager.com/api-externe/raml-build/resources/contacts/search.raml
130
- export const ContactSearchSchema = z.object({
131
- keywords: z.string().optional().describe("Mots-clés (défaut: nom + prénom + société + fonction + périmètre technique). "
132
- + "Combinable avec `keywordsType`."),
133
- keywordsType: z.enum([
134
- "default", "lastName", "firstName", "fullName", "strictFullName",
135
- "companyFullName", "emails", "phones", "socialNetworks",
136
- ]).optional().describe("Champ ciblé. Défaut: 'default'. Pour 'fullName' utiliser `keywords = 'NOM#PRENOM'`. "
137
- + "Pour 'companyFullName' utiliser `keywords = 'CSOCid#NOM#PRENOM'`."),
227
+ export const ContactSearchSchema = z
228
+ .object({
229
+ keywords: z
230
+ .string()
231
+ .optional()
232
+ .describe("Mots-clés (défaut: nom + prénom + société + fonction + périmètre technique). " +
233
+ "Combinable avec `keywordsType`."),
234
+ keywordsType: z
235
+ .enum([
236
+ "default",
237
+ "lastName",
238
+ "firstName",
239
+ "fullName",
240
+ "strictFullName",
241
+ "companyFullName",
242
+ "emails",
243
+ "phones",
244
+ "socialNetworks",
245
+ ])
246
+ .optional()
247
+ .describe("Champ ciblé. Défaut: 'default'. Pour 'fullName' utiliser `keywords = 'NOM#PRENOM'`. " +
248
+ "Pour 'companyFullName' utiliser `keywords = 'CSOCid#NOM#PRENOM'`."),
138
249
  perimeterManagers: perimeterManagersField,
139
250
  perimeterAgencies: perimeterAgenciesField,
140
251
  perimeterPoles: perimeterPolesField,
@@ -143,32 +254,44 @@ export const ContactSearchSchema = z.object({
143
254
  narrowPerimeter: narrowPerimeterField,
144
255
  states: intArray("IDs d'états de contact (dictionnaire setting.state.contact)."),
145
256
  companyStates: intArray("IDs d'états des sociétés rattachées (dictionnaire setting.state.company)."),
146
- typesOf: intArray("IDs de types de contact (dictionnaire setting.typeOf.contact). "
147
- + "⚠️ Le paramètre s'appelle `typesOf` (avec un 's'), PAS `typeOf`."),
257
+ typesOf: intArray("IDs de types de contact (dictionnaire setting.typeOf.contact). " +
258
+ "⚠️ Le paramètre s'appelle `typesOf` (avec un 's'), PAS `typeOf`."),
148
259
  origins: strArray("IDs d'origines (dictionnaire setting.origin)."),
149
260
  activityAreas: strArray("IDs de secteurs d'activité de la société."),
150
261
  expertiseAreas: strArray("IDs de domaines d'expertise de la société."),
151
262
  tools: strArray("IDs d'outils. Logique OU par défaut, '#AND#' en 1er pour ET."),
152
263
  influencers: intArray("IDs de contacts influenceurs."),
153
264
  flags: intArray("IDs de tags."),
154
- period: z.string().optional().describe("Filtre temporel : 'created', 'updated', 'noAction'/'withActions'/'withoutActions'. "
155
- + "À combiner avec `startDate` + `endDate`."),
265
+ period: z
266
+ .string()
267
+ .optional()
268
+ .describe("Filtre temporel : 'created', 'updated', 'noAction'/'withActions'/'withoutActions'. " +
269
+ "À combiner avec `startDate` + `endDate`."),
156
270
  startDate: startDateField,
157
271
  endDate: endDateField,
158
- completeness: strArray("Filtre par complétude des champs au format `fieldId:mode` (fieldId: email/phone/socialNetworks ; "
159
- + "mode: empty/filled). Logique OU par défaut, '#AND#' en 1er pour ET. Ex: ['email:empty','phone:empty']."),
160
- shields: z.array(z.enum(["uncomplete", "minimum", "complete"])).optional()
272
+ completeness: strArray("Filtre par complétude des champs au format `fieldId:mode` (fieldId: email/phone/socialNetworks ; " +
273
+ "mode: empty/filled). Logique OU par défaut, '#AND#' en 1er pour ET. Ex: ['email:empty','phone:empty']."),
274
+ shields: z
275
+ .array(z.enum(["uncomplete", "minimum", "complete"]))
276
+ .optional()
161
277
  .describe("Niveau de complétude."),
162
278
  sort: sortField,
163
279
  order: orderField,
164
280
  page: pageField,
165
281
  pageSize: pageSizeField,
166
- }).strict();
282
+ })
283
+ .strict();
167
284
  // ---- Company search schema ----
168
285
  // Source: https://doc.boondmanager.com/api-externe/raml-build/resources/companies/search.raml
169
- export const CompanySearchSchema = z.object({
170
- keywords: z.string().optional().describe("Mots-clés (défaut: nom + ville + pays + expertise + informations). Combinable avec `keywordsType`."),
171
- keywordsType: z.enum(["default", "name", "phones", "emails", "socialNetworks"]).optional()
286
+ export const CompanySearchSchema = z
287
+ .object({
288
+ keywords: z
289
+ .string()
290
+ .optional()
291
+ .describe("Mots-clés (défaut: nom + ville + pays + expertise + informations). Combinable avec `keywordsType`."),
292
+ keywordsType: z
293
+ .enum(["default", "name", "phones", "emails", "socialNetworks"])
294
+ .optional()
172
295
  .describe("Champ ciblé par `keywords`. Défaut: 'default'."),
173
296
  perimeterManagers: perimeterManagersField,
174
297
  perimeterAgencies: perimeterAgenciesField,
@@ -181,29 +304,41 @@ export const CompanySearchSchema = z.object({
181
304
  origins: strArray("IDs d'origines (dictionnaire setting.origin)."),
182
305
  influencers: intArray("IDs d'influenceurs."),
183
306
  flags: intArray("IDs de tags."),
184
- period: z.string().optional().describe("Filtre temporel : 'created', 'updated', 'noAction'/'withActions'/'withoutActions'. "
185
- + "À combiner avec `startDate` + `endDate`."),
307
+ period: z
308
+ .string()
309
+ .optional()
310
+ .describe("Filtre temporel : 'created', 'updated', 'noAction'/'withActions'/'withoutActions'. " +
311
+ "À combiner avec `startDate` + `endDate`."),
186
312
  startDate: startDateField,
187
313
  endDate: endDateField,
188
- shields: z.array(z.enum(["uncomplete", "minimum", "complete"])).optional()
314
+ shields: z
315
+ .array(z.enum(["uncomplete", "minimum", "complete"]))
316
+ .optional()
189
317
  .describe("Niveau de complétude."),
190
318
  sort: sortField,
191
319
  order: orderField,
192
320
  page: pageField,
193
321
  pageSize: pageSizeField,
194
- }).strict();
322
+ })
323
+ .strict();
195
324
  // ---- Opportunity search schema ----
196
325
  // Source: https://doc.boondmanager.com/api-externe/raml-build/resources/opportunities/search.raml
197
- export const OpportunitySearchSchema = z.object({
198
- keywords: z.string().optional().describe("Mots-clés. Pour cibler par ID préfixé : 'AOnnn' (opportunité), 'CSOCnnn' (société), "
199
- + "'CCONnnn' (contact), 'CANDnnn' (candidat), 'COMPnnn' (ressource), 'PRODnnn' (produit). "
200
- + "Sinon recherche plein texte sur titre/société."),
326
+ export const OpportunitySearchSchema = z
327
+ .object({
328
+ keywords: z
329
+ .string()
330
+ .optional()
331
+ .describe("Mots-clés. Pour cibler par ID préfixé : 'AOnnn' (opportunité), 'CSOCnnn' (société), " +
332
+ "'CCONnnn' (contact), 'CANDnnn' (candidat), 'COMPnnn' (ressource), 'PRODnnn' (produit). " +
333
+ "Sinon recherche plein texte sur titre/société."),
201
334
  perimeterManagers: perimeterManagersField,
202
335
  perimeterAgencies: perimeterAgenciesField,
203
336
  perimeterPoles: perimeterPolesField,
204
337
  perimeterBusinessUnits: perimeterBusinessUnitsField,
205
338
  perimeterDynamic: perimeterDynamicField,
206
- perimeterManagersType: z.enum(["main", "hr"]).optional()
339
+ perimeterManagersType: z
340
+ .enum(["main", "hr"])
341
+ .optional()
207
342
  .describe("Type de responsable visé par `perimeterManagers` (main/hr)."),
208
343
  narrowPerimeter: narrowPerimeterField,
209
344
  opportunityStates: intArray("IDs d'états d'opportunité (dictionnaire setting.state.opportunity)."),
@@ -216,23 +351,33 @@ export const OpportunitySearchSchema = z.object({
216
351
  durations: intArray("IDs de durées (dictionnaire setting.duration)."),
217
352
  origins: strArray("IDs d'origines."),
218
353
  flags: intArray("IDs de tags."),
219
- period: z.string().optional().describe("Filtre temporel : 'created' (création), 'started', 'closingDate' (date de closing), "
220
- + "'updated', 'updatedPositioning', 'noAction'/'withActions'/'withoutActions'. "
221
- + "À combiner avec `startDate` + `endDate`."),
354
+ period: z
355
+ .string()
356
+ .optional()
357
+ .describe("Filtre temporel : 'created' (création), 'started', 'closingDate' (date de closing), " +
358
+ "'updated', 'updatedPositioning', 'noAction'/'withActions'/'withoutActions'. " +
359
+ "À combiner avec `startDate` + `endDate`."),
222
360
  startDate: startDateField,
223
361
  endDate: endDateField,
224
- shields: z.array(z.enum(["uncomplete", "minimum", "complete"])).optional()
362
+ shields: z
363
+ .array(z.enum(["uncomplete", "minimum", "complete"]))
364
+ .optional()
225
365
  .describe("Niveau de complétude."),
226
366
  sort: sortField,
227
367
  order: orderField,
228
368
  page: pageField,
229
369
  pageSize: pageSizeField,
230
- }).strict();
370
+ })
371
+ .strict();
231
372
  // ---- Project search schema ----
232
373
  // Source: https://doc.boondmanager.com/api-externe/raml-build/resources/projects/search.raml
233
- export const ProjectSearchSchema = z.object({
234
- keywords: z.string().optional().describe("Mots-clés. Pour cibler par ID préfixé : 'PRJnnn' (projet), 'CSOCnnn' (société), "
235
- + "'CCONnnn' (contact), 'AOnnn' (opportunité), 'COMPnnn' (ressource), 'CTRnnn' (contrat)."),
374
+ export const ProjectSearchSchema = z
375
+ .object({
376
+ keywords: z
377
+ .string()
378
+ .optional()
379
+ .describe("Mots-clés. Pour cibler par ID préfixé : 'PRJnnn' (projet), 'CSOCnnn' (société), " +
380
+ "'CCONnnn' (contact), 'AOnnn' (opportunité), 'COMPnnn' (ressource), 'CTRnnn' (contrat)."),
236
381
  perimeterManagers: perimeterManagersField,
237
382
  perimeterAgencies: perimeterAgenciesField,
238
383
  perimeterPoles: perimeterPolesField,
@@ -245,26 +390,38 @@ export const ProjectSearchSchema = z.object({
245
390
  expertiseAreas: strArray("IDs de domaines d'expertise."),
246
391
  activityAreas: strArray("IDs de secteurs d'activité."),
247
392
  flags: intArray("IDs de tags."),
248
- period: z.string().optional().describe("Filtre temporel : 'running' (en cours), 'created', 'started', 'stopped', 'closed', 'updated', "
249
- + "'hasAdditionalDataOrPurchase'. À combiner avec `startDate` + `endDate`."),
393
+ period: z
394
+ .string()
395
+ .optional()
396
+ .describe("Filtre temporel : 'running' (en cours), 'created', 'started', 'stopped', 'closed', 'updated', " +
397
+ "'hasAdditionalDataOrPurchase'. À combiner avec `startDate` + `endDate`."),
250
398
  startDate: startDateField,
251
399
  endDate: endDateField,
252
400
  sort: sortField,
253
401
  order: orderField,
254
402
  page: pageField,
255
403
  pageSize: pageSizeField,
256
- }).strict();
404
+ })
405
+ .strict();
257
406
  // ID param schema
258
- export const IdSchema = z.object({
407
+ export const IdSchema = z
408
+ .object({
259
409
  id: z.string().min(1).describe("Identifiant unique de l'entité BoondManager"),
260
- }).strict();
410
+ })
411
+ .strict();
261
412
  // ID + tab param schema
262
- export const IdTabSchema = z.object({
413
+ export const IdTabSchema = z
414
+ .object({
263
415
  id: z.string().min(1).describe("Identifiant unique de l'entité"),
264
- tab: z.string().optional().describe("Onglet spécifique à récupérer (information, technical, financial, actions, contracts, documents)"),
265
- }).strict();
416
+ tab: z
417
+ .string()
418
+ .optional()
419
+ .describe("Onglet spécifique à récupérer (information, technical, financial, actions, contracts, documents)"),
420
+ })
421
+ .strict();
266
422
  // ---- Candidate schemas ----
267
- export const CandidateCreateSchema = z.object({
423
+ export const CandidateCreateSchema = z
424
+ .object({
268
425
  firstName: z.string().min(1).describe("Prénom du candidat"),
269
426
  lastName: z.string().min(1).describe("Nom de famille du candidat"),
270
427
  email1: z.string().email().optional().describe("Email principal"),
@@ -275,8 +432,10 @@ export const CandidateCreateSchema = z.object({
275
432
  state: z.number().int().optional().describe("État du candidat (0=en cours, 1=placé, 2=archivé...)"),
276
433
  mainSkills: z.string().optional().describe("Compétences principales (texte libre)"),
277
434
  note: z.string().optional().describe("Notes / commentaires"),
278
- }).strict();
279
- export const CandidateUpdateSchema = z.object({
435
+ })
436
+ .strict();
437
+ export const CandidateUpdateSchema = z
438
+ .object({
280
439
  id: z.string().min(1).describe("ID du candidat à modifier"),
281
440
  firstName: z.string().optional().describe("Prénom"),
282
441
  lastName: z.string().optional().describe("Nom"),
@@ -288,9 +447,11 @@ export const CandidateUpdateSchema = z.object({
288
447
  state: z.number().int().optional().describe("État du candidat"),
289
448
  mainSkills: z.string().optional().describe("Compétences principales"),
290
449
  note: z.string().optional().describe("Notes"),
291
- }).strict();
450
+ })
451
+ .strict();
292
452
  // ---- Resource schemas ----
293
- export const ResourceCreateSchema = z.object({
453
+ export const ResourceCreateSchema = z
454
+ .object({
294
455
  firstName: z.string().min(1).describe("Prénom de la ressource/collaborateur"),
295
456
  lastName: z.string().min(1).describe("Nom de famille"),
296
457
  email1: z.string().email().optional().describe("Email principal"),
@@ -300,8 +461,10 @@ export const ResourceCreateSchema = z.object({
300
461
  title: z.string().optional().describe("Titre / poste"),
301
462
  state: z.number().int().optional().describe("État de la ressource"),
302
463
  note: z.string().optional().describe("Notes"),
303
- }).strict();
304
- export const ResourceUpdateSchema = z.object({
464
+ })
465
+ .strict();
466
+ export const ResourceUpdateSchema = z
467
+ .object({
305
468
  id: z.string().min(1).describe("ID de la ressource à modifier"),
306
469
  firstName: z.string().optional().describe("Prénom"),
307
470
  lastName: z.string().optional().describe("Nom"),
@@ -312,9 +475,11 @@ export const ResourceUpdateSchema = z.object({
312
475
  title: z.string().optional().describe("Titre / poste"),
313
476
  state: z.number().int().optional().describe("État"),
314
477
  note: z.string().optional().describe("Notes"),
315
- }).strict();
478
+ })
479
+ .strict();
316
480
  // ---- Contact schemas ----
317
- export const ContactCreateSchema = z.object({
481
+ export const ContactCreateSchema = z
482
+ .object({
318
483
  firstName: z.string().min(1).describe("Prénom du contact"),
319
484
  lastName: z.string().min(1).describe("Nom de famille"),
320
485
  email1: z.string().email().optional().describe("Email principal"),
@@ -324,8 +489,10 @@ export const ContactCreateSchema = z.object({
324
489
  title: z.string().optional().describe("Titre / fonction"),
325
490
  companyId: z.string().optional().describe("ID de la société associée"),
326
491
  note: z.string().optional().describe("Notes"),
327
- }).strict();
328
- export const ContactUpdateSchema = z.object({
492
+ })
493
+ .strict();
494
+ export const ContactUpdateSchema = z
495
+ .object({
329
496
  id: z.string().min(1).describe("ID du contact à modifier"),
330
497
  firstName: z.string().optional().describe("Prénom"),
331
498
  lastName: z.string().optional().describe("Nom"),
@@ -334,9 +501,11 @@ export const ContactUpdateSchema = z.object({
334
501
  city: z.string().optional().describe("Ville"),
335
502
  title: z.string().optional().describe("Titre / fonction"),
336
503
  note: z.string().optional().describe("Notes"),
337
- }).strict();
504
+ })
505
+ .strict();
338
506
  // ---- Company schemas ----
339
- export const CompanyCreateSchema = z.object({
507
+ export const CompanyCreateSchema = z
508
+ .object({
340
509
  name: z.string().min(1).describe("Nom de la société"),
341
510
  email1: z.string().email().optional().describe("Email de la société"),
342
511
  phone1: z.string().optional().describe("Téléphone"),
@@ -346,8 +515,10 @@ export const CompanyCreateSchema = z.object({
346
515
  siret: z.string().optional().describe("Numéro SIRET"),
347
516
  state: z.number().int().optional().describe("État de la société"),
348
517
  note: z.string().optional().describe("Notes"),
349
- }).strict();
350
- export const CompanyUpdateSchema = z.object({
518
+ })
519
+ .strict();
520
+ export const CompanyUpdateSchema = z
521
+ .object({
351
522
  id: z.string().min(1).describe("ID de la société à modifier"),
352
523
  name: z.string().optional().describe("Nom"),
353
524
  email1: z.string().email().optional().describe("Email"),
@@ -358,9 +529,11 @@ export const CompanyUpdateSchema = z.object({
358
529
  siret: z.string().optional().describe("Numéro SIRET"),
359
530
  state: z.number().int().optional().describe("État"),
360
531
  note: z.string().optional().describe("Notes"),
361
- }).strict();
532
+ })
533
+ .strict();
362
534
  // ---- Opportunity schemas ----
363
- export const OpportunityCreateSchema = z.object({
535
+ export const OpportunityCreateSchema = z
536
+ .object({
364
537
  name: z.string().min(1).describe("Nom / titre de l'opportunité"),
365
538
  companyId: z.string().optional().describe("ID de la société cliente"),
366
539
  contactId: z.string().optional().describe("ID du contact associé"),
@@ -368,17 +541,21 @@ export const OpportunityCreateSchema = z.object({
368
541
  startDate: z.string().optional().describe("Date de début prévue (YYYY-MM-DD)"),
369
542
  endDate: z.string().optional().describe("Date de fin prévue (YYYY-MM-DD)"),
370
543
  note: z.string().optional().describe("Notes / description"),
371
- }).strict();
372
- export const OpportunityUpdateSchema = z.object({
544
+ })
545
+ .strict();
546
+ export const OpportunityUpdateSchema = z
547
+ .object({
373
548
  id: z.string().min(1).describe("ID de l'opportunité à modifier"),
374
549
  name: z.string().optional().describe("Nom / titre"),
375
550
  state: z.number().int().optional().describe("État"),
376
551
  startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
377
552
  endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
378
553
  note: z.string().optional().describe("Notes"),
379
- }).strict();
554
+ })
555
+ .strict();
380
556
  // ---- Action schemas ----
381
- export const ActionSearchSchema = z.object({
557
+ export const ActionSearchSchema = z
558
+ .object({
382
559
  keywords: z.string().optional().describe("Mots-clés de recherche"),
383
560
  candidateId: z.string().optional().describe("Filtrer par ID candidat"),
384
561
  resourceId: z.string().optional().describe("Filtrer par ID ressource"),
@@ -386,8 +563,10 @@ export const ActionSearchSchema = z.object({
386
563
  companyId: z.string().optional().describe("Filtrer par ID société"),
387
564
  page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
388
565
  pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE).describe("Résultats par page"),
389
- }).strict();
390
- export const ActionCreateSchema = z.object({
566
+ })
567
+ .strict();
568
+ export const ActionCreateSchema = z
569
+ .object({
391
570
  typeOf: z.string().min(1).describe("Type d'action (ex: call, email, meeting, note)"),
392
571
  subject: z.string().optional().describe("Sujet de l'action"),
393
572
  content: z.string().optional().describe("Contenu / description"),
@@ -397,25 +576,48 @@ export const ActionCreateSchema = z.object({
397
576
  resourceId: z.string().optional().describe("ID de la ressource associée"),
398
577
  contactId: z.string().optional().describe("ID du contact associé"),
399
578
  companyId: z.string().optional().describe("ID de la société associée"),
400
- }).strict();
579
+ })
580
+ .strict();
401
581
  // ---- Timesheet schemas ----
402
- export const ResourceTimesheetSchema = z.object({
582
+ export const ResourceTimesheetSchema = z
583
+ .object({
403
584
  resourceId: z.string().min(1).describe("ID de la ressource"),
404
585
  month: z.number().int().min(1).max(12).optional().describe("Mois (1-12). Si omis, mois courant."),
405
586
  year: z.number().int().min(2000).optional().describe("Année (ex: 2025). Si omis, année courante."),
406
- }).strict();
407
- export const TimesheetSearchSchema = z.object({
408
- startDate: z.string().optional().describe("Date de début de période (YYYY-MM-DD)"),
409
- endDate: z.string().optional().describe("Date de fin de période (YYYY-MM-DD)"),
587
+ })
588
+ .strict();
589
+ // `/times-reports` requires `startMonth` and `endMonth` in YYYY-MM form. Passing
590
+ // YYYY-MM-DD or omitting them surfaces a 422 "Missing required attribute" from
591
+ // the API, so the schema enforces both at the boundary.
592
+ export const TimesheetSearchSchema = z
593
+ .object({
594
+ startMonth: z
595
+ .string()
596
+ .regex(/^\d{4}-\d{2}$/)
597
+ .describe("Mois de début au format YYYY-MM (ex: '2025-01'). Requis."),
598
+ endMonth: z
599
+ .string()
600
+ .regex(/^\d{4}-\d{2}$/)
601
+ .describe("Mois de fin au format YYYY-MM (ex: '2025-03'). Requis."),
602
+ keywords: z.string().optional().describe("Mots-clés (préfixes 'TPS', 'COMP'...)."),
410
603
  page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
411
- pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE)
604
+ pageSize: z
605
+ .number()
606
+ .int()
607
+ .min(1)
608
+ .max(MAX_PAGE_SIZE)
609
+ .default(DEFAULT_PAGE_SIZE)
412
610
  .describe(`Nombre de résultats par page (max: ${MAX_PAGE_SIZE}, défaut: ${DEFAULT_PAGE_SIZE})`),
413
- }).strict();
414
- export const TimesheetGetSchema = z.object({
611
+ })
612
+ .strict();
613
+ export const TimesheetGetSchema = z
614
+ .object({
415
615
  id: z.string().min(1).describe("Identifiant unique de la feuille de temps"),
416
- }).strict();
616
+ })
617
+ .strict();
417
618
  // ---- Project schemas ----
418
- export const ProjectCreateSchema = z.object({
619
+ export const ProjectCreateSchema = z
620
+ .object({
419
621
  name: z.string().min(1).describe("Nom du projet / mission"),
420
622
  companyId: z.string().optional().describe("ID de la société cliente"),
421
623
  contactId: z.string().optional().describe("ID du contact associé"),
@@ -424,17 +626,21 @@ export const ProjectCreateSchema = z.object({
424
626
  startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
425
627
  endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
426
628
  note: z.string().optional().describe("Notes / description du projet"),
427
- }).strict();
428
- export const ProjectUpdateSchema = z.object({
629
+ })
630
+ .strict();
631
+ export const ProjectUpdateSchema = z
632
+ .object({
429
633
  id: z.string().min(1).describe("ID du projet à modifier"),
430
634
  name: z.string().optional().describe("Nom du projet"),
431
635
  state: z.number().int().optional().describe("État du projet"),
432
636
  startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
433
637
  endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
434
638
  note: z.string().optional().describe("Notes"),
435
- }).strict();
639
+ })
640
+ .strict();
436
641
  // ---- Invoice schemas ----
437
- export const InvoiceCreateSchema = z.object({
642
+ export const InvoiceCreateSchema = z
643
+ .object({
438
644
  reference: z.string().optional().describe("Référence de la facture"),
439
645
  companyId: z.string().optional().describe("ID de la société facturée"),
440
646
  projectId: z.string().optional().describe("ID du projet associé"),
@@ -444,8 +650,10 @@ export const InvoiceCreateSchema = z.object({
444
650
  amountExcludingTax: z.number().optional().describe("Montant HT"),
445
651
  taxRate: z.number().optional().describe("Taux de TVA (%)"),
446
652
  note: z.string().optional().describe("Notes / commentaires"),
447
- }).strict();
448
- export const InvoiceUpdateSchema = z.object({
653
+ })
654
+ .strict();
655
+ export const InvoiceUpdateSchema = z
656
+ .object({
449
657
  id: z.string().min(1).describe("ID de la facture à modifier"),
450
658
  reference: z.string().optional().describe("Référence de la facture"),
451
659
  state: z.number().int().optional().describe("État de la facture"),
@@ -454,19 +662,26 @@ export const InvoiceUpdateSchema = z.object({
454
662
  amountExcludingTax: z.number().optional().describe("Montant HT"),
455
663
  taxRate: z.number().optional().describe("Taux de TVA (%)"),
456
664
  note: z.string().optional().describe("Notes"),
457
- }).strict();
458
- export const InvoiceSearchSchema = z.object({
665
+ })
666
+ .strict();
667
+ export const InvoiceSearchSchema = z
668
+ .object({
459
669
  keywords: z.string().optional().describe("Mots-clés de recherche (référence, société...)"),
460
670
  companyId: z.string().optional().describe("Filtrer par ID société"),
461
671
  projectId: z.string().optional().describe("Filtrer par ID projet"),
462
672
  startDate: z.string().optional().describe("Date de début de période (YYYY-MM-DD)"),
463
673
  endDate: z.string().optional().describe("Date de fin de période (YYYY-MM-DD)"),
464
- period: z.string().optional().describe("Type de période (created, updated, expectedPayment, performedPayment, period)"),
674
+ period: z
675
+ .string()
676
+ .optional()
677
+ .describe("Type de période (created, updated, expectedPayment, performedPayment, period)"),
465
678
  page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
466
679
  pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE).describe("Résultats par page"),
467
- }).strict();
680
+ })
681
+ .strict();
468
682
  // ---- Order schemas (Bons de commande) ----
469
- export const OrderCreateSchema = z.object({
683
+ export const OrderCreateSchema = z
684
+ .object({
470
685
  reference: z.string().optional().describe("Référence du bon de commande"),
471
686
  companyId: z.string().optional().describe("ID de la société"),
472
687
  projectId: z.string().optional().describe("ID du projet associé"),
@@ -474,24 +689,30 @@ export const OrderCreateSchema = z.object({
474
689
  orderDate: z.string().optional().describe("Date du bon de commande (YYYY-MM-DD)"),
475
690
  amountExcludingTax: z.number().optional().describe("Montant HT"),
476
691
  note: z.string().optional().describe("Notes / commentaires"),
477
- }).strict();
478
- export const OrderUpdateSchema = z.object({
692
+ })
693
+ .strict();
694
+ export const OrderUpdateSchema = z
695
+ .object({
479
696
  id: z.string().min(1).describe("ID du bon de commande à modifier"),
480
697
  reference: z.string().optional().describe("Référence"),
481
698
  state: z.number().int().optional().describe("État"),
482
699
  orderDate: z.string().optional().describe("Date (YYYY-MM-DD)"),
483
700
  amountExcludingTax: z.number().optional().describe("Montant HT"),
484
701
  note: z.string().optional().describe("Notes"),
485
- }).strict();
486
- export const OrderSearchSchema = z.object({
702
+ })
703
+ .strict();
704
+ export const OrderSearchSchema = z
705
+ .object({
487
706
  keywords: z.string().optional().describe("Mots-clés de recherche"),
488
707
  companyId: z.string().optional().describe("Filtrer par ID société"),
489
708
  projectId: z.string().optional().describe("Filtrer par ID projet"),
490
709
  page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
491
710
  pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE).describe("Résultats par page"),
492
- }).strict();
711
+ })
712
+ .strict();
493
713
  // ---- Delivery schemas (Livraisons / CRA) ----
494
- export const DeliverySearchSchema = z.object({
714
+ export const DeliverySearchSchema = z
715
+ .object({
495
716
  keywords: z.string().optional().describe("Mots-clés de recherche"),
496
717
  projectId: z.string().optional().describe("Filtrer par ID projet"),
497
718
  companyId: z.string().optional().describe("Filtrer par ID société"),
@@ -499,33 +720,41 @@ export const DeliverySearchSchema = z.object({
499
720
  endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
500
721
  page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
501
722
  pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE).describe("Résultats par page"),
502
- }).strict();
723
+ })
724
+ .strict();
503
725
  // ---- Absence schemas ----
504
- export const AbsenceCreateSchema = z.object({
726
+ export const AbsenceCreateSchema = z
727
+ .object({
505
728
  resourceId: z.string().min(1).describe("ID de la ressource en absence"),
506
729
  typeOf: z.string().min(1).describe("Type d'absence (congé payé, RTT, maladie, sans solde...)"),
507
730
  startDate: z.string().min(1).describe("Date de début (YYYY-MM-DD)"),
508
731
  endDate: z.string().min(1).describe("Date de fin (YYYY-MM-DD)"),
509
732
  state: z.number().int().optional().describe("État de la demande (0=en attente, 1=validé, 2=refusé...)"),
510
733
  note: z.string().optional().describe("Commentaire / motif"),
511
- }).strict();
512
- export const AbsenceUpdateSchema = z.object({
734
+ })
735
+ .strict();
736
+ export const AbsenceUpdateSchema = z
737
+ .object({
513
738
  id: z.string().min(1).describe("ID de l'absence à modifier"),
514
739
  startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
515
740
  endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
516
741
  state: z.number().int().optional().describe("État de la demande"),
517
742
  note: z.string().optional().describe("Commentaire / motif"),
518
- }).strict();
519
- export const AbsenceSearchSchema = z.object({
743
+ })
744
+ .strict();
745
+ export const AbsenceSearchSchema = z
746
+ .object({
520
747
  keywords: z.string().optional().describe("Mots-clés de recherche"),
521
748
  resourceId: z.string().optional().describe("Filtrer par ID ressource"),
522
749
  startDate: z.string().optional().describe("Date de début de période (YYYY-MM-DD)"),
523
750
  endDate: z.string().optional().describe("Date de fin de période (YYYY-MM-DD)"),
524
751
  page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
525
752
  pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE).describe("Résultats par page"),
526
- }).strict();
753
+ })
754
+ .strict();
527
755
  // ---- Expense schemas (Notes de frais) ----
528
- export const ExpenseCreateSchema = z.object({
756
+ export const ExpenseCreateSchema = z
757
+ .object({
529
758
  resourceId: z.string().min(1).describe("ID de la ressource"),
530
759
  projectId: z.string().optional().describe("ID du projet associé"),
531
760
  typeOf: z.string().optional().describe("Type de frais (transport, repas, hébergement...)"),
@@ -534,14 +763,18 @@ export const ExpenseCreateSchema = z.object({
534
763
  currency: z.string().optional().describe("Devise (EUR, USD...)"),
535
764
  state: z.number().int().optional().describe("État de la note de frais"),
536
765
  note: z.string().optional().describe("Description / justification"),
537
- }).strict();
538
- export const ExpenseUpdateSchema = z.object({
766
+ })
767
+ .strict();
768
+ export const ExpenseUpdateSchema = z
769
+ .object({
539
770
  id: z.string().min(1).describe("ID de la note de frais à modifier"),
540
771
  amount: z.number().optional().describe("Montant"),
541
772
  state: z.number().int().optional().describe("État"),
542
773
  note: z.string().optional().describe("Description"),
543
- }).strict();
544
- export const ExpenseSearchSchema = z.object({
774
+ })
775
+ .strict();
776
+ export const ExpenseSearchSchema = z
777
+ .object({
545
778
  keywords: z.string().optional().describe("Mots-clés de recherche"),
546
779
  resourceId: z.string().optional().describe("Filtrer par ID ressource"),
547
780
  projectId: z.string().optional().describe("Filtrer par ID projet"),
@@ -549,17 +782,21 @@ export const ExpenseSearchSchema = z.object({
549
782
  endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
550
783
  page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
551
784
  pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE).describe("Résultats par page"),
552
- }).strict();
785
+ })
786
+ .strict();
553
787
  // ---- Product schemas ----
554
- export const ProductCreateSchema = z.object({
788
+ export const ProductCreateSchema = z
789
+ .object({
555
790
  name: z.string().min(1).describe("Nom du produit"),
556
791
  reference: z.string().optional().describe("Référence du produit"),
557
792
  unitPrice: z.number().optional().describe("Prix unitaire HT"),
558
793
  taxRate: z.number().optional().describe("Taux de TVA (%)"),
559
794
  state: z.number().int().optional().describe("État du produit"),
560
795
  note: z.string().optional().describe("Description du produit"),
561
- }).strict();
562
- export const ProductUpdateSchema = z.object({
796
+ })
797
+ .strict();
798
+ export const ProductUpdateSchema = z
799
+ .object({
563
800
  id: z.string().min(1).describe("ID du produit à modifier"),
564
801
  name: z.string().optional().describe("Nom du produit"),
565
802
  reference: z.string().optional().describe("Référence"),
@@ -567,9 +804,11 @@ export const ProductUpdateSchema = z.object({
567
804
  taxRate: z.number().optional().describe("Taux de TVA (%)"),
568
805
  state: z.number().int().optional().describe("État"),
569
806
  note: z.string().optional().describe("Description"),
570
- }).strict();
807
+ })
808
+ .strict();
571
809
  // ---- Positioning schemas ----
572
- export const PositioningCreateSchema = z.object({
810
+ export const PositioningCreateSchema = z
811
+ .object({
573
812
  candidateId: z.string().optional().describe("ID du candidat positionné"),
574
813
  resourceId: z.string().optional().describe("ID de la ressource positionnée"),
575
814
  projectId: z.string().optional().describe("ID du projet"),
@@ -578,8 +817,10 @@ export const PositioningCreateSchema = z.object({
578
817
  startDate: z.string().optional().describe("Date de début (YYYY-MM-DD)"),
579
818
  endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
580
819
  note: z.string().optional().describe("Notes / commentaires"),
581
- }).strict();
582
- export const PositioningSearchSchema = z.object({
820
+ })
821
+ .strict();
822
+ export const PositioningSearchSchema = z
823
+ .object({
583
824
  keywords: z.string().optional().describe("Mots-clés de recherche"),
584
825
  candidateId: z.string().optional().describe("Filtrer par ID candidat"),
585
826
  resourceId: z.string().optional().describe("Filtrer par ID ressource"),
@@ -587,9 +828,11 @@ export const PositioningSearchSchema = z.object({
587
828
  opportunityId: z.string().optional().describe("Filtrer par ID opportunité"),
588
829
  page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
589
830
  pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE).describe("Résultats par page"),
590
- }).strict();
831
+ })
832
+ .strict();
591
833
  // ---- Payment schemas ----
592
- export const PaymentSearchSchema = z.object({
834
+ export const PaymentSearchSchema = z
835
+ .object({
593
836
  keywords: z.string().optional().describe("Mots-clés de recherche"),
594
837
  invoiceId: z.string().optional().describe("Filtrer par ID facture"),
595
838
  companyId: z.string().optional().describe("Filtrer par ID société"),
@@ -597,16 +840,136 @@ export const PaymentSearchSchema = z.object({
597
840
  endDate: z.string().optional().describe("Date de fin (YYYY-MM-DD)"),
598
841
  page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
599
842
  pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE).describe("Résultats par page"),
600
- }).strict();
843
+ })
844
+ .strict();
601
845
  // ---- Advantage schemas ----
602
- export const AdvantageSearchSchema = z.object({
846
+ export const AdvantageSearchSchema = z
847
+ .object({
603
848
  keywords: z.string().optional().describe("Mots-clés de recherche"),
604
849
  resourceId: z.string().optional().describe("Filtrer par ID ressource"),
605
850
  page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
606
851
  pageSize: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE).describe("Résultats par page"),
607
- }).strict();
852
+ })
853
+ .strict();
608
854
  // ---- Application schemas ----
609
- export const DictionaryGetSchema = z.object({
610
- dictionaryType: z.string().min(1).describe("Type de dictionnaire (ex: typeOf/actions, typeOf/absences, states/candidates, states/resources, states/opportunities, states/projects, states/invoices, countries, currencies, languages...)"),
611
- }).strict();
855
+ // ---- Validation schemas ----
856
+ // `/validations` requires `startMonth` + `endMonth` (YYYY-MM). Optional filters
857
+ // from the official RAML follow.
858
+ export const ValidationSearchSchema = z
859
+ .object({
860
+ startMonth: z
861
+ .string()
862
+ .regex(/^\d{4}-\d{2}$/)
863
+ .describe("Mois de début YYYY-MM. Requis."),
864
+ endMonth: z
865
+ .string()
866
+ .regex(/^\d{4}-\d{2}$/)
867
+ .describe("Mois de fin YYYY-MM. Requis."),
868
+ keywords: z
869
+ .string()
870
+ .optional()
871
+ .describe("Mots-clés. Préfixes acceptés : 'TPS' (CRA), 'EXP' (frais), 'ABS' (absence), 'COMP' (ressource)."),
872
+ documentTypes: z
873
+ .array(z.enum(["absencesReport", "timesReport", "expensesReport"]))
874
+ .optional()
875
+ .describe("Types de documents à valider."),
876
+ resourceTypes: z
877
+ .array(z.number().int())
878
+ .optional()
879
+ .describe("IDs de types de ressource (dictionnaire setting.typeOf.resource)."),
880
+ validationStates: z
881
+ .array(z.enum(["waitingForValidation", "validated", "rejected"]))
882
+ .optional()
883
+ .describe("États de validation."),
884
+ validationAlerts: z.boolean().optional().describe("Filtrer sur les validations avec alertes."),
885
+ page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
886
+ pageSize: z
887
+ .number()
888
+ .int()
889
+ .min(1)
890
+ .max(MAX_PAGE_SIZE)
891
+ .default(DEFAULT_PAGE_SIZE)
892
+ .describe(`Nombre de résultats par page (max: ${MAX_PAGE_SIZE}, défaut: ${DEFAULT_PAGE_SIZE})`),
893
+ })
894
+ .strict();
895
+ // ---- Notification schemas ----
896
+ // `/notifications` requires the singular `category` (activity/thread/corporate)
897
+ // per the official RAML. Optional `state` filters read/unread; `parentType`
898
+ // narrows by entity module.
899
+ export const NotificationSearchSchema = z
900
+ .object({
901
+ category: z
902
+ .enum(["activity", "thread", "corporate"])
903
+ .describe("Catégorie (requis): 'activity' (notifications d'activité), 'thread' (messages), 'corporate' (annonces)."),
904
+ state: z.enum(["new", "read"]).optional().describe("Filtrer par état de lecture."),
905
+ parentType: z
906
+ .array(z.string())
907
+ .optional()
908
+ .describe("Types de modules parents (ex: 'contract', 'global', 'project'...)."),
909
+ page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
910
+ pageSize: z
911
+ .number()
912
+ .int()
913
+ .min(1)
914
+ .max(MAX_PAGE_SIZE)
915
+ .default(DEFAULT_PAGE_SIZE)
916
+ .describe(`Nombre de résultats par page (max: ${MAX_PAGE_SIZE}, défaut: ${DEFAULT_PAGE_SIZE})`),
917
+ })
918
+ .strict();
919
+ // ---- Reporting schemas ----
920
+ // `/reporting-companies` and `/reporting-resources` require `startDate` +
921
+ // `endDate` (YYYY-MM-DD); the others tolerate omission but still benefit from
922
+ // a period filter.
923
+ export const ReportingDateRequiredSchema = z
924
+ .object({
925
+ startDate: z
926
+ .string()
927
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
928
+ .describe("Date de début YYYY-MM-DD. Requis."),
929
+ endDate: z
930
+ .string()
931
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
932
+ .describe("Date de fin YYYY-MM-DD. Requis."),
933
+ keywords: z.string().optional().describe("Mots-clés."),
934
+ page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
935
+ pageSize: z
936
+ .number()
937
+ .int()
938
+ .min(1)
939
+ .max(MAX_PAGE_SIZE)
940
+ .default(DEFAULT_PAGE_SIZE)
941
+ .describe(`Nombre de résultats par page (max: ${MAX_PAGE_SIZE}, défaut: ${DEFAULT_PAGE_SIZE})`),
942
+ })
943
+ .strict();
944
+ export const ReportingDateOptionalSchema = z
945
+ .object({
946
+ startDate: z
947
+ .string()
948
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
949
+ .optional()
950
+ .describe("Date de début YYYY-MM-DD."),
951
+ endDate: z
952
+ .string()
953
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
954
+ .optional()
955
+ .describe("Date de fin YYYY-MM-DD."),
956
+ keywords: z.string().optional().describe("Mots-clés."),
957
+ page: z.number().int().min(1).max(MAX_SEARCH_PAGE).default(1).describe(`Numéro de page (max: ${MAX_SEARCH_PAGE})`),
958
+ pageSize: z
959
+ .number()
960
+ .int()
961
+ .min(1)
962
+ .max(MAX_PAGE_SIZE)
963
+ .default(DEFAULT_PAGE_SIZE)
964
+ .describe(`Nombre de résultats par page (max: ${MAX_PAGE_SIZE}, défaut: ${DEFAULT_PAGE_SIZE})`),
965
+ })
966
+ .strict();
967
+ export const DictionaryGetSchema = z
968
+ .object({
969
+ dictionaryType: z
970
+ .string()
971
+ .min(1)
972
+ .describe("Type de dictionnaire (ex: typeOf/actions, typeOf/absences, states/candidates, states/resources, states/opportunities, states/projects, states/invoices, countries, currencies, languages...)"),
973
+ })
974
+ .strict();
612
975
  //# sourceMappingURL=index.js.map