@blackcode_sa/metaestetics-api 1.15.16 → 1.15.17
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/admin/index.d.mts +377 -222
- package/dist/admin/index.d.ts +377 -222
- package/dist/admin/index.js +625 -206
- package/dist/admin/index.mjs +624 -206
- package/dist/backoffice/index.d.mts +24 -0
- package/dist/backoffice/index.d.ts +24 -0
- package/dist/index.d.mts +371 -4
- package/dist/index.d.ts +371 -4
- package/dist/index.js +2227 -1580
- package/dist/index.mjs +1543 -891
- package/package.json +1 -1
- package/src/admin/aggregation/appointment/README.md +24 -2
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +46 -0
- package/src/admin/booking/README.md +61 -2
- package/src/admin/booking/booking.admin.ts +257 -0
- package/src/admin/booking/booking.calculator.ts +139 -1
- package/src/admin/booking/booking.types.ts +17 -0
- package/src/admin/calendar/README.md +56 -1
- package/src/admin/calendar/index.ts +1 -0
- package/src/admin/calendar/resource-calendar.admin.ts +198 -0
- package/src/config/index.ts +1 -0
- package/src/config/tiers.config.ts +116 -0
- package/src/services/index.ts +1 -0
- package/src/services/plan-config.service.ts +55 -0
- package/src/services/resource/README.md +119 -0
- package/src/services/resource/index.ts +1 -0
- package/src/services/resource/resource.service.ts +555 -0
- package/src/services/tier-enforcement.ts +15 -10
- package/src/types/appointment/index.ts +7 -0
- package/src/types/calendar/index.ts +1 -0
- package/src/types/clinic/index.ts +1 -0
- package/src/types/clinic/rbac.types.ts +3 -2
- package/src/types/index.ts +6 -0
- package/src/types/procedure/index.ts +6 -0
- package/src/types/resource/README.md +153 -0
- package/src/types/resource/index.ts +199 -0
- package/src/types/system/index.ts +1 -0
- package/src/types/system/planConfig.types.ts +86 -0
- package/src/validations/README.md +94 -0
- package/src/validations/index.ts +1 -0
- package/src/validations/procedure.schema.ts +12 -0
- package/src/validations/resource.schema.ts +57 -0
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "../../backoffice/types/static/pricing.types";
|
|
22
22
|
import { MediaResource } from "../../services/media/media.service";
|
|
23
23
|
import type { ProcedureProduct } from "../../backoffice/types/procedure-product.types";
|
|
24
|
+
import type { ResourceRequirement } from "../resource";
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Procedure represents a specific medical procedure that can be performed by a practitioner in a clinic
|
|
@@ -84,6 +85,8 @@ export interface Procedure {
|
|
|
84
85
|
doctorInfo: DoctorInfo;
|
|
85
86
|
/** Aggregated review information for this procedure */
|
|
86
87
|
reviewInfo: ProcedureReviewInfo;
|
|
88
|
+
/** Optional resources required for this procedure (e.g., surgery room, device) */
|
|
89
|
+
resourceRequirements?: ResourceRequirement[];
|
|
87
90
|
/** Whether this procedure is active */
|
|
88
91
|
isActive: boolean;
|
|
89
92
|
/** When this procedure was created */
|
|
@@ -119,6 +122,7 @@ export interface CreateProcedureData {
|
|
|
119
122
|
practitionerId: string;
|
|
120
123
|
clinicBranchId: string;
|
|
121
124
|
photos?: MediaResource[];
|
|
125
|
+
resourceRequirements?: ResourceRequirement[];
|
|
122
126
|
}
|
|
123
127
|
|
|
124
128
|
/**
|
|
@@ -148,6 +152,7 @@ export interface UpdateProcedureData {
|
|
|
148
152
|
productId?: string;
|
|
149
153
|
clinicBranchId?: string;
|
|
150
154
|
photos?: MediaResource[];
|
|
155
|
+
resourceRequirements?: ResourceRequirement[];
|
|
151
156
|
}
|
|
152
157
|
|
|
153
158
|
/**
|
|
@@ -180,4 +185,5 @@ export interface ProcedureSummaryInfo {
|
|
|
180
185
|
clinicName: string;
|
|
181
186
|
practitionerId: string;
|
|
182
187
|
practitionerName: string;
|
|
188
|
+
resourceRequirements?: ResourceRequirement[];
|
|
183
189
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Resource Types (`Api/src/types/resource/`)
|
|
2
|
+
|
|
3
|
+
Type definitions for the Clinic Resources system. These types are used across the API package, Cloud Functions, and the ClinicApp frontend (via the `@blackcode_sa/metaestetics-api` package).
|
|
4
|
+
|
|
5
|
+
## File: `index.ts`
|
|
6
|
+
|
|
7
|
+
### Collection Constants
|
|
8
|
+
|
|
9
|
+
| Constant | Value | Description |
|
|
10
|
+
|----------|-------|-------------|
|
|
11
|
+
| `RESOURCES_COLLECTION` | `"resources"` | Subcollection under clinics |
|
|
12
|
+
| `RESOURCE_INSTANCES_SUBCOLLECTION` | `"instances"` | Subcollection under resources |
|
|
13
|
+
| `RESOURCE_CALENDAR_SUBCOLLECTION` | `"calendar"` | Subcollection under instances |
|
|
14
|
+
|
|
15
|
+
### Firestore Path
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
clinics/{clinicId}/resources/{resourceId}
|
|
19
|
+
clinics/{clinicId}/resources/{resourceId}/instances/{instanceId}
|
|
20
|
+
clinics/{clinicId}/resources/{resourceId}/instances/{instanceId}/calendar/{eventId}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Enums
|
|
24
|
+
|
|
25
|
+
#### `ResourceCategory`
|
|
26
|
+
Categorizes the type of physical resource.
|
|
27
|
+
|
|
28
|
+
| Value | Description |
|
|
29
|
+
|-------|-------------|
|
|
30
|
+
| `MEDICAL_DEVICE` | Medical equipment (e.g., laser machine) |
|
|
31
|
+
| `ROOM` | General room (e.g., consultation room) |
|
|
32
|
+
| `SURGERY_SUITE` | Operating/surgery room |
|
|
33
|
+
| `EQUIPMENT` | General equipment |
|
|
34
|
+
| `OTHER` | Uncategorized |
|
|
35
|
+
|
|
36
|
+
#### `ResourceStatus`
|
|
37
|
+
Tracks the operational state of a resource or instance.
|
|
38
|
+
|
|
39
|
+
| Value | Description |
|
|
40
|
+
|-------|-------------|
|
|
41
|
+
| `ACTIVE` | Available for booking |
|
|
42
|
+
| `INACTIVE` | Soft-deleted or disabled |
|
|
43
|
+
| `MAINTENANCE` | Temporarily unavailable |
|
|
44
|
+
|
|
45
|
+
### Interfaces
|
|
46
|
+
|
|
47
|
+
#### `Resource`
|
|
48
|
+
The main resource definition owned by a clinic branch.
|
|
49
|
+
|
|
50
|
+
| Field | Type | Description |
|
|
51
|
+
|-------|------|-------------|
|
|
52
|
+
| `id` | `string` | Unique identifier |
|
|
53
|
+
| `clinicBranchId` | `string` | Owning clinic branch |
|
|
54
|
+
| `name` | `string` | Display name |
|
|
55
|
+
| `nameLower` | `string` | Lowercase for case-insensitive search |
|
|
56
|
+
| `category` | `ResourceCategory` | Type of resource |
|
|
57
|
+
| `description` | `string?` | Optional description |
|
|
58
|
+
| `quantity` | `number` | Number of physical instances |
|
|
59
|
+
| `status` | `ResourceStatus` | Current status |
|
|
60
|
+
| `linkedProcedureIds` | `string[]` | Procedures requiring this resource |
|
|
61
|
+
| `createdAt` | `Timestamp` | Creation timestamp |
|
|
62
|
+
| `updatedAt` | `Timestamp` | Last update timestamp |
|
|
63
|
+
|
|
64
|
+
#### `ResourceInstance`
|
|
65
|
+
A single physical unit of a resource. Each instance has its own calendar.
|
|
66
|
+
|
|
67
|
+
| Field | Type | Description |
|
|
68
|
+
|-------|------|-------------|
|
|
69
|
+
| `id` | `string` | Unique identifier |
|
|
70
|
+
| `resourceId` | `string` | Parent resource ID |
|
|
71
|
+
| `clinicBranchId` | `string` | Owning clinic branch |
|
|
72
|
+
| `label` | `string` | Auto-generated: `"{Resource.name} #{index}"` |
|
|
73
|
+
| `index` | `number` | 1-based index |
|
|
74
|
+
| `status` | `ResourceStatus` | Current status |
|
|
75
|
+
| `createdAt` / `updatedAt` | `Timestamp` | Timestamps |
|
|
76
|
+
|
|
77
|
+
#### `ResourceCalendarEvent`
|
|
78
|
+
A calendar event on a resource instance. Can be a booking event (auto-created during appointment creation) or a blocking event (manually created by admin for maintenance/downtime).
|
|
79
|
+
|
|
80
|
+
| Field | Type | Description |
|
|
81
|
+
|-------|------|-------------|
|
|
82
|
+
| `id` | `string` | Unique identifier |
|
|
83
|
+
| `resourceId` | `string` | Parent resource |
|
|
84
|
+
| `resourceInstanceId` | `string` | Specific instance |
|
|
85
|
+
| `clinicBranchId` | `string` | Clinic branch |
|
|
86
|
+
| `eventType` | `CalendarEventType` | `RESOURCE_BOOKING` for bookings, `BLOCKING` for manual blocks |
|
|
87
|
+
| `appointmentId` | `string?` | Linked appointment (only for booking events) |
|
|
88
|
+
| `procedureId` | `string?` | Procedure requiring this resource (only for booking events) |
|
|
89
|
+
| `practitionerId` | `string?` | Practitioner performing procedure (only for booking events) |
|
|
90
|
+
| `patientId` | `string?` | Patient for the appointment (only for booking events) |
|
|
91
|
+
| `eventTime` | `CalendarEventTime` | Start and end times |
|
|
92
|
+
| `status` | `CalendarEventStatus` | `CONFIRMED` for blocking events, mirrors appointment status for bookings |
|
|
93
|
+
| `eventName` | `string` | Display name |
|
|
94
|
+
| `description` | `string?` | Optional description (primarily for blocking events) |
|
|
95
|
+
| `createdAt` / `updatedAt` | `Timestamp` | Timestamps |
|
|
96
|
+
|
|
97
|
+
#### `ResourceRequirement`
|
|
98
|
+
Lightweight reference stored on procedures.
|
|
99
|
+
|
|
100
|
+
| Field | Type | Description |
|
|
101
|
+
|-------|------|-------------|
|
|
102
|
+
| `resourceId` | `string` | ID of the required resource |
|
|
103
|
+
| `resourceName` | `string` | Snapshot of resource name |
|
|
104
|
+
| `resourceCategory` | `ResourceCategory` | Snapshot of category |
|
|
105
|
+
|
|
106
|
+
#### `ResourceBookingInfo`
|
|
107
|
+
Stored on the appointment after resource allocation.
|
|
108
|
+
|
|
109
|
+
| Field | Type | Description |
|
|
110
|
+
|-------|------|-------------|
|
|
111
|
+
| `resourceId` | `string` | Booked resource |
|
|
112
|
+
| `resourceName` | `string` | Resource name snapshot |
|
|
113
|
+
| `resourceInstanceId` | `string` | Allocated instance |
|
|
114
|
+
| `resourceInstanceLabel` | `string` | Instance label (e.g., "Room #2") |
|
|
115
|
+
| `calendarEventId` | `string` | Calendar event on instance |
|
|
116
|
+
|
|
117
|
+
#### `CreateResourceData` / `UpdateResourceData`
|
|
118
|
+
Input types for resource CRUD operations. See source for field details.
|
|
119
|
+
|
|
120
|
+
#### `CreateResourceBlockingEventParams`
|
|
121
|
+
Parameters for creating a blocking event on a resource instance.
|
|
122
|
+
|
|
123
|
+
| Field | Type | Description |
|
|
124
|
+
|-------|------|-------------|
|
|
125
|
+
| `clinicBranchId` | `string` | Clinic branch |
|
|
126
|
+
| `resourceId` | `string` | Parent resource |
|
|
127
|
+
| `resourceInstanceId` | `string` | Target instance |
|
|
128
|
+
| `eventName` | `string` | Display name (e.g., "Maintenance") |
|
|
129
|
+
| `eventTime` | `CalendarEventTime` | Block start and end times |
|
|
130
|
+
| `description` | `string?` | Optional reason for the block |
|
|
131
|
+
|
|
132
|
+
#### `UpdateResourceBlockingEventParams`
|
|
133
|
+
Parameters for updating an existing blocking event. Only provided fields are updated.
|
|
134
|
+
|
|
135
|
+
| Field | Type | Description |
|
|
136
|
+
|-------|------|-------------|
|
|
137
|
+
| `clinicBranchId` | `string` | Clinic branch |
|
|
138
|
+
| `resourceId` | `string` | Parent resource |
|
|
139
|
+
| `resourceInstanceId` | `string` | Target instance |
|
|
140
|
+
| `eventId` | `string` | ID of the event to update |
|
|
141
|
+
| `eventName` | `string?` | New display name |
|
|
142
|
+
| `eventTime` | `CalendarEventTime?` | New time range |
|
|
143
|
+
| `description` | `string?` | New description |
|
|
144
|
+
|
|
145
|
+
### Related Type Extensions
|
|
146
|
+
|
|
147
|
+
This system also extends existing types:
|
|
148
|
+
|
|
149
|
+
- **`Procedure`** (`types/procedure/index.ts`): Added optional `resourceRequirements?: ResourceRequirement[]`
|
|
150
|
+
- **`Appointment`** (`types/appointment/index.ts`): Added optional `resourceBookings?: ResourceBookingInfo[]`
|
|
151
|
+
- **`CalendarEventType`** (`types/calendar/index.ts`): Added `RESOURCE_BOOKING = "resource_booking"`
|
|
152
|
+
|
|
153
|
+
See [main Resource System README](../../../docs/RESOURCE_SYSTEM.md) for the full system overview.
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { Timestamp, FieldValue } from "firebase/firestore";
|
|
2
|
+
import type { CalendarEventTime, CalendarEventStatus } from "../calendar";
|
|
3
|
+
import { CalendarEventType } from "../calendar";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Firestore collection/subcollection names for the resource system
|
|
7
|
+
*/
|
|
8
|
+
export const RESOURCES_COLLECTION = "resources";
|
|
9
|
+
export const RESOURCE_INSTANCES_SUBCOLLECTION = "instances";
|
|
10
|
+
export const RESOURCE_CALENDAR_SUBCOLLECTION = "calendar";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Category of a clinic resource
|
|
14
|
+
*/
|
|
15
|
+
export enum ResourceCategory {
|
|
16
|
+
MEDICAL_DEVICE = "medical_device",
|
|
17
|
+
ROOM = "room",
|
|
18
|
+
SURGERY_SUITE = "surgery_suite",
|
|
19
|
+
EQUIPMENT = "equipment",
|
|
20
|
+
OTHER = "other",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Status of a resource or resource instance
|
|
25
|
+
*/
|
|
26
|
+
export enum ResourceStatus {
|
|
27
|
+
ACTIVE = "active",
|
|
28
|
+
INACTIVE = "inactive",
|
|
29
|
+
MAINTENANCE = "maintenance",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A resource definition owned by a clinic branch.
|
|
34
|
+
* Stored at: clinics/{clinicId}/resources/{resourceId}
|
|
35
|
+
*/
|
|
36
|
+
export interface Resource {
|
|
37
|
+
/** Unique identifier */
|
|
38
|
+
id: string;
|
|
39
|
+
/** Clinic branch this resource belongs to */
|
|
40
|
+
clinicBranchId: string;
|
|
41
|
+
/** Display name of the resource */
|
|
42
|
+
name: string;
|
|
43
|
+
/** Lowercase name for case-insensitive search */
|
|
44
|
+
nameLower: string;
|
|
45
|
+
/** Category of the resource */
|
|
46
|
+
category: ResourceCategory;
|
|
47
|
+
/** Optional description */
|
|
48
|
+
description?: string;
|
|
49
|
+
/** Number of physical instances (e.g., 3 surgery rooms) */
|
|
50
|
+
quantity: number;
|
|
51
|
+
/** Current status */
|
|
52
|
+
status: ResourceStatus;
|
|
53
|
+
/** IDs of procedures that require this resource (denormalized for quick lookup) */
|
|
54
|
+
linkedProcedureIds: string[];
|
|
55
|
+
createdAt: Timestamp;
|
|
56
|
+
updatedAt: Timestamp;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A single physical instance of a resource (e.g., "Surgery Room #2").
|
|
61
|
+
* Each instance has its own calendar for tracking bookings.
|
|
62
|
+
* Stored at: clinics/{clinicId}/resources/{resourceId}/instances/{instanceId}
|
|
63
|
+
*/
|
|
64
|
+
export interface ResourceInstance {
|
|
65
|
+
/** Unique identifier */
|
|
66
|
+
id: string;
|
|
67
|
+
/** Parent resource ID */
|
|
68
|
+
resourceId: string;
|
|
69
|
+
/** Clinic branch this instance belongs to */
|
|
70
|
+
clinicBranchId: string;
|
|
71
|
+
/** Display label, auto-generated: "{Resource.name} #{index}" */
|
|
72
|
+
label: string;
|
|
73
|
+
/** 1-based index among instances of the same resource */
|
|
74
|
+
index: number;
|
|
75
|
+
/** Current status */
|
|
76
|
+
status: ResourceStatus;
|
|
77
|
+
createdAt: Timestamp;
|
|
78
|
+
updatedAt: Timestamp;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Calendar event on a resource instance.
|
|
83
|
+
* Can be either a booking event (created during appointment creation) or a blocking event (created manually by admin).
|
|
84
|
+
* Stored at: clinics/{clinicId}/resources/{resourceId}/instances/{instanceId}/calendar/{eventId}
|
|
85
|
+
*/
|
|
86
|
+
export interface ResourceCalendarEvent {
|
|
87
|
+
/** Unique identifier */
|
|
88
|
+
id: string;
|
|
89
|
+
/** Parent resource ID */
|
|
90
|
+
resourceId: string;
|
|
91
|
+
/** Instance this event is on */
|
|
92
|
+
resourceInstanceId: string;
|
|
93
|
+
/** Clinic branch */
|
|
94
|
+
clinicBranchId: string;
|
|
95
|
+
/** Type of event — RESOURCE_BOOKING for appointment bookings, BLOCKING for manual blocks */
|
|
96
|
+
eventType: CalendarEventType;
|
|
97
|
+
/** Appointment that booked this resource (only for RESOURCE_BOOKING events) */
|
|
98
|
+
appointmentId?: string;
|
|
99
|
+
/** Procedure requiring this resource (only for RESOURCE_BOOKING events) */
|
|
100
|
+
procedureId?: string;
|
|
101
|
+
/** Practitioner performing the procedure (only for RESOURCE_BOOKING events) */
|
|
102
|
+
practitionerId?: string;
|
|
103
|
+
/** Patient for the appointment (only for RESOURCE_BOOKING events) */
|
|
104
|
+
patientId?: string;
|
|
105
|
+
/** Time range of the event */
|
|
106
|
+
eventTime: CalendarEventTime;
|
|
107
|
+
/** Event status — CONFIRMED for blocking events, mirrors appointment status for bookings */
|
|
108
|
+
status: CalendarEventStatus;
|
|
109
|
+
/** Display name for the event */
|
|
110
|
+
eventName: string;
|
|
111
|
+
/** Optional description (primarily for blocking events, e.g., reason for maintenance) */
|
|
112
|
+
description?: string;
|
|
113
|
+
createdAt: Timestamp;
|
|
114
|
+
updatedAt: Timestamp;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Lightweight reference stored on procedures to indicate which resources are required.
|
|
119
|
+
* Denormalized snapshot of resource info at the time of linking.
|
|
120
|
+
*/
|
|
121
|
+
export interface ResourceRequirement {
|
|
122
|
+
/** ID of the required resource */
|
|
123
|
+
resourceId: string;
|
|
124
|
+
/** Name of the resource (snapshot) */
|
|
125
|
+
resourceName: string;
|
|
126
|
+
/** Category of the resource (snapshot) */
|
|
127
|
+
resourceCategory: ResourceCategory;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Info about a specific resource instance booking, stored on the appointment
|
|
132
|
+
* after a resource has been allocated during appointment creation.
|
|
133
|
+
*/
|
|
134
|
+
export interface ResourceBookingInfo {
|
|
135
|
+
/** ID of the booked resource */
|
|
136
|
+
resourceId: string;
|
|
137
|
+
/** Name of the resource (snapshot) */
|
|
138
|
+
resourceName: string;
|
|
139
|
+
/** ID of the specific instance that was allocated */
|
|
140
|
+
resourceInstanceId: string;
|
|
141
|
+
/** Label of the instance (e.g., "Surgery Room #2") */
|
|
142
|
+
resourceInstanceLabel: string;
|
|
143
|
+
/** ID of the calendar event created on the instance */
|
|
144
|
+
calendarEventId: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Data required to create a new resource
|
|
149
|
+
*/
|
|
150
|
+
export interface CreateResourceData {
|
|
151
|
+
clinicBranchId: string;
|
|
152
|
+
name: string;
|
|
153
|
+
category: ResourceCategory;
|
|
154
|
+
description?: string;
|
|
155
|
+
quantity: number;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Data allowed for updating an existing resource
|
|
160
|
+
*/
|
|
161
|
+
export interface UpdateResourceData {
|
|
162
|
+
name?: string;
|
|
163
|
+
category?: ResourceCategory;
|
|
164
|
+
description?: string;
|
|
165
|
+
/** Changing quantity: increase creates new instances, decrease is blocked if instances have future bookings */
|
|
166
|
+
quantity?: number;
|
|
167
|
+
status?: ResourceStatus;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Parameters for creating a blocking event on a resource instance.
|
|
172
|
+
* Used to mark an instance as unavailable for a time period (maintenance, out of order, etc.).
|
|
173
|
+
*/
|
|
174
|
+
export interface CreateResourceBlockingEventParams {
|
|
175
|
+
clinicBranchId: string;
|
|
176
|
+
resourceId: string;
|
|
177
|
+
resourceInstanceId: string;
|
|
178
|
+
/** Display name for the blocking event (e.g., "Maintenance", "Out of Order") */
|
|
179
|
+
eventName: string;
|
|
180
|
+
/** Time range during which the instance is blocked */
|
|
181
|
+
eventTime: CalendarEventTime;
|
|
182
|
+
/** Optional reason/description for the block */
|
|
183
|
+
description?: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Parameters for updating an existing blocking event on a resource instance.
|
|
188
|
+
* Only provided fields are updated.
|
|
189
|
+
*/
|
|
190
|
+
export interface UpdateResourceBlockingEventParams {
|
|
191
|
+
clinicBranchId: string;
|
|
192
|
+
resourceId: string;
|
|
193
|
+
resourceInstanceId: string;
|
|
194
|
+
/** ID of the blocking event to update */
|
|
195
|
+
eventId: string;
|
|
196
|
+
eventName?: string;
|
|
197
|
+
eventTime?: CalendarEventTime;
|
|
198
|
+
description?: string;
|
|
199
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './planConfig.types';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Plan Configuration — stored in Firestore at `system/planConfig`.
|
|
3
|
+
* Editable from the admin dashboard. Hardcoded defaults in tiers.config.ts
|
|
4
|
+
* serve as fallback when the Firestore document doesn't exist.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface TierDefinition {
|
|
8
|
+
name: string;
|
|
9
|
+
limits: {
|
|
10
|
+
maxProvidersPerBranch: number;
|
|
11
|
+
maxProceduresPerProvider: number;
|
|
12
|
+
maxBranches: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PlanDefinition {
|
|
17
|
+
/** Stripe price ID. null for the free plan. */
|
|
18
|
+
priceId: string | null;
|
|
19
|
+
/** Monthly price in the plan currency (e.g. 199 for CHF 199). */
|
|
20
|
+
priceMonthly: number;
|
|
21
|
+
currency: string;
|
|
22
|
+
description: string;
|
|
23
|
+
/** Stripe product ID — needed when auto-creating new prices. */
|
|
24
|
+
stripeProductId?: string | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SeatAddonDefinition {
|
|
28
|
+
priceId: string;
|
|
29
|
+
pricePerUnit: number;
|
|
30
|
+
currency: string;
|
|
31
|
+
allowedTiers: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface BranchAddonTierPrice {
|
|
35
|
+
priceId: string;
|
|
36
|
+
pricePerUnit: number;
|
|
37
|
+
currency: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface BranchAddonDefinition {
|
|
41
|
+
connect: BranchAddonTierPrice;
|
|
42
|
+
pro: BranchAddonTierPrice;
|
|
43
|
+
allowedTiers: string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ProcedureAddonDefinition {
|
|
47
|
+
priceId: string;
|
|
48
|
+
pricePerBlock: number;
|
|
49
|
+
proceduresPerBlock: number;
|
|
50
|
+
currency: string;
|
|
51
|
+
allowedTiers: string[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface PlanConfigDocument {
|
|
55
|
+
version: number;
|
|
56
|
+
updatedAt: any; // Firestore Timestamp (any to avoid SDK coupling)
|
|
57
|
+
updatedBy: string;
|
|
58
|
+
|
|
59
|
+
/** Tier definitions with usage limits. Keys: 'free', 'connect', 'pro'. */
|
|
60
|
+
tiers: Record<string, TierDefinition>;
|
|
61
|
+
|
|
62
|
+
/** Plan pricing and Stripe config. Keys: 'FREE', 'CONNECT', 'PRO'. */
|
|
63
|
+
plans: Record<string, PlanDefinition>;
|
|
64
|
+
|
|
65
|
+
/** Add-on pricing and Stripe config. */
|
|
66
|
+
addons: {
|
|
67
|
+
seat: SeatAddonDefinition;
|
|
68
|
+
branch: BranchAddonDefinition;
|
|
69
|
+
procedure: ProcedureAddonDefinition;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** Auto-computed: maps Stripe priceId → Firestore subscriptionModel ('connect'/'pro'). */
|
|
73
|
+
priceToModel: Record<string, string>;
|
|
74
|
+
|
|
75
|
+
/** Auto-computed: maps Stripe priceId → plan type label ('CONNECT'/'PRO'). */
|
|
76
|
+
priceToType: Record<string, string>;
|
|
77
|
+
|
|
78
|
+
/** Auto-computed: all add-on price IDs for webhook filtering. */
|
|
79
|
+
addonPriceIds: string[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Firestore path for the plan config document. */
|
|
83
|
+
export const PLAN_CONFIG_PATH = 'system/planConfig';
|
|
84
|
+
|
|
85
|
+
/** Firestore subcollection path for version history. */
|
|
86
|
+
export const PLAN_CONFIG_HISTORY_PATH = 'system/planConfig/history';
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Validation Schemas (`Api/src/validations/`)
|
|
2
|
+
|
|
3
|
+
Zod validation schemas for API input validation. Each schema file corresponds to a domain entity.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
| File | Domain | Description |
|
|
8
|
+
|------|--------|-------------|
|
|
9
|
+
| `appointment.schema.ts` | Appointments | Create/update appointment validation |
|
|
10
|
+
| `calendar.schema.ts` | Calendar | Calendar event validation |
|
|
11
|
+
| `clinic.schema.ts` | Clinics | Clinic CRUD validation |
|
|
12
|
+
| `common.schema.ts` | Shared | Common reusable schemas |
|
|
13
|
+
| `media.schema.ts` | Media | File upload validation |
|
|
14
|
+
| `notification.schema.ts` | Notifications | Notification payload validation |
|
|
15
|
+
| `patient.schema.ts` | Patients | Patient profile validation |
|
|
16
|
+
| `practitioner.schema.ts` | Practitioners | Practitioner profile validation |
|
|
17
|
+
| `procedure.schema.ts` | Procedures | Procedure CRUD validation |
|
|
18
|
+
| `procedure-product.schema.ts` | Products | Procedure product pricing validation |
|
|
19
|
+
| `profile-info.schema.ts` | Profiles | Shared profile info validation |
|
|
20
|
+
| **`resource.schema.ts`** | **Resources** | **Resource CRUD + blocking event validation** |
|
|
21
|
+
| `reviews.schema.ts` | Reviews | Patient review validation |
|
|
22
|
+
| `shared.schema.ts` | Shared | Cross-domain shared schemas |
|
|
23
|
+
|
|
24
|
+
## Resource Validation (`resource.schema.ts`)
|
|
25
|
+
|
|
26
|
+
Part of the [Clinic Resources System](../../../docs/RESOURCE_SYSTEM.md).
|
|
27
|
+
|
|
28
|
+
### `createResourceSchema`
|
|
29
|
+
|
|
30
|
+
Validates input for creating a new resource.
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
{
|
|
34
|
+
clinicBranchId: z.string().min(1), // Required clinic branch ID
|
|
35
|
+
name: z.string().min(1).max(200), // Resource name (1-200 chars)
|
|
36
|
+
category: z.nativeEnum(ResourceCategory), // Must be valid ResourceCategory
|
|
37
|
+
description: z.string().max(1000).optional(), // Optional description
|
|
38
|
+
quantity: z.number().int().min(1).max(100), // 1-100 instances
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### `updateResourceSchema`
|
|
43
|
+
|
|
44
|
+
Validates input for updating an existing resource. All fields are optional.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
{
|
|
48
|
+
name: z.string().min(1).max(200).optional(),
|
|
49
|
+
category: z.nativeEnum(ResourceCategory).optional(),
|
|
50
|
+
description: z.string().max(1000).optional(),
|
|
51
|
+
quantity: z.number().int().min(1).max(100).optional(),
|
|
52
|
+
status: z.nativeEnum(ResourceStatus).optional(),
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Note on quantity updates:** Validation only ensures the value is within range. The business logic for quantity decrease (checking for active bookings on affected instances) is handled by `ResourceService.updateResource()`.
|
|
57
|
+
|
|
58
|
+
### `createResourceBlockingEventSchema`
|
|
59
|
+
|
|
60
|
+
Validates input for creating a blocking event on a resource instance.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
{
|
|
64
|
+
clinicBranchId: z.string().min(1),
|
|
65
|
+
resourceId: z.string().min(1),
|
|
66
|
+
resourceInstanceId: z.string().min(1),
|
|
67
|
+
eventName: z.string().min(1).max(200), // Event name (1-200 chars)
|
|
68
|
+
eventTime: z.object({
|
|
69
|
+
start: z.any(), // Firestore Timestamp
|
|
70
|
+
end: z.any(), // Firestore Timestamp
|
|
71
|
+
}),
|
|
72
|
+
description: z.string().max(1000).optional(), // Optional reason
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `updateResourceBlockingEventSchema`
|
|
77
|
+
|
|
78
|
+
Validates input for updating a blocking event. All fields except IDs are optional.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
{
|
|
82
|
+
clinicBranchId: z.string().min(1),
|
|
83
|
+
resourceId: z.string().min(1),
|
|
84
|
+
resourceInstanceId: z.string().min(1),
|
|
85
|
+
eventId: z.string().min(1),
|
|
86
|
+
eventName: z.string().min(1).max(200).optional(),
|
|
87
|
+
eventTime: z.object({ start: z.any(), end: z.any() }).optional(),
|
|
88
|
+
description: z.string().max(1000).optional(),
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Export
|
|
93
|
+
|
|
94
|
+
All schemas are exported via `index.ts`.
|
package/src/validations/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from "./patient/token.schema";
|
|
|
15
15
|
export * from "./practitioner.schema";
|
|
16
16
|
export * from "./procedure.schema";
|
|
17
17
|
export * from "./profile-info.schema";
|
|
18
|
+
export * from "./resource.schema";
|
|
18
19
|
export * from "./reviews.schema";
|
|
19
20
|
export * from "./schemas";
|
|
20
21
|
export * from "./shared.schema";
|
|
@@ -4,11 +4,21 @@ import {
|
|
|
4
4
|
Currency,
|
|
5
5
|
PricingMeasure,
|
|
6
6
|
} from "../backoffice/types/static/pricing.types";
|
|
7
|
+
import { ResourceCategory } from "../types/resource";
|
|
7
8
|
import { clinicInfoSchema, doctorInfoSchema } from "./shared.schema";
|
|
8
9
|
import { procedureReviewInfoSchema } from "./reviews.schema";
|
|
9
10
|
import { mediaResourceSchema } from "./media.schema";
|
|
10
11
|
import { procedureProductDataSchema } from "./procedure-product.schema";
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Schema for a resource requirement reference on a procedure
|
|
15
|
+
*/
|
|
16
|
+
export const resourceRequirementSchema = z.object({
|
|
17
|
+
resourceId: z.string().min(1),
|
|
18
|
+
resourceName: z.string().min(1),
|
|
19
|
+
resourceCategory: z.nativeEnum(ResourceCategory),
|
|
20
|
+
});
|
|
21
|
+
|
|
12
22
|
/**
|
|
13
23
|
* Schema for validating stored procedure product data (with full product objects).
|
|
14
24
|
* This is used when validating complete procedure documents from Firestore.
|
|
@@ -61,6 +71,7 @@ export const createProcedureSchema = z.object({
|
|
|
61
71
|
practitionerId: z.string().min(1),
|
|
62
72
|
clinicBranchId: z.string().min(1),
|
|
63
73
|
photos: z.array(mediaResourceSchema).optional(),
|
|
74
|
+
resourceRequirements: z.array(resourceRequirementSchema).optional(),
|
|
64
75
|
});
|
|
65
76
|
|
|
66
77
|
/**
|
|
@@ -83,6 +94,7 @@ export const updateProcedureSchema = z.object({
|
|
|
83
94
|
productId: z.string().optional(),
|
|
84
95
|
clinicBranchId: z.string().optional(),
|
|
85
96
|
photos: z.array(mediaResourceSchema).optional(),
|
|
97
|
+
resourceRequirements: z.array(resourceRequirementSchema).optional(),
|
|
86
98
|
});
|
|
87
99
|
|
|
88
100
|
/**
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ResourceCategory, ResourceStatus } from "../types/resource";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Schema for creating a new resource
|
|
6
|
+
*/
|
|
7
|
+
export const createResourceSchema = z.object({
|
|
8
|
+
clinicBranchId: z.string().min(1),
|
|
9
|
+
name: z.string().min(1).max(200),
|
|
10
|
+
category: z.nativeEnum(ResourceCategory),
|
|
11
|
+
description: z.string().max(1000).optional(),
|
|
12
|
+
quantity: z.number().int().min(1).max(100),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Schema for updating an existing resource
|
|
17
|
+
*/
|
|
18
|
+
export const updateResourceSchema = z.object({
|
|
19
|
+
name: z.string().min(1).max(200).optional(),
|
|
20
|
+
category: z.nativeEnum(ResourceCategory).optional(),
|
|
21
|
+
description: z.string().max(1000).optional(),
|
|
22
|
+
quantity: z.number().int().min(1).max(100).optional(),
|
|
23
|
+
status: z.nativeEnum(ResourceStatus).optional(),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Schema for creating a blocking event on a resource instance
|
|
28
|
+
*/
|
|
29
|
+
export const createResourceBlockingEventSchema = z.object({
|
|
30
|
+
clinicBranchId: z.string().min(1),
|
|
31
|
+
resourceId: z.string().min(1),
|
|
32
|
+
resourceInstanceId: z.string().min(1),
|
|
33
|
+
eventName: z.string().min(1).max(200),
|
|
34
|
+
eventTime: z.object({
|
|
35
|
+
start: z.any(),
|
|
36
|
+
end: z.any(),
|
|
37
|
+
}),
|
|
38
|
+
description: z.string().max(1000).optional(),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Schema for updating a blocking event on a resource instance
|
|
43
|
+
*/
|
|
44
|
+
export const updateResourceBlockingEventSchema = z.object({
|
|
45
|
+
clinicBranchId: z.string().min(1),
|
|
46
|
+
resourceId: z.string().min(1),
|
|
47
|
+
resourceInstanceId: z.string().min(1),
|
|
48
|
+
eventId: z.string().min(1),
|
|
49
|
+
eventName: z.string().min(1).max(200).optional(),
|
|
50
|
+
eventTime: z
|
|
51
|
+
.object({
|
|
52
|
+
start: z.any(),
|
|
53
|
+
end: z.any(),
|
|
54
|
+
})
|
|
55
|
+
.optional(),
|
|
56
|
+
description: z.string().max(1000).optional(),
|
|
57
|
+
});
|