@rentcheck/biz 1.0.63 → 1.0.65
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/permissions/inspections.d.ts +8 -1
- package/dist/permissions/inspections.js +17 -1
- package/dist/rules/inspections/index.d.ts +33 -4
- package/dist/rules/inspections/index.js +395 -2
- package/dist/rules/teammates/index.js +1 -1
- package/dist/utils/dates/index.d.ts +3 -1
- package/dist/utils/dates/index.js +11 -3
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +2 -1
- package/dist/utils/inspection-features/sorting.d.ts +1 -1
- package/dist/utils/inspection-templates/build-template-features.js +1 -0
- package/dist/utils/inspection-templates/common.d.ts +15 -0
- package/dist/utils/inspection-templates/common.js +42 -1
- package/dist/utils/inspection-templates/template-logic.d.ts +2 -2
- package/dist/utils/inspections/index.d.ts +2 -0
- package/dist/utils/inspections/index.js +13 -0
- package/dist/utils/permission-groups/index.d.ts +2 -0
- package/dist/utils/permission-groups/index.js +11 -0
- package/dist/utils/properties/index.d.ts +3 -0
- package/dist/utils/properties/index.js +30 -0
- package/dist/utils/subscriptions/index.d.ts +1 -0
- package/dist/utils/subscriptions/index.js +8 -1
- package/dist/utils/tenants/index.d.ts +2 -2
- package/dist/utils/tenants/index.js +3 -3
- package/package.json +5 -1
- package/src/permissions/inspections.ts +47 -1
- package/src/rules/inspections/index.ts +450 -5
- package/src/rules/teammates/index.ts +1 -1
- package/src/utils/dates/index.ts +10 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/inspection-features/sorting.ts +2 -2
- package/src/utils/inspection-templates/build-template-features.ts +2 -0
- package/src/utils/inspection-templates/common.ts +51 -0
- package/src/utils/inspection-templates/template-logic.ts +2 -2
- package/src/utils/inspections/index.ts +15 -0
- package/src/utils/permission-groups/index.ts +11 -0
- package/src/utils/properties/index.ts +47 -0
- package/src/utils/subscriptions/index.ts +10 -0
- package/src/utils/tenants/index.ts +6 -6
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.userIsInvitedTeammate = void 0;
|
|
4
|
+
const userIsInvitedTeammate = (inspection) => {
|
|
5
|
+
const validRoles = ['invitee-write', 'invitee-readonly'];
|
|
6
|
+
const validAssigneeTypes = ['teammates'];
|
|
7
|
+
if (validRoles.includes(inspection.role) &&
|
|
8
|
+
validAssigneeTypes.includes(inspection.assigned_recipients.type)) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
};
|
|
13
|
+
exports.userIsInvitedTeammate = userIsInvitedTeammate;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildDigest = void 0;
|
|
4
|
+
const buildDigest = (data) => {
|
|
5
|
+
return {
|
|
6
|
+
id: data.id,
|
|
7
|
+
name: data.name,
|
|
8
|
+
user_count: data.users.length,
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
exports.buildDigest = buildDigest;
|
|
@@ -1 +1,4 @@
|
|
|
1
|
+
import { APIProperty, AppFolioIntegration, Property, RentmanagerIntegration } from '@rentcheck/types';
|
|
1
2
|
export * from './validations';
|
|
3
|
+
export declare const propertyIsOutOfSync: (property: Property, integration?: AppFolioIntegration | RentmanagerIntegration) => boolean;
|
|
4
|
+
export declare const getIntegrationVendor: (property: APIProperty | Property) => string | undefined;
|
|
@@ -13,5 +13,35 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
13
13
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
16
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.getIntegrationVendor = exports.propertyIsOutOfSync = void 0;
|
|
21
|
+
const moment_1 = __importDefault(require("moment"));
|
|
17
22
|
__exportStar(require("./validations"), exports);
|
|
23
|
+
const propertyIsOutOfSync = (property, integration) => {
|
|
24
|
+
if (property.property_type === 'Community') {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (!integration) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (!property.rentmanager_sync && !property.appfolio_sync) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
const sync = property.rentmanager_sync || property.appfolio_sync;
|
|
34
|
+
return ((sync === null || sync === void 0 ? void 0 : sync.last_sync) &&
|
|
35
|
+
(0, moment_1.default)((0, moment_1.default)()).diff(sync.last_sync.toDate(), 'days') >= 7);
|
|
36
|
+
};
|
|
37
|
+
exports.propertyIsOutOfSync = propertyIsOutOfSync;
|
|
38
|
+
const getIntegrationVendor = (property) => {
|
|
39
|
+
if (property.rentmanager_sync) {
|
|
40
|
+
return 'Rent Manager';
|
|
41
|
+
}
|
|
42
|
+
if (property.appfolio_sync) {
|
|
43
|
+
return 'AppFolio';
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
};
|
|
47
|
+
exports.getIntegrationVendor = getIntegrationVendor;
|
|
@@ -3,3 +3,4 @@ import { ChargebeeEventSubscription } from '@rentcheck/types/dist/integrations/C
|
|
|
3
3
|
export declare const isOnEssentialsPlan: (subscription?: ApiSubscription | null) => boolean;
|
|
4
4
|
export declare const isOnProPlan: (subscription?: ApiSubscription | ChargebeeEventSubscription | null) => boolean;
|
|
5
5
|
export declare const hasPropertySyncIntegrationAddons: (subscription: ApiSubscription | Subscription | null) => boolean;
|
|
6
|
+
export declare const hasPremiumVideoAddons: (subscription: ApiSubscription | Subscription | null) => boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.hasPropertySyncIntegrationAddons = exports.isOnProPlan = exports.isOnEssentialsPlan = void 0;
|
|
3
|
+
exports.hasPremiumVideoAddons = exports.hasPropertySyncIntegrationAddons = exports.isOnProPlan = exports.isOnEssentialsPlan = void 0;
|
|
4
4
|
const integrations_1 = require("../../permissions/integrations");
|
|
5
5
|
const isOnEssentialsPlan = (subscription) => {
|
|
6
6
|
if (!(subscription === null || subscription === void 0 ? void 0 : subscription.plan)) {
|
|
@@ -33,3 +33,10 @@ const hasPropertySyncIntegrationAddons = (subscription) => {
|
|
|
33
33
|
integrations_1.RentManager.canInstall(subscription));
|
|
34
34
|
};
|
|
35
35
|
exports.hasPropertySyncIntegrationAddons = hasPropertySyncIntegrationAddons;
|
|
36
|
+
const hasPremiumVideoAddons = (subscription) => {
|
|
37
|
+
if (!subscription) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return subscription.addons.some((a) => a.id.startsWith('premium-video'));
|
|
41
|
+
};
|
|
42
|
+
exports.hasPremiumVideoAddons = hasPremiumVideoAddons;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const getSyncData: (tenant: Tenant) =>
|
|
1
|
+
import { SyncData, Tenant } from '@rentcheck/types';
|
|
2
|
+
export declare const getSyncData: (tenant: Tenant) => SyncData;
|
|
@@ -4,13 +4,13 @@ exports.getSyncData = void 0;
|
|
|
4
4
|
const dates_1 = require("../dates");
|
|
5
5
|
const getSyncData = (tenant) => {
|
|
6
6
|
if (tenant.appfolio_sync) {
|
|
7
|
-
return Object.assign(Object.assign({
|
|
7
|
+
return Object.assign(Object.assign({ vendor: 'appfolio' }, tenant.appfolio_sync), { last_sync: (0, dates_1.parse)('firestore', tenant.appfolio_sync.last_sync, null) });
|
|
8
8
|
}
|
|
9
9
|
if (tenant.rentmanager_sync) {
|
|
10
|
-
return Object.assign(Object.assign({
|
|
10
|
+
return Object.assign(Object.assign({ vendor: 'rentmanager' }, tenant.rentmanager_sync), { last_sync: (0, dates_1.parse)('firestore', tenant.rentmanager_sync.last_sync, null) });
|
|
11
11
|
}
|
|
12
12
|
if (tenant.yardi_sync) {
|
|
13
|
-
return Object.assign(Object.assign({
|
|
13
|
+
return Object.assign(Object.assign({ vendor: 'yardi' }, tenant.yardi_sync), { last_sync: (0, dates_1.parse)('firestore', tenant.yardi_sync, null) });
|
|
14
14
|
}
|
|
15
15
|
return undefined;
|
|
16
16
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rentcheck/biz",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.65",
|
|
4
4
|
"description": "RC biz",
|
|
5
5
|
"author": "engineering@getrentcheck.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,12 +16,16 @@
|
|
|
16
16
|
"@babel/runtime": "^7.16.7",
|
|
17
17
|
"@types/lodash": "^4.14.194",
|
|
18
18
|
"jest": "^27.5.1",
|
|
19
|
+
"moment": "^2.24.0",
|
|
19
20
|
"typescript": "^4.2.3"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
23
|
"@rentcheck/types": "^1.0.0",
|
|
23
24
|
"lodash": "^4.17.21"
|
|
24
25
|
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"moment": "^2.24.0"
|
|
28
|
+
},
|
|
25
29
|
"localDependencies": {
|
|
26
30
|
"@rentcheck/types": "../types"
|
|
27
31
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AccountSettings,
|
|
3
|
+
ApiSubscription,
|
|
4
|
+
Inspection,
|
|
5
|
+
PermissionGroup,
|
|
6
|
+
Profile,
|
|
7
|
+
} from '@rentcheck/types';
|
|
2
8
|
import * as Utils from '../utils';
|
|
3
9
|
|
|
4
10
|
export const canRecordVideo = (subscription?: ApiSubscription | null) => {
|
|
@@ -47,3 +53,43 @@ export const areFlagDefaultPhotosEnabled = (
|
|
|
47
53
|
|
|
48
54
|
return accountSettings.is_maintenance_flag_default_photos_enabled;
|
|
49
55
|
};
|
|
56
|
+
|
|
57
|
+
type CanAccessParams = {
|
|
58
|
+
user: Profile;
|
|
59
|
+
inspection: Inspection;
|
|
60
|
+
permissionGroups: PermissionGroup[];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const canAccess = ({
|
|
64
|
+
user,
|
|
65
|
+
inspection,
|
|
66
|
+
permissionGroups,
|
|
67
|
+
}: CanAccessParams): boolean => {
|
|
68
|
+
const userIdIsInAuthorizedUserIds = inspection.authorized_user_ids.includes(
|
|
69
|
+
user.id
|
|
70
|
+
);
|
|
71
|
+
if (userIdIsInAuthorizedUserIds) return true;
|
|
72
|
+
|
|
73
|
+
const userOrgIds = user.organizations.map((org) => org.id);
|
|
74
|
+
const userOrOrgsHaveAccessToInspection = userOrgIds.some((orgId) =>
|
|
75
|
+
inspection.authorized_user_ids.includes(orgId)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const validInspectionTemplatesForUser =
|
|
79
|
+
permissionGroups
|
|
80
|
+
?.map((permissionGroup) => permissionGroup.inspection_templates)
|
|
81
|
+
?.flat() ?? [];
|
|
82
|
+
const userPermissionGroupsHaveAccessToInspectionTemplate =
|
|
83
|
+
validInspectionTemplatesForUser?.includes(
|
|
84
|
+
inspection.inspection_template.id
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
userOrOrgsHaveAccessToInspection &&
|
|
89
|
+
userPermissionGroupsHaveAccessToInspectionTemplate
|
|
90
|
+
) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return false;
|
|
95
|
+
};
|
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
APIProperty,
|
|
3
|
+
ApiInspection,
|
|
4
|
+
ApiInspectionWithTemplate,
|
|
5
|
+
Feature,
|
|
6
|
+
InspectionRole,
|
|
7
|
+
InspectionStatusDisplay,
|
|
8
|
+
Profile,
|
|
9
|
+
Recipients,
|
|
10
|
+
} from '@rentcheck/types';
|
|
2
11
|
|
|
3
12
|
import { Users } from '../../utils';
|
|
13
|
+
import { userIsInvitedTeammate } from '../../utils/inspections';
|
|
4
14
|
|
|
5
15
|
export const shouldShowLibraryOption = (
|
|
6
|
-
inspection?:
|
|
16
|
+
inspection?: ApiInspection,
|
|
7
17
|
profile?: Profile
|
|
8
18
|
) => {
|
|
9
19
|
if (!inspection) return false;
|
|
@@ -11,7 +21,7 @@ export const shouldShowLibraryOption = (
|
|
|
11
21
|
return Users.canPerformInspection(inspection) && !Users.isRenter(profile);
|
|
12
22
|
};
|
|
13
23
|
|
|
14
|
-
export const shouldShow360Option = (inspection?:
|
|
24
|
+
export const shouldShow360Option = (inspection?: ApiInspection) => {
|
|
15
25
|
if (!inspection) return false;
|
|
16
26
|
|
|
17
27
|
return inspection.settings.can_take_360_photos
|
|
@@ -20,12 +30,447 @@ export const shouldShow360Option = (inspection?: ApiInspectionWithTemplate) => {
|
|
|
20
30
|
};
|
|
21
31
|
|
|
22
32
|
export const shouldShowVideoOption = (
|
|
23
|
-
inspection?:
|
|
24
|
-
profile?: Profile
|
|
33
|
+
inspection?: ApiInspection,
|
|
34
|
+
profile?: Profile,
|
|
35
|
+
feature?: Feature
|
|
25
36
|
) => {
|
|
37
|
+
if (feature?.is_video_enabled) return true;
|
|
38
|
+
|
|
26
39
|
if (!inspection) return false;
|
|
27
40
|
|
|
28
41
|
return inspection.settings.can_record_video && !Users.isRenter(profile)
|
|
29
42
|
? !!inspection.fast_track
|
|
30
43
|
: false;
|
|
31
44
|
};
|
|
45
|
+
|
|
46
|
+
export const canApprove = (inspection: ApiInspection, profile?: Profile) => {
|
|
47
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
48
|
+
'Awaiting Review',
|
|
49
|
+
'Revision Review',
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
if (!profile) return false;
|
|
53
|
+
|
|
54
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
55
|
+
|
|
56
|
+
if (inspection.role !== 'creator') return false;
|
|
57
|
+
|
|
58
|
+
return true;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const canReject = (inspection: ApiInspection, profile?: Profile) => {
|
|
62
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
63
|
+
'Awaiting Review',
|
|
64
|
+
'Revision Review',
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
if (!profile) return false;
|
|
68
|
+
|
|
69
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
70
|
+
|
|
71
|
+
if (inspection.role !== 'creator') return false;
|
|
72
|
+
|
|
73
|
+
if (inspection.review) return false;
|
|
74
|
+
|
|
75
|
+
return true;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const canEditLabel = (inspection: ApiInspection) => {
|
|
79
|
+
if (userIsInvitedTeammate(inspection)) return true;
|
|
80
|
+
|
|
81
|
+
const validRoles: InspectionRole[] = ['creator', 'self-perform'];
|
|
82
|
+
|
|
83
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
84
|
+
|
|
85
|
+
return true;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const canCancelReviewRequest = (inspection: ApiInspection) => {
|
|
89
|
+
const validRoles: InspectionRole[] = ['creator'];
|
|
90
|
+
|
|
91
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
92
|
+
|
|
93
|
+
if (inspection.inspection_status !== 'In Review') return false;
|
|
94
|
+
|
|
95
|
+
if (inspection.assigned_recipients.type !== 'emails') return false;
|
|
96
|
+
|
|
97
|
+
return true;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const canCancelRevisionRequest = (inspection: ApiInspection) => {
|
|
101
|
+
const validRoles: InspectionRole[] = ['creator'];
|
|
102
|
+
|
|
103
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
104
|
+
|
|
105
|
+
if (inspection.inspection_status !== 'Revision Requested') return false;
|
|
106
|
+
|
|
107
|
+
return true;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const canArchive = (inspection: ApiInspection) => {
|
|
111
|
+
if (userIsInvitedTeammate(inspection)) return true;
|
|
112
|
+
|
|
113
|
+
const validRoles: InspectionRole[] = ['creator', 'self-perform'];
|
|
114
|
+
|
|
115
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
116
|
+
|
|
117
|
+
if (inspection.archived) return false;
|
|
118
|
+
|
|
119
|
+
return true;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const canUnarchive = (inspection: ApiInspection) => {
|
|
123
|
+
if (userIsInvitedTeammate(inspection)) return true;
|
|
124
|
+
|
|
125
|
+
const validRoles: InspectionRole[] = ['creator', 'self-perform'];
|
|
126
|
+
|
|
127
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
128
|
+
|
|
129
|
+
if (!inspection.archived) return false;
|
|
130
|
+
|
|
131
|
+
return true;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const canDelete = (inspection: ApiInspection) => {
|
|
135
|
+
if (userIsInvitedTeammate(inspection)) return true;
|
|
136
|
+
|
|
137
|
+
const validRoles: InspectionRole[] = ['creator', 'self-perform'];
|
|
138
|
+
const validStatuses: InspectionStatusDisplay[] = ['Scheduled', 'Not Started'];
|
|
139
|
+
|
|
140
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
141
|
+
|
|
142
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
143
|
+
|
|
144
|
+
return true;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const canSendReminder = (inspection: ApiInspection) => {
|
|
148
|
+
const validRoles: InspectionRole[] = ['creator'];
|
|
149
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
150
|
+
'Not Started',
|
|
151
|
+
'Started',
|
|
152
|
+
'Revision Requested',
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
156
|
+
|
|
157
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
158
|
+
|
|
159
|
+
return true;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export const canMarkAsComplete = (inspection: ApiInspection) => {
|
|
163
|
+
const validRoles: InspectionRole[] = ['creator', 'self-perform'];
|
|
164
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
165
|
+
'Started',
|
|
166
|
+
'Revision Requested',
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
170
|
+
|
|
171
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
172
|
+
|
|
173
|
+
return true;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const canDownloadImages = (
|
|
177
|
+
inspection: ApiInspection,
|
|
178
|
+
features: Feature[]
|
|
179
|
+
) => {
|
|
180
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
181
|
+
'In Review',
|
|
182
|
+
'Completed',
|
|
183
|
+
'Awaiting Review',
|
|
184
|
+
'Revision Review',
|
|
185
|
+
'Approved',
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
189
|
+
|
|
190
|
+
if (!features.some((f) => f.images.length > 0 || f.videos.length > 0))
|
|
191
|
+
return false;
|
|
192
|
+
|
|
193
|
+
return true;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export const canDownloadMaintenanceFlags = (
|
|
197
|
+
inspection: ApiInspection,
|
|
198
|
+
features: Feature[]
|
|
199
|
+
) => {
|
|
200
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
201
|
+
'In Review',
|
|
202
|
+
'Completed',
|
|
203
|
+
'Awaiting Review',
|
|
204
|
+
'Revision Review',
|
|
205
|
+
'Approved',
|
|
206
|
+
];
|
|
207
|
+
|
|
208
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
209
|
+
|
|
210
|
+
if (!features.some((f) => f.maintenance_flags.length > 0)) return false;
|
|
211
|
+
|
|
212
|
+
return true;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
export const canRequestSignatures = (inspection: ApiInspection) => {
|
|
216
|
+
const validRoles: InspectionRole[] = ['creator', 'self-perform'];
|
|
217
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
218
|
+
'In Review',
|
|
219
|
+
'Completed',
|
|
220
|
+
'Awaiting Review',
|
|
221
|
+
'Revision Review',
|
|
222
|
+
'Approved',
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
226
|
+
|
|
227
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
228
|
+
|
|
229
|
+
return true;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
export const canCreateMaintenanceReport = (
|
|
233
|
+
inspection: ApiInspection,
|
|
234
|
+
features: Feature[]
|
|
235
|
+
) => {
|
|
236
|
+
const validRoles: InspectionRole[] = ['creator', 'self-perform'];
|
|
237
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
238
|
+
'In Review',
|
|
239
|
+
'Completed',
|
|
240
|
+
'Awaiting Review',
|
|
241
|
+
'Revision Review',
|
|
242
|
+
'Approved',
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
246
|
+
|
|
247
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
248
|
+
|
|
249
|
+
if (!features.some((f) => f.maintenance_flags.length > 0)) return false;
|
|
250
|
+
|
|
251
|
+
return true;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
export const canShowCompletedDate = (inspection: ApiInspection) => {
|
|
255
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
256
|
+
'Awaiting Review',
|
|
257
|
+
'Completed',
|
|
258
|
+
'Revision Requested',
|
|
259
|
+
'Revision Review',
|
|
260
|
+
'In Review',
|
|
261
|
+
'Approved',
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
265
|
+
|
|
266
|
+
if (!inspection.completed_date) return false;
|
|
267
|
+
|
|
268
|
+
return true;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export const canShowCompletedBy = (inspection: ApiInspection) => {
|
|
272
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
273
|
+
'Awaiting Review',
|
|
274
|
+
'Completed',
|
|
275
|
+
'Revision Requested',
|
|
276
|
+
'Revision Review',
|
|
277
|
+
'In Review',
|
|
278
|
+
'Approved',
|
|
279
|
+
];
|
|
280
|
+
|
|
281
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
282
|
+
|
|
283
|
+
if (!inspection.completed_by) return false;
|
|
284
|
+
|
|
285
|
+
return true;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
export const canShowDueDate = (inspection: ApiInspection) => {
|
|
289
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
290
|
+
'Scheduled',
|
|
291
|
+
'Not Started',
|
|
292
|
+
'Started',
|
|
293
|
+
];
|
|
294
|
+
|
|
295
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
296
|
+
|
|
297
|
+
return true;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
export const canShowAssignee = (inspection: ApiInspection) => {
|
|
301
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
302
|
+
'Scheduled',
|
|
303
|
+
'Not Started',
|
|
304
|
+
'Started',
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
308
|
+
|
|
309
|
+
return true;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
export const canEditAssignee = (inspection: ApiInspection) => {
|
|
313
|
+
if (!canShowAssignee(inspection)) return false;
|
|
314
|
+
|
|
315
|
+
if (userIsInvitedTeammate(inspection)) return true;
|
|
316
|
+
|
|
317
|
+
const validRoles: InspectionRole[] = ['creator', 'self-perform'];
|
|
318
|
+
|
|
319
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
320
|
+
|
|
321
|
+
return true;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
export const canShowInviteDate = (inspection: ApiInspection) => {
|
|
325
|
+
const validStatuses: InspectionStatusDisplay[] = ['Scheduled'];
|
|
326
|
+
const validAssigneeTypes: Recipients['type'][] = [
|
|
327
|
+
'emails',
|
|
328
|
+
'residents',
|
|
329
|
+
'teammates',
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
333
|
+
|
|
334
|
+
if (!validAssigneeTypes.includes(inspection.assigned_recipients.type))
|
|
335
|
+
return false;
|
|
336
|
+
|
|
337
|
+
if (!inspection.invite_date) return false;
|
|
338
|
+
|
|
339
|
+
return true;
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export const canShowRevisionDueDate = (inspection: ApiInspection) => {
|
|
343
|
+
const validStatuses: InspectionStatusDisplay[] = ['Revision Requested'];
|
|
344
|
+
|
|
345
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
346
|
+
|
|
347
|
+
if (!inspection.rejection?.due_date) return false;
|
|
348
|
+
|
|
349
|
+
return true;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
export const canShowReviewDueDate = (inspection: ApiInspection) => {
|
|
353
|
+
const validStatuses: InspectionStatusDisplay[] = ['In Review'];
|
|
354
|
+
|
|
355
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
356
|
+
|
|
357
|
+
if (!inspection.review?.due_date) return false;
|
|
358
|
+
|
|
359
|
+
return true;
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
export const canShowHyperlinkToPropertyPage = (inspection: ApiInspection) => {
|
|
363
|
+
if (userIsInvitedTeammate(inspection)) return true;
|
|
364
|
+
|
|
365
|
+
const validRoles: InspectionRole[] = ['creator', 'self-perform'];
|
|
366
|
+
|
|
367
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
368
|
+
|
|
369
|
+
return true;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export const canEditDueDate = (inspection: ApiInspection) => {
|
|
373
|
+
if (!canShowDueDate(inspection)) return false;
|
|
374
|
+
|
|
375
|
+
const validRoles: InspectionRole[] = ['creator', 'self-perform'];
|
|
376
|
+
|
|
377
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
378
|
+
|
|
379
|
+
return true;
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
export const canShare = (inspection: ApiInspection) => {
|
|
383
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
384
|
+
'In Review',
|
|
385
|
+
'Completed',
|
|
386
|
+
'Awaiting Review',
|
|
387
|
+
'Revision Review',
|
|
388
|
+
'Approved',
|
|
389
|
+
];
|
|
390
|
+
|
|
391
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
392
|
+
|
|
393
|
+
return true;
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
export const canDownloadReport = (inspection: ApiInspection) => {
|
|
397
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
398
|
+
'In Review',
|
|
399
|
+
'Completed',
|
|
400
|
+
'Awaiting Review',
|
|
401
|
+
'Revision Review',
|
|
402
|
+
'Approved',
|
|
403
|
+
];
|
|
404
|
+
|
|
405
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
406
|
+
|
|
407
|
+
return true;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
export const canCopyInspectionDeeplink = (inspection: ApiInspection) => {
|
|
411
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
412
|
+
'Scheduled',
|
|
413
|
+
'Not Started',
|
|
414
|
+
'Started',
|
|
415
|
+
'Revision Requested',
|
|
416
|
+
];
|
|
417
|
+
|
|
418
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
419
|
+
|
|
420
|
+
return true;
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
export const canManualSync = (
|
|
424
|
+
inspection: ApiInspectionWithTemplate,
|
|
425
|
+
property: APIProperty
|
|
426
|
+
) => {
|
|
427
|
+
/**
|
|
428
|
+
* If the property is not synced to either provider we don't
|
|
429
|
+
* have anywhere to sync the inspection to.
|
|
430
|
+
*/
|
|
431
|
+
if (!property.rentmanager_sync && !property.appfolio_sync) return false;
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* if the inspection has already been synced and we registered
|
|
435
|
+
* no errors, we don't need to show the button. Manual sync is
|
|
436
|
+
* only intended to be shown as an initial sync or a retry.
|
|
437
|
+
*/
|
|
438
|
+
if (inspection.sync_data && !inspection.sync_data.error) return false;
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Only inspections that are Completed or In Review can be synced.
|
|
442
|
+
*/
|
|
443
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
444
|
+
'In Review',
|
|
445
|
+
'Completed',
|
|
446
|
+
'Awaiting Review',
|
|
447
|
+
'Revision Review',
|
|
448
|
+
'Approved',
|
|
449
|
+
];
|
|
450
|
+
|
|
451
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
452
|
+
|
|
453
|
+
return true;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
export const canShowSyncStatus = (inspection: ApiInspectionWithTemplate) => {
|
|
457
|
+
if (!inspection.sync_data) return false;
|
|
458
|
+
|
|
459
|
+
const validStatuses: InspectionStatusDisplay[] = [
|
|
460
|
+
'In Review',
|
|
461
|
+
'Completed',
|
|
462
|
+
'Awaiting Review',
|
|
463
|
+
'Revision Review',
|
|
464
|
+
'Approved',
|
|
465
|
+
];
|
|
466
|
+
|
|
467
|
+
if (!validStatuses.includes(inspection.inspection_status)) return false;
|
|
468
|
+
|
|
469
|
+
if (userIsInvitedTeammate(inspection)) return true;
|
|
470
|
+
|
|
471
|
+
const validRoles: InspectionRole[] = ['creator', 'self-perform'];
|
|
472
|
+
|
|
473
|
+
if (!validRoles.includes(inspection.role)) return false;
|
|
474
|
+
|
|
475
|
+
return true;
|
|
476
|
+
};
|