@ingenx-io/valets-schema-mcp-server 0.1.0 → 0.1.2
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/README.md +65 -0
- package/data/docs/collections/firestore-paths.md +48 -0
- package/data/docs/decisions/migrations.md +56 -0
- package/data/docs/decisions/summary.md +78 -0
- package/data/docs/enums/booking-status.md +26 -0
- package/data/docs/enums/customer-payment-status.md +26 -0
- package/data/docs/enums/customer-payment-target-type.md +23 -0
- package/data/docs/enums/delivery-type.md +23 -0
- package/data/docs/enums/event-status.md +30 -0
- package/data/docs/enums/fulfillment-status.md +32 -0
- package/data/docs/enums/loyalty-transaction-type.md +32 -0
- package/data/docs/enums/order-status.md +65 -0
- package/data/docs/enums/payment-method.md +36 -0
- package/data/docs/enums/payment-proof-status.md +23 -0
- package/data/docs/enums/payment-status.md +34 -0
- package/data/docs/enums/return-status.md +32 -0
- package/data/docs/enums/session-status.md +32 -0
- package/data/docs/enums/ticket-status.md +29 -0
- package/data/docs/index.md +95 -0
- package/data/docs/models/booking-version.md +295 -0
- package/data/docs/models/booking.md +1754 -0
- package/data/docs/models/customer-payment-allocation.md +336 -0
- package/data/docs/models/customer-payment.md +392 -0
- package/data/docs/models/customer.md +475 -0
- package/data/docs/models/event.md +386 -0
- package/data/docs/models/loyalty-config.md +317 -0
- package/data/docs/models/loyalty-reward.md +236 -0
- package/data/docs/models/loyalty-status.md +328 -0
- package/data/docs/models/loyalty-transaction.md +326 -0
- package/data/docs/models/metrics-current.md +532 -0
- package/data/docs/models/metrics-daily.md +548 -0
- package/data/docs/models/metrics-monthly.md +548 -0
- package/data/docs/models/order-item.md +361 -0
- package/data/docs/models/order.md +1637 -0
- package/data/docs/models/sale.md +540 -0
- package/data/docs/models/ticket.md +405 -0
- package/data/docs/triggers/event-ticket-triggers.md +204 -0
- package/data/docs/triggers/loyalty-automation.md +123 -0
- package/data/static/decisions.json +966 -0
- package/data/static/llms.txt +1046 -0
- package/data/static/openapi.yaml +3090 -0
- package/data/static/schemas.json +4012 -0
- package/package.json +1 -1
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Ticket"
|
|
3
|
+
sidebar_label: "Ticket"
|
|
4
|
+
sidebar_position: 17
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Ticket
|
|
8
|
+
|
|
9
|
+
<details>
|
|
10
|
+
<summary>Example JSON</summary>
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"id": "bk_abc123def456",
|
|
15
|
+
"eventId": "eve_ref123",
|
|
16
|
+
"companyId": "comp_xyz789",
|
|
17
|
+
"customerId": null,
|
|
18
|
+
"customerName": null,
|
|
19
|
+
"customerEmail": null,
|
|
20
|
+
"customerPhone": null,
|
|
21
|
+
"status": "status",
|
|
22
|
+
"usedAt": "usedAt",
|
|
23
|
+
"usedBy": null,
|
|
24
|
+
"usedByName": null,
|
|
25
|
+
"price": null,
|
|
26
|
+
"notes": null,
|
|
27
|
+
"createdAt": "createdAt",
|
|
28
|
+
"updatedAt": "updatedAt",
|
|
29
|
+
"createdBy": null
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
</details>
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
- [1. Property `id`](#id)
|
|
37
|
+
- [2. Property `eventId`](#eventId)
|
|
38
|
+
- [3. Property `companyId`](#companyId)
|
|
39
|
+
- [4. Property `customerId`](#customerId)
|
|
40
|
+
- [5. Property `customerName`](#customerName)
|
|
41
|
+
- [6. Property `customerEmail`](#customerEmail)
|
|
42
|
+
- [7. Property `customerPhone`](#customerPhone)
|
|
43
|
+
- [8. Property `status`](#status)
|
|
44
|
+
- [9. Property `usedAt`](#usedAt)
|
|
45
|
+
- [9.1. Property `firestore-timestamp`](#usedAt_anyOf_i0)
|
|
46
|
+
- [9.1.1. Property `_seconds`](#usedAt_anyOf_i0__seconds)
|
|
47
|
+
- [9.1.2. Property `_nanoseconds`](#usedAt_anyOf_i0__nanoseconds)
|
|
48
|
+
- [9.2. Property `item 1`](#usedAt_anyOf_i1)
|
|
49
|
+
- [10. Property `usedBy`](#usedBy)
|
|
50
|
+
- [11. Property `usedByName`](#usedByName)
|
|
51
|
+
- [12. Property `price`](#price)
|
|
52
|
+
- [13. Property `notes`](#notes)
|
|
53
|
+
- [14. Property `createdAt`](#createdAt)
|
|
54
|
+
- [14.1. Property `_seconds`](#usedAt_anyOf_i0__seconds)
|
|
55
|
+
- [14.2. Property `_nanoseconds`](#usedAt_anyOf_i0__nanoseconds)
|
|
56
|
+
- [15. Property `updatedAt`](#updatedAt)
|
|
57
|
+
- [15.1. Property `_seconds`](#usedAt_anyOf_i0__seconds)
|
|
58
|
+
- [15.2. Property `_nanoseconds`](#usedAt_anyOf_i0__nanoseconds)
|
|
59
|
+
- [16. Property `createdBy`](#createdBy)
|
|
60
|
+
|
|
61
|
+
| | |
|
|
62
|
+
| ------------------------- | -------------------- |
|
|
63
|
+
| **Type** | `object` |
|
|
64
|
+
| **Required** | No |
|
|
65
|
+
| **Additional properties** | Not allowed |
|
|
66
|
+
| **Defined in** | #/definitions/ticket |
|
|
67
|
+
|
|
68
|
+
**Description:** Ticket model (D32). Collection: companies/\{companyId\}/events/\{eventId\}/tickets/\{ticketId\}. Mobile-only today; Dashboard in Wave 4.
|
|
69
|
+
|
|
70
|
+
| Property | Pattern | Type | Deprecated | Definition | Title/Description |
|
|
71
|
+
| ---------------------------------- | ------- | ---------------- | ---------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| + [id](#id ) | No | string | No | - | (Read-only) Firestore document ID. Note: Ticket does not have a uid field. |
|
|
73
|
+
| + [eventId](#eventId ) | No | string | No | - | (Immutable) FK → Event.id. Parent event this ticket belongs to. Set at creation. |
|
|
74
|
+
| + [companyId](#companyId ) | No | string | No | - | (Immutable, Denormalized) FK → Company document ID. Denormalized from parent Event for direct queries. |
|
|
75
|
+
| - [customerId](#customerId ) | No | string or null | No | - | FK → Customer.id. Optional structured link to Customer document (D29). Added additively alongside denormalized fields. |
|
|
76
|
+
| - [customerName](#customerName ) | No | string or null | No | - | (Denormalized) From Customer.name at write time. |
|
|
77
|
+
| - [customerEmail](#customerEmail ) | No | string or null | No | - | (Denormalized) From Customer.email at write time. |
|
|
78
|
+
| - [customerPhone](#customerPhone ) | No | string or null | No | - | (Denormalized) From Customer.phone at write time. |
|
|
79
|
+
| + [status](#status ) | No | enum (of string) | No | In #/definitions/ticket-status | Ticket lifecycle (D32). SCREAMING_SNAKE per D04. MIG-10 migrates legacy lowercase values. |
|
|
80
|
+
| - [usedAt](#usedAt ) | No | Combination | No | - | (Read-only) Timestamp when ticket was scanned/used. Set by scan operation. |
|
|
81
|
+
| - [usedBy](#usedBy ) | No | string or null | No | - | (Read-only) FK → User/staff UID who scanned the ticket. |
|
|
82
|
+
| - [usedByName](#usedByName ) | No | string or null | No | - | (Read-only, Denormalized) From User display name at scan time. |
|
|
83
|
+
| - [price](#price ) | No | number or null | No | - | - |
|
|
84
|
+
| - [notes](#notes ) | No | string or null | No | - | - |
|
|
85
|
+
| + [createdAt](#createdAt ) | No | object | No | In #/definitions/firestore-timestamp | (Read-only) Server-generated creation timestamp. |
|
|
86
|
+
| + [updatedAt](#updatedAt ) | No | object | No | In #/definitions/firestore-timestamp | (Read-only) Server-generated update timestamp. |
|
|
87
|
+
| - [createdBy](#createdBy ) | No | string or null | No | - | (Immutable) FK → User/staff UID who created this ticket. |
|
|
88
|
+
|
|
89
|
+
## <a name="id"></a>1. Property `id`
|
|
90
|
+
|
|
91
|
+
| | |
|
|
92
|
+
| ------------ | -------- |
|
|
93
|
+
| **Type** | `string` |
|
|
94
|
+
| **Required** | Yes |
|
|
95
|
+
|
|
96
|
+
**Description:** (Read-only) Firestore document ID. Note: Ticket does not have a uid field.
|
|
97
|
+
|
|
98
|
+
:::warning Server-set
|
|
99
|
+
Do not include in write requests. This field is set exclusively by the server (Firestore trigger or Admin SDK). Clients that send it will have the value silently ignored or may receive a validation error.
|
|
100
|
+
:::
|
|
101
|
+
|
|
102
|
+
## <a name="eventId"></a>2. Property `eventId`
|
|
103
|
+
|
|
104
|
+
| | |
|
|
105
|
+
| ------------ | -------- |
|
|
106
|
+
| **Type** | `string` |
|
|
107
|
+
| **Required** | Yes |
|
|
108
|
+
|
|
109
|
+
**Description:** (Immutable) FK → Event.id. Parent event this ticket belongs to. Set at creation.
|
|
110
|
+
|
|
111
|
+
:::info Immutable
|
|
112
|
+
Set at creation only. This field cannot be modified after the document is created. Include it in CREATE payloads; omit it (or leave unchanged) in UPDATE payloads.
|
|
113
|
+
:::
|
|
114
|
+
|
|
115
|
+
## <a name="companyId"></a>3. Property `companyId`
|
|
116
|
+
|
|
117
|
+
| | |
|
|
118
|
+
| ------------ | -------- |
|
|
119
|
+
| **Type** | `string` |
|
|
120
|
+
| **Required** | Yes |
|
|
121
|
+
|
|
122
|
+
**Description:** (Immutable, Denormalized) FK → Company document ID. Denormalized from parent Event for direct queries.
|
|
123
|
+
|
|
124
|
+
:::info Immutable
|
|
125
|
+
Set at creation only. This field cannot be modified after the document is created. Include it in CREATE payloads; omit it (or leave unchanged) in UPDATE payloads.
|
|
126
|
+
:::
|
|
127
|
+
|
|
128
|
+
## <a name="customerId"></a>4. Property `customerId`
|
|
129
|
+
|
|
130
|
+
| | |
|
|
131
|
+
| ------------ | ---------------- |
|
|
132
|
+
| **Type** | `string or null` |
|
|
133
|
+
| **Required** | No |
|
|
134
|
+
|
|
135
|
+
**Description:** FK → Customer.id. Optional structured link to Customer document (D29). Added additively alongside denormalized fields.
|
|
136
|
+
|
|
137
|
+
:::note
|
|
138
|
+
Added additively per D29. Denormalized customerName/email/phone are kept for backwards compatibility. Null on tickets created before D29 was applied.
|
|
139
|
+
:::
|
|
140
|
+
|
|
141
|
+
:::info See also
|
|
142
|
+
**Decisions:** `D29`
|
|
143
|
+
:::
|
|
144
|
+
|
|
145
|
+
## <a name="customerName"></a>5. Property `customerName`
|
|
146
|
+
|
|
147
|
+
| | |
|
|
148
|
+
| ------------ | ---------------- |
|
|
149
|
+
| **Type** | `string or null` |
|
|
150
|
+
| **Required** | No |
|
|
151
|
+
|
|
152
|
+
**Description:** (Denormalized) From Customer.name at write time.
|
|
153
|
+
|
|
154
|
+
## <a name="customerEmail"></a>6. Property `customerEmail`
|
|
155
|
+
|
|
156
|
+
| | |
|
|
157
|
+
| ------------ | ---------------- |
|
|
158
|
+
| **Type** | `string or null` |
|
|
159
|
+
| **Required** | No |
|
|
160
|
+
|
|
161
|
+
**Description:** (Denormalized) From Customer.email at write time.
|
|
162
|
+
|
|
163
|
+
## <a name="customerPhone"></a>7. Property `customerPhone`
|
|
164
|
+
|
|
165
|
+
| | |
|
|
166
|
+
| ------------ | ---------------- |
|
|
167
|
+
| **Type** | `string or null` |
|
|
168
|
+
| **Required** | No |
|
|
169
|
+
|
|
170
|
+
**Description:** (Denormalized) From Customer.phone at write time.
|
|
171
|
+
|
|
172
|
+
## <a name="status"></a>8. Property `status`
|
|
173
|
+
|
|
174
|
+
| | |
|
|
175
|
+
| -------------- | --------------------------- |
|
|
176
|
+
| **Type** | `enum (of string)` |
|
|
177
|
+
| **Required** | Yes |
|
|
178
|
+
| **Defined in** | #/definitions/ticket-status |
|
|
179
|
+
|
|
180
|
+
**Description:** Ticket lifecycle (D32). SCREAMING_SNAKE per D04. MIG-10 migrates legacy lowercase values.
|
|
181
|
+
|
|
182
|
+
Must be one of:
|
|
183
|
+
* "VALID"
|
|
184
|
+
* "USED"
|
|
185
|
+
* "CANCELLED"
|
|
186
|
+
|
|
187
|
+
## <a name="usedAt"></a>9. Property `usedAt`
|
|
188
|
+
|
|
189
|
+
| | |
|
|
190
|
+
| ------------------------- | ---------------- |
|
|
191
|
+
| **Type** | `combining` |
|
|
192
|
+
| **Required** | No |
|
|
193
|
+
| **Additional properties** | Any type allowed |
|
|
194
|
+
|
|
195
|
+
**Description:** (Read-only) Timestamp when ticket was scanned/used. Set by scan operation.
|
|
196
|
+
|
|
197
|
+
| Any of(Option) |
|
|
198
|
+
| --------------------------------------- |
|
|
199
|
+
| [firestore-timestamp](#usedAt_anyOf_i0) |
|
|
200
|
+
| [item 1](#usedAt_anyOf_i1) |
|
|
201
|
+
|
|
202
|
+
### <a name="usedAt_anyOf_i0"></a>9.1. Property `firestore-timestamp`
|
|
203
|
+
|
|
204
|
+
| | |
|
|
205
|
+
| ------------------------- | --------------------------------- |
|
|
206
|
+
| **Type** | `object` |
|
|
207
|
+
| **Required** | No |
|
|
208
|
+
| **Additional properties** | Not allowed |
|
|
209
|
+
| **Defined in** | #/definitions/firestore-timestamp |
|
|
210
|
+
|
|
211
|
+
**Description:** Firestore Timestamp serialized representation
|
|
212
|
+
|
|
213
|
+
| Property | Pattern | Type | Deprecated | Definition | Title/Description |
|
|
214
|
+
| ------------------------------------------------ | ------- | ------- | ---------- | ---------- | ----------------- |
|
|
215
|
+
| + [_seconds](#usedAt_anyOf_i0__seconds ) | No | integer | No | - | - |
|
|
216
|
+
| + [_nanoseconds](#usedAt_anyOf_i0__nanoseconds ) | No | integer | No | - | - |
|
|
217
|
+
|
|
218
|
+
#### <a name="usedAt_anyOf_i0__seconds"></a>9.1.1. Property `_seconds`
|
|
219
|
+
|
|
220
|
+
| | |
|
|
221
|
+
| ------------ | --------- |
|
|
222
|
+
| **Type** | `integer` |
|
|
223
|
+
| **Required** | Yes |
|
|
224
|
+
|
|
225
|
+
| Restrictions | |
|
|
226
|
+
| ------------ | ---------------------- |
|
|
227
|
+
| **Minimum** | ≥ -9007199254740991 |
|
|
228
|
+
| **Maximum** | ≤ 9007199254740991 |
|
|
229
|
+
|
|
230
|
+
#### <a name="usedAt_anyOf_i0__nanoseconds"></a>9.1.2. Property `_nanoseconds`
|
|
231
|
+
|
|
232
|
+
| | |
|
|
233
|
+
| ------------ | --------- |
|
|
234
|
+
| **Type** | `integer` |
|
|
235
|
+
| **Required** | Yes |
|
|
236
|
+
|
|
237
|
+
| Restrictions | |
|
|
238
|
+
| ------------ | ---------------------- |
|
|
239
|
+
| **Minimum** | ≥ -9007199254740991 |
|
|
240
|
+
| **Maximum** | ≤ 9007199254740991 |
|
|
241
|
+
|
|
242
|
+
### <a name="usedAt_anyOf_i1"></a>9.2. Property `item 1`
|
|
243
|
+
|
|
244
|
+
| | |
|
|
245
|
+
| ------------ | ------ |
|
|
246
|
+
| **Type** | `null` |
|
|
247
|
+
| **Required** | No |
|
|
248
|
+
|
|
249
|
+
:::warning Server-set
|
|
250
|
+
Do not include in write requests. This field is set exclusively by the server (Firestore trigger or Admin SDK). Clients that send it will have the value silently ignored or may receive a validation error.
|
|
251
|
+
:::
|
|
252
|
+
|
|
253
|
+
## <a name="usedBy"></a>10. Property `usedBy`
|
|
254
|
+
|
|
255
|
+
| | |
|
|
256
|
+
| ------------ | ---------------- |
|
|
257
|
+
| **Type** | `string or null` |
|
|
258
|
+
| **Required** | No |
|
|
259
|
+
|
|
260
|
+
**Description:** (Read-only) FK → User/staff UID who scanned the ticket.
|
|
261
|
+
|
|
262
|
+
:::warning Server-set
|
|
263
|
+
Do not include in write requests. This field is set exclusively by the server (Firestore trigger or Admin SDK). Clients that send it will have the value silently ignored or may receive a validation error.
|
|
264
|
+
:::
|
|
265
|
+
|
|
266
|
+
## <a name="usedByName"></a>11. Property `usedByName`
|
|
267
|
+
|
|
268
|
+
| | |
|
|
269
|
+
| ------------ | ---------------- |
|
|
270
|
+
| **Type** | `string or null` |
|
|
271
|
+
| **Required** | No |
|
|
272
|
+
|
|
273
|
+
**Description:** (Read-only, Denormalized) From User display name at scan time.
|
|
274
|
+
|
|
275
|
+
:::warning Server-set
|
|
276
|
+
Do not include in write requests. This field is set exclusively by the server (Firestore trigger or Admin SDK). Clients that send it will have the value silently ignored or may receive a validation error.
|
|
277
|
+
:::
|
|
278
|
+
|
|
279
|
+
## <a name="price"></a>12. Property `price`
|
|
280
|
+
|
|
281
|
+
| | |
|
|
282
|
+
| ------------ | ---------------- |
|
|
283
|
+
| **Type** | `number or null` |
|
|
284
|
+
| **Required** | No |
|
|
285
|
+
|
|
286
|
+
## <a name="notes"></a>13. Property `notes`
|
|
287
|
+
|
|
288
|
+
| | |
|
|
289
|
+
| ------------ | ---------------- |
|
|
290
|
+
| **Type** | `string or null` |
|
|
291
|
+
| **Required** | No |
|
|
292
|
+
|
|
293
|
+
## <a name="createdAt"></a>14. Property `createdAt`
|
|
294
|
+
|
|
295
|
+
| | |
|
|
296
|
+
| ------------------------- | --------------------------------- |
|
|
297
|
+
| **Type** | `object` |
|
|
298
|
+
| **Required** | Yes |
|
|
299
|
+
| **Additional properties** | Not allowed |
|
|
300
|
+
| **Defined in** | #/definitions/firestore-timestamp |
|
|
301
|
+
|
|
302
|
+
**Description:** (Read-only) Server-generated creation timestamp.
|
|
303
|
+
|
|
304
|
+
| Property | Pattern | Type | Deprecated | Definition | Title/Description |
|
|
305
|
+
| ------------------------------------------------ | ------- | ------- | ---------- | ---------- | ----------------- |
|
|
306
|
+
| + [_seconds](#usedAt_anyOf_i0__seconds ) | No | integer | No | - | - |
|
|
307
|
+
| + [_nanoseconds](#usedAt_anyOf_i0__nanoseconds ) | No | integer | No | - | - |
|
|
308
|
+
|
|
309
|
+
### <a name="usedAt_anyOf_i0__seconds"></a>14.1. Property `_seconds`
|
|
310
|
+
|
|
311
|
+
| | |
|
|
312
|
+
| ------------ | --------- |
|
|
313
|
+
| **Type** | `integer` |
|
|
314
|
+
| **Required** | Yes |
|
|
315
|
+
|
|
316
|
+
| Restrictions | |
|
|
317
|
+
| ------------ | ---------------------- |
|
|
318
|
+
| **Minimum** | ≥ -9007199254740991 |
|
|
319
|
+
| **Maximum** | ≤ 9007199254740991 |
|
|
320
|
+
|
|
321
|
+
### <a name="usedAt_anyOf_i0__nanoseconds"></a>14.2. Property `_nanoseconds`
|
|
322
|
+
|
|
323
|
+
| | |
|
|
324
|
+
| ------------ | --------- |
|
|
325
|
+
| **Type** | `integer` |
|
|
326
|
+
| **Required** | Yes |
|
|
327
|
+
|
|
328
|
+
| Restrictions | |
|
|
329
|
+
| ------------ | ---------------------- |
|
|
330
|
+
| **Minimum** | ≥ -9007199254740991 |
|
|
331
|
+
| **Maximum** | ≤ 9007199254740991 |
|
|
332
|
+
|
|
333
|
+
:::warning Server-set
|
|
334
|
+
Do not include in write requests. This field is set exclusively by the server (Firestore trigger or Admin SDK). Clients that send it will have the value silently ignored or may receive a validation error.
|
|
335
|
+
:::
|
|
336
|
+
|
|
337
|
+
## <a name="updatedAt"></a>15. Property `updatedAt`
|
|
338
|
+
|
|
339
|
+
| | |
|
|
340
|
+
| ------------------------- | --------------------------------- |
|
|
341
|
+
| **Type** | `object` |
|
|
342
|
+
| **Required** | Yes |
|
|
343
|
+
| **Additional properties** | Not allowed |
|
|
344
|
+
| **Defined in** | #/definitions/firestore-timestamp |
|
|
345
|
+
|
|
346
|
+
**Description:** (Read-only) Server-generated update timestamp.
|
|
347
|
+
|
|
348
|
+
| Property | Pattern | Type | Deprecated | Definition | Title/Description |
|
|
349
|
+
| ------------------------------------------------ | ------- | ------- | ---------- | ---------- | ----------------- |
|
|
350
|
+
| + [_seconds](#usedAt_anyOf_i0__seconds ) | No | integer | No | - | - |
|
|
351
|
+
| + [_nanoseconds](#usedAt_anyOf_i0__nanoseconds ) | No | integer | No | - | - |
|
|
352
|
+
|
|
353
|
+
### <a name="usedAt_anyOf_i0__seconds"></a>15.1. Property `_seconds`
|
|
354
|
+
|
|
355
|
+
| | |
|
|
356
|
+
| ------------ | --------- |
|
|
357
|
+
| **Type** | `integer` |
|
|
358
|
+
| **Required** | Yes |
|
|
359
|
+
|
|
360
|
+
| Restrictions | |
|
|
361
|
+
| ------------ | ---------------------- |
|
|
362
|
+
| **Minimum** | ≥ -9007199254740991 |
|
|
363
|
+
| **Maximum** | ≤ 9007199254740991 |
|
|
364
|
+
|
|
365
|
+
### <a name="usedAt_anyOf_i0__nanoseconds"></a>15.2. Property `_nanoseconds`
|
|
366
|
+
|
|
367
|
+
| | |
|
|
368
|
+
| ------------ | --------- |
|
|
369
|
+
| **Type** | `integer` |
|
|
370
|
+
| **Required** | Yes |
|
|
371
|
+
|
|
372
|
+
| Restrictions | |
|
|
373
|
+
| ------------ | ---------------------- |
|
|
374
|
+
| **Minimum** | ≥ -9007199254740991 |
|
|
375
|
+
| **Maximum** | ≤ 9007199254740991 |
|
|
376
|
+
|
|
377
|
+
:::warning Server-set
|
|
378
|
+
Do not include in write requests. This field is set exclusively by the server (Firestore trigger or Admin SDK). Clients that send it will have the value silently ignored or may receive a validation error.
|
|
379
|
+
:::
|
|
380
|
+
|
|
381
|
+
## <a name="createdBy"></a>16. Property `createdBy`
|
|
382
|
+
|
|
383
|
+
| | |
|
|
384
|
+
| ------------ | ---------------- |
|
|
385
|
+
| **Type** | `string or null` |
|
|
386
|
+
| **Required** | No |
|
|
387
|
+
|
|
388
|
+
**Description:** (Immutable) FK → User/staff UID who created this ticket.
|
|
389
|
+
|
|
390
|
+
----------------------------------------------------------------------------------------------------------------------------
|
|
391
|
+
Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2026-03-09 at 13:15:53 +0000
|
|
392
|
+
|
|
393
|
+
:::info Immutable
|
|
394
|
+
Set at creation only. This field cannot be modified after the document is created. Include it in CREATE payloads; omit it (or leave unchanged) in UPDATE payloads.
|
|
395
|
+
:::
|
|
396
|
+
|
|
397
|
+
## Related Decisions
|
|
398
|
+
|
|
399
|
+
| Decision | Title |
|
|
400
|
+
|---|---|
|
|
401
|
+
| **D27** | Dashboard ticket scanning |
|
|
402
|
+
| **D28** | Firebase Event/Ticket triggers |
|
|
403
|
+
| **D29** | Ticket-to-customer linkage model |
|
|
404
|
+
| **D30** | Ticket tiers/types support timing |
|
|
405
|
+
| **D31** | LegacyTicketMapper deprecation gate |
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Event & Ticket Triggers"
|
|
3
|
+
sidebar_label: "Event & Ticket Triggers"
|
|
4
|
+
sidebar_position: 2
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Event & Ticket Triggers
|
|
8
|
+
|
|
9
|
+
> **Decision**: D28 (locked, deferred to Wave 4) — Add Firebase Event/Ticket triggers when Dashboard Event support (D26) is built. Not before — Events are Mobile-only today.
|
|
10
|
+
> **Wave**: 4 — Feature Parity.
|
|
11
|
+
> **Status**: Specified. **Do not implement until D26 (Dashboard Events) is in progress.**
|
|
12
|
+
|
|
13
|
+
These triggers maintain denormalized counters on [`Event`](/models/event) documents and send notifications on ticket lifecycle changes.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## `onTicketCreate`
|
|
18
|
+
|
|
19
|
+
Fires when a new [`Ticket`](/models/ticket) document is created.
|
|
20
|
+
|
|
21
|
+
**Path**: `companies/{companyId}/events/{eventId}/tickets/{ticketId}`
|
|
22
|
+
**Event**: `onCreate`
|
|
23
|
+
|
|
24
|
+
### Behavior
|
|
25
|
+
|
|
26
|
+
1. Atomically increment `Event.ticketsSold` on the parent event document.
|
|
27
|
+
2. Check capacity: if `Event.maxTickets` is set and `ticketsSold >= maxTickets`, update `Event.status` to `COMPLETED` (sold out). This is an optimistic check — a secondary validation should run on ticket creation too.
|
|
28
|
+
3. Send a confirmation notification to the customer (email/WhatsApp) if `Ticket.customerEmail` or `Ticket.customerPhone` is set.
|
|
29
|
+
|
|
30
|
+
### Inputs
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
interface TicketCreateEvent {
|
|
34
|
+
data: Ticket; // the new ticket document
|
|
35
|
+
params: {
|
|
36
|
+
companyId: string;
|
|
37
|
+
eventId: string;
|
|
38
|
+
ticketId: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Outputs
|
|
44
|
+
|
|
45
|
+
**Write 1** — Increment counter on parent Event:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// Atomic increment on: companies/{companyId}/events/{eventId}
|
|
49
|
+
{
|
|
50
|
+
ticketsSold: FieldValue.increment(1),
|
|
51
|
+
updatedAt: FieldValue.serverTimestamp(),
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Write 2** (conditional) — Mark event as sold out:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// Only if ticketsSold >= maxTickets after increment
|
|
59
|
+
{
|
|
60
|
+
status: 'COMPLETED', // EventStatus.COMPLETED = sold out
|
|
61
|
+
updatedAt: FieldValue.serverTimestamp(),
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
:::warning Capacity enforcement
|
|
66
|
+
Firestore has no native "max documents" guard. The trigger is the enforcement layer. For high-concurrency events, also add a Firestore Security Rule that rejects ticket writes when `ticketsSold >= maxTickets` using the current document value.
|
|
67
|
+
:::
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## `onTicketUpdate`
|
|
72
|
+
|
|
73
|
+
Fires when an existing [`Ticket`](/models/ticket) document is updated.
|
|
74
|
+
|
|
75
|
+
**Path**: `companies/{companyId}/events/{eventId}/tickets/{ticketId}`
|
|
76
|
+
**Event**: `onUpdate`
|
|
77
|
+
|
|
78
|
+
### Behavior
|
|
79
|
+
|
|
80
|
+
Compare `before.status` vs `after.status`:
|
|
81
|
+
|
|
82
|
+
| Transition | Action |
|
|
83
|
+
|---|---|
|
|
84
|
+
| any → `USED` | Increment `Event.ticketsUsed`. Set `Ticket.usedAt` and `Ticket.usedBy` (if not already set by client). |
|
|
85
|
+
| any → `CANCELLED` | Decrement `Event.ticketsSold`. If event was `COMPLETED` (sold out), revert to `ACTIVE`. |
|
|
86
|
+
| `CANCELLED` → `VALID` | Increment `Event.ticketsSold` (re-activation). |
|
|
87
|
+
| any other | No counter change. |
|
|
88
|
+
|
|
89
|
+
### Inputs
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
interface TicketUpdateEvent {
|
|
93
|
+
data: {
|
|
94
|
+
before: Ticket; // document state before the update
|
|
95
|
+
after: Ticket; // document state after the update
|
|
96
|
+
};
|
|
97
|
+
params: {
|
|
98
|
+
companyId: string;
|
|
99
|
+
eventId: string;
|
|
100
|
+
ticketId: string;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Outputs (status-dependent)
|
|
106
|
+
|
|
107
|
+
**On → `USED`**:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Atomic update on Event
|
|
111
|
+
{ ticketsUsed: FieldValue.increment(1), updatedAt: FieldValue.serverTimestamp() }
|
|
112
|
+
|
|
113
|
+
// Patch usedAt on Ticket if client didn't set it
|
|
114
|
+
{ usedAt: FieldValue.serverTimestamp() }
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**On → `CANCELLED`**:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Atomic update on Event
|
|
121
|
+
{ ticketsSold: FieldValue.increment(-1), updatedAt: FieldValue.serverTimestamp() }
|
|
122
|
+
|
|
123
|
+
// Conditional: if event was COMPLETED (sold out), revert to ACTIVE
|
|
124
|
+
{ status: 'ACTIVE', updatedAt: FieldValue.serverTimestamp() }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## `onEventUpdate` (metrics)
|
|
130
|
+
|
|
131
|
+
Fires when an [`Event`](/models/event) document is updated.
|
|
132
|
+
|
|
133
|
+
**Path**: `companies/{companyId}/events/{eventId}`
|
|
134
|
+
**Event**: `onUpdate`
|
|
135
|
+
|
|
136
|
+
### Behavior
|
|
137
|
+
|
|
138
|
+
This trigger handles notification side-effects of event-level status changes.
|
|
139
|
+
|
|
140
|
+
| Status transition | Action |
|
|
141
|
+
|---|---|
|
|
142
|
+
| any → `CANCELLED` | Notify all ticket holders (customerEmail / customerPhone on each Ticket subcollection). Set `Ticket.status = CANCELLED` for all non-cancelled tickets. |
|
|
143
|
+
| `DRAFT` → `ACTIVE` | Send event launch notification to opted-in customers (if notification list implemented). |
|
|
144
|
+
|
|
145
|
+
:::info Scope for Wave 4
|
|
146
|
+
The `CANCELLED` → notify-all-ticket-holders path is the critical one to implement. The `DRAFT → ACTIVE` notification is a nice-to-have.
|
|
147
|
+
:::
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## BookingVersion trigger (`onBookingWrite`)
|
|
152
|
+
|
|
153
|
+
> **Decision**: D18 (locked) — Firebase owns server-side BookingVersion generation.
|
|
154
|
+
> **Wave**: 3 — Server-Side Logic.
|
|
155
|
+
|
|
156
|
+
Fires on any write to a [`Booking`](/models/booking) document.
|
|
157
|
+
|
|
158
|
+
**Path**: `companies/{companyId}/bookings/{bookingId}`
|
|
159
|
+
**Event**: `onWrite` (covers onCreate, onUpdate, onDelete)
|
|
160
|
+
|
|
161
|
+
### Behavior
|
|
162
|
+
|
|
163
|
+
1. Determine `changeType`: `CREATE` if `before` is null, `DELETE` if `after` is null, otherwise `UPDATE`.
|
|
164
|
+
2. Compute `fieldsChanged` by diffing `before` and `after` document data (top-level keys for now; dot-notation for known nested objects like `bookingDates`).
|
|
165
|
+
3. Write a new [`BookingVersion`](/models/booking-version) document to the `versions/` subcollection.
|
|
166
|
+
|
|
167
|
+
### Inputs
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
interface BookingWriteEvent {
|
|
171
|
+
data: {
|
|
172
|
+
before: Booking | null; // null on CREATE
|
|
173
|
+
after: Booking | null; // null on DELETE
|
|
174
|
+
};
|
|
175
|
+
params: {
|
|
176
|
+
companyId: string;
|
|
177
|
+
bookingId: string;
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Output — Write 1: new BookingVersion document
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Written to: companies/{companyId}/bookings/{bookingId}/versions/{auto-id}
|
|
186
|
+
{
|
|
187
|
+
bookingId: params.bookingId,
|
|
188
|
+
companyId: params.companyId,
|
|
189
|
+
changeType: 'CREATE' | 'UPDATE' | 'DELETE',
|
|
190
|
+
changedAt: FieldValue.serverTimestamp(),
|
|
191
|
+
changedBy: context.auth?.uid ?? null,
|
|
192
|
+
changedByRole: detectRole(context), // 'dashboard' | 'mobile' | 'firebase' | 'cli' | 'unknown'
|
|
193
|
+
fieldsChanged: computeDiff(before, after), // null for CREATE
|
|
194
|
+
snapshot: after ?? before, // full document snapshot
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
:::note changedByRole detection
|
|
199
|
+
`changedByRole` is inferred from the auth token claims in the write context:
|
|
200
|
+
- `custom_claims.role === 'staff'` → `'dashboard'`
|
|
201
|
+
- `custom_claims.role === 'mobile'` → `'mobile'`
|
|
202
|
+
- No auth (Admin SDK) → `'firebase'` or `'cli'`
|
|
203
|
+
- Otherwise → `'unknown'`
|
|
204
|
+
:::
|