Skip to content

Commit e7c0af1

Browse files
authored
Merge pull request #3236 from SeedCompany/project-workflow
2 parents cc712d8 + d9378b6 commit e7c0af1

File tree

76 files changed

+3192
-1581
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+3192
-1581
lines changed

dbschema/migrations/00010-m1sznh4.edgeql

Lines changed: 70 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dbschema/project-workflow.esdl

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
module Project {
2+
type WorkflowEvent {
3+
required project: default::Project {
4+
readonly := true;
5+
on target delete delete source;
6+
};
7+
required who: default::Actor {
8+
readonly := true;
9+
default := global default::currentActor;
10+
};
11+
required at: datetime {
12+
readonly := true;
13+
default := datetime_of_statement();
14+
};
15+
transitionKey: uuid {
16+
readonly := true;
17+
};
18+
required to: Step {
19+
readonly := true;
20+
};
21+
notes: default::RichText {
22+
readonly := true;
23+
};
24+
25+
trigger setProjectStep after insert for all do (
26+
update default::Project
27+
filter default::Project in __new__.project
28+
set {
29+
step := default::Project.latestWorkflowEvent.to ?? Project::Step.EarlyConversations
30+
}
31+
);
32+
trigger refreshProjectStep after delete for all do (
33+
update default::Project
34+
filter default::Project in __old__.project
35+
set {
36+
step := default::Project.latestWorkflowEvent.to ?? Project::Step.EarlyConversations
37+
}
38+
);
39+
}
40+
41+
scalar type Step extending enum<
42+
EarlyConversations,
43+
PendingConceptApproval,
44+
PrepForConsultantEndorsement,
45+
PendingConsultantEndorsement,
46+
PrepForFinancialEndorsement,
47+
PendingFinancialEndorsement,
48+
FinalizingProposal,
49+
PendingRegionalDirectorApproval,
50+
PendingZoneDirectorApproval,
51+
PendingFinanceConfirmation,
52+
OnHoldFinanceConfirmation,
53+
DidNotDevelop,
54+
Rejected,
55+
Active,
56+
ActiveChangedPlan,
57+
DiscussingChangeToPlan,
58+
PendingChangeToPlanApproval,
59+
PendingChangeToPlanConfirmation,
60+
DiscussingSuspension,
61+
PendingSuspensionApproval,
62+
Suspended,
63+
DiscussingReactivation,
64+
PendingReactivationApproval,
65+
DiscussingTermination,
66+
PendingTerminationApproval,
67+
FinalizingCompletion,
68+
Terminated,
69+
Completed,
70+
>;
71+
72+
scalar type Status extending enum<
73+
InDevelopment,
74+
Active,
75+
Terminated,
76+
Completed,
77+
DidNotDevelop,
78+
>;
79+
80+
function statusFromStep(step: Step) -> Status
81+
using (
82+
with dev := {
83+
Step.EarlyConversations,
84+
Step.PendingConceptApproval,
85+
Step.PrepForConsultantEndorsement,
86+
Step.PendingConsultantEndorsement,
87+
Step.PrepForFinancialEndorsement,
88+
Step.PendingFinancialEndorsement,
89+
Step.FinalizingProposal,
90+
Step.PendingRegionalDirectorApproval,
91+
Step.PendingZoneDirectorApproval,
92+
Step.PendingFinanceConfirmation,
93+
Step.OnHoldFinanceConfirmation,
94+
},
95+
active := {
96+
Step.Active,
97+
Step.ActiveChangedPlan,
98+
Step.DiscussingChangeToPlan,
99+
Step.PendingChangeToPlanApproval,
100+
Step.PendingChangeToPlanConfirmation,
101+
Step.DiscussingSuspension,
102+
Step.PendingSuspensionApproval,
103+
Step.Suspended,
104+
Step.DiscussingReactivation,
105+
Step.PendingReactivationApproval,
106+
Step.DiscussingTermination,
107+
Step.PendingTerminationApproval,
108+
Step.FinalizingCompletion,
109+
}
110+
select
111+
Status.InDevelopment if step in dev else
112+
Status.Active if step in active else
113+
Status.DidNotDevelop if step = Step.DidNotDevelop else
114+
Status.DidNotDevelop if step = Step.Rejected else
115+
Status.Terminated if step = Step.Terminated else
116+
Status.Completed if step = Step.Completed else
117+
Status.InDevelopment
118+
)
119+
}

dbschema/project.esdl

Lines changed: 12 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,21 @@ module default {
4848
)) else .departmentId
4949
);
5050
};
51-
51+
5252
required step: Project::Step {
5353
default := Project::Step.EarlyConversations;
5454
};
55-
required stepChangedAt: datetime {
56-
default := .createdAt;
57-
rewrite update using (datetime_of_statement() if .step != __old__.step else .stepChangedAt);
58-
}
59-
property status := Project::statusFromStep(.step);
60-
55+
status := Project::statusFromStep(.step);
56+
latestWorkflowEvent := (select .workflowEvents order by .at desc limit 1);
57+
workflowEvents := .<project[is Project::WorkflowEvent];
58+
trigger assertMatchingLatestWorkflowEvent after insert, update for each do (
59+
assert(
60+
__new__.latestWorkflowEvent.to ?= __new__.step
61+
or (not exists __new__.latestWorkflowEvent and __new__.step = Project::Step.EarlyConversations),
62+
message := "Project step must match the latest workflow event"
63+
)
64+
);
65+
6166
mouStart: cal::local_date;
6267
mouEnd: cal::local_date;
6368
constraint expression on (.mouEnd >= .mouStart);
@@ -199,83 +204,4 @@ module Project {
199204
on target delete allow;
200205
};
201206
}
202-
203-
scalar type Step extending enum<
204-
EarlyConversations,
205-
PendingConceptApproval,
206-
PrepForConsultantEndorsement,
207-
PendingConsultantEndorsement,
208-
PrepForFinancialEndorsement,
209-
PendingFinancialEndorsement,
210-
FinalizingProposal,
211-
PendingRegionalDirectorApproval,
212-
PendingZoneDirectorApproval,
213-
PendingFinanceConfirmation,
214-
OnHoldFinanceConfirmation,
215-
DidNotDevelop,
216-
Rejected,
217-
Active,
218-
ActiveChangedPlan,
219-
DiscussingChangeToPlan,
220-
PendingChangeToPlanApproval,
221-
PendingChangeToPlanConfirmation,
222-
DiscussingSuspension,
223-
PendingSuspensionApproval,
224-
Suspended,
225-
DiscussingReactivation,
226-
PendingReactivationApproval,
227-
DiscussingTermination,
228-
PendingTerminationApproval,
229-
FinalizingCompletion,
230-
Terminated,
231-
Completed,
232-
>;
233-
234-
scalar type Status extending enum<
235-
InDevelopment,
236-
Active,
237-
Terminated,
238-
Completed,
239-
DidNotDevelop,
240-
>;
241-
242-
function statusFromStep(step: Step) -> Status
243-
using (
244-
with dev := {
245-
Step.EarlyConversations,
246-
Step.PendingConceptApproval,
247-
Step.PrepForConsultantEndorsement,
248-
Step.PendingConsultantEndorsement,
249-
Step.PrepForFinancialEndorsement,
250-
Step.PendingFinancialEndorsement,
251-
Step.FinalizingProposal,
252-
Step.PendingRegionalDirectorApproval,
253-
Step.PendingZoneDirectorApproval,
254-
Step.PendingFinanceConfirmation,
255-
Step.OnHoldFinanceConfirmation,
256-
},
257-
active := {
258-
Step.Active,
259-
Step.ActiveChangedPlan,
260-
Step.DiscussingChangeToPlan,
261-
Step.PendingChangeToPlanApproval,
262-
Step.PendingChangeToPlanConfirmation,
263-
Step.DiscussingSuspension,
264-
Step.PendingSuspensionApproval,
265-
Step.Suspended,
266-
Step.DiscussingReactivation,
267-
Step.PendingReactivationApproval,
268-
Step.DiscussingTermination,
269-
Step.PendingTerminationApproval,
270-
Step.FinalizingCompletion,
271-
}
272-
select
273-
Status.InDevelopment if step in dev else
274-
Status.Active if step in active else
275-
Status.DidNotDevelop if step = Step.DidNotDevelop else
276-
Status.DidNotDevelop if step = Step.Rejected else
277-
Status.Terminated if step = Step.Terminated else
278-
Status.Completed if step = Step.Completed else
279-
Status.InDevelopment
280-
)
281207
}

dbschema/seeds/008.projects.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export default (async function ({ e, db, print }) {
111111
return;
112112
}
113113

114-
for (const { type, ...project } of newProjects) {
114+
for (const { type, step, ...project } of newProjects) {
115115
const insertQ = e.insert(e[`${type}Project`], {
116116
...project,
117117
...mapValues.fromList(
@@ -137,6 +137,12 @@ export default (async function ({ e, db, print }) {
137137
set: { projects: projectRef },
138138
}));
139139
await pcQuery.run(db);
140+
141+
if (step !== 'EarlyConversations') {
142+
await e
143+
.insert(e.Project.WorkflowEvent, { project: projectRef, to: step })
144+
.run(db);
145+
}
140146
}
141147
print({ 'Added Projects': newProjects.map((t) => t.name) });
142148
} satisfies SeedFn);

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@
9090
"nanoid": "^4.0.2",
9191
"neo4j-driver": "^5.20.0",
9292
"p-retry": "^5.1.2",
93-
"pako": "^2.1.0",
9493
"pkg-up": "^4.0.0",
9594
"plur": "^5.1.0",
9695
"prismjs-terminal": "^1.2.3",
@@ -125,7 +124,6 @@
125124
"@types/lodash": "^4.14.200",
126125
"@types/luxon": "^3.3.3",
127126
"@types/node": "^20.12.5",
128-
"@types/pako": "^2.0.2",
129127
"@types/prismjs": "^1.26.2",
130128
"@types/react": "^18.2.33",
131129
"@types/stack-trace": "^0.0.32",

src/components/authorization/policies/by-role/consultant.policy.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,9 @@ import { member, Policy, Role } from '../util';
3838
]),
3939
r.Unavailability.read,
4040
r.User.read.create,
41+
r.ProjectWorkflowEvent.read.transitions(
42+
'Pending Consultant Endorsement -> Prep for Financial Endorsement With Consultant Endorsement',
43+
'Pending Consultant Endorsement -> Prep for Financial Endorsement Without Consultant Endorsement',
44+
).execute,
4145
])
4246
export class ConsultantPolicy {}

src/components/authorization/policies/by-role/controller.policy.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,16 @@ import { Policy, Role } from '../util';
55
// keep multiline format
66
r.Organization.delete,
77
r.Partner.delete,
8+
r.ProjectWorkflowEvent.read.transitions(
9+
'Pending & On Hold Finance Confirmation -> Active',
10+
'Pending Finance Confirmation -> Pending Regional Director Approval',
11+
'Pending Finance Confirmation -> Did Not Develop',
12+
'Pending Finance Confirmation -> On Hold Finance Confirmation',
13+
'Pending & On Hold Finance Confirmation -> Finalizing Proposal',
14+
'Pending & On Hold Finance Confirmation -> Rejected',
15+
'Pending Change To Plan Confirmation -> Discussing Change To Plan',
16+
'Pending Change To Plan Confirmation -> Active Changed Plan',
17+
'Pending Change To Plan Confirmation -> Back To Active',
18+
).execute,
819
])
920
export class ControllerPolicy {}

src/components/authorization/policies/by-role/field-operations-director.policy.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import { Policy, Role } from '../util';
1515
r.Product.edit.create.delete,
1616
r.Project.edit,
1717
r.ProjectMember.edit.create.delete,
18+
r.ProjectWorkflowEvent.read.transitions(
19+
'Pending Zone Director Approval -> Pending Finance Confirmation',
20+
'Pending Zone Director Approval -> Finalizing Proposal',
21+
'Pending Zone Director Approval -> Rejected',
22+
).execute,
1823
r.PeriodicReport.edit,
1924
r.StepProgress.edit,
2025
])

src/components/authorization/policies/by-role/financial-analyst.policy.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ import {
6161
])
6262
.children((c) => c.posts.edit),
6363
r.ProjectMember.read.when(member).edit.create.delete,
64+
r.ProjectWorkflowEvent.read.transitions(
65+
'Pending Financial Endorsement -> Finalizing Proposal With Financial Endorsement',
66+
'Pending Financial Endorsement -> Finalizing Proposal Without Financial Endorsement',
67+
'Finalizing Completion -> Back To Active',
68+
'Finalizing Completion -> Completed',
69+
).execute,
6470
r.PeriodicReport.read.when(member).edit,
6571
r.StepProgress.read,
6672
r.Unavailability.read,

0 commit comments

Comments
 (0)