@blackcode_sa/metaestetics-api 1.12.32 → 1.12.34
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 +68 -21
- package/dist/admin/index.d.ts +68 -21
- package/dist/admin/index.js +35 -5
- package/dist/admin/index.mjs +35 -5
- package/dist/index.d.mts +169 -22
- package/dist/index.d.ts +169 -22
- package/dist/index.js +2787 -1793
- package/dist/index.mjs +2662 -1668
- package/package.json +1 -1
- package/src/admin/booking/booking.admin.ts +40 -4
- package/src/services/appointment/appointment.service.ts +278 -0
- package/src/services/appointment/utils/extended-procedure.utils.ts +285 -0
- package/src/services/appointment/utils/form-initialization.utils.ts +225 -0
- package/src/services/appointment/utils/zone-management.utils.ts +335 -0
- package/src/services/patient/patient.service.ts +46 -0
- package/src/services/procedure/procedure.service.ts +54 -0
- package/src/types/appointment/index.ts +75 -26
- package/src/validations/appointment.schema.ts +100 -4
|
@@ -76,8 +76,12 @@ export interface ProcedureExtendedInfo {
|
|
|
76
76
|
procedureTechnologyName: string;
|
|
77
77
|
procedureProductBrandId: string;
|
|
78
78
|
procedureProductBrandName: string;
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
procedureProducts: Array<{
|
|
80
|
+
productId: string;
|
|
81
|
+
productName: string;
|
|
82
|
+
brandId: string;
|
|
83
|
+
brandName: string;
|
|
84
|
+
}>;
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
/**
|
|
@@ -132,26 +136,39 @@ export interface ZonePhotoUploadData {
|
|
|
132
136
|
}
|
|
133
137
|
|
|
134
138
|
/**
|
|
135
|
-
* Interface for
|
|
139
|
+
* Interface for zone item data (products or notes per zone)
|
|
136
140
|
*/
|
|
137
|
-
export interface
|
|
138
|
-
|
|
141
|
+
export interface ZoneItemData {
|
|
142
|
+
productId?: string;
|
|
143
|
+
productName?: string;
|
|
144
|
+
productBrandId?: string;
|
|
145
|
+
productBrandName?: string;
|
|
146
|
+
belongingProcedureId: string;
|
|
147
|
+
type: 'item' | 'note';
|
|
148
|
+
price?: number;
|
|
149
|
+
currency?: Currency;
|
|
150
|
+
unitOfMeasurement?: PricingMeasure;
|
|
151
|
+
priceOverrideAmount?: number; // If set, takes precedence over price
|
|
152
|
+
quantity?: number;
|
|
153
|
+
parentZone: string; // Zone key in format "category.zone" (e.g., "face.forehead")
|
|
154
|
+
subzones: string[];
|
|
155
|
+
notes?: string;
|
|
156
|
+
subtotal?: number;
|
|
157
|
+
ionNumber?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @deprecated Use ZoneItemData instead
|
|
162
|
+
*/
|
|
163
|
+
export interface BillingPerZone {
|
|
139
164
|
Product: string;
|
|
140
|
-
/** Product ID */
|
|
141
165
|
ProductId: string | null;
|
|
142
|
-
/** Quantity used (can be decimal) */
|
|
143
166
|
Quantity: number;
|
|
144
|
-
/** Unit of measurement */
|
|
145
167
|
UnitOfMeasurement: PricingMeasure;
|
|
146
|
-
/** Unit price for the product */
|
|
147
168
|
UnitPrice: number;
|
|
148
|
-
/** Currency for the unit price */
|
|
149
169
|
UnitCurency: Currency;
|
|
150
|
-
/** Calculated subtotal */
|
|
151
170
|
Subtotal: number;
|
|
152
|
-
/** Optional billing note */
|
|
153
171
|
Note: string | null;
|
|
154
|
-
/** Ion/Batch number for traceability */
|
|
155
172
|
IonNumber: string | null;
|
|
156
173
|
}
|
|
157
174
|
|
|
@@ -167,29 +184,61 @@ export interface FinalBilling {
|
|
|
167
184
|
taxPrice: number;
|
|
168
185
|
/** Final price including tax */
|
|
169
186
|
finalPrice: number;
|
|
170
|
-
/** Total final quantity across all zones */
|
|
171
|
-
finalQuantity: number; // Not sure we should keep this as well, we keep track of this per item
|
|
172
187
|
/** Currency for the final billing */
|
|
173
188
|
currency: Currency;
|
|
174
|
-
|
|
175
|
-
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Interface for product metadata in appointment
|
|
193
|
+
*/
|
|
194
|
+
export interface AppointmentProductMetadata {
|
|
195
|
+
productId: string;
|
|
196
|
+
productName: string;
|
|
197
|
+
brandId: string;
|
|
198
|
+
brandName: string;
|
|
199
|
+
procedureId: string;
|
|
200
|
+
price: number;
|
|
201
|
+
currency: Currency;
|
|
202
|
+
unitOfMeasurement: PricingMeasure;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Interface for extended procedures in appointment
|
|
207
|
+
*/
|
|
208
|
+
export interface ExtendedProcedureInfo {
|
|
209
|
+
procedureId: string;
|
|
210
|
+
procedureName: string;
|
|
211
|
+
procedureFamily?: ProcedureFamily;
|
|
212
|
+
procedureCategoryId: string;
|
|
213
|
+
procedureCategoryName: string;
|
|
214
|
+
procedureSubCategoryId: string;
|
|
215
|
+
procedureSubCategoryName: string;
|
|
216
|
+
procedureTechnologyId: string;
|
|
217
|
+
procedureTechnologyName: string;
|
|
218
|
+
procedureProducts: Array<{
|
|
219
|
+
productId: string;
|
|
220
|
+
productName: string;
|
|
221
|
+
brandId: string;
|
|
222
|
+
brandName: string;
|
|
223
|
+
}>;
|
|
176
224
|
}
|
|
177
225
|
|
|
178
226
|
/**
|
|
179
227
|
* Interface for appointment metadata containing zone-specific information
|
|
180
228
|
*/
|
|
181
229
|
export interface AppointmentMetadata {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
zoneBilling: Record<string, BillingPerZone> | null; // This is going to change, name it zonesData, and expand this to accept more than one item per key (which is basically zone like category.zone)
|
|
188
|
-
/** Final billing calculations for the appointment */
|
|
230
|
+
selectedZones: string[] | null;
|
|
231
|
+
zonePhotos: Record<string, BeforeAfterPerZone> | null;
|
|
232
|
+
zonesData?: Record<string, ZoneItemData[]> | null;
|
|
233
|
+
appointmentProducts?: AppointmentProductMetadata[];
|
|
234
|
+
extendedProcedures?: ExtendedProcedureInfo[];
|
|
189
235
|
finalbilling: FinalBilling | null;
|
|
190
|
-
/** Final note for the appointment */
|
|
191
236
|
finalizationNotes: string | null;
|
|
192
|
-
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* @deprecated Use zonesData instead
|
|
240
|
+
*/
|
|
241
|
+
zoneBilling?: Record<string, BillingPerZone> | null;
|
|
193
242
|
}
|
|
194
243
|
|
|
195
244
|
/**
|
|
@@ -53,8 +53,14 @@ export const procedureExtendedInfoSchema = z.object({
|
|
|
53
53
|
procedureTechnologyName: z.string(),
|
|
54
54
|
procedureProductBrandId: z.string(),
|
|
55
55
|
procedureProductBrandName: z.string(),
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
procedureProducts: z.array(
|
|
57
|
+
z.object({
|
|
58
|
+
productId: z.string().min(MIN_STRING_LENGTH, 'Product ID is required'),
|
|
59
|
+
productName: z.string().min(MIN_STRING_LENGTH, 'Product name is required'),
|
|
60
|
+
brandId: z.string().min(MIN_STRING_LENGTH, 'Brand ID is required'),
|
|
61
|
+
brandName: z.string().min(MIN_STRING_LENGTH, 'Brand name is required'),
|
|
62
|
+
})
|
|
63
|
+
),
|
|
58
64
|
});
|
|
59
65
|
|
|
60
66
|
export const linkedFormInfoSchema = z.object({
|
|
@@ -164,18 +170,108 @@ export const finalBillingSchema = z.object({
|
|
|
164
170
|
taxRate: z.number().min(0).max(1, 'Tax rate must be between 0 and 1'),
|
|
165
171
|
taxPrice: z.number().min(0, 'Tax price must be non-negative'),
|
|
166
172
|
finalPrice: z.number().min(0, 'Final price must be non-negative'),
|
|
167
|
-
|
|
173
|
+
currency: z.nativeEnum(Currency),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Schema for zone item data (product or note per zone)
|
|
178
|
+
*/
|
|
179
|
+
export const zoneItemDataSchema = z.object({
|
|
180
|
+
productId: z.string().optional(),
|
|
181
|
+
productName: z.string().optional(),
|
|
182
|
+
productBrandId: z.string().optional(),
|
|
183
|
+
productBrandName: z.string().optional(),
|
|
184
|
+
belongingProcedureId: z.string().min(MIN_STRING_LENGTH, 'Belonging procedure ID is required'),
|
|
185
|
+
type: z.enum(['item', 'note'], {
|
|
186
|
+
required_error: 'Type must be either "item" or "note"',
|
|
187
|
+
}),
|
|
188
|
+
price: z.number().min(0, 'Price must be non-negative').optional(),
|
|
189
|
+
currency: z.nativeEnum(Currency).optional(),
|
|
190
|
+
unitOfMeasurement: z.nativeEnum(PricingMeasure).optional(),
|
|
191
|
+
priceOverrideAmount: z.number().min(0, 'Price override amount must be non-negative').optional(),
|
|
192
|
+
quantity: z.number().min(0, 'Quantity must be non-negative').optional(),
|
|
193
|
+
parentZone: z.string().min(MIN_STRING_LENGTH, 'Parent zone is required').refine(
|
|
194
|
+
val => {
|
|
195
|
+
const parts = val.split('.');
|
|
196
|
+
return parts.length === 2;
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
message: 'Parent zone must be in "category.zone" format (e.g., "face.forehead")',
|
|
200
|
+
}
|
|
201
|
+
),
|
|
202
|
+
subzones: z.array(z.string()).refine(
|
|
203
|
+
val => {
|
|
204
|
+
return val.every(subzone => {
|
|
205
|
+
const parts = subzone.split('.');
|
|
206
|
+
return parts.length === 3;
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
message: 'Subzones must be in "category.zone.subzone" format (e.g., "face.forehead.left")',
|
|
211
|
+
}
|
|
212
|
+
),
|
|
213
|
+
notes: z.string().max(MAX_STRING_LENGTH_LONG, 'Notes too long').optional(),
|
|
214
|
+
subtotal: z.number().min(0, 'Subtotal must be non-negative').optional(),
|
|
215
|
+
ionNumber: z.string().optional(),
|
|
216
|
+
}).refine(
|
|
217
|
+
data => {
|
|
218
|
+
if (data.type === 'item') {
|
|
219
|
+
return !!(data.productId && data.productName && (data.price || data.priceOverrideAmount));
|
|
220
|
+
}
|
|
221
|
+
return true;
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
message: 'Item type requires productId, productName, and either price or priceOverrideAmount',
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Schema for appointment product metadata
|
|
230
|
+
*/
|
|
231
|
+
export const appointmentProductMetadataSchema = z.object({
|
|
232
|
+
productId: z.string().min(MIN_STRING_LENGTH, 'Product ID is required'),
|
|
233
|
+
productName: z.string().min(MIN_STRING_LENGTH, 'Product name is required'),
|
|
234
|
+
brandId: z.string().min(MIN_STRING_LENGTH, 'Brand ID is required'),
|
|
235
|
+
brandName: z.string().min(MIN_STRING_LENGTH, 'Brand name is required'),
|
|
236
|
+
procedureId: z.string().min(MIN_STRING_LENGTH, 'Procedure ID is required'),
|
|
237
|
+
price: z.number().min(0, 'Price must be non-negative'),
|
|
168
238
|
currency: z.nativeEnum(Currency),
|
|
169
239
|
unitOfMeasurement: z.nativeEnum(PricingMeasure),
|
|
170
240
|
});
|
|
171
241
|
|
|
242
|
+
/**
|
|
243
|
+
* Schema for extended procedure info
|
|
244
|
+
*/
|
|
245
|
+
export const extendedProcedureInfoSchema = z.object({
|
|
246
|
+
procedureId: z.string().min(MIN_STRING_LENGTH, 'Procedure ID is required'),
|
|
247
|
+
procedureName: z.string().min(MIN_STRING_LENGTH, 'Procedure name is required'),
|
|
248
|
+
procedureFamily: z.any(),
|
|
249
|
+
procedureCategoryId: z.string().min(MIN_STRING_LENGTH, 'Category ID is required'),
|
|
250
|
+
procedureCategoryName: z.string().min(MIN_STRING_LENGTH, 'Category name is required'),
|
|
251
|
+
procedureSubCategoryId: z.string().min(MIN_STRING_LENGTH, 'Subcategory ID is required'),
|
|
252
|
+
procedureSubCategoryName: z.string().min(MIN_STRING_LENGTH, 'Subcategory name is required'),
|
|
253
|
+
procedureTechnologyId: z.string().min(MIN_STRING_LENGTH, 'Technology ID is required'),
|
|
254
|
+
procedureTechnologyName: z.string().min(MIN_STRING_LENGTH, 'Technology name is required'),
|
|
255
|
+
procedureProducts: z.array(
|
|
256
|
+
z.object({
|
|
257
|
+
productId: z.string().min(MIN_STRING_LENGTH, 'Product ID is required'),
|
|
258
|
+
productName: z.string().min(MIN_STRING_LENGTH, 'Product name is required'),
|
|
259
|
+
brandId: z.string().min(MIN_STRING_LENGTH, 'Brand ID is required'),
|
|
260
|
+
brandName: z.string().min(MIN_STRING_LENGTH, 'Brand name is required'),
|
|
261
|
+
})
|
|
262
|
+
),
|
|
263
|
+
});
|
|
264
|
+
|
|
172
265
|
/**
|
|
173
266
|
* Schema for appointment metadata containing zone-specific information
|
|
174
267
|
*/
|
|
175
268
|
export const appointmentMetadataSchema = z.object({
|
|
176
269
|
selectedZones: z.array(z.string()).nullable(),
|
|
177
270
|
zonePhotos: z.record(z.string(), beforeAfterPerZoneSchema).nullable(),
|
|
178
|
-
|
|
271
|
+
zonesData: z.record(z.string(), z.array(zoneItemDataSchema)).nullable(),
|
|
272
|
+
appointmentProducts: z.array(appointmentProductMetadataSchema),
|
|
273
|
+
extendedProcedures: z.array(extendedProcedureInfoSchema),
|
|
274
|
+
zoneBilling: z.record(z.string(), billingPerZoneSchema).nullable().optional(),
|
|
179
275
|
finalbilling: finalBillingSchema.nullable(),
|
|
180
276
|
finalizationNotes: z.string().nullable(),
|
|
181
277
|
});
|