@valentine-efagene/qshelter-common 2.0.98 → 2.0.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/client/browser.d.ts +5 -0
- package/dist/generated/client/client.d.ts +5 -0
- package/dist/generated/client/commonInputTypes.d.ts +90 -0
- package/dist/generated/client/enums.d.ts +26 -0
- package/dist/generated/client/enums.js +23 -0
- package/dist/generated/client/internal/class.d.ts +11 -0
- package/dist/generated/client/internal/class.js +2 -2
- package/dist/generated/client/internal/prismaNamespace.d.ts +176 -1
- package/dist/generated/client/internal/prismaNamespace.js +95 -1
- package/dist/generated/client/internal/prismaNamespaceBrowser.d.ts +96 -0
- package/dist/generated/client/internal/prismaNamespaceBrowser.js +95 -1
- package/dist/generated/client/models/Amenity.d.ts +183 -3
- package/dist/generated/client/models/ApplicationDocument.d.ts +183 -1
- package/dist/generated/client/models/ApplicationEvent.d.ts +190 -14
- package/dist/generated/client/models/ApplicationPayment.d.ts +225 -1
- package/dist/generated/client/models/ApplicationPhase.d.ts +272 -26
- package/dist/generated/client/models/DocumentationPhase.d.ts +224 -24
- package/dist/generated/client/models/DocumentationStep.d.ts +237 -1
- package/dist/generated/client/models/DocumentationStepApproval.d.ts +159 -1
- package/dist/generated/client/models/DocumentationStepDocument.d.ts +150 -10
- package/dist/generated/client/models/EventHandlerExecution.d.ts +208 -14
- package/dist/generated/client/models/PaymentInstallment.d.ts +228 -14
- package/dist/generated/client/models/PaymentMethodPhaseDocument.d.ts +178 -14
- package/dist/generated/client/models/PaymentMethodPhaseField.d.ts +208 -14
- package/dist/generated/client/models/PaymentMethodPhaseStep.d.ts +180 -14
- package/dist/generated/client/models/PaymentPhase.d.ts +214 -14
- package/dist/generated/client/models/PhaseEventAttachment.d.ts +178 -14
- package/dist/generated/client/models/PropertyAmenity.d.ts +145 -11
- package/dist/generated/client/models/PropertyDocument.d.ts +164 -12
- package/dist/generated/client/models/PropertyMedia.d.ts +183 -17
- package/dist/generated/client/models/PropertyPaymentMethodLink.d.ts +159 -13
- package/dist/generated/client/models/PropertyPaymentMethodPhase.d.ts +270 -14
- package/dist/generated/client/models/PropertyUnit.d.ts +230 -14
- package/dist/generated/client/models/PropertyVariant.d.ts +256 -14
- package/dist/generated/client/models/PropertyVariantAmenity.d.ts +145 -11
- package/dist/generated/client/models/PropertyVariantMedia.d.ts +171 -13
- package/dist/generated/client/models/QuestionnaireField.d.ts +232 -14
- package/dist/generated/client/models/QuestionnairePhase.d.ts +207 -1
- package/dist/generated/client/models/StepEventAttachment.d.ts +178 -14
- package/dist/generated/client/models/Tenant.d.ts +11653 -1153
- package/dist/generated/client/models/WorkflowBlocker.d.ts +1432 -0
- package/dist/generated/client/models/WorkflowBlocker.js +1 -0
- package/dist/generated/client/models/index.d.ts +1 -0
- package/dist/generated/client/models/index.js +1 -0
- package/dist/generated/client/models.d.ts +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/middleware/auth-context.d.ts +63 -6
- package/dist/src/middleware/auth-context.js +132 -13
- package/dist/src/prisma/tenant.js +26 -32
- package/dist/src/types/action-status.d.ts +137 -0
- package/dist/src/types/action-status.js +402 -0
- package/package.json +1 -1
- package/prisma/migrations/20260113000000_remove_workflow_analytics_summary/migration.sql +5 -0
- package/prisma/migrations/20260113110450_add_tenant_id_to_child_models/migration.sql +334 -0
- package/prisma/schema.prisma +273 -60
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -61,4 +61,5 @@ export type * from './models/EventHandlerExecution.js';
|
|
|
61
61
|
export type * from './models/DomainEvent.js';
|
|
62
62
|
export type * from './models/PropertyTransferRequest.js';
|
|
63
63
|
export type * from './models/ApprovalRequest.js';
|
|
64
|
+
export type * from './models/WorkflowBlocker.js';
|
|
64
65
|
export type * from './commonInputTypes.js';
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -20,11 +20,15 @@ export interface AuthContext {
|
|
|
20
20
|
roles?: string[];
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* Extracts auth context from API Gateway authorizer.
|
|
23
|
+
* Extracts auth context from API Gateway authorizer or JWT token.
|
|
24
24
|
*
|
|
25
25
|
* Priority:
|
|
26
|
-
* 1. Production: requestContext.authorizer (set by API Gateway)
|
|
27
|
-
* 2.
|
|
26
|
+
* 1. Production: requestContext.authorizer (set by API Gateway Lambda Authorizer)
|
|
27
|
+
* 2. Fallback: Decode JWT from Authorization header (LocalStack/dev/tests)
|
|
28
|
+
*
|
|
29
|
+
* In production, the Lambda Authorizer validates the JWT and injects context.
|
|
30
|
+
* In LocalStack (no authorizer), we decode the JWT directly since it contains
|
|
31
|
+
* all the same information: sub (userId), tenantId, email, roles.
|
|
28
32
|
*
|
|
29
33
|
* @param req Express request object
|
|
30
34
|
* @returns AuthContext or null if not authenticated
|
|
@@ -54,18 +58,71 @@ export declare function requireAuth(req: Request, res: Response, next: NextFunct
|
|
|
54
58
|
*/
|
|
55
59
|
export declare function getAuthContext(req: Request): AuthContext;
|
|
56
60
|
/**
|
|
57
|
-
* Test helper to generate
|
|
58
|
-
*
|
|
61
|
+
* Test helper to generate authorization header with JWT.
|
|
62
|
+
*
|
|
63
|
+
* Since auth context is now extracted directly from the JWT,
|
|
64
|
+
* tests only need to pass the Authorization header with a valid token.
|
|
59
65
|
*
|
|
60
66
|
* @example
|
|
61
67
|
* ```typescript
|
|
62
68
|
* const response = await request(app)
|
|
63
69
|
* .post('/users')
|
|
64
|
-
* .set(
|
|
70
|
+
* .set('Authorization', `Bearer ${token}`)
|
|
65
71
|
* .send({ name: 'John' });
|
|
66
72
|
* ```
|
|
73
|
+
*
|
|
74
|
+
* @deprecated Use Authorization header directly with JWT token.
|
|
75
|
+
* This helper is kept for backward compatibility.
|
|
67
76
|
*/
|
|
68
77
|
export declare function authHeaders(userId: string, tenantId: string, extras?: {
|
|
69
78
|
email?: string;
|
|
70
79
|
roles?: string[];
|
|
80
|
+
token?: string;
|
|
71
81
|
}): Record<string, string>;
|
|
82
|
+
/**
|
|
83
|
+
* Standard role names used across the platform.
|
|
84
|
+
*/
|
|
85
|
+
export declare const ROLES: {
|
|
86
|
+
readonly SUPER_ADMIN: "SUPER_ADMIN";
|
|
87
|
+
readonly TENANT_ADMIN: "TENANT_ADMIN";
|
|
88
|
+
readonly LOAN_OFFICER: "LOAN_OFFICER";
|
|
89
|
+
readonly CUSTOMER: "CUSTOMER";
|
|
90
|
+
readonly VIEWER: "VIEWER";
|
|
91
|
+
};
|
|
92
|
+
export type RoleName = (typeof ROLES)[keyof typeof ROLES];
|
|
93
|
+
/**
|
|
94
|
+
* Roles that have admin privileges (can manage resources).
|
|
95
|
+
*/
|
|
96
|
+
export declare const ADMIN_ROLES: RoleName[];
|
|
97
|
+
/**
|
|
98
|
+
* Check if user has any of the specified roles.
|
|
99
|
+
*/
|
|
100
|
+
export declare function hasAnyRole(userRoles: string[] | undefined, requiredRoles: string[]): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Check if user has admin privileges.
|
|
103
|
+
*/
|
|
104
|
+
export declare function isAdmin(userRoles: string[] | undefined): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Middleware factory that requires user to have specific role(s).
|
|
107
|
+
* Uses roles from API Gateway authorizer context.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* // Require any admin role
|
|
112
|
+
* router.post('/payment-plans', requireRole(ADMIN_ROLES), createPaymentPlan);
|
|
113
|
+
*
|
|
114
|
+
* // Require specific role
|
|
115
|
+
* router.delete('/users/:id', requireRole(['SUPER_ADMIN']), deleteUser);
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export declare function requireRole(allowedRoles: string[]): (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
|
|
119
|
+
/**
|
|
120
|
+
* Middleware that requires admin privileges.
|
|
121
|
+
* Shorthand for requireRole(ADMIN_ROLES).
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* router.post('/payment-methods', requireAdmin, createPaymentMethod);
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare function requireAdmin(req: Request, res: Response, next: NextFunction): Response<any, Record<string, any>> | undefined;
|
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Safely decode JWT payload without verification.
|
|
3
|
+
* Used to extract claims like roles when authorizer context isn't available.
|
|
4
|
+
* Note: This is NOT validation - we trust the token was already validated upstream.
|
|
5
|
+
*/
|
|
6
|
+
function decodeJwtPayload(token) {
|
|
7
|
+
try {
|
|
8
|
+
const parts = token.split('.');
|
|
9
|
+
if (parts.length !== 3)
|
|
10
|
+
return null;
|
|
11
|
+
const payload = Buffer.from(parts[1], 'base64').toString('utf-8');
|
|
12
|
+
return JSON.parse(payload);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Extracts auth context from API Gateway authorizer or JWT token.
|
|
3
20
|
*
|
|
4
21
|
* Priority:
|
|
5
|
-
* 1. Production: requestContext.authorizer (set by API Gateway)
|
|
6
|
-
* 2.
|
|
22
|
+
* 1. Production: requestContext.authorizer (set by API Gateway Lambda Authorizer)
|
|
23
|
+
* 2. Fallback: Decode JWT from Authorization header (LocalStack/dev/tests)
|
|
24
|
+
*
|
|
25
|
+
* In production, the Lambda Authorizer validates the JWT and injects context.
|
|
26
|
+
* In LocalStack (no authorizer), we decode the JWT directly since it contains
|
|
27
|
+
* all the same information: sub (userId), tenantId, email, roles.
|
|
7
28
|
*
|
|
8
29
|
* @param req Express request object
|
|
9
30
|
* @returns AuthContext or null if not authenticated
|
|
@@ -20,15 +41,30 @@ export function extractAuthContext(req) {
|
|
|
20
41
|
roles: authorizer.roles ? JSON.parse(authorizer.roles) : [],
|
|
21
42
|
};
|
|
22
43
|
}
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
44
|
+
// Fallback: Decode JWT directly (LocalStack, local dev, tests)
|
|
45
|
+
// The JWT already contains: sub (userId), tenantId, email, roles
|
|
46
|
+
const authHeader = req.headers['authorization'];
|
|
47
|
+
if (authHeader?.startsWith('Bearer ')) {
|
|
48
|
+
const token = authHeader.substring(7);
|
|
49
|
+
const payload = decodeJwtPayload(token);
|
|
50
|
+
if (payload?.sub && payload?.tenantId) {
|
|
51
|
+
return {
|
|
52
|
+
userId: payload.sub,
|
|
53
|
+
tenantId: payload.tenantId,
|
|
54
|
+
email: payload.email,
|
|
55
|
+
roles: Array.isArray(payload.roles) ? payload.roles : [],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Legacy fallback: Mock headers for unit tests without real JWTs
|
|
60
|
+
// These should only be used in unit tests, not E2E or production
|
|
61
|
+
const mockUserId = req.headers['x-authorizer-user-id'];
|
|
62
|
+
const mockTenantId = req.headers['x-authorizer-tenant-id'];
|
|
63
|
+
if (mockUserId && mockTenantId) {
|
|
28
64
|
const rolesHeader = req.headers['x-authorizer-roles'];
|
|
29
65
|
return {
|
|
30
|
-
userId,
|
|
31
|
-
tenantId,
|
|
66
|
+
userId: mockUserId,
|
|
67
|
+
tenantId: mockTenantId,
|
|
32
68
|
email: req.headers['x-authorizer-email'],
|
|
33
69
|
roles: rolesHeader ? JSON.parse(rolesHeader) : [],
|
|
34
70
|
};
|
|
@@ -79,18 +115,29 @@ export function getAuthContext(req) {
|
|
|
79
115
|
return auth;
|
|
80
116
|
}
|
|
81
117
|
/**
|
|
82
|
-
* Test helper to generate
|
|
83
|
-
*
|
|
118
|
+
* Test helper to generate authorization header with JWT.
|
|
119
|
+
*
|
|
120
|
+
* Since auth context is now extracted directly from the JWT,
|
|
121
|
+
* tests only need to pass the Authorization header with a valid token.
|
|
84
122
|
*
|
|
85
123
|
* @example
|
|
86
124
|
* ```typescript
|
|
87
125
|
* const response = await request(app)
|
|
88
126
|
* .post('/users')
|
|
89
|
-
* .set(
|
|
127
|
+
* .set('Authorization', `Bearer ${token}`)
|
|
90
128
|
* .send({ name: 'John' });
|
|
91
129
|
* ```
|
|
130
|
+
*
|
|
131
|
+
* @deprecated Use Authorization header directly with JWT token.
|
|
132
|
+
* This helper is kept for backward compatibility.
|
|
92
133
|
*/
|
|
93
134
|
export function authHeaders(userId, tenantId, extras) {
|
|
135
|
+
// If a token is provided, just use that (preferred)
|
|
136
|
+
if (extras?.token) {
|
|
137
|
+
return { 'Authorization': `Bearer ${extras.token}` };
|
|
138
|
+
}
|
|
139
|
+
// Legacy: Build mock headers for tests without real JWT
|
|
140
|
+
// This is only for unit tests that don't have access to real tokens
|
|
94
141
|
return {
|
|
95
142
|
'x-authorizer-user-id': userId,
|
|
96
143
|
'x-authorizer-tenant-id': tenantId,
|
|
@@ -98,3 +145,75 @@ export function authHeaders(userId, tenantId, extras) {
|
|
|
98
145
|
...(extras?.roles && { 'x-authorizer-roles': JSON.stringify(extras.roles) }),
|
|
99
146
|
};
|
|
100
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Standard role names used across the platform.
|
|
150
|
+
*/
|
|
151
|
+
export const ROLES = {
|
|
152
|
+
SUPER_ADMIN: 'SUPER_ADMIN',
|
|
153
|
+
TENANT_ADMIN: 'TENANT_ADMIN',
|
|
154
|
+
LOAN_OFFICER: 'LOAN_OFFICER',
|
|
155
|
+
CUSTOMER: 'CUSTOMER',
|
|
156
|
+
VIEWER: 'VIEWER',
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Roles that have admin privileges (can manage resources).
|
|
160
|
+
*/
|
|
161
|
+
export const ADMIN_ROLES = [ROLES.SUPER_ADMIN, ROLES.TENANT_ADMIN, ROLES.LOAN_OFFICER];
|
|
162
|
+
/**
|
|
163
|
+
* Check if user has any of the specified roles.
|
|
164
|
+
*/
|
|
165
|
+
export function hasAnyRole(userRoles, requiredRoles) {
|
|
166
|
+
if (!userRoles || userRoles.length === 0)
|
|
167
|
+
return false;
|
|
168
|
+
return requiredRoles.some(role => userRoles.includes(role));
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Check if user has admin privileges.
|
|
172
|
+
*/
|
|
173
|
+
export function isAdmin(userRoles) {
|
|
174
|
+
return hasAnyRole(userRoles, ADMIN_ROLES);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Middleware factory that requires user to have specific role(s).
|
|
178
|
+
* Uses roles from API Gateway authorizer context.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* // Require any admin role
|
|
183
|
+
* router.post('/payment-plans', requireRole(ADMIN_ROLES), createPaymentPlan);
|
|
184
|
+
*
|
|
185
|
+
* // Require specific role
|
|
186
|
+
* router.delete('/users/:id', requireRole(['SUPER_ADMIN']), deleteUser);
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
export function requireRole(allowedRoles) {
|
|
190
|
+
return function (req, res, next) {
|
|
191
|
+
const auth = extractAuthContext(req);
|
|
192
|
+
if (!auth) {
|
|
193
|
+
return res.status(401).json({
|
|
194
|
+
success: false,
|
|
195
|
+
error: 'Unauthorized - authentication required'
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (!hasAnyRole(auth.roles, allowedRoles)) {
|
|
199
|
+
return res.status(403).json({
|
|
200
|
+
success: false,
|
|
201
|
+
error: 'Forbidden - insufficient permissions',
|
|
202
|
+
requiredRoles: allowedRoles,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
next();
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Middleware that requires admin privileges.
|
|
210
|
+
* Shorthand for requireRole(ADMIN_ROLES).
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```typescript
|
|
214
|
+
* router.post('/payment-methods', requireAdmin, createPaymentMethod);
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
export function requireAdmin(req, res, next) {
|
|
218
|
+
return requireRole(ADMIN_ROLES)(req, res, next);
|
|
219
|
+
}
|
|
@@ -1,30 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* INVERTED APPROACH:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* This reduces the risk of accidentally omitting a new model from tenant scoping.
|
|
2
|
+
* INVERTED APPROACH: List models that DON'T have a tenantId field.
|
|
3
|
+
* All other models are assumed to be tenant-scoped.
|
|
4
|
+
* This is easier to manage since most models ARE tenant-scoped.
|
|
6
5
|
*/
|
|
7
6
|
/**
|
|
8
|
-
* Models that are
|
|
9
|
-
* These
|
|
10
|
-
* - Don't have a tenantId field (system tables)
|
|
11
|
-
* - Have optional tenantId but are designed to work across tenants (User)
|
|
12
|
-
* - Are cross-tenant lookup/join tables (TenantMembership)
|
|
7
|
+
* Models that are truly global and have NO tenantId field at all.
|
|
8
|
+
* These are excluded from tenant filtering/injection.
|
|
13
9
|
*/
|
|
14
10
|
const GLOBAL_MODELS = [
|
|
15
|
-
//
|
|
11
|
+
// System-level entities with no tenant ownership
|
|
12
|
+
"tenant",
|
|
16
13
|
"user",
|
|
17
|
-
// TenantMembership is the user-tenant join table (queries by userId or tenantId)
|
|
18
14
|
"tenantMembership",
|
|
19
|
-
// System/infrastructure tables without tenantId
|
|
20
|
-
"tenant",
|
|
21
|
-
// Legacy role assignment (global, not tenant-scoped)
|
|
22
|
-
"userRole",
|
|
23
|
-
"rolePermission",
|
|
24
|
-
"refreshToken",
|
|
25
|
-
"passwordReset",
|
|
26
|
-
"wallet",
|
|
27
|
-
"domainEvent",
|
|
28
15
|
];
|
|
29
16
|
/**
|
|
30
17
|
* Models that have OPTIONAL tenant scoping (nullable tenantId).
|
|
@@ -45,9 +32,16 @@ function isOptionalTenantModel(model) {
|
|
|
45
32
|
return OPTIONAL_TENANT_MODELS.includes(model);
|
|
46
33
|
}
|
|
47
34
|
/**
|
|
48
|
-
*
|
|
35
|
+
* Check if model is tenant-scoped (required tenantId)
|
|
36
|
+
* A model is tenant-scoped if it's NOT global and NOT optional-tenant
|
|
49
37
|
*/
|
|
50
38
|
function isTenantScopedModel(model) {
|
|
39
|
+
return !isGlobalModel(model) && !isOptionalTenantModel(model);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Check if model has any tenant scoping (required or optional)
|
|
43
|
+
*/
|
|
44
|
+
function hasTenantField(model) {
|
|
51
45
|
return !isGlobalModel(model);
|
|
52
46
|
}
|
|
53
47
|
/**
|
|
@@ -68,7 +62,7 @@ export function createTenantPrisma(prisma, context) {
|
|
|
68
62
|
query: {
|
|
69
63
|
$allModels: {
|
|
70
64
|
async findMany({ model, args, query }) {
|
|
71
|
-
if (
|
|
65
|
+
if (hasTenantField(model)) {
|
|
72
66
|
const tenantFilter = isOptionalTenantModel(model)
|
|
73
67
|
? { OR: [{ tenantId }, { tenantId: null }] }
|
|
74
68
|
: { tenantId };
|
|
@@ -80,7 +74,7 @@ export function createTenantPrisma(prisma, context) {
|
|
|
80
74
|
return query(args);
|
|
81
75
|
},
|
|
82
76
|
async findFirst({ model, args, query }) {
|
|
83
|
-
if (
|
|
77
|
+
if (hasTenantField(model)) {
|
|
84
78
|
const tenantFilter = isOptionalTenantModel(model)
|
|
85
79
|
? { OR: [{ tenantId }, { tenantId: null }] }
|
|
86
80
|
: { tenantId };
|
|
@@ -94,7 +88,7 @@ export function createTenantPrisma(prisma, context) {
|
|
|
94
88
|
async findUnique({ model, args, query }) {
|
|
95
89
|
// findUnique can only filter by unique fields, so we verify after fetch
|
|
96
90
|
const result = await query(args);
|
|
97
|
-
if (result &&
|
|
91
|
+
if (result && hasTenantField(model)) {
|
|
98
92
|
const record = result;
|
|
99
93
|
if (isOptionalTenantModel(model)) {
|
|
100
94
|
// Allow null tenantId (global) or matching tenantId
|
|
@@ -111,7 +105,7 @@ export function createTenantPrisma(prisma, context) {
|
|
|
111
105
|
return result;
|
|
112
106
|
},
|
|
113
107
|
async create({ model, args, query }) {
|
|
114
|
-
if (isTenantScopedModel(model)
|
|
108
|
+
if (isTenantScopedModel(model)) {
|
|
115
109
|
// Inject tenantId for required tenant models
|
|
116
110
|
args.data.tenantId = tenantId;
|
|
117
111
|
}
|
|
@@ -124,10 +118,10 @@ export function createTenantPrisma(prisma, context) {
|
|
|
124
118
|
return query(args);
|
|
125
119
|
},
|
|
126
120
|
async createMany({ model, args, query }) {
|
|
127
|
-
if (
|
|
121
|
+
if (hasTenantField(model)) {
|
|
128
122
|
const data = Array.isArray(args.data) ? args.data : [args.data];
|
|
129
123
|
args.data = data.map((item) => {
|
|
130
|
-
if (
|
|
124
|
+
if (isTenantScopedModel(model)) {
|
|
131
125
|
return { ...item, tenantId };
|
|
132
126
|
}
|
|
133
127
|
// For optional models, inject if not explicitly set
|
|
@@ -140,7 +134,7 @@ export function createTenantPrisma(prisma, context) {
|
|
|
140
134
|
return query(args);
|
|
141
135
|
},
|
|
142
136
|
async update({ model, args, query }) {
|
|
143
|
-
if (
|
|
137
|
+
if (hasTenantField(model)) {
|
|
144
138
|
// Verify tenant ownership before update
|
|
145
139
|
const tenantFilter = isOptionalTenantModel(model)
|
|
146
140
|
? { OR: [{ tenantId }, { tenantId: null }] }
|
|
@@ -153,7 +147,7 @@ export function createTenantPrisma(prisma, context) {
|
|
|
153
147
|
return query(args);
|
|
154
148
|
},
|
|
155
149
|
async updateMany({ model, args, query }) {
|
|
156
|
-
if (
|
|
150
|
+
if (hasTenantField(model)) {
|
|
157
151
|
const tenantFilter = isOptionalTenantModel(model)
|
|
158
152
|
? { OR: [{ tenantId }, { tenantId: null }] }
|
|
159
153
|
: { tenantId };
|
|
@@ -165,7 +159,7 @@ export function createTenantPrisma(prisma, context) {
|
|
|
165
159
|
return query(args);
|
|
166
160
|
},
|
|
167
161
|
async delete({ model, args, query }) {
|
|
168
|
-
if (
|
|
162
|
+
if (hasTenantField(model)) {
|
|
169
163
|
const tenantFilter = isOptionalTenantModel(model)
|
|
170
164
|
? { OR: [{ tenantId }, { tenantId: null }] }
|
|
171
165
|
: { tenantId };
|
|
@@ -177,7 +171,7 @@ export function createTenantPrisma(prisma, context) {
|
|
|
177
171
|
return query(args);
|
|
178
172
|
},
|
|
179
173
|
async deleteMany({ model, args, query }) {
|
|
180
|
-
if (
|
|
174
|
+
if (hasTenantField(model)) {
|
|
181
175
|
const tenantFilter = isOptionalTenantModel(model)
|
|
182
176
|
? { OR: [{ tenantId }, { tenantId: null }] }
|
|
183
177
|
: { tenantId };
|
|
@@ -189,7 +183,7 @@ export function createTenantPrisma(prisma, context) {
|
|
|
189
183
|
return query(args);
|
|
190
184
|
},
|
|
191
185
|
async count({ model, args, query }) {
|
|
192
|
-
if (
|
|
186
|
+
if (hasTenantField(model)) {
|
|
193
187
|
const tenantFilter = isOptionalTenantModel(model)
|
|
194
188
|
? { OR: [{ tenantId }, { tenantId: null }] }
|
|
195
189
|
: { tenantId };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action Status Types - Back-end driven UI indicators
|
|
3
|
+
*
|
|
4
|
+
* This module provides types for indicating who needs to act next
|
|
5
|
+
* at various levels of the application (application, phase, step).
|
|
6
|
+
*
|
|
7
|
+
* The frontend uses this to show:
|
|
8
|
+
* - "Awaiting your action" (CUSTOMER)
|
|
9
|
+
* - "Under review" (ADMIN)
|
|
10
|
+
* - "Processing..." (SYSTEM)
|
|
11
|
+
* - "Completed" (NONE)
|
|
12
|
+
* - "Awaiting payment" (CUSTOMER for payment phases)
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The actor who needs to take the next action
|
|
16
|
+
*/
|
|
17
|
+
export declare enum NextActor {
|
|
18
|
+
/** Customer must take action (upload, sign, pay) */
|
|
19
|
+
CUSTOMER = "CUSTOMER",
|
|
20
|
+
/** Admin must take action (review, approve, reject) */
|
|
21
|
+
ADMIN = "ADMIN",
|
|
22
|
+
/** System is processing (auto-generation, webhook, etc.) */
|
|
23
|
+
SYSTEM = "SYSTEM",
|
|
24
|
+
/** No action required - completed or waiting for external event */
|
|
25
|
+
NONE = "NONE"
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* High-level action categories for easier UI grouping
|
|
29
|
+
*/
|
|
30
|
+
export declare enum ActionCategory {
|
|
31
|
+
/** Document upload/reupload needed */
|
|
32
|
+
UPLOAD = "UPLOAD",
|
|
33
|
+
/** Signature required */
|
|
34
|
+
SIGNATURE = "SIGNATURE",
|
|
35
|
+
/** Review/approval needed */
|
|
36
|
+
REVIEW = "REVIEW",
|
|
37
|
+
/** Payment required */
|
|
38
|
+
PAYMENT = "PAYMENT",
|
|
39
|
+
/** Waiting for external process */
|
|
40
|
+
PROCESSING = "PROCESSING",
|
|
41
|
+
/** Phase/step/application completed */
|
|
42
|
+
COMPLETED = "COMPLETED",
|
|
43
|
+
/** Waiting for previous phase/step */
|
|
44
|
+
WAITING = "WAITING"
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Detailed action status for a step, phase, or application
|
|
48
|
+
*/
|
|
49
|
+
export interface ActionStatus {
|
|
50
|
+
/** Who needs to act next */
|
|
51
|
+
nextActor: NextActor;
|
|
52
|
+
/** Category of action required */
|
|
53
|
+
actionCategory: ActionCategory;
|
|
54
|
+
/** Human-readable description of what's needed */
|
|
55
|
+
actionRequired: string;
|
|
56
|
+
/** Optional: Additional context (e.g., "2 of 3 documents uploaded") */
|
|
57
|
+
progress?: string;
|
|
58
|
+
/** Optional: When this action is due (for time-sensitive actions) */
|
|
59
|
+
dueDate?: Date | string | null;
|
|
60
|
+
/** Optional: Whether this is blocking the overall workflow */
|
|
61
|
+
isBlocking?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Step-level action status with step details
|
|
65
|
+
*/
|
|
66
|
+
export interface StepActionStatus extends ActionStatus {
|
|
67
|
+
stepId: string;
|
|
68
|
+
stepName: string;
|
|
69
|
+
stepType: string;
|
|
70
|
+
stepOrder: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Phase-level action status with aggregated step info
|
|
74
|
+
*/
|
|
75
|
+
export interface PhaseActionStatus extends ActionStatus {
|
|
76
|
+
phaseId: string;
|
|
77
|
+
phaseName: string;
|
|
78
|
+
phaseType: string;
|
|
79
|
+
phaseCategory: string;
|
|
80
|
+
/** Current step requiring attention (if documentation phase) */
|
|
81
|
+
currentStep?: StepActionStatus | null;
|
|
82
|
+
/** Summary of step progress (e.g., "3 of 5 steps completed") */
|
|
83
|
+
stepsProgress?: string;
|
|
84
|
+
/** For payment phases: payment progress summary */
|
|
85
|
+
paymentProgress?: string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Application-level action status with phase info
|
|
89
|
+
*/
|
|
90
|
+
export interface ApplicationActionStatus extends ActionStatus {
|
|
91
|
+
applicationId: string;
|
|
92
|
+
applicationNumber: string;
|
|
93
|
+
/** Current phase requiring attention */
|
|
94
|
+
currentPhase?: PhaseActionStatus | null;
|
|
95
|
+
/** Summary of phase progress */
|
|
96
|
+
phasesProgress?: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Compute action status for a documentation step
|
|
100
|
+
*/
|
|
101
|
+
export declare function computeStepActionStatus(step: {
|
|
102
|
+
id: string;
|
|
103
|
+
name: string;
|
|
104
|
+
stepType: string;
|
|
105
|
+
order: number;
|
|
106
|
+
status: string;
|
|
107
|
+
actionReason?: string | null;
|
|
108
|
+
dueDate?: Date | string | null;
|
|
109
|
+
}, pendingDocuments?: number, totalDocuments?: number): StepActionStatus;
|
|
110
|
+
/**
|
|
111
|
+
* Compute action status for a phase based on its category and current state
|
|
112
|
+
*/
|
|
113
|
+
export declare function computePhaseActionStatus(phase: {
|
|
114
|
+
id: string;
|
|
115
|
+
name: string;
|
|
116
|
+
phaseType: string;
|
|
117
|
+
phaseCategory: string;
|
|
118
|
+
status: string;
|
|
119
|
+
dueDate?: Date | string | null;
|
|
120
|
+
documentationPhase?: {
|
|
121
|
+
currentStep?: any | null;
|
|
122
|
+
steps?: any[];
|
|
123
|
+
completedStepsCount?: number;
|
|
124
|
+
totalStepsCount?: number;
|
|
125
|
+
approvedDocumentsCount?: number;
|
|
126
|
+
requiredDocumentsCount?: number;
|
|
127
|
+
} | null;
|
|
128
|
+
paymentPhase?: {
|
|
129
|
+
totalAmount?: number;
|
|
130
|
+
paidAmount?: number;
|
|
131
|
+
installments?: any[];
|
|
132
|
+
} | null;
|
|
133
|
+
questionnairePhase?: {
|
|
134
|
+
completedFieldsCount?: number;
|
|
135
|
+
totalFieldsCount?: number;
|
|
136
|
+
} | null;
|
|
137
|
+
}): PhaseActionStatus;
|