@updraft-solutions/mcrit-sdk 0.11.2 → 0.13.0
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/{chunk-5AUKIEY3.cjs → chunk-OPJ4YGN2.cjs} +153 -12
- package/dist/{chunk-JUAGKQGN.js → chunk-SW6VWDVT.js} +153 -12
- package/dist/{training-D3dneyB8.d.cts → fbo-yr7LUerk.d.cts} +4284 -9699
- package/dist/{training-D3dneyB8.d.ts → fbo-yr7LUerk.d.ts} +4284 -9699
- package/dist/index.cjs +39 -7
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +48 -16
- package/dist/operating-hours-CRZMz4E5.d.cts +574 -0
- package/dist/operating-hours-DqN2V6Jq.d.ts +574 -0
- package/dist/schemas/index.cjs +34 -2
- package/dist/schemas/index.d.cts +15 -15
- package/dist/schemas/index.d.ts +15 -15
- package/dist/schemas/index.js +33 -1
- package/dist/types/index.cjs +1 -1
- package/dist/types/index.d.cts +18 -573
- package/dist/types/index.d.ts +18 -573
- package/dist/types/index.js +1 -1
- package/package.json +1 -1
- package/src/schemas/common.ts +23 -0
- package/src/schemas/fbo.spec.ts +49 -0
- package/src/schemas/fbo.ts +126 -0
- package/src/schemas/index.ts +1 -0
- package/src/schemas/models.ts +43 -10
- package/src/types/fbo.ts +16 -0
- package/src/types/index.ts +1 -0
- /package/dist/{chunk-UV5JLKAL.js → chunk-ASPK4M5R.js} +0 -0
- /package/dist/{chunk-YVC6Q3YU.cjs → chunk-CJHCF4FW.cjs} +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { fboDutySchema, fboFlightSchema } from "./fbo";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Fixtures are copied from the API's FlightResource / DutyResource shapes, not
|
|
6
|
+
* invented, so a schema that drifts from the payload fails here rather than at
|
|
7
|
+
* runtime in the app.
|
|
8
|
+
*/
|
|
9
|
+
const flightPayload = {
|
|
10
|
+
id: 44796, type: "ppr", uuid: "0f1b3d5e-0000-4000-8000-000000000001", flight_status: "pending",
|
|
11
|
+
company_id: 34, author_id: null,
|
|
12
|
+
ac_call: "DEABC", ac_reg: null, ac_type: "C172", operator: "PrivateWings",
|
|
13
|
+
etdZ: "2026-09-01T20:00:00.000000Z", etdZ_no_time: false, etaZ: null, etaZ_no_time: false,
|
|
14
|
+
departure_airport_id: 2753, destination_airport_id: null,
|
|
15
|
+
ifr: false, wx: false, landings: 1, remarks: null,
|
|
16
|
+
contact: { name: "Erika Muster", email: "erika@example.test", phone: null, company: null, fax: null },
|
|
17
|
+
ground_services: { stairs: false, hangar: false, car_access: false, lifter: false, rental_car: false, refueling: false, gpu: false },
|
|
18
|
+
acceptance_status: {
|
|
19
|
+
fbo: { status: "pending", is_required: true, decided_by_id: null, updated_at: "2026-08-26T10:00:00.000000Z" },
|
|
20
|
+
airport: { status: "unknown", is_required: false, decided_by_id: null, updated_at: null },
|
|
21
|
+
customs: { status: "unknown", is_required: false, decided_by_id: null, updated_at: null },
|
|
22
|
+
},
|
|
23
|
+
created_at: "2026-08-26T10:00:00.000000Z", updated_at: "2026-08-26T10:00:00.000000Z",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe("fboFlightSchema", () => {
|
|
27
|
+
it("parses a PPR exactly as FlightResource emits it", () => {
|
|
28
|
+
expect(fboFlightSchema.parse(flightPayload).type).toBe("ppr");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("rejects a status outside AcceptanceStatusData's vocabulary", () => {
|
|
32
|
+
const bad = { ...flightPayload, acceptance_status: { ...flightPayload.acceptance_status, fbo: { ...flightPayload.acceptance_status.fbo, status: "idle" } } };
|
|
33
|
+
expect(() => fboFlightSchema.parse(bad)).toThrow();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("rejects the flight-school type — the FBO domain never holds a 'booking'", () => {
|
|
37
|
+
expect(() => fboFlightSchema.parse({ ...flightPayload, type: "booking" })).toThrow();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("fboDutySchema", () => {
|
|
42
|
+
it("accepts a userless, occupation-less duty — 27% of the migrated roster has no user", () => {
|
|
43
|
+
expect(fboDutySchema.parse({
|
|
44
|
+
id: 1, company_id: 35, user_id: null, author_id: null, occupation_id: null, scheduled_flight_id: null,
|
|
45
|
+
start: "2026-09-01T18:00:00.000000Z", end: "2026-09-01T23:00:00.000000Z", all_day: false, remarks: null,
|
|
46
|
+
created_at: null, updated_at: null,
|
|
47
|
+
}).user_id).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { zContactDTO } from "./common";
|
|
3
|
+
import { airportSchema } from "./models";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* FBO domain — flights (scheduled movements and PPRs), duties, occupations and
|
|
7
|
+
* the per-company FBO profile. Shapes mirror the API's FBO resources exactly:
|
|
8
|
+
* `FlightResource`, `DutyResource`, `OccupationResource`, and the `fbo` block on
|
|
9
|
+
* `CompanyResource`.
|
|
10
|
+
*
|
|
11
|
+
* Distinct from `scheduledFlightSchema` in models.ts, which predates the FBO
|
|
12
|
+
* domain and does not describe these payloads (it has `booking_id`,
|
|
13
|
+
* `departure_time`, `flight_number` — none of which the API emits here).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** One acceptance axis. `status` mirrors `AcceptanceStatusData`'s `#[In]` rule. */
|
|
17
|
+
export const fboAcceptanceAxisSchema = z.object({
|
|
18
|
+
status: z.enum(["pending", "accepted", "rejected", "unknown"]),
|
|
19
|
+
is_required: z.boolean(),
|
|
20
|
+
decided_by_id: z.number().nullable(),
|
|
21
|
+
updated_at: z.string().nullable(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const fboAcceptanceStatusSchema = z.object({
|
|
25
|
+
fbo: fboAcceptanceAxisSchema,
|
|
26
|
+
airport: fboAcceptanceAxisSchema,
|
|
27
|
+
customs: fboAcceptanceAxisSchema,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const fboGroundServicesSchema = z.object({
|
|
31
|
+
stairs: z.boolean(),
|
|
32
|
+
hangar: z.boolean(),
|
|
33
|
+
car_access: z.boolean(),
|
|
34
|
+
lifter: z.boolean(),
|
|
35
|
+
rental_car: z.boolean(),
|
|
36
|
+
refueling: z.boolean(),
|
|
37
|
+
gpu: z.boolean(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export const fboFlightTypeSchema = z.enum(["scheduledFlight", "ppr"]);
|
|
41
|
+
export const fboFlightStatusSchema = z.enum(["active", "pending", "cancelled"]);
|
|
42
|
+
|
|
43
|
+
export const fboFlightSchema = z.object({
|
|
44
|
+
id: z.number(),
|
|
45
|
+
type: fboFlightTypeSchema,
|
|
46
|
+
uuid: z.string(),
|
|
47
|
+
flight_status: fboFlightStatusSchema,
|
|
48
|
+
company_id: z.number(),
|
|
49
|
+
author_id: z.number().nullable(),
|
|
50
|
+
|
|
51
|
+
ac_call: z.string().nullable(),
|
|
52
|
+
ac_reg: z.string().nullable(),
|
|
53
|
+
ac_type: z.string().nullable(),
|
|
54
|
+
operator: z.string().nullable(),
|
|
55
|
+
|
|
56
|
+
etdZ: z.string().nullable(),
|
|
57
|
+
/** The filer gave a date but no time; `etdZ` then holds a placeholder midnight. */
|
|
58
|
+
etdZ_no_time: z.boolean(),
|
|
59
|
+
etaZ: z.string().nullable(),
|
|
60
|
+
etaZ_no_time: z.boolean(),
|
|
61
|
+
|
|
62
|
+
departure_airport_id: z.number().nullable(),
|
|
63
|
+
departure_airport: airportSchema.optional(),
|
|
64
|
+
destination_airport_id: z.number().nullable(),
|
|
65
|
+
destination_airport: airportSchema.optional(),
|
|
66
|
+
|
|
67
|
+
ifr: z.boolean(),
|
|
68
|
+
wx: z.boolean(),
|
|
69
|
+
landings: z.number().nullable(),
|
|
70
|
+
remarks: z.string().nullable(),
|
|
71
|
+
|
|
72
|
+
contact: zContactDTO,
|
|
73
|
+
ground_services: fboGroundServicesSchema,
|
|
74
|
+
acceptance_status: fboAcceptanceStatusSchema,
|
|
75
|
+
|
|
76
|
+
created_at: z.string(),
|
|
77
|
+
updated_at: z.string(),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
export const fboOccupationSchema = z.object({
|
|
81
|
+
id: z.number(),
|
|
82
|
+
company_id: z.number(),
|
|
83
|
+
author_id: z.number().nullable(),
|
|
84
|
+
title: z.string(),
|
|
85
|
+
description: z.string().nullable(),
|
|
86
|
+
is_absence: z.boolean(),
|
|
87
|
+
created_at: z.string().nullable(),
|
|
88
|
+
updated_at: z.string().nullable(),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export const fboDutySchema = z.object({
|
|
92
|
+
id: z.number(),
|
|
93
|
+
company_id: z.number().nullable(),
|
|
94
|
+
user_id: z.number().nullable(),
|
|
95
|
+
author_id: z.number().nullable(),
|
|
96
|
+
occupation_id: z.number().nullable(),
|
|
97
|
+
occupation: fboOccupationSchema.optional(),
|
|
98
|
+
scheduled_flight_id: z.number().nullable(),
|
|
99
|
+
start: z.string(),
|
|
100
|
+
end: z.string(),
|
|
101
|
+
all_day: z.boolean(),
|
|
102
|
+
remarks: z.string().nullable(),
|
|
103
|
+
created_at: z.string().nullable(),
|
|
104
|
+
updated_at: z.string().nullable(),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/** Served by `GET companies/{id}/fbo/meta` so no client hardcodes the list. */
|
|
108
|
+
export const fboRoleSchema = z.enum(["fbo", "atc", "wx", "customs", "handling", "fuel", "operator"]);
|
|
109
|
+
|
|
110
|
+
/** The `fbo` block on the company payload — present iff the company holds a profile. */
|
|
111
|
+
export const fboCompanyProfileSchema = z.object({
|
|
112
|
+
role: fboRoleSchema,
|
|
113
|
+
flights_require_approval: z.boolean(),
|
|
114
|
+
notify_on_movement: z.boolean(),
|
|
115
|
+
has_duties: z.boolean(),
|
|
116
|
+
has_airplanes: z.boolean(),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
export const fboDecisionAxisSchema = z.enum(["fbo", "airport", "customs"]);
|
|
120
|
+
export const fboDecisionSchema = z.enum(["accepted", "rejected"]);
|
|
121
|
+
|
|
122
|
+
/** `POST companies/{id}/fbo/flights/{flight}/decision` */
|
|
123
|
+
export const fboDecisionInputSchema = z.object({
|
|
124
|
+
axis: fboDecisionAxisSchema,
|
|
125
|
+
status: fboDecisionSchema,
|
|
126
|
+
});
|
package/src/schemas/index.ts
CHANGED
package/src/schemas/models.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import {
|
|
3
3
|
BaseModel,
|
|
4
|
+
companyIdentitySchema,
|
|
4
5
|
companySchema,
|
|
5
6
|
contactAttributeSchema,
|
|
6
7
|
mediaCollection,
|
|
@@ -14,14 +15,32 @@ import {
|
|
|
14
15
|
import { equipmentSchema } from "./equipment";
|
|
15
16
|
import { courseCheckSyllabusSchema } from "./training";
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Identity-only membership — the shape nested in list rows (server:
|
|
20
|
+
* `MembershipIdentityResource`). It answers "who is this" and nothing else:
|
|
21
|
+
* the full nesting carries the company payload, i.e. the requesting user's
|
|
22
|
+
* whole permission list, repeated on every row.
|
|
23
|
+
*/
|
|
24
|
+
export const membershipIdentitySchema = z.object({
|
|
18
25
|
type: z.literal("membership"),
|
|
19
26
|
id: z.number(),
|
|
20
27
|
user_id: z.number(),
|
|
21
28
|
user: userSchema.optional(),
|
|
22
29
|
company_id: z.number(),
|
|
23
|
-
company: companySchema.nullable().optional(),
|
|
24
30
|
name: z.string(),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The membership as nested in another model's payload: identity, plus the
|
|
35
|
+
* identity-only company for the surfaces that list rows across companies
|
|
36
|
+
* (pre-paid packages, course enrollments).
|
|
37
|
+
*/
|
|
38
|
+
export const nestedMembershipSchema = membershipIdentitySchema.extend({
|
|
39
|
+
company: companyIdentitySchema.nullable().optional(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const baseMembershipSchema = membershipIdentitySchema.extend({
|
|
43
|
+
company: companyIdentitySchema.nullable().optional(),
|
|
25
44
|
phone: z.string().nullable().optional(),
|
|
26
45
|
status: z.string(),
|
|
27
46
|
instructor: z.boolean(),
|
|
@@ -92,7 +111,7 @@ export const feeSchema = BaseModel.extend({
|
|
|
92
111
|
prices: z.array(priceSchema).optional(),
|
|
93
112
|
provider: z.string().nullable().optional(),
|
|
94
113
|
company_id: z.number().optional(),
|
|
95
|
-
company:
|
|
114
|
+
company: companyIdentitySchema.optional(),
|
|
96
115
|
ident: z.string().optional(),
|
|
97
116
|
category: z.string().optional(),
|
|
98
117
|
subcategory: z.string().nullable().optional(),
|
|
@@ -273,7 +292,7 @@ export const airplanePermissionSchema = z.object({
|
|
|
273
292
|
airplane_id: z.number(),
|
|
274
293
|
airplane: airplaneSchema.optional(),
|
|
275
294
|
membership_id: z.number(),
|
|
276
|
-
membership:
|
|
295
|
+
membership: nestedMembershipSchema.optional(),
|
|
277
296
|
notes: z.string().nullable().optional(),
|
|
278
297
|
conditions: z.string().nullable().optional(),
|
|
279
298
|
expires_at: z.string().nullable().optional(),
|
|
@@ -404,7 +423,7 @@ export const extendedAirplaneSchema = airplaneSchema.extend({
|
|
|
404
423
|
})
|
|
405
424
|
.optional(),
|
|
406
425
|
foreflight_profile_url: z.string().optional(),
|
|
407
|
-
company:
|
|
426
|
+
company: companyIdentitySchema.optional(),
|
|
408
427
|
latestPosition: aircraftPositionSchema.nullable().optional(),
|
|
409
428
|
total_flight_time_minutes: z.number().nullable().optional(),
|
|
410
429
|
elapsed_time: z.number().nullable().optional(),
|
|
@@ -476,6 +495,9 @@ export const bookingSchema = BaseModel.extend({
|
|
|
476
495
|
author: userSchema.optional(),
|
|
477
496
|
company_id: z.number(),
|
|
478
497
|
company_name: z.string().nullable().optional(),
|
|
498
|
+
// Single-booking payloads only (BookingDetailResource): the dialog gates
|
|
499
|
+
// on this company, which a shared aircraft can make a partner company.
|
|
500
|
+
// List rows never carry it.
|
|
479
501
|
company: companySchema.optional(),
|
|
480
502
|
airplane: airplaneSchema.optional(),
|
|
481
503
|
etdZ: z.string(),
|
|
@@ -494,6 +516,9 @@ export const bookingSchema = BaseModel.extend({
|
|
|
494
516
|
.enum(["draft", "awaiting_signatures", "signed"])
|
|
495
517
|
.nullable()
|
|
496
518
|
.optional(),
|
|
519
|
+
// Single-booking payloads only (BookingDetailResource): neighbour lookups
|
|
520
|
+
// cost two queries per booking and only the dialog's prev/next navigation
|
|
521
|
+
// uses them.
|
|
497
522
|
next_booking_id: z.number().nullable().optional(),
|
|
498
523
|
previous_booking_id: z.number().nullable().optional(),
|
|
499
524
|
maintenance: z.number().optional(),
|
|
@@ -793,7 +818,7 @@ export const flightLogSchema = z.object({
|
|
|
793
818
|
ac_call: z.string().nullable().optional(),
|
|
794
819
|
booking_id: z.number().nullable().optional(),
|
|
795
820
|
membership_id: z.number().nullable().optional(),
|
|
796
|
-
membership:
|
|
821
|
+
membership: nestedMembershipSchema.optional(),
|
|
797
822
|
billing_profile_id: z.number().nullable().optional(),
|
|
798
823
|
author_id: z.number(),
|
|
799
824
|
author: userSchema.optional(),
|
|
@@ -843,7 +868,10 @@ export const invoiceSchema = z.object({
|
|
|
843
868
|
id: z.number(),
|
|
844
869
|
title: z.string(),
|
|
845
870
|
membership_id: z.number(),
|
|
846
|
-
|
|
871
|
+
// List rows (InvoiceListResource) carry the identity-only membership; the
|
|
872
|
+
// single-invoice payload carries the full one. Typed as the identity shape —
|
|
873
|
+
// the guaranteed part — with the rest passed through.
|
|
874
|
+
membership: nestedMembershipSchema.optional(),
|
|
847
875
|
// InvoiceResource always emits `billing_profile_id` (non-optional key,
|
|
848
876
|
// nullable FK value). `billingProfile` is a `whenLoaded('billingProfile')`
|
|
849
877
|
// relation, so it stays optional. (Fix round 1: as of the original Task 16
|
|
@@ -854,7 +882,12 @@ export const invoiceSchema = z.object({
|
|
|
854
882
|
billingProfile: billingProfileSchema.optional(),
|
|
855
883
|
company_id: z.number().optional(),
|
|
856
884
|
company: companySchema.optional(),
|
|
885
|
+
// Numbering: invoices are numbered in `sequence_number`, receipts in
|
|
886
|
+
// `receipt_number`. `document_number` is whichever applies — render that.
|
|
887
|
+
document_type: z.string().nullable().optional(),
|
|
857
888
|
sequence_number: z.string().nullable().optional(),
|
|
889
|
+
receipt_number: z.string().nullable().optional(),
|
|
890
|
+
document_number: z.string().nullable().optional(),
|
|
858
891
|
order_number: z.string().nullable().optional(),
|
|
859
892
|
invoice_date: z.string().nullable().optional(),
|
|
860
893
|
due_date: z.string().nullable().optional(),
|
|
@@ -1054,7 +1087,7 @@ export const courseEnrollmentSchema = z.object({
|
|
|
1054
1087
|
created_at: z.string(),
|
|
1055
1088
|
updated_at: z.string(),
|
|
1056
1089
|
course: courseSchema.optional(),
|
|
1057
|
-
membership:
|
|
1090
|
+
membership: nestedMembershipSchema.optional(),
|
|
1058
1091
|
mentor: userSchema.optional(),
|
|
1059
1092
|
// Present when eager-loaded (BaseCourseEnrollmentResource). Lazy: both
|
|
1060
1093
|
// schemas are declared later in this file; there is no true cycle.
|
|
@@ -1098,7 +1131,7 @@ export const eventSchema = z.object({
|
|
|
1098
1131
|
type: z.literal("event"),
|
|
1099
1132
|
id: z.number(),
|
|
1100
1133
|
company_id: z.number(),
|
|
1101
|
-
company:
|
|
1134
|
+
company: companyIdentitySchema.optional(),
|
|
1102
1135
|
title: z.string(),
|
|
1103
1136
|
description: z.string().nullable().optional(),
|
|
1104
1137
|
media: mediaCollection,
|
|
@@ -1427,7 +1460,7 @@ export const expenseItemSchema = z.object({
|
|
|
1427
1460
|
// maximum length"). No true cycle: invoiceSchema does not reference
|
|
1428
1461
|
// expenseItemSchema back.
|
|
1429
1462
|
invoice: z.lazy((): z.ZodTypeAny => invoiceSchema).optional(),
|
|
1430
|
-
membership:
|
|
1463
|
+
membership: nestedMembershipSchema.optional(),
|
|
1431
1464
|
prePaidPackageAssignments: z
|
|
1432
1465
|
.array(prePaidPackageAssignmentSchema)
|
|
1433
1466
|
.optional(),
|
package/src/types/fbo.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type * as Fbo from "../schemas/fbo";
|
|
3
|
+
|
|
4
|
+
export type FboAcceptanceAxis = z.infer<typeof Fbo.fboAcceptanceAxisSchema>;
|
|
5
|
+
export type FboAcceptanceStatus = z.infer<typeof Fbo.fboAcceptanceStatusSchema>;
|
|
6
|
+
export type FboGroundServices = z.infer<typeof Fbo.fboGroundServicesSchema>;
|
|
7
|
+
export type FboFlightType = z.infer<typeof Fbo.fboFlightTypeSchema>;
|
|
8
|
+
export type FboFlightStatus = z.infer<typeof Fbo.fboFlightStatusSchema>;
|
|
9
|
+
export type FboFlight = z.infer<typeof Fbo.fboFlightSchema>;
|
|
10
|
+
export type FboOccupation = z.infer<typeof Fbo.fboOccupationSchema>;
|
|
11
|
+
export type FboDuty = z.infer<typeof Fbo.fboDutySchema>;
|
|
12
|
+
export type FboRole = z.infer<typeof Fbo.fboRoleSchema>;
|
|
13
|
+
export type FboCompanyProfile = z.infer<typeof Fbo.fboCompanyProfileSchema>;
|
|
14
|
+
export type FboDecisionAxis = z.infer<typeof Fbo.fboDecisionAxisSchema>;
|
|
15
|
+
export type FboDecision = z.infer<typeof Fbo.fboDecisionSchema>;
|
|
16
|
+
export type FboDecisionInput = z.infer<typeof Fbo.fboDecisionInputSchema>;
|
package/src/types/index.ts
CHANGED
|
File without changes
|
|
File without changes
|