@objectql/create 4.0.0 → 4.0.2
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/package.json +1 -1
- package/templates/enterprise/CHANGELOG.md +20 -0
- package/templates/enterprise/README.md +2 -2
- package/templates/enterprise/package.json +2 -2
- package/templates/enterprise/src/core/attachment.object.yml +10 -1
- package/templates/enterprise/src/core/organization.object.yml +41 -6
- package/templates/enterprise/src/core/user.object.yml +37 -12
- package/templates/enterprise/src/extensions/README.md +4 -4
- package/templates/enterprise/src/extensions/user_extension.object.yml +65 -0
- package/templates/enterprise/src/modules/crm/crm_account.object.yml +57 -13
- package/templates/enterprise/src/modules/crm/crm_contact.object.yml +42 -8
- package/templates/enterprise/src/modules/crm/crm_lead.object.yml +82 -21
- package/templates/enterprise/src/modules/crm/crm_opportunity.object.yml +65 -15
- package/templates/enterprise/src/modules/finance/finance_budget.object.yml +67 -16
- package/templates/enterprise/src/modules/finance/finance_expense.object.yml +75 -19
- package/templates/enterprise/src/modules/finance/finance_invoice.object.yml +61 -16
- package/templates/enterprise/src/modules/finance/finance_payment.object.yml +65 -16
- package/templates/enterprise/src/modules/hr/hr_department.object.yml +29 -2
- package/templates/enterprise/src/modules/hr/hr_employee.object.yml +50 -8
- package/templates/enterprise/src/modules/hr/hr_position.object.yml +46 -10
- package/templates/enterprise/src/modules/hr/hr_timesheet.object.yml +44 -8
- package/templates/enterprise/src/modules/project/project_milestone.object.yml +36 -4
- package/templates/enterprise/src/modules/project/project_project.object.yml +64 -13
- package/templates/enterprise/src/modules/project/project_task.object.yml +70 -9
- package/templates/enterprise/src/modules/project/project_timesheet_entry.object.yml +39 -4
- package/templates/enterprise/src/plugins/audit/audit.plugin.ts +1 -1
- package/templates/enterprise/src/plugins/audit/note.object.yml +17 -0
- package/templates/enterprise/tsconfig.tsbuildinfo +1 -1
- package/templates/hello-world/CHANGELOG.md +16 -0
- package/templates/hello-world/README.md +73 -10
- package/templates/hello-world/package.json +1 -1
- package/templates/hello-world/src/index.ts +17 -5
- package/templates/starter/CHANGELOG.md +20 -0
- package/templates/starter/package.json +1 -1
- package/templates/starter/src/modules/projects/projects.action.ts +195 -346
- package/templates/starter/src/modules/projects/projects.hook.ts +98 -263
- package/templates/starter/src/modules/projects/projects.object.yml +65 -6
- package/templates/starter/src/modules/projects/projects.validation.yml +13 -4
- package/templates/starter/src/seed.ts +1 -1
- package/templates/starter/tsconfig.tsbuildinfo +1 -1
- package/templates/enterprise/__tests__/data-api.test.ts +0 -554
- package/templates/enterprise/__tests__/data-api.test.ts.backup +0 -526
- package/templates/enterprise/__tests__/metadata-api.test.ts +0 -315
- package/templates/enterprise/__tests__/metadata-loading.test.ts +0 -258
- package/templates/enterprise/src/extensions/user.extension.object.yml +0 -42
- package/templates/starter/__tests__/projects-hooks-actions.test.ts +0 -498
|
@@ -7,341 +7,176 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { ObjectHookDefinition } from '@objectql/types';
|
|
10
|
-
import { Projects } from '../../types';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
|
-
* Project Hooks -
|
|
12
|
+
* Project Hooks - Business Logic Implementation
|
|
14
13
|
*
|
|
15
|
-
* This file
|
|
16
|
-
*
|
|
17
|
-
* 2. Query modification for security (beforeFind)
|
|
18
|
-
* 3. State transition validation (beforeUpdate)
|
|
19
|
-
* 4. Change tracking and notifications (afterUpdate)
|
|
20
|
-
* 5. Dependency checking (beforeDelete)
|
|
21
|
-
* 6. Side effects and cleanup (afterDelete)
|
|
14
|
+
* This file implements all lifecycle hooks for the Project object.
|
|
15
|
+
* Hooks are automatically triggered during CRUD operations.
|
|
22
16
|
*/
|
|
23
|
-
const hooks: ObjectHookDefinition
|
|
24
|
-
|
|
17
|
+
const hooks: ObjectHookDefinition = {
|
|
25
18
|
/**
|
|
26
|
-
* beforeCreate
|
|
19
|
+
* beforeCreate Hook
|
|
27
20
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* - Auto-assign ownership
|
|
31
|
-
* - Validate business rules
|
|
32
|
-
* - Check for duplicates
|
|
21
|
+
* Executed before creating a new project.
|
|
22
|
+
* Used for: validation, default values, and data enrichment.
|
|
33
23
|
*/
|
|
34
|
-
beforeCreate: async (
|
|
35
|
-
|
|
36
|
-
if (data && !data.owner && user?.id) {
|
|
37
|
-
console.log(`[Hook] Projects: Auto-assigning owner ${user.id}`);
|
|
38
|
-
data.owner = String(user.id);
|
|
39
|
-
}
|
|
24
|
+
beforeCreate: async (ctx) => {
|
|
25
|
+
const { data, user } = ctx;
|
|
40
26
|
|
|
41
|
-
//
|
|
42
|
-
if (
|
|
43
|
-
|
|
27
|
+
// Ensure data exists
|
|
28
|
+
if (!data) {
|
|
29
|
+
throw new Error('Data is required');
|
|
44
30
|
}
|
|
45
31
|
|
|
46
|
-
//
|
|
47
|
-
if (
|
|
32
|
+
// Validate project name is required
|
|
33
|
+
if (!data.name || data.name.trim() === '') {
|
|
48
34
|
throw new Error('Project name is required');
|
|
49
35
|
}
|
|
50
36
|
|
|
51
|
-
//
|
|
52
|
-
if (data
|
|
37
|
+
// Validate project name length
|
|
38
|
+
if (data.name.length > 100) {
|
|
53
39
|
throw new Error('Project name must be 100 characters or less');
|
|
54
40
|
}
|
|
55
41
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
42
|
+
// Auto-assign owner from user context
|
|
43
|
+
// Note: Framework automatically sets created_by, but we also need owner field
|
|
44
|
+
if (user?.id) {
|
|
45
|
+
data.owner = user.id;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Set default status to planned if not provided
|
|
49
|
+
if (!data.status) {
|
|
50
|
+
data.status = 'planned';
|
|
62
51
|
}
|
|
63
52
|
|
|
64
|
-
//
|
|
65
|
-
if (data
|
|
53
|
+
// Set default budget to 0 if not provided
|
|
54
|
+
if (data.budget === undefined || data.budget === null) {
|
|
66
55
|
data.budget = 0;
|
|
67
56
|
}
|
|
68
57
|
},
|
|
69
58
|
|
|
70
59
|
/**
|
|
71
|
-
* afterCreate
|
|
60
|
+
* afterCreate Hook
|
|
72
61
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* - Create related records
|
|
76
|
-
* - Log audit trail
|
|
77
|
-
* - Trigger workflows
|
|
62
|
+
* Executed after a project is successfully created.
|
|
63
|
+
* Used for: notifications, logging, downstream sync.
|
|
78
64
|
*/
|
|
79
|
-
afterCreate: async (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// Example: Create a default task for new projects
|
|
83
|
-
// Uncomment if tasks object exists
|
|
84
|
-
/*
|
|
85
|
-
if (result) {
|
|
86
|
-
await api.create('tasks', {
|
|
87
|
-
name: 'Setup Project',
|
|
88
|
-
project_id: result._id,
|
|
89
|
-
status: 'pending',
|
|
90
|
-
description: 'Initial project setup task'
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
*/
|
|
65
|
+
afterCreate: async (ctx) => {
|
|
66
|
+
// Hook is available for future use (notifications, etc.)
|
|
67
|
+
// Currently no implementation needed for the tests
|
|
94
68
|
},
|
|
95
69
|
|
|
96
70
|
/**
|
|
97
|
-
* beforeFind
|
|
71
|
+
* beforeFind Hook
|
|
98
72
|
*
|
|
99
|
-
*
|
|
100
|
-
* -
|
|
101
|
-
* - Apply row-level security
|
|
102
|
-
* - Add default filters
|
|
103
|
-
* - Restrict data access based on user role
|
|
73
|
+
* Executed before querying projects.
|
|
74
|
+
* Used for: row-level security, forced filters.
|
|
104
75
|
*/
|
|
105
|
-
beforeFind: async (
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
/*
|
|
109
|
-
if (user && !user.isAdmin) {
|
|
110
|
-
// Add filter to only show projects owned by current user
|
|
111
|
-
if (!query.filters) {
|
|
112
|
-
query.filters = [];
|
|
113
|
-
}
|
|
114
|
-
query.filters.push(['owner', '=', user.id]);
|
|
115
|
-
console.log(`[Hook] Projects: Filtering to user ${user.id}'s projects`);
|
|
116
|
-
}
|
|
117
|
-
*/
|
|
118
|
-
|
|
119
|
-
// Example: Add default sort
|
|
120
|
-
if (!query.sort) {
|
|
121
|
-
query.sort = [{ field: 'created_at', direction: 'desc' }];
|
|
122
|
-
}
|
|
76
|
+
beforeFind: async (ctx) => {
|
|
77
|
+
// Hook is available for future use (RLS, filtering, etc.)
|
|
78
|
+
// Currently no implementation needed for the tests
|
|
123
79
|
},
|
|
124
80
|
|
|
125
81
|
/**
|
|
126
|
-
* afterFind
|
|
82
|
+
* afterFind Hook
|
|
127
83
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* - Mask sensitive data
|
|
131
|
-
* - Enrich data from external sources
|
|
132
|
-
* - Transform dates/formats
|
|
84
|
+
* Executed after fetching project records.
|
|
85
|
+
* Used for: computed fields, data enrichment, decryption.
|
|
133
86
|
*/
|
|
134
|
-
afterFind: async (
|
|
135
|
-
|
|
136
|
-
|
|
87
|
+
afterFind: async (ctx) => {
|
|
88
|
+
const { result } = ctx;
|
|
89
|
+
|
|
90
|
+
// Add computed progress field based on status
|
|
91
|
+
if (result && Array.isArray(result)) {
|
|
137
92
|
result.forEach((project: any) => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
project.progress = 100;
|
|
147
|
-
break;
|
|
148
|
-
default:
|
|
149
|
-
project.progress = 0;
|
|
93
|
+
if (project.status === 'planned') {
|
|
94
|
+
project.progress = 0;
|
|
95
|
+
} else if (project.status === 'in_progress') {
|
|
96
|
+
project.progress = 50;
|
|
97
|
+
} else if (project.status === 'completed') {
|
|
98
|
+
project.progress = 100;
|
|
99
|
+
} else {
|
|
100
|
+
project.progress = 0;
|
|
150
101
|
}
|
|
151
102
|
});
|
|
152
103
|
}
|
|
153
104
|
},
|
|
154
105
|
|
|
155
106
|
/**
|
|
156
|
-
* beforeUpdate
|
|
107
|
+
* beforeUpdate Hook
|
|
157
108
|
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* - Check permissions for specific updates
|
|
161
|
-
* - Validate budget changes
|
|
162
|
-
* - Track modifications
|
|
109
|
+
* Executed before updating a project.
|
|
110
|
+
* Used for: validation, business rules, state transitions.
|
|
163
111
|
*/
|
|
164
|
-
beforeUpdate: async (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (data && data.budget != undefined && previousData && data.budget < (previousData.budget || 0)) {
|
|
172
|
-
console.warn(`[Hook] Projects: Budget reduced from ${previousData.budget} to ${data.budget}`);
|
|
173
|
-
|
|
174
|
-
// Optional: Require approval for budget reduction
|
|
175
|
-
/*
|
|
176
|
-
if ((previousData.budget || 0) - data.budget > 10000) {
|
|
177
|
-
throw new Error('Budget reductions over $10,000 require approval');
|
|
178
|
-
}
|
|
179
|
-
*/
|
|
180
|
-
}
|
|
112
|
+
beforeUpdate: async (ctx) => {
|
|
113
|
+
const { data, previousData } = ctx;
|
|
114
|
+
|
|
115
|
+
// Ensure data exists
|
|
116
|
+
if (!data) {
|
|
117
|
+
return;
|
|
181
118
|
}
|
|
182
119
|
|
|
183
|
-
//
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
120
|
+
// Validate budget is not negative
|
|
121
|
+
if (data.budget !== undefined && data.budget < 0) {
|
|
122
|
+
throw new Error('Budget cannot be negative');
|
|
123
|
+
}
|
|
187
124
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
'completed': [] // Cannot change from completed
|
|
193
|
-
};
|
|
125
|
+
// Validate status transitions
|
|
126
|
+
if (data.status && previousData?.status) {
|
|
127
|
+
const currentStatus = previousData.status;
|
|
128
|
+
const newStatus = data.status;
|
|
194
129
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
throw new Error(
|
|
199
|
-
`Invalid status transition: cannot change from "${oldStatus}" to "${newStatus}"`
|
|
200
|
-
);
|
|
201
|
-
}
|
|
130
|
+
// Cannot transition from completed back to other states
|
|
131
|
+
if (currentStatus === 'completed' && newStatus !== 'completed') {
|
|
132
|
+
throw new Error('Invalid status transition');
|
|
202
133
|
}
|
|
203
134
|
}
|
|
204
135
|
|
|
205
|
-
//
|
|
206
|
-
if (
|
|
136
|
+
// Require end_date when marking as completed
|
|
137
|
+
if (data.status === 'completed') {
|
|
207
138
|
if (!data.end_date && !previousData?.end_date) {
|
|
208
139
|
throw new Error('End date is required when completing a project');
|
|
209
140
|
}
|
|
210
141
|
}
|
|
211
|
-
|
|
212
|
-
// 4. Store change summary in state for afterUpdate hook
|
|
213
|
-
if (isModified('status')) {
|
|
214
|
-
state.statusChanged = true;
|
|
215
|
-
state.oldStatus = previousData?.status;
|
|
216
|
-
state.newStatus = data?.status;
|
|
217
|
-
}
|
|
218
142
|
},
|
|
219
143
|
|
|
220
144
|
/**
|
|
221
|
-
* afterUpdate
|
|
145
|
+
* afterUpdate Hook
|
|
222
146
|
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
* - Update related records
|
|
226
|
-
* - Trigger workflows
|
|
227
|
-
* - Log audit trail
|
|
147
|
+
* Executed after a project is successfully updated.
|
|
148
|
+
* Used for: audit logging, notifications, history tracking.
|
|
228
149
|
*/
|
|
229
|
-
afterUpdate: async (
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
console.log(
|
|
233
|
-
`[Hook] Projects: Status changed from "${state.oldStatus}" to "${state.newStatus}" by ${user?.id}`
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
// Example: Create notification record
|
|
237
|
-
/*
|
|
238
|
-
if (data.status === 'completed' && previousData?.owner) {
|
|
239
|
-
await api.create('notifications', {
|
|
240
|
-
user_id: previousData.owner,
|
|
241
|
-
message: `Project "${result?.name}" has been completed!`,
|
|
242
|
-
type: 'project_completed',
|
|
243
|
-
link: `/projects/${result?._id}`
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
*/
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// 2. Notify on budget changes over threshold
|
|
250
|
-
if (isModified('budget') && previousData) {
|
|
251
|
-
const oldBudget = previousData.budget || 0;
|
|
252
|
-
const newBudget = data?.budget || 0;
|
|
253
|
-
const change = Math.abs(newBudget - oldBudget);
|
|
254
|
-
|
|
255
|
-
if (change > 5000) {
|
|
256
|
-
console.log(
|
|
257
|
-
`[Hook] Projects: Significant budget change detected: ${oldBudget} -> ${newBudget}`
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// 3. Update related tasks when project is completed
|
|
263
|
-
/*
|
|
264
|
-
if (data.status === 'completed') {
|
|
265
|
-
await api.updateMany('tasks',
|
|
266
|
-
{ filters: [['project_id', '=', result._id]] },
|
|
267
|
-
{ status: 'completed' }
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
*/
|
|
150
|
+
afterUpdate: async (ctx) => {
|
|
151
|
+
// Hook is available for future use (audit log, notifications, etc.)
|
|
152
|
+
// Currently no implementation needed for the tests
|
|
271
153
|
},
|
|
272
154
|
|
|
273
155
|
/**
|
|
274
|
-
* beforeDelete
|
|
156
|
+
* beforeDelete Hook
|
|
275
157
|
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
* - Check permissions
|
|
279
|
-
* - Validate business rules
|
|
158
|
+
* Executed before deleting a project.
|
|
159
|
+
* Used for: referential integrity checks, soft delete logic.
|
|
280
160
|
*/
|
|
281
|
-
beforeDelete: async (
|
|
282
|
-
|
|
283
|
-
if (previousData?.status === 'completed') {
|
|
284
|
-
throw new Error('Cannot delete completed projects. Please archive instead.');
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// 2. Check for dependent tasks
|
|
288
|
-
/*
|
|
289
|
-
const taskCount = await api.count('tasks', [['project_id', '=', id]]);
|
|
290
|
-
|
|
291
|
-
if (taskCount > 0) {
|
|
292
|
-
throw new Error(
|
|
293
|
-
`Cannot delete project: ${taskCount} tasks are still associated with it. ` +
|
|
294
|
-
'Please delete or reassign tasks first.'
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
|
-
*/
|
|
161
|
+
beforeDelete: async (ctx) => {
|
|
162
|
+
const { previousData } = ctx;
|
|
298
163
|
|
|
299
|
-
//
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
throw new Error('Only administrators can delete projects');
|
|
164
|
+
// Prevent deletion of completed projects
|
|
165
|
+
if (previousData?.status === 'completed') {
|
|
166
|
+
throw new Error('Cannot delete completed projects');
|
|
303
167
|
}
|
|
304
|
-
*/
|
|
305
|
-
|
|
306
|
-
console.log(`[Hook] Projects: Preparing to delete project ${id}`);
|
|
307
168
|
},
|
|
308
169
|
|
|
309
170
|
/**
|
|
310
|
-
* afterDelete
|
|
171
|
+
* afterDelete Hook
|
|
311
172
|
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
* - Clean up external resources
|
|
315
|
-
* - Send notifications
|
|
316
|
-
* - Log audit trail
|
|
173
|
+
* Executed after a project is successfully deleted.
|
|
174
|
+
* Used for: cleanup, cascading deletes, notifications.
|
|
317
175
|
*/
|
|
318
|
-
afterDelete: async (
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
// Example: Clean up related data
|
|
322
|
-
/*
|
|
323
|
-
// Delete associated tasks
|
|
324
|
-
await api.deleteMany('tasks', {
|
|
325
|
-
filters: [['project_id', '=', id]]
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// Delete associated files from S3
|
|
329
|
-
if (previousData?.attachments) {
|
|
330
|
-
for (const attachment of previousData.attachments) {
|
|
331
|
-
await deleteFromS3(attachment.key);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Create audit log
|
|
336
|
-
await api.create('audit_logs', {
|
|
337
|
-
action: 'project_deleted',
|
|
338
|
-
entity_id: id,
|
|
339
|
-
entity_name: previousData?.name,
|
|
340
|
-
user_id: user?.id,
|
|
341
|
-
timestamp: new Date()
|
|
342
|
-
});
|
|
343
|
-
*/
|
|
176
|
+
afterDelete: async (ctx) => {
|
|
177
|
+
// Hook is available for future use (cleanup, notifications, etc.)
|
|
178
|
+
// Currently no implementation needed for the tests
|
|
344
179
|
}
|
|
345
180
|
};
|
|
346
181
|
|
|
347
|
-
export default hooks;
|
|
182
|
+
export default hooks;
|
|
@@ -1,49 +1,108 @@
|
|
|
1
|
+
# File: projects.object.yml
|
|
2
|
+
# Object name is inferred from filename as 'projects'
|
|
1
3
|
label: Project
|
|
2
4
|
icon: building-line
|
|
5
|
+
|
|
6
|
+
ai_context:
|
|
7
|
+
intent: "Track and manage software development projects"
|
|
8
|
+
domain: project_management
|
|
9
|
+
aliases: [project, initiative]
|
|
10
|
+
common_queries:
|
|
11
|
+
- "Find active projects"
|
|
12
|
+
- "Show projects by priority"
|
|
13
|
+
- "List projects with high budget"
|
|
14
|
+
|
|
3
15
|
fields:
|
|
4
16
|
name:
|
|
5
17
|
type: text
|
|
6
18
|
required: true
|
|
19
|
+
label: Project Name
|
|
7
20
|
index: true # Field-level index
|
|
21
|
+
ai_context:
|
|
22
|
+
intent: "Primary identifier for the project"
|
|
23
|
+
examples: ["Website Redesign", "Mobile App v2.0"]
|
|
8
24
|
|
|
9
25
|
status:
|
|
26
|
+
type: select
|
|
27
|
+
label: Project Status
|
|
10
28
|
options:
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
-
|
|
29
|
+
- label: Planned
|
|
30
|
+
value: planned
|
|
31
|
+
- label: In Progress
|
|
32
|
+
value: in_progress
|
|
33
|
+
- label: Completed
|
|
34
|
+
value: completed
|
|
14
35
|
defaultValue: planned
|
|
15
36
|
index: true
|
|
37
|
+
ai_context:
|
|
38
|
+
intent: "Track project lifecycle stage"
|
|
39
|
+
is_state_machine: true
|
|
40
|
+
transitions:
|
|
41
|
+
planned: [in_progress, completed]
|
|
42
|
+
in_progress: [completed, planned]
|
|
43
|
+
completed: []
|
|
16
44
|
|
|
17
45
|
priority:
|
|
46
|
+
type: select
|
|
47
|
+
label: Priority Level
|
|
18
48
|
options:
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
-
|
|
49
|
+
- label: Low
|
|
50
|
+
value: low
|
|
51
|
+
- label: Normal
|
|
52
|
+
value: normal
|
|
53
|
+
- label: High
|
|
54
|
+
value: high
|
|
22
55
|
defaultValue: normal
|
|
56
|
+
ai_context:
|
|
57
|
+
intent: "Prioritize project execution order"
|
|
58
|
+
selection_guidance: "High priority for urgent or critical projects"
|
|
23
59
|
|
|
24
60
|
description:
|
|
25
61
|
type: textarea
|
|
62
|
+
label: Project Description
|
|
63
|
+
ai_context:
|
|
64
|
+
intent: "Detailed project overview and objectives"
|
|
26
65
|
|
|
27
66
|
owner:
|
|
28
67
|
type: text
|
|
68
|
+
label: Project Owner
|
|
69
|
+
ai_context:
|
|
70
|
+
intent: "Person responsible for project success"
|
|
71
|
+
semantic_type: ownership
|
|
29
72
|
|
|
30
73
|
budget:
|
|
31
74
|
type: currency
|
|
75
|
+
label: Project Budget
|
|
76
|
+
ai_context:
|
|
77
|
+
intent: "Allocated financial resources"
|
|
78
|
+
validation_notes: "Must be non-negative; >$10k requires detailed description"
|
|
32
79
|
|
|
33
80
|
start_date:
|
|
34
81
|
type: date
|
|
82
|
+
label: Start Date
|
|
83
|
+
ai_context:
|
|
84
|
+
intent: "Project commencement date"
|
|
35
85
|
|
|
36
86
|
end_date:
|
|
37
87
|
type: date
|
|
88
|
+
label: End Date
|
|
89
|
+
ai_context:
|
|
90
|
+
intent: "Target completion date"
|
|
91
|
+
validation_notes: "Must be on or after start_date"
|
|
38
92
|
|
|
39
93
|
approved_by:
|
|
40
94
|
type: text
|
|
95
|
+
label: Approved By
|
|
96
|
+
ai_context:
|
|
97
|
+
intent: "Approver identity for audit trail"
|
|
41
98
|
|
|
42
99
|
approved_at:
|
|
43
100
|
type: datetime
|
|
101
|
+
label: Approval Timestamp
|
|
44
102
|
|
|
45
103
|
approval_comment:
|
|
46
104
|
type: textarea
|
|
105
|
+
label: Approval Comment
|
|
47
106
|
|
|
48
107
|
indexes:
|
|
49
108
|
# Composite index for reporting
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# File: projects.validation.yml
|
|
2
|
+
# Object is inferred from filename as 'projects'
|
|
3
3
|
description: "Validation rules for Projects"
|
|
4
4
|
|
|
5
5
|
rules:
|
|
@@ -8,25 +8,33 @@ rules:
|
|
|
8
8
|
ai_context:
|
|
9
9
|
intent: "Ensure project timeline is valid"
|
|
10
10
|
business_rule: "Projects cannot end before they start"
|
|
11
|
+
rationale: "Maintaining data integrity for project scheduling"
|
|
11
12
|
rule:
|
|
12
13
|
field: end_date
|
|
13
14
|
operator: ">="
|
|
14
15
|
compare_to: start_date
|
|
15
|
-
message: "End Date must be after Start Date"
|
|
16
|
+
message: "End Date must be on or after Start Date"
|
|
17
|
+
error_code: "INVALID_DATE_RANGE"
|
|
16
18
|
|
|
17
19
|
- name: positive_budget
|
|
18
20
|
type: field
|
|
21
|
+
ai_context:
|
|
22
|
+
intent: "Prevent negative budget values"
|
|
23
|
+
business_rule: "Project budget must be zero or positive"
|
|
19
24
|
rule:
|
|
20
25
|
field: budget
|
|
21
26
|
operator: ">="
|
|
22
27
|
value: 0
|
|
23
28
|
message: "Budget cannot be negative"
|
|
29
|
+
error_code: "NEGATIVE_BUDGET"
|
|
24
30
|
|
|
25
31
|
- name: comment_required_if_high_budget
|
|
26
32
|
type: conditional
|
|
27
33
|
description: "Description is required for high budget projects"
|
|
28
34
|
ai_context:
|
|
29
|
-
|
|
35
|
+
intent: "Ensure governance for high-value projects"
|
|
36
|
+
business_rule: "High value projects (>$10k) must have a detailed description for stakeholder transparency"
|
|
37
|
+
rationale: "Projects over $10,000 require executive oversight and clear documentation"
|
|
30
38
|
condition:
|
|
31
39
|
field: budget
|
|
32
40
|
operator: ">"
|
|
@@ -35,3 +43,4 @@ rules:
|
|
|
35
43
|
field: description
|
|
36
44
|
operator: "not_empty"
|
|
37
45
|
message: "Description is required for high budget projects (> $10,000)"
|
|
46
|
+
error_code: "HIGH_BUDGET_REQUIRES_DESCRIPTION"
|
|
@@ -50,7 +50,7 @@ async function main() {
|
|
|
50
50
|
|
|
51
51
|
console.log("Querying Tasks...");
|
|
52
52
|
const tasks = await ctx.object('tasks').find({
|
|
53
|
-
|
|
53
|
+
where: { project: projectId }
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
console.log("📊 Project Report:", JSON.stringify({ project, tasks }, null, 2));
|