@ingenx-io/valets-schema-mcp-server 0.1.1 → 0.1.3

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.
Files changed (43) hide show
  1. package/data/docs/collections/firestore-paths.md +49 -0
  2. package/data/docs/decisions/migrations.md +56 -0
  3. package/data/docs/decisions/summary.md +78 -0
  4. package/data/docs/enums/booking-status.md +26 -0
  5. package/data/docs/enums/customer-payment-status.md +26 -0
  6. package/data/docs/enums/customer-payment-target-type.md +23 -0
  7. package/data/docs/enums/delivery-type.md +23 -0
  8. package/data/docs/enums/event-status.md +30 -0
  9. package/data/docs/enums/fulfillment-status.md +32 -0
  10. package/data/docs/enums/loyalty-transaction-type.md +32 -0
  11. package/data/docs/enums/order-status.md +65 -0
  12. package/data/docs/enums/payment-method.md +36 -0
  13. package/data/docs/enums/payment-proof-status.md +23 -0
  14. package/data/docs/enums/payment-status.md +34 -0
  15. package/data/docs/enums/return-status.md +32 -0
  16. package/data/docs/enums/session-status.md +32 -0
  17. package/data/docs/enums/ticket-status.md +29 -0
  18. package/data/docs/index.md +102 -0
  19. package/data/docs/models/booking-version.md +295 -0
  20. package/data/docs/models/booking.md +1754 -0
  21. package/data/docs/models/customer-payment-allocation.md +336 -0
  22. package/data/docs/models/customer-payment.md +392 -0
  23. package/data/docs/models/customer.md +475 -0
  24. package/data/docs/models/event.md +386 -0
  25. package/data/docs/models/loyalty-config.md +317 -0
  26. package/data/docs/models/loyalty-reward.md +236 -0
  27. package/data/docs/models/loyalty-status.md +328 -0
  28. package/data/docs/models/loyalty-transaction.md +326 -0
  29. package/data/docs/models/metrics-current.md +532 -0
  30. package/data/docs/models/metrics-daily.md +548 -0
  31. package/data/docs/models/metrics-monthly.md +548 -0
  32. package/data/docs/models/order-item.md +361 -0
  33. package/data/docs/models/order.md +1637 -0
  34. package/data/docs/models/payment-summary.md +123 -0
  35. package/data/docs/models/sale.md +540 -0
  36. package/data/docs/models/ticket.md +405 -0
  37. package/data/docs/triggers/event-ticket-triggers.md +204 -0
  38. package/data/docs/triggers/loyalty-automation.md +123 -0
  39. package/data/static/decisions.json +966 -0
  40. package/data/static/llms.txt +1056 -0
  41. package/data/static/openapi.yaml +3090 -0
  42. package/data/static/schemas.json +4055 -0
  43. package/package.json +1 -1
@@ -0,0 +1,3090 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: '@valets/schema'
4
+ description: 'Canonical data model for the Valets platform (Firestore document schemas).
5
+
6
+
7
+ Each model appears in up to three variants:
8
+
9
+ - **{Model}** — read model (full Firestore document, includes server-set fields)
10
+
11
+ - **{Model}Create** — write model for document creation; uses allOf composition
12
+ referencing {Model}. `readOnly` fields must not be sent.
13
+
14
+ - **{Model}Update** — write model for partial updates / PATCH; uses allOf composition.
15
+ All fields optional; `readOnly` and `x-immutable` fields must not be sent.
16
+
17
+
18
+ Server-owned models (e.g. BookingVersion, LoyaltyStatus) only have the read variant.'
19
+ version: 0.1.0
20
+ paths: {}
21
+ components:
22
+ schemas:
23
+ BookingStatus:
24
+ type: string
25
+ enum:
26
+ - PENDING
27
+ - CONFIRMED
28
+ - COMPLETED
29
+ - CANCELLED
30
+ - CANCELLATION_REQUESTED
31
+ - COMPLETED_MIXED
32
+ description: Booking lifecycle status. COMPLETED_MIXED = some sessions completed,
33
+ others cancelled/no-show.
34
+ CustomerPaymentStatus:
35
+ type: string
36
+ enum:
37
+ - PENDING
38
+ - CONFIRMED
39
+ - PARTIALLY_APPLIED
40
+ - FULLY_APPLIED
41
+ - REFUNDED
42
+ - CANCELLED
43
+ description: Customer payment lifecycle status (D22). Tracks allocation progress
44
+ of received payments.
45
+ CustomerPaymentTargetType:
46
+ type: string
47
+ enum:
48
+ - BOOKING
49
+ - ORDER
50
+ - PURCHASE
51
+ description: Target document type for customer payment allocation.
52
+ DeliveryType:
53
+ type: string
54
+ enum:
55
+ - ON_SITE
56
+ - PICK_UP
57
+ - DELIVERY
58
+ description: Fulfillment channel for an order. Determines whether the customer
59
+ comes to the business (ON_SITE), collects their order themselves (PICK_UP),
60
+ or receives a physical delivery (DELIVERY). Drives whether fulfillmentStatus
61
+ is relevant.
62
+ EventStatus:
63
+ type: string
64
+ enum:
65
+ - DRAFT
66
+ - ACTIVE
67
+ - CANCELLED
68
+ - COMPLETED
69
+ description: Ticketed event lifecycle (D32). Mobile-only today; Dashboard in
70
+ Wave 4.
71
+ FulfillmentStatus:
72
+ type: string
73
+ enum:
74
+ - PREPARING
75
+ - PARTIALLY_SHIPPED
76
+ - SHIPPED
77
+ - IN_TRANSIT
78
+ - DELIVERED
79
+ - PICKED_UP
80
+ description: Delivery/fulfillment lifecycle (D34). Optional — null for in-person
81
+ orders.
82
+ LoyaltyTransactionType:
83
+ type: string
84
+ enum:
85
+ - EARNED
86
+ - REDEEMED
87
+ - ADJUSTED
88
+ - EXPIRED
89
+ - BONUS
90
+ - REFUND
91
+ description: Loyalty point transaction type (D07). SCREAMING_SNAKE past tense.
92
+ OrderStatus:
93
+ type: string
94
+ enum:
95
+ - PENDING
96
+ - CONFIRMED
97
+ - PROCESSING
98
+ - READY
99
+ - COMPLETED
100
+ - CANCELLED
101
+ - EXPIRED
102
+ description: Core order lifecycle status (D03, D34). Universal across all business
103
+ types. Replaces the Dashboard's legacy 20-value flat enum (MIG-11).
104
+ PaymentMethod:
105
+ type: string
106
+ enum:
107
+ - CASH
108
+ - CREDIT_CARD
109
+ - ORANGE_MONEY
110
+ - WAVE
111
+ - MTN_MONEY
112
+ - MOOV_MONEY
113
+ - BANK_TRANSFER
114
+ - PAYPAL
115
+ - STRIPE
116
+ - OTHER
117
+ description: Unified payment method set with African + global methods (D02).
118
+ PaymentProofStatus:
119
+ type: string
120
+ enum:
121
+ - PENDING
122
+ - APPROVED
123
+ - REJECTED
124
+ description: Payment proof review status. Used by Order and Booking payment
125
+ proof workflows.
126
+ PaymentStatus:
127
+ type: string
128
+ enum:
129
+ - PENDING
130
+ - PAID
131
+ - PARTIALLY_PAID
132
+ - FAILED
133
+ - REFUND_PROCESSING
134
+ - REFUNDED
135
+ - PARTIALLY_REFUNDED
136
+ description: Payment lifecycle status (D01 amended). Used by Order, Sale/Purchase,
137
+ Booking.
138
+ ReturnStatus:
139
+ type: string
140
+ enum:
141
+ - RETURN_REQUESTED
142
+ - RETURN_PROCESSING
143
+ - RETURNED
144
+ - EXCHANGE_REQUESTED
145
+ - EXCHANGE_PROCESSING
146
+ - EXCHANGE_COMPLETED
147
+ description: Post-sale return/exchange lifecycle (D34). Optional — null until
148
+ return or exchange initiated.
149
+ SessionStatus:
150
+ type: string
151
+ enum:
152
+ - PENDING
153
+ - CONFIRMED
154
+ - CANCELLATION_REQUESTED
155
+ - COMPLETED
156
+ - NO_SHOW
157
+ - CANCELLED
158
+ description: Per-date/per-slot booking session status (D19). Dashboard is sole
159
+ writer; Mobile is read-only.
160
+ TicketStatus:
161
+ type: string
162
+ enum:
163
+ - VALID
164
+ - USED
165
+ - CANCELLED
166
+ description: Event ticket status (D32). VALID = active and unused.
167
+ FirestoreTimestamp:
168
+ type: object
169
+ properties:
170
+ _seconds:
171
+ type: integer
172
+ minimum: -9007199254740991
173
+ maximum: 9007199254740991
174
+ _nanoseconds:
175
+ type: integer
176
+ minimum: -9007199254740991
177
+ maximum: 9007199254740991
178
+ required:
179
+ - _seconds
180
+ - _nanoseconds
181
+ additionalProperties: false
182
+ description: Firestore Timestamp serialized representation
183
+ Booking:
184
+ type: object
185
+ properties:
186
+ id:
187
+ type: string
188
+ readOnly: true
189
+ description: (Read-only) Firestore document ID.
190
+ uid:
191
+ type: string
192
+ readOnly: true
193
+ description: (Read-only) Entity UID. Often mirrors id.
194
+ companyId:
195
+ x-immutable: true
196
+ description: '(Immutable) FK → Company document ID. Note: optional in current
197
+ schema — should be required (see ID consistency audit).'
198
+ type:
199
+ - string
200
+ - 'null'
201
+ status:
202
+ $ref: '#/components/schemas/BookingStatus'
203
+ description: Booking lifecycle status. COMPLETED_MIXED = some sessions completed,
204
+ others cancelled/no-show.
205
+ totalAmount:
206
+ type: number
207
+ x-note: Booking uses `totalAmount`; Order uses `amount` for the equivalent
208
+ field (D05 locked `amount` as canonical for orders). Pending cross-model
209
+ alignment.
210
+ x-see:
211
+ decisions:
212
+ - D05
213
+ bookingDates:
214
+ type: array
215
+ items:
216
+ type: object
217
+ properties:
218
+ date:
219
+ type: string
220
+ selectedTimeSlots:
221
+ type: array
222
+ items:
223
+ type: object
224
+ properties:
225
+ id:
226
+ type: number
227
+ name:
228
+ type: string
229
+ timeRange:
230
+ type: string
231
+ price:
232
+ type: number
233
+ description: Canonical price field (D11). Replaces legacy standardPrice/fullPrice
234
+ (MIG-08).
235
+ notMainPurposePrice:
236
+ type: number
237
+ description: Price when slot is not the main purpose (D11).
238
+ Canonical name; replaces legacy aliases (MIG-08).
239
+ hours:
240
+ type: string
241
+ startTime:
242
+ type: string
243
+ endTime:
244
+ type: string
245
+ isNightSlot:
246
+ type: boolean
247
+ isNotMainPurpose:
248
+ type: boolean
249
+ isNotMainPurposeReason:
250
+ type: string
251
+ isNotMainPurposeOtherReason:
252
+ type: string
253
+ _deleted:
254
+ description: Soft-delete flag. Dashboard writes; Mobile filters
255
+ (IG-6).
256
+ type: boolean
257
+ _deletedAt:
258
+ $ref: '#/components/schemas/FirestoreTimestamp'
259
+ description: Firestore Timestamp serialized representation
260
+ _deletedBy:
261
+ description: FK → User/staff UID who soft-deleted this item.
262
+ type: string
263
+ _lastModifiedAt:
264
+ $ref: '#/components/schemas/FirestoreTimestamp'
265
+ description: Firestore Timestamp serialized representation
266
+ _lastModifiedBy:
267
+ description: FK → User/staff UID who last modified this item.
268
+ type: string
269
+ _version:
270
+ description: Monotonic version counter for conflict detection.
271
+ type: number
272
+ required:
273
+ - id
274
+ - name
275
+ - timeRange
276
+ - price
277
+ - notMainPurposePrice
278
+ - hours
279
+ - startTime
280
+ - endTime
281
+ additionalProperties: false
282
+ description: Selected time slot within a BookingDate. Carries CRDT
283
+ metadata for conflict-free editing.
284
+ slotKitTypes:
285
+ type: object
286
+ propertyNames:
287
+ type: string
288
+ additionalProperties:
289
+ type: string
290
+ description: Kit type per slot (IG-11). Firebase parity gap — exists
291
+ in Firestore but Firebase type lacked it.
292
+ slotAddOns:
293
+ type: object
294
+ propertyNames:
295
+ type: string
296
+ additionalProperties:
297
+ type: array
298
+ items:
299
+ type: string
300
+ description: Add-on IDs per slot. Numeric IDs to be normalized to
301
+ descriptive names (MIG-07/D10).
302
+ extraHours:
303
+ type: number
304
+ status:
305
+ $ref: '#/components/schemas/SessionStatus'
306
+ description: Per-date session status (D19). Dashboard is sole writer;
307
+ Mobile is read-only.
308
+ statusUpdatedAt:
309
+ $ref: '#/components/schemas/FirestoreTimestamp'
310
+ readOnly: true
311
+ description: (Read-only) Timestamp of last status change.
312
+ statusUpdatedBy:
313
+ description: FK → User/staff UID who updated this date status.
314
+ type: string
315
+ slotStatuses:
316
+ description: Per-slot session statuses (D19). Keyed by slot ID string.
317
+ type: object
318
+ propertyNames:
319
+ type: string
320
+ additionalProperties:
321
+ $ref: '#/components/schemas/SessionStatus'
322
+ description: Per-date/per-slot booking session status (D19). Dashboard
323
+ is sole writer; Mobile is read-only.
324
+ slotStatusUpdatedAt:
325
+ type: object
326
+ propertyNames:
327
+ type: string
328
+ additionalProperties:
329
+ $ref: '#/components/schemas/FirestoreTimestamp'
330
+ description: Firestore Timestamp serialized representation
331
+ slotStatusUpdatedBy:
332
+ type: object
333
+ propertyNames:
334
+ type: string
335
+ additionalProperties:
336
+ type: string
337
+ _deleted:
338
+ description: Soft-delete flag. Dashboard writes; Mobile filters (IG-6).
339
+ type: boolean
340
+ _deletedAt:
341
+ $ref: '#/components/schemas/FirestoreTimestamp'
342
+ description: Firestore Timestamp serialized representation
343
+ _deletedBy:
344
+ description: FK → User/staff UID who soft-deleted this item.
345
+ type: string
346
+ _lastModifiedAt:
347
+ $ref: '#/components/schemas/FirestoreTimestamp'
348
+ description: Firestore Timestamp serialized representation
349
+ _lastModifiedBy:
350
+ description: FK → User/staff UID who last modified this item.
351
+ type: string
352
+ _version:
353
+ description: Monotonic version counter for conflict detection.
354
+ type: number
355
+ required:
356
+ - date
357
+ - selectedTimeSlots
358
+ - slotKitTypes
359
+ - slotAddOns
360
+ - extraHours
361
+ additionalProperties: false
362
+ description: Single date entry within a Booking. Contains time slots,
363
+ session statuses (D19), and CRDT metadata.
364
+ customerId:
365
+ description: FK → Customer.id (Firestore doc ID). Links booking to customer
366
+ record.
367
+ type:
368
+ - string
369
+ - 'null'
370
+ client:
371
+ denormalized: true
372
+ description: (Denormalized) Embedded client snapshot from Customer at write
373
+ time.
374
+ type:
375
+ - object
376
+ - 'null'
377
+ properties:
378
+ firstName:
379
+ type: string
380
+ lastName:
381
+ type: string
382
+ email:
383
+ type: string
384
+ phone:
385
+ type: string
386
+ additionalProperties: false
387
+ customerName:
388
+ denormalized: true
389
+ description: (Denormalized) From Customer.name at write time. Canonical
390
+ field per D24.
391
+ type:
392
+ - string
393
+ - 'null'
394
+ customerEmail:
395
+ denormalized: true
396
+ description: (Denormalized) From Customer.email at write time. Canonical
397
+ field per D24.
398
+ type:
399
+ - string
400
+ - 'null'
401
+ customerPhone:
402
+ denormalized: true
403
+ description: (Denormalized) From Customer.phone at write time. Canonical
404
+ field per D24.
405
+ type:
406
+ - string
407
+ - 'null'
408
+ clientName:
409
+ denormalized: true
410
+ deprecated: true
411
+ x-replaced-by: customerName
412
+ description: (Denormalized) Legacy — use `customerName`. D24 standardized
413
+ to customer* prefix.
414
+ type:
415
+ - string
416
+ - 'null'
417
+ clientReference:
418
+ type: string
419
+ clientEmail:
420
+ denormalized: true
421
+ deprecated: true
422
+ x-replaced-by: customerEmail
423
+ description: (Denormalized) Legacy — use `customerEmail`. D24 standardized
424
+ to customer* prefix.
425
+ type:
426
+ - string
427
+ - 'null'
428
+ clientPhone:
429
+ denormalized: true
430
+ deprecated: true
431
+ x-replaced-by: customerPhone
432
+ description: (Denormalized) Legacy — use `customerPhone`. D24 standardized
433
+ to customer* prefix.
434
+ type:
435
+ - string
436
+ - 'null'
437
+ service:
438
+ type:
439
+ - object
440
+ - 'null'
441
+ properties:
442
+ name:
443
+ type: string
444
+ additionalProperties: false
445
+ serviceId:
446
+ description: FK → Service document ID.
447
+ type:
448
+ - string
449
+ - 'null'
450
+ serviceName:
451
+ denormalized: true
452
+ description: (Denormalized) From Service.name at write time.
453
+ type:
454
+ - string
455
+ - 'null'
456
+ timeSlot:
457
+ type:
458
+ - string
459
+ - 'null'
460
+ date:
461
+ type:
462
+ - string
463
+ - 'null'
464
+ startDate:
465
+ type:
466
+ - string
467
+ - 'null'
468
+ endDate:
469
+ type:
470
+ - string
471
+ - 'null'
472
+ startTime:
473
+ type:
474
+ - string
475
+ - 'null'
476
+ endTime:
477
+ type:
478
+ - string
479
+ - 'null'
480
+ notes:
481
+ type:
482
+ - array
483
+ - 'null'
484
+ items:
485
+ type: object
486
+ properties:
487
+ id:
488
+ type: string
489
+ readOnly: true
490
+ description: (Read-only) Note ID.
491
+ text:
492
+ type: string
493
+ createdAt:
494
+ $ref: '#/components/schemas/FirestoreTimestamp'
495
+ description: (Read-only) Firestore Timestamp serialized representation.
496
+ readOnly: true
497
+ createdBy:
498
+ x-immutable: true
499
+ description: (Immutable) FK → User/staff UID who created this note.
500
+ type: string
501
+ createdByName:
502
+ denormalized: true
503
+ description: (Denormalized) From User display name at write time.
504
+ type: string
505
+ _deleted:
506
+ description: Soft-delete flag. Dashboard writes; Mobile filters (IG-6).
507
+ type: boolean
508
+ _deletedAt:
509
+ $ref: '#/components/schemas/FirestoreTimestamp'
510
+ description: Firestore Timestamp serialized representation
511
+ _deletedBy:
512
+ description: FK → User/staff UID who soft-deleted this item.
513
+ type: string
514
+ _lastModifiedAt:
515
+ $ref: '#/components/schemas/FirestoreTimestamp'
516
+ description: Firestore Timestamp serialized representation
517
+ _lastModifiedBy:
518
+ description: FK → User/staff UID who last modified this item.
519
+ type: string
520
+ _version:
521
+ description: Monotonic version counter for conflict detection.
522
+ type: number
523
+ required:
524
+ - id
525
+ - text
526
+ - createdAt
527
+ additionalProperties: false
528
+ description: Timestamped note attached to a Booking. Carries CRDT metadata.
529
+ technicalInfo:
530
+ type:
531
+ - string
532
+ - 'null'
533
+ paymentStatus:
534
+ anyOf:
535
+ - $ref: '#/components/schemas/PaymentStatus'
536
+ - type: 'null'
537
+ description: Payment lifecycle status (D01 amended). Used by Order, Sale/Purchase,
538
+ Booking.
539
+ amountPaid:
540
+ type:
541
+ - number
542
+ - 'null'
543
+ amountRefunded:
544
+ type:
545
+ - number
546
+ - 'null'
547
+ amountPending:
548
+ type:
549
+ - number
550
+ - 'null'
551
+ purchaseId:
552
+ description: FK → Sale.id. Link to associated Sale document.
553
+ type:
554
+ - string
555
+ - 'null'
556
+ paymentStatusChangeReason:
557
+ type:
558
+ - string
559
+ - 'null'
560
+ paymentStatusChangedBy:
561
+ description: FK → User/staff UID who changed payment status.
562
+ type:
563
+ - string
564
+ - 'null'
565
+ paymentStatusChangedAt:
566
+ anyOf:
567
+ - $ref: '#/components/schemas/FirestoreTimestamp'
568
+ - type: 'null'
569
+ description: Firestore Timestamp serialized representation
570
+ paymentProofUrl:
571
+ description: URL to uploaded payment proof image/document.
572
+ type:
573
+ - string
574
+ - 'null'
575
+ paymentProofStatus:
576
+ anyOf:
577
+ - $ref: '#/components/schemas/PaymentProofStatus'
578
+ - type: 'null'
579
+ description: Payment proof review status. Used by Order and Booking payment
580
+ proof workflows.
581
+ paymentProofAddedAt:
582
+ anyOf:
583
+ - $ref: '#/components/schemas/FirestoreTimestamp'
584
+ - type: 'null'
585
+ description: Firestore Timestamp serialized representation
586
+ paymentProofAddedBy:
587
+ description: FK → User/staff UID who uploaded the payment proof.
588
+ type:
589
+ - string
590
+ - 'null'
591
+ paymentProofReviewedBy:
592
+ description: FK → User/staff UID who reviewed the payment proof.
593
+ type:
594
+ - string
595
+ - 'null'
596
+ paymentProofReviewedAt:
597
+ anyOf:
598
+ - $ref: '#/components/schemas/FirestoreTimestamp'
599
+ - type: 'null'
600
+ description: Firestore Timestamp serialized representation
601
+ paymentProofRejectionReason:
602
+ type:
603
+ - string
604
+ - 'null'
605
+ cancellationRequestedById:
606
+ description: FK → User/staff UID who requested cancellation.
607
+ type:
608
+ - string
609
+ - 'null'
610
+ cancellationRequestReason:
611
+ type:
612
+ - string
613
+ - 'null'
614
+ cancellationProcessedById:
615
+ description: FK → User/staff UID who processed the cancellation.
616
+ type:
617
+ - string
618
+ - 'null'
619
+ cancellationProcessedAt:
620
+ anyOf:
621
+ - $ref: '#/components/schemas/FirestoreTimestamp'
622
+ - type: 'null'
623
+ description: Firestore Timestamp serialized representation
624
+ cancelledByRole:
625
+ type:
626
+ - string
627
+ - 'null'
628
+ cancellationReason:
629
+ type:
630
+ - string
631
+ - 'null'
632
+ createdAt:
633
+ $ref: '#/components/schemas/FirestoreTimestamp'
634
+ description: (Read-only) Server-generated creation timestamp.
635
+ readOnly: true
636
+ updatedAt:
637
+ anyOf:
638
+ - $ref: '#/components/schemas/FirestoreTimestamp'
639
+ - type: 'null'
640
+ readOnly: true
641
+ description: (Read-only) Server-generated update timestamp.
642
+ createdBy:
643
+ x-immutable: true
644
+ description: (Immutable) FK → User/staff UID who created this booking.
645
+ type:
646
+ - string
647
+ - 'null'
648
+ updatedBy:
649
+ description: FK → User/staff UID who last updated this booking.
650
+ type:
651
+ - string
652
+ - 'null'
653
+ createdFromBackend:
654
+ description: When true, suppresses Firebase notification triggers (D20/IG-8).
655
+ type:
656
+ - boolean
657
+ - 'null'
658
+ required:
659
+ - id
660
+ - uid
661
+ - status
662
+ - totalAmount
663
+ - bookingDates
664
+ - clientReference
665
+ - createdAt
666
+ additionalProperties: false
667
+ description: 'Booking model. Collection: companies/{companyId}/bookings/{bookingId}.
668
+ All platforms. Uses BookingStatus (6 values) and SessionStatus (D19) for per-date/slot
669
+ tracking.'
670
+ BookingCreate:
671
+ allOf:
672
+ - $ref: '#/components/schemas/Booking'
673
+ description: Write payload for creating a new Booking document. Fields marked
674
+ `readOnly` are server-set and must not be included. Fields marked `x-immutable`
675
+ may be set once at creation.
676
+ required:
677
+ - status
678
+ - totalAmount
679
+ - bookingDates
680
+ - clientReference
681
+ BookingUpdate:
682
+ allOf:
683
+ - $ref: '#/components/schemas/Booking'
684
+ description: Write payload for partial update (PATCH) of a Booking document.
685
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
686
+ sent.
687
+ BookingVersion:
688
+ type: object
689
+ properties:
690
+ id:
691
+ type: string
692
+ readOnly: true
693
+ description: (Read-only) Firestore document ID. Server-generated version
694
+ record.
695
+ bookingId:
696
+ type: string
697
+ x-immutable: true
698
+ description: (Immutable) FK → Booking.id. Parent booking this version records.
699
+ companyId:
700
+ type: string
701
+ x-immutable: true
702
+ description: (Immutable) FK → Company document ID. Denormalized for direct
703
+ queries.
704
+ changeType:
705
+ type: string
706
+ enum:
707
+ - CREATE
708
+ - UPDATE
709
+ - DELETE
710
+ x-immutable: true
711
+ x-note: DELETE versions are written when a booking is soft-deleted (CRDT
712
+ _deleted flag). Hard deletes bypass this trail.
713
+ description: '(Immutable) What caused this version record: CREATE, UPDATE,
714
+ or DELETE.'
715
+ changedAt:
716
+ $ref: '#/components/schemas/FirestoreTimestamp'
717
+ description: (Read-only) Timestamp of the write that created this version.
718
+ Server-set.
719
+ readOnly: true
720
+ changedBy:
721
+ x-immutable: true
722
+ x-note: Null when the write originated from a Firebase trigger, CLI script,
723
+ or unknown source.
724
+ description: (Immutable) FK → User/staff UID who made the change. Null for
725
+ server-originated writes.
726
+ type:
727
+ - string
728
+ - 'null'
729
+ changedByRole:
730
+ x-immutable: true
731
+ x-note: Populated by the Firebase trigger from the write context. Helps
732
+ distinguish client vs. server writes.
733
+ description: '(Immutable) Which platform/context made the write: DASHBOARD,
734
+ MOBILE, FIREBASE (trigger), CLI (migration script), or UNKNOWN. SCREAMING_SNAKE
735
+ per D04.'
736
+ type:
737
+ - string
738
+ - 'null'
739
+ enum:
740
+ - DASHBOARD
741
+ - MOBILE
742
+ - FIREBASE
743
+ - CLI
744
+ - UNKNOWN
745
+ fieldsChanged:
746
+ x-immutable: true
747
+ readOnly: true
748
+ x-note: Dot-notation paths for nested fields (e.g. "bookingDates.0.status").
749
+ Populated by diff in Firebase trigger. Null for CREATE versions.
750
+ description: (Immutable, Read-only) List of field paths that changed in
751
+ this write. Populated by the Firebase trigger diff. Null for CREATE records.
752
+ type:
753
+ - array
754
+ - 'null'
755
+ items:
756
+ type: string
757
+ snapshot:
758
+ type: object
759
+ propertyNames:
760
+ type: string
761
+ additionalProperties: {}
762
+ x-immutable: true
763
+ readOnly: true
764
+ x-note: Full Booking document at the time of write. Typed as a string-keyed
765
+ record to avoid circular schema reference. Consumers cast to Booking at
766
+ runtime.
767
+ x-see:
768
+ decisions:
769
+ - D18
770
+ description: (Immutable, Read-only) Full snapshot of the Booking document
771
+ immediately after the write. Untyped to avoid circular schema dependency
772
+ — cast to Booking at runtime.
773
+ required:
774
+ - id
775
+ - bookingId
776
+ - companyId
777
+ - changeType
778
+ - changedAt
779
+ - snapshot
780
+ additionalProperties: false
781
+ description: 'BookingVersion model (D18). Collection: companies/{companyId}/bookings/{bookingId}/versions/{versionId}.
782
+ Server-owned audit trail written by Firebase trigger on every Booking mutation.
783
+ Captures writes from all clients.'
784
+ Customer:
785
+ type: object
786
+ properties:
787
+ id:
788
+ type: string
789
+ readOnly: true
790
+ description: (Read-only) Firestore document ID. This is the canonical FK
791
+ target — other models reference Customer via this field.
792
+ uid:
793
+ type: string
794
+ readOnly: true
795
+ description: (Read-only) Entity UID. Often mirrors id.
796
+ name:
797
+ type: string
798
+ email:
799
+ type:
800
+ - string
801
+ - 'null'
802
+ phone:
803
+ type:
804
+ - string
805
+ - 'null'
806
+ address:
807
+ type:
808
+ - string
809
+ - 'null'
810
+ notes:
811
+ type:
812
+ - string
813
+ - 'null'
814
+ tags:
815
+ type:
816
+ - array
817
+ - 'null'
818
+ items:
819
+ type: string
820
+ communicationEntries:
821
+ description: Embedded CRM log (D25). Stays as array, not subcollection.
822
+ type:
823
+ - array
824
+ - 'null'
825
+ items:
826
+ type: object
827
+ properties:
828
+ timestamp:
829
+ $ref: '#/components/schemas/FirestoreTimestamp'
830
+ description: Firestore Timestamp serialized representation
831
+ type:
832
+ type: string
833
+ description: Communication channel (e.g. phone, email, meeting).
834
+ summary:
835
+ type: string
836
+ staffMember:
837
+ description: Staff member name involved in this communication.
838
+ type: string
839
+ required:
840
+ - timestamp
841
+ - type
842
+ - summary
843
+ additionalProperties: false
844
+ description: CRM communication log entry (D25). Embedded array on Customer
845
+ document.
846
+ createdAt:
847
+ $ref: '#/components/schemas/FirestoreTimestamp'
848
+ description: (Read-only) Server-generated creation timestamp.
849
+ readOnly: true
850
+ lastOrderDate:
851
+ anyOf:
852
+ - $ref: '#/components/schemas/FirestoreTimestamp'
853
+ - type: 'null'
854
+ readOnly: true
855
+ description: (Read-only) Updated by server when orders are placed.
856
+ balance:
857
+ readOnly: true
858
+ description: (Read-only) Outstanding balance. Dashboard-originated, server-calculated.
859
+ type:
860
+ - number
861
+ - 'null'
862
+ creditLimit:
863
+ type:
864
+ - number
865
+ - 'null'
866
+ lastPaymentDate:
867
+ anyOf:
868
+ - $ref: '#/components/schemas/FirestoreTimestamp'
869
+ - type: 'null'
870
+ readOnly: true
871
+ description: (Read-only) Updated by server when payments are recorded.
872
+ totalPaid:
873
+ readOnly: true
874
+ description: (Read-only) Server-calculated total paid amount.
875
+ type:
876
+ - number
877
+ - 'null'
878
+ totalOwed:
879
+ readOnly: true
880
+ description: (Read-only) Server-calculated total owed amount.
881
+ type:
882
+ - number
883
+ - 'null'
884
+ loyaltyPoints:
885
+ denormalized: true
886
+ readOnly: true
887
+ description: (Read-only, Denormalized) Derived summary from loyalty/status
888
+ subcollection (D08). Source of truth is LoyaltyStatus.pointsBalance.
889
+ type:
890
+ - number
891
+ - 'null'
892
+ required:
893
+ - id
894
+ - uid
895
+ - name
896
+ - createdAt
897
+ additionalProperties: false
898
+ description: 'Customer model. Collection: companies/{companyId}/customers/{customerId}.
899
+ Canonical across all platforms.'
900
+ CustomerCreate:
901
+ allOf:
902
+ - $ref: '#/components/schemas/Customer'
903
+ description: Write payload for creating a new Customer document. Fields marked
904
+ `readOnly` are server-set and must not be included. Fields marked `x-immutable`
905
+ may be set once at creation.
906
+ required:
907
+ - name
908
+ CustomerUpdate:
909
+ allOf:
910
+ - $ref: '#/components/schemas/Customer'
911
+ description: Write payload for partial update (PATCH) of a Customer document.
912
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
913
+ sent.
914
+ CustomerPayment:
915
+ type: object
916
+ properties:
917
+ id:
918
+ readOnly: true
919
+ description: '(Read-only) Firestore document ID. Note: optional in current
920
+ schema — some legacy docs may lack this field.'
921
+ type:
922
+ - string
923
+ - 'null'
924
+ companyId:
925
+ type: string
926
+ x-immutable: true
927
+ description: (Immutable) FK → Company document ID. Scopes all queries.
928
+ customerId:
929
+ type: string
930
+ x-immutable: true
931
+ description: (Immutable) FK → Customer.id (Firestore doc ID). Links payment
932
+ to customer. Set at creation.
933
+ customerName:
934
+ denormalized: true
935
+ description: (Denormalized) From Customer.name at write time.
936
+ type:
937
+ - string
938
+ - 'null'
939
+ amount:
940
+ type: number
941
+ currency:
942
+ type: string
943
+ const: XOF
944
+ description: Currency code. Locked to XOF (West African CFA franc) for now.
945
+ paymentDate:
946
+ $ref: '#/components/schemas/FirestoreTimestamp'
947
+ description: Firestore Timestamp serialized representation
948
+ paymentMethod:
949
+ $ref: '#/components/schemas/PaymentMethod'
950
+ description: Unified payment method set with African + global methods (D02).
951
+ referenceNumber:
952
+ type: string
953
+ description: Unique payment reference (receipt number, transaction ID, etc.).
954
+ allocatedAmount:
955
+ type: number
956
+ readOnly: true
957
+ description: (Read-only) Total amount allocated to bookings/orders/purchases
958
+ via allocations. Server-calculated.
959
+ unappliedAmount:
960
+ type: number
961
+ readOnly: true
962
+ description: (Read-only) Remaining unallocated amount (amount - allocatedAmount).
963
+ Server-calculated.
964
+ status:
965
+ $ref: '#/components/schemas/CustomerPaymentStatus'
966
+ description: Customer payment lifecycle status (D22). Tracks allocation
967
+ progress of received payments.
968
+ notes:
969
+ type:
970
+ - string
971
+ - 'null'
972
+ recordedBy:
973
+ type: string
974
+ x-immutable: true
975
+ description: (Immutable) FK → User/staff UID who recorded the payment. Required
976
+ audit field (D22/IG-7).
977
+ recordedByName:
978
+ x-immutable: true
979
+ denormalized: true
980
+ description: (Immutable, Denormalized) From User display name at creation
981
+ time.
982
+ type:
983
+ - string
984
+ - 'null'
985
+ createdAt:
986
+ $ref: '#/components/schemas/FirestoreTimestamp'
987
+ description: (Read-only) Server-generated creation timestamp.
988
+ readOnly: true
989
+ updatedAt:
990
+ anyOf:
991
+ - $ref: '#/components/schemas/FirestoreTimestamp'
992
+ - type: 'null'
993
+ readOnly: true
994
+ description: (Read-only) Server-generated update timestamp.
995
+ required:
996
+ - companyId
997
+ - customerId
998
+ - amount
999
+ - currency
1000
+ - paymentDate
1001
+ - paymentMethod
1002
+ - referenceNumber
1003
+ - allocatedAmount
1004
+ - unappliedAmount
1005
+ - status
1006
+ - recordedBy
1007
+ - createdAt
1008
+ additionalProperties: false
1009
+ description: 'CustomerPayment model (D22). Collection: companies/{companyId}/customerPayments/{paymentId}.
1010
+ Dashboard-only today; Mobile read in Wave 2, write in Wave 4 (trusted-party
1011
+ workflow).'
1012
+ CustomerPaymentCreate:
1013
+ allOf:
1014
+ - $ref: '#/components/schemas/CustomerPayment'
1015
+ description: Write payload for creating a new CustomerPayment document. Fields
1016
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
1017
+ may be set once at creation.
1018
+ required:
1019
+ - companyId
1020
+ - customerId
1021
+ - amount
1022
+ - currency
1023
+ - paymentDate
1024
+ - paymentMethod
1025
+ - referenceNumber
1026
+ - status
1027
+ - recordedBy
1028
+ CustomerPaymentUpdate:
1029
+ allOf:
1030
+ - $ref: '#/components/schemas/CustomerPayment'
1031
+ description: Write payload for partial update (PATCH) of a CustomerPayment document.
1032
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
1033
+ sent.
1034
+ CustomerPaymentAllocation:
1035
+ type: object
1036
+ properties:
1037
+ id:
1038
+ readOnly: true
1039
+ description: '(Read-only) Firestore document ID. Note: optional in current
1040
+ schema.'
1041
+ type:
1042
+ - string
1043
+ - 'null'
1044
+ companyId:
1045
+ type: string
1046
+ x-immutable: true
1047
+ description: (Immutable) FK → Company document ID.
1048
+ paymentId:
1049
+ type: string
1050
+ x-immutable: true
1051
+ description: (Immutable) FK → CustomerPayment.id. Parent payment this allocation
1052
+ draws from.
1053
+ customerId:
1054
+ type: string
1055
+ x-immutable: true
1056
+ description: (Immutable) FK → Customer.id (Firestore doc ID).
1057
+ targetId:
1058
+ type: string
1059
+ x-immutable: true
1060
+ description: (Immutable) FK → Booking.id, Order.id, or Sale.id (polymorphic).
1061
+ See targetType for discriminator.
1062
+ targetType:
1063
+ $ref: '#/components/schemas/CustomerPaymentTargetType'
1064
+ description: '(Immutable) Discriminator for targetId: BOOKING, ORDER, or
1065
+ PURCHASE.'
1066
+ x-immutable: true
1067
+ targetReference:
1068
+ type:
1069
+ - string
1070
+ - 'null'
1071
+ allocatedAmount:
1072
+ type: number
1073
+ x-immutable: true
1074
+ description: (Immutable) Amount allocated in this allocation. Set at creation.
1075
+ transferredToAllocationId:
1076
+ description: FK → CustomerPaymentAllocation.id. Self-reference for transfer
1077
+ chains.
1078
+ type:
1079
+ - string
1080
+ - 'null'
1081
+ transferredFromAllocationId:
1082
+ description: FK → CustomerPaymentAllocation.id. Self-reference for transfer
1083
+ chains.
1084
+ type:
1085
+ - string
1086
+ - 'null'
1087
+ transferredAt:
1088
+ anyOf:
1089
+ - $ref: '#/components/schemas/FirestoreTimestamp'
1090
+ - type: 'null'
1091
+ description: Firestore Timestamp serialized representation
1092
+ createdBy:
1093
+ type: string
1094
+ x-immutable: true
1095
+ description: (Immutable) FK → User/staff UID who created this allocation.
1096
+ createdByName:
1097
+ x-immutable: true
1098
+ denormalized: true
1099
+ description: (Immutable, Denormalized) From User display name at creation
1100
+ time.
1101
+ type:
1102
+ - string
1103
+ - 'null'
1104
+ createdAt:
1105
+ $ref: '#/components/schemas/FirestoreTimestamp'
1106
+ description: (Read-only) Server-generated creation timestamp.
1107
+ readOnly: true
1108
+ required:
1109
+ - companyId
1110
+ - paymentId
1111
+ - customerId
1112
+ - targetId
1113
+ - targetType
1114
+ - allocatedAmount
1115
+ - createdBy
1116
+ - createdAt
1117
+ additionalProperties: false
1118
+ description: 'CustomerPaymentAllocation model. Collection: companies/{companyId}/customerPaymentAllocations/{allocId}.
1119
+ Links customer payments to bookings, orders, or purchases.'
1120
+ CustomerPaymentAllocationCreate:
1121
+ allOf:
1122
+ - $ref: '#/components/schemas/CustomerPaymentAllocation'
1123
+ description: Write payload for creating a new CustomerPaymentAllocation document.
1124
+ Fields marked `readOnly` are server-set and must not be included. Fields marked
1125
+ `x-immutable` may be set once at creation.
1126
+ required:
1127
+ - companyId
1128
+ - paymentId
1129
+ - customerId
1130
+ - targetId
1131
+ - targetType
1132
+ - allocatedAmount
1133
+ - createdBy
1134
+ CustomerPaymentAllocationUpdate:
1135
+ allOf:
1136
+ - $ref: '#/components/schemas/CustomerPaymentAllocation'
1137
+ description: Write payload for partial update (PATCH) of a CustomerPaymentAllocation
1138
+ document. All fields optional. Fields marked `readOnly` or `x-immutable` must
1139
+ not be sent.
1140
+ Event:
1141
+ type: object
1142
+ properties:
1143
+ id:
1144
+ type: string
1145
+ readOnly: true
1146
+ description: '(Read-only) Firestore document ID. Note: Event does not have
1147
+ a uid field.'
1148
+ companyId:
1149
+ type: string
1150
+ x-immutable: true
1151
+ description: (Immutable) FK → Company document ID. Scopes all queries.
1152
+ name:
1153
+ type: string
1154
+ description:
1155
+ type:
1156
+ - string
1157
+ - 'null'
1158
+ location:
1159
+ type:
1160
+ - string
1161
+ - 'null'
1162
+ startDate:
1163
+ $ref: '#/components/schemas/FirestoreTimestamp'
1164
+ description: Firestore Timestamp serialized representation
1165
+ endDate:
1166
+ anyOf:
1167
+ - $ref: '#/components/schemas/FirestoreTimestamp'
1168
+ - type: 'null'
1169
+ description: Firestore Timestamp serialized representation
1170
+ status:
1171
+ $ref: '#/components/schemas/EventStatus'
1172
+ description: Event lifecycle status (D32). SCREAMING_SNAKE per D04. MIG-09
1173
+ migrates legacy lowercase values.
1174
+ maxTickets:
1175
+ type:
1176
+ - integer
1177
+ - 'null'
1178
+ minimum: -9007199254740991
1179
+ maximum: 9007199254740991
1180
+ ticketsSold:
1181
+ type: integer
1182
+ minimum: -9007199254740991
1183
+ maximum: 9007199254740991
1184
+ readOnly: true
1185
+ description: '(Read-only) Counter: total tickets sold. Updated by Firebase
1186
+ triggers (D28).'
1187
+ ticketsUsed:
1188
+ type: integer
1189
+ minimum: -9007199254740991
1190
+ maximum: 9007199254740991
1191
+ readOnly: true
1192
+ description: '(Read-only) Counter: tickets scanned/used. Updated by Firebase
1193
+ triggers (D28).'
1194
+ ticketPrice:
1195
+ type:
1196
+ - number
1197
+ - 'null'
1198
+ createdAt:
1199
+ $ref: '#/components/schemas/FirestoreTimestamp'
1200
+ description: (Read-only) Server-generated creation timestamp.
1201
+ readOnly: true
1202
+ updatedAt:
1203
+ $ref: '#/components/schemas/FirestoreTimestamp'
1204
+ description: (Read-only) Server-generated update timestamp.
1205
+ readOnly: true
1206
+ createdBy:
1207
+ x-immutable: true
1208
+ description: (Immutable) FK → User/staff UID who created this event.
1209
+ type:
1210
+ - string
1211
+ - 'null'
1212
+ required:
1213
+ - id
1214
+ - companyId
1215
+ - name
1216
+ - startDate
1217
+ - status
1218
+ - ticketsSold
1219
+ - ticketsUsed
1220
+ - createdAt
1221
+ - updatedAt
1222
+ additionalProperties: false
1223
+ description: 'Event model (D26, D32). Collection: companies/{companyId}/events/{eventId}.
1224
+ Mobile-only today; Dashboard in Wave 4.'
1225
+ EventCreate:
1226
+ allOf:
1227
+ - $ref: '#/components/schemas/Event'
1228
+ description: Write payload for creating a new Event document. Fields marked
1229
+ `readOnly` are server-set and must not be included. Fields marked `x-immutable`
1230
+ may be set once at creation.
1231
+ required:
1232
+ - companyId
1233
+ - name
1234
+ - startDate
1235
+ - status
1236
+ EventUpdate:
1237
+ allOf:
1238
+ - $ref: '#/components/schemas/Event'
1239
+ description: Write payload for partial update (PATCH) of a Event document. All
1240
+ fields optional. Fields marked `readOnly` or `x-immutable` must not be sent.
1241
+ LoyaltyConfig:
1242
+ type: object
1243
+ properties:
1244
+ id:
1245
+ readOnly: true
1246
+ description: (Read-only) Firestore document ID. Singleton doc — typically
1247
+ "config".
1248
+ type:
1249
+ - string
1250
+ - 'null'
1251
+ isEnabled:
1252
+ type: boolean
1253
+ pointSystem:
1254
+ type: string
1255
+ enum:
1256
+ - SPENDING
1257
+ - PRODUCT
1258
+ - VISIT
1259
+ description: 'How points are earned: SPENDING (per currency spent), PRODUCT
1260
+ (per product purchased), or VISIT (per visit). SCREAMING_SNAKE per D04.'
1261
+ pointsPerCurrency:
1262
+ description: '[Deprecated alias: pointsPerCurrencyUnit — renamed by MIG-06]'
1263
+ deprecated: true
1264
+ type:
1265
+ - number
1266
+ - 'null'
1267
+ pointsPerVisit:
1268
+ description: Points earned per visit (when pointSystem is visit).
1269
+ type:
1270
+ - number
1271
+ - 'null'
1272
+ pointValue:
1273
+ description: Monetary value of one point for redemption.
1274
+ type:
1275
+ - number
1276
+ - 'null'
1277
+ minimumRedeemPoints:
1278
+ description: Minimum points balance required before redemption is allowed.
1279
+ type:
1280
+ - integer
1281
+ - 'null'
1282
+ minimum: -9007199254740991
1283
+ maximum: 9007199254740991
1284
+ welcomeBonusPoints:
1285
+ description: One-time points bonus for new loyalty enrollees.
1286
+ type:
1287
+ - integer
1288
+ - 'null'
1289
+ minimum: -9007199254740991
1290
+ maximum: 9007199254740991
1291
+ pointsExpirationDays:
1292
+ description: '[Deprecated alias: pointsExpiryMonths — renamed and converted
1293
+ months*30 to days by MIG-06]'
1294
+ deprecated: true
1295
+ anyOf:
1296
+ - type: integer
1297
+ minimum: -9007199254740991
1298
+ maximum: 9007199254740991
1299
+ - type: 'null'
1300
+ createdAt:
1301
+ anyOf:
1302
+ - $ref: '#/components/schemas/FirestoreTimestamp'
1303
+ - type: 'null'
1304
+ readOnly: true
1305
+ description: (Read-only) Server-generated creation timestamp.
1306
+ updatedAt:
1307
+ anyOf:
1308
+ - $ref: '#/components/schemas/FirestoreTimestamp'
1309
+ - type: 'null'
1310
+ readOnly: true
1311
+ description: (Read-only) Server-generated update timestamp.
1312
+ required:
1313
+ - isEnabled
1314
+ - pointSystem
1315
+ additionalProperties: false
1316
+ description: 'LoyaltyConfig model (D21). Collection: companies/{companyId}/loyaltySettings/config.
1317
+ 1 doc per company. Uses canonical field names per D21 (MIG-06 renames legacy
1318
+ aliases).'
1319
+ LoyaltyConfigCreate:
1320
+ allOf:
1321
+ - $ref: '#/components/schemas/LoyaltyConfig'
1322
+ description: Write payload for creating a new LoyaltyConfig document. Fields
1323
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
1324
+ may be set once at creation.
1325
+ required:
1326
+ - isEnabled
1327
+ - pointSystem
1328
+ LoyaltyConfigUpdate:
1329
+ allOf:
1330
+ - $ref: '#/components/schemas/LoyaltyConfig'
1331
+ description: Write payload for partial update (PATCH) of a LoyaltyConfig document.
1332
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
1333
+ sent.
1334
+ LoyaltyReward:
1335
+ type: object
1336
+ properties:
1337
+ id:
1338
+ type: string
1339
+ readOnly: true
1340
+ description: (Read-only) Firestore document ID.
1341
+ name:
1342
+ type: string
1343
+ description:
1344
+ type:
1345
+ - string
1346
+ - 'null'
1347
+ pointsRequired:
1348
+ type: integer
1349
+ minimum: -9007199254740991
1350
+ maximum: 9007199254740991
1351
+ description: Points cost to redeem this reward.
1352
+ isActive:
1353
+ type: boolean
1354
+ description: Whether this reward is currently available for redemption.
1355
+ imageUrl:
1356
+ type:
1357
+ - string
1358
+ - 'null'
1359
+ rewardType:
1360
+ description: Category of reward (e.g. discount, free_item, service).
1361
+ type:
1362
+ - string
1363
+ - 'null'
1364
+ discountValue:
1365
+ description: Discount amount when rewardType is discount.
1366
+ type:
1367
+ - number
1368
+ - 'null'
1369
+ productId:
1370
+ description: FK → Product.id. Linked product when rewardType is free_item.
1371
+ type:
1372
+ - string
1373
+ - 'null'
1374
+ createdAt:
1375
+ $ref: '#/components/schemas/FirestoreTimestamp'
1376
+ description: (Read-only) Server-generated creation timestamp.
1377
+ readOnly: true
1378
+ updatedAt:
1379
+ anyOf:
1380
+ - $ref: '#/components/schemas/FirestoreTimestamp'
1381
+ - type: 'null'
1382
+ readOnly: true
1383
+ description: (Read-only) Server-generated update timestamp.
1384
+ required:
1385
+ - id
1386
+ - name
1387
+ - pointsRequired
1388
+ - isActive
1389
+ - createdAt
1390
+ additionalProperties: false
1391
+ description: 'LoyaltyReward model. Collection: companies/{companyId}/loyaltyRewards/{rewardId}.
1392
+ Reward catalog for point redemption (IG-10).'
1393
+ LoyaltyRewardCreate:
1394
+ allOf:
1395
+ - $ref: '#/components/schemas/LoyaltyReward'
1396
+ description: Write payload for creating a new LoyaltyReward document. Fields
1397
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
1398
+ may be set once at creation.
1399
+ required:
1400
+ - name
1401
+ - pointsRequired
1402
+ - isActive
1403
+ LoyaltyRewardUpdate:
1404
+ allOf:
1405
+ - $ref: '#/components/schemas/LoyaltyReward'
1406
+ description: Write payload for partial update (PATCH) of a LoyaltyReward document.
1407
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
1408
+ sent.
1409
+ LoyaltyStatus:
1410
+ type: object
1411
+ properties:
1412
+ customerId:
1413
+ x-immutable: true
1414
+ description: '(Immutable) FK → Customer.id. Note: optional despite being
1415
+ in customer subcollection (path already contains custId).'
1416
+ type:
1417
+ - string
1418
+ - 'null'
1419
+ pointsBalance:
1420
+ type: integer
1421
+ minimum: -9007199254740991
1422
+ maximum: 9007199254740991
1423
+ readOnly: true
1424
+ description: '(Read-only) Current available points balance. Canonical name
1425
+ (D08). Dashboard: pointsBalance, Mobile: currentPoints. Updated by transaction
1426
+ triggers.'
1427
+ totalPointsEarned:
1428
+ type: integer
1429
+ minimum: -9007199254740991
1430
+ maximum: 9007199254740991
1431
+ readOnly: true
1432
+ description: '(Read-only) Lifetime total points earned. Canonical name (D08).
1433
+ Dashboard: totalPointsEarned, Mobile: lifetimePoints. Updated by transaction
1434
+ triggers.'
1435
+ redeemedPoints:
1436
+ readOnly: true
1437
+ description: (Read-only) Total redeemed points. Updated by transaction triggers.
1438
+ type:
1439
+ - integer
1440
+ - 'null'
1441
+ minimum: -9007199254740991
1442
+ maximum: 9007199254740991
1443
+ lastActivityDate:
1444
+ anyOf:
1445
+ - $ref: '#/components/schemas/FirestoreTimestamp'
1446
+ - type: 'null'
1447
+ readOnly: true
1448
+ description: '(Read-only) Last earn/redeem activity. Canonical name (D08).
1449
+ Mobile alias: lastEarnedAt.'
1450
+ lastRedeemedAt:
1451
+ anyOf:
1452
+ - $ref: '#/components/schemas/FirestoreTimestamp'
1453
+ - type: 'null'
1454
+ readOnly: true
1455
+ description: (Read-only) Last redemption timestamp.
1456
+ tier:
1457
+ type:
1458
+ - string
1459
+ - 'null'
1460
+ createdAt:
1461
+ anyOf:
1462
+ - $ref: '#/components/schemas/FirestoreTimestamp'
1463
+ - type: 'null'
1464
+ readOnly: true
1465
+ description: (Read-only) Server-generated creation timestamp.
1466
+ updatedAt:
1467
+ anyOf:
1468
+ - $ref: '#/components/schemas/FirestoreTimestamp'
1469
+ - type: 'null'
1470
+ readOnly: true
1471
+ description: (Read-only) Server-generated update timestamp.
1472
+ required:
1473
+ - pointsBalance
1474
+ - totalPointsEarned
1475
+ additionalProperties: false
1476
+ description: 'LoyaltyStatus model (D08). Collection: companies/{companyId}/customers/{custId}/loyalty/status.
1477
+ Source of truth for customer points. Customer doc loyaltyPoints is a derived
1478
+ summary only.'
1479
+ LoyaltyTransaction:
1480
+ type: object
1481
+ properties:
1482
+ id:
1483
+ type: string
1484
+ readOnly: true
1485
+ description: (Read-only) Firestore document ID.
1486
+ customerId:
1487
+ x-immutable: true
1488
+ description: '(Immutable) FK → Customer.id. Note: optional despite being
1489
+ in customer subcollection.'
1490
+ type:
1491
+ - string
1492
+ - 'null'
1493
+ type:
1494
+ $ref: '#/components/schemas/LoyaltyTransactionType'
1495
+ description: (Immutable) Transaction type (D07). SCREAMING_SNAKE per D04.
1496
+ MIG-05 migrates legacy lowercase values.
1497
+ x-immutable: true
1498
+ pointsChange:
1499
+ type: integer
1500
+ minimum: -9007199254740991
1501
+ maximum: 9007199254740991
1502
+ x-immutable: true
1503
+ description: '(Immutable) Points delta (+/-). Canonical name (D07). Dashboard:
1504
+ pointsChange, Mobile: points.'
1505
+ description:
1506
+ type:
1507
+ - string
1508
+ - 'null'
1509
+ reason:
1510
+ type:
1511
+ - string
1512
+ - 'null'
1513
+ relatedPurchaseId:
1514
+ x-immutable: true
1515
+ description: (Immutable) FK → Sale.id. Linked sale that triggered this transaction.
1516
+ type:
1517
+ - string
1518
+ - 'null'
1519
+ relatedOrderId:
1520
+ x-immutable: true
1521
+ description: (Immutable) FK → Order.id. Linked order that triggered this
1522
+ transaction.
1523
+ type:
1524
+ - string
1525
+ - 'null'
1526
+ relatedRewardId:
1527
+ x-immutable: true
1528
+ description: (Immutable) FK → LoyaltyReward.id. Reward redeemed in this
1529
+ transaction.
1530
+ type:
1531
+ - string
1532
+ - 'null'
1533
+ orderId:
1534
+ x-immutable: true
1535
+ description: '(Immutable) FK → Order.id. Note: may overlap with relatedOrderId
1536
+ — naming inconsistency from Mobile.'
1537
+ type:
1538
+ - string
1539
+ - 'null'
1540
+ sessionId:
1541
+ description: Session/booking reference. Context-dependent identifier.
1542
+ type:
1543
+ - string
1544
+ - 'null'
1545
+ orderAmount:
1546
+ type:
1547
+ - number
1548
+ - 'null'
1549
+ transactionDate:
1550
+ $ref: '#/components/schemas/FirestoreTimestamp'
1551
+ description: '(Immutable) When the transaction occurred. Canonical name
1552
+ (D07). Dashboard: transactionDate, Mobile: createdAt.'
1553
+ x-immutable: true
1554
+ pointsBalanceAfter:
1555
+ readOnly: true
1556
+ description: '(Read-only) Running balance after this transaction. Canonical
1557
+ name (D07). Dashboard: pointsBalanceAfter, Mobile: balanceAfter. Server-calculated.'
1558
+ type:
1559
+ - integer
1560
+ - 'null'
1561
+ minimum: -9007199254740991
1562
+ maximum: 9007199254740991
1563
+ createdBy:
1564
+ x-immutable: true
1565
+ description: (Immutable) FK → User/staff UID who created this transaction.
1566
+ type:
1567
+ - string
1568
+ - 'null'
1569
+ createdByName:
1570
+ x-immutable: true
1571
+ denormalized: true
1572
+ description: (Immutable, Denormalized) From User display name at creation
1573
+ time.
1574
+ type:
1575
+ - string
1576
+ - 'null'
1577
+ required:
1578
+ - id
1579
+ - type
1580
+ - pointsChange
1581
+ - transactionDate
1582
+ additionalProperties: false
1583
+ description: 'LoyaltyTransaction model (D07). Collection: companies/{companyId}/customers/{custId}/loyaltyTransactions/{txId}.
1584
+ Type values use SCREAMING_SNAKE per D04 (MIG-05 migrates lowercase).'
1585
+ LoyaltyTransactionCreate:
1586
+ allOf:
1587
+ - $ref: '#/components/schemas/LoyaltyTransaction'
1588
+ description: Write payload for creating a new LoyaltyTransaction document. Fields
1589
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
1590
+ may be set once at creation.
1591
+ required:
1592
+ - type
1593
+ - pointsChange
1594
+ - transactionDate
1595
+ LoyaltyTransactionUpdate:
1596
+ allOf:
1597
+ - $ref: '#/components/schemas/LoyaltyTransaction'
1598
+ description: Write payload for partial update (PATCH) of a LoyaltyTransaction
1599
+ document. All fields optional. Fields marked `readOnly` or `x-immutable` must
1600
+ not be sent.
1601
+ MetricsCurrent:
1602
+ type: object
1603
+ properties:
1604
+ todayOrdersCount:
1605
+ type: integer
1606
+ minimum: -9007199254740991
1607
+ maximum: 9007199254740991
1608
+ readOnly: true
1609
+ description: (Read-only) Orders created today (UTC). Resets at midnight.
1610
+ pendingOrdersCount:
1611
+ type: integer
1612
+ minimum: -9007199254740991
1613
+ maximum: 9007199254740991
1614
+ readOnly: true
1615
+ description: (Read-only) Orders currently in PENDING status (all-time cumulative).
1616
+ Corrected by full recalc on order updates.
1617
+ orderCompletionRate30d:
1618
+ type: number
1619
+ readOnly: true
1620
+ description: (Read-only) Percentage of orders completed or delivered in
1621
+ the last 30 days. Always full recalc.
1622
+ todayPurchasesCount:
1623
+ type: integer
1624
+ minimum: -9007199254740991
1625
+ maximum: 9007199254740991
1626
+ readOnly: true
1627
+ description: (Read-only) Purchases created today (UTC). Resets at midnight.
1628
+ todayPurchasesSum:
1629
+ type: number
1630
+ readOnly: true
1631
+ description: (Read-only) Total value of purchases created today (UTC). Resets
1632
+ at midnight.
1633
+ averagePurchaseAmount:
1634
+ type: number
1635
+ readOnly: true
1636
+ description: (Read-only) Average purchase value across last 200 purchases
1637
+ (rolling, not time-windowed).
1638
+ monthlyRevenue:
1639
+ type: number
1640
+ readOnly: true
1641
+ description: (Read-only) Total purchase value in the current calendar month
1642
+ (UTC).
1643
+ monthlyPurchasesCount:
1644
+ type: integer
1645
+ minimum: -9007199254740991
1646
+ maximum: 9007199254740991
1647
+ readOnly: true
1648
+ description: (Read-only) Number of purchases in the current calendar month
1649
+ (UTC).
1650
+ todayBookingsCount:
1651
+ type: integer
1652
+ minimum: -9007199254740991
1653
+ maximum: 9007199254740991
1654
+ readOnly: true
1655
+ description: (Read-only) Bookings with date == today AND status in [PENDING,
1656
+ CONFIRMED]. Resets at midnight.
1657
+ bookingsCreatedToday:
1658
+ type: integer
1659
+ minimum: -9007199254740991
1660
+ maximum: 9007199254740991
1661
+ readOnly: true
1662
+ description: (Read-only) Bookings with createdAt today (all statuses). Resets
1663
+ at midnight.
1664
+ todayBookingsConfirmedAmount:
1665
+ type: number
1666
+ readOnly: true
1667
+ description: (Read-only) Sum of totalAmount for bookings created today with
1668
+ status CONFIRMED or COMPLETED. Resets at midnight.
1669
+ monthlyBookingsConfirmedAmount:
1670
+ type: number
1671
+ readOnly: true
1672
+ description: (Read-only) Sum of totalAmount for bookings created this month
1673
+ with status CONFIRMED or COMPLETED.
1674
+ todayCollectedAmount:
1675
+ type: number
1676
+ readOnly: true
1677
+ description: (Read-only) Sum of totalAmount for bookings where PAYMENT_PAID_AT
1678
+ is today. Resets at midnight.
1679
+ todayRevenue:
1680
+ type: number
1681
+ readOnly: true
1682
+ description: (Read-only) Sum of totalAmount for COMPLETED bookings where
1683
+ startDate == endDate == today (numeric YYYYMMDD). Resets at midnight.
1684
+ bookingsPendingPaymentVerification:
1685
+ type: integer
1686
+ minimum: -9007199254740991
1687
+ maximum: 9007199254740991
1688
+ readOnly: true
1689
+ description: (Read-only) Bookings with status PENDING/CANCELLATION_REQUESTED
1690
+ and a payment proof uploaded but not yet verified. Current state, always
1691
+ full recalc.
1692
+ bookingsPendingValidation:
1693
+ type: integer
1694
+ minimum: -9007199254740991
1695
+ maximum: 9007199254740991
1696
+ readOnly: true
1697
+ description: (Read-only) PENDING bookings with startDate within ±30 days
1698
+ of today. Rolling window, always full recalc.
1699
+ bookingsPendingValidation24h:
1700
+ type: integer
1701
+ minimum: -9007199254740991
1702
+ maximum: 9007199254740991
1703
+ readOnly: true
1704
+ description: (Read-only) PENDING bookings created in the last 24h with startDate
1705
+ >= tomorrow. Always full recalc.
1706
+ customersCount:
1707
+ type: integer
1708
+ minimum: -9007199254740991
1709
+ maximum: 9007199254740991
1710
+ readOnly: true
1711
+ description: (Read-only) Total customer count (all-time cumulative).
1712
+ newCustomersThisMonth:
1713
+ type: integer
1714
+ minimum: -9007199254740991
1715
+ maximum: 9007199254740991
1716
+ readOnly: true
1717
+ description: (Read-only) Customers with createdAt in the current calendar
1718
+ month (UTC).
1719
+ averageRating:
1720
+ type: number
1721
+ readOnly: true
1722
+ description: (Read-only) Average rating from the last 200 reviews (rolling).
1723
+ Always full recalc.
1724
+ lowStockItemsCount:
1725
+ type: integer
1726
+ minimum: -9007199254740991
1727
+ maximum: 9007199254740991
1728
+ readOnly: true
1729
+ description: (Read-only) Active stock items where currentQuantity <= minimumQuantity.
1730
+ Current state, always full recalc.
1731
+ activeRecurringPaymentsCount:
1732
+ type: integer
1733
+ minimum: -9007199254740991
1734
+ maximum: 9007199254740991
1735
+ readOnly: true
1736
+ description: (Read-only) Recurring payments with status ACTIVE. Current
1737
+ state, always full recalc.
1738
+ monthlyRecurringRevenue:
1739
+ type: number
1740
+ readOnly: true
1741
+ description: (Read-only) Sum of amount for ACTIVE + MONTHLY recurring payments.
1742
+ Always full recalc.
1743
+ computedForDay:
1744
+ type: string
1745
+ readOnly: true
1746
+ description: (Read-only) YYYY-MM-DD string indicating which day these metrics
1747
+ reflect. Used to detect day boundaries and trigger midnight resets.
1748
+ generatedAt:
1749
+ $ref: '#/components/schemas/FirestoreTimestamp'
1750
+ description: (Read-only) Server timestamp of the last metrics write.
1751
+ readOnly: true
1752
+ required:
1753
+ - todayOrdersCount
1754
+ - pendingOrdersCount
1755
+ - orderCompletionRate30d
1756
+ - todayPurchasesCount
1757
+ - todayPurchasesSum
1758
+ - averagePurchaseAmount
1759
+ - monthlyRevenue
1760
+ - monthlyPurchasesCount
1761
+ - todayBookingsCount
1762
+ - bookingsCreatedToday
1763
+ - todayBookingsConfirmedAmount
1764
+ - monthlyBookingsConfirmedAmount
1765
+ - todayCollectedAmount
1766
+ - todayRevenue
1767
+ - bookingsPendingPaymentVerification
1768
+ - bookingsPendingValidation
1769
+ - bookingsPendingValidation24h
1770
+ - customersCount
1771
+ - newCustomersThisMonth
1772
+ - averageRating
1773
+ - lowStockItemsCount
1774
+ - activeRecurringPaymentsCount
1775
+ - monthlyRecurringRevenue
1776
+ - computedForDay
1777
+ - generatedAt
1778
+ additionalProperties: false
1779
+ description: 'Server-computed company metrics. Collection: companies/{companyId}/metrics/current
1780
+ (singleton). All fields are server-set — clients must never write to this
1781
+ collection. See also MetricsDaily and MetricsMonthly for historical snapshots.'
1782
+ MetricsCurrentCreate:
1783
+ allOf:
1784
+ - $ref: '#/components/schemas/MetricsCurrent'
1785
+ description: Write payload for creating a new MetricsCurrent document. Fields
1786
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
1787
+ may be set once at creation.
1788
+ MetricsCurrentUpdate:
1789
+ allOf:
1790
+ - $ref: '#/components/schemas/MetricsCurrent'
1791
+ description: Write payload for partial update (PATCH) of a MetricsCurrent document.
1792
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
1793
+ sent.
1794
+ MetricsDaily:
1795
+ type: object
1796
+ properties:
1797
+ todayOrdersCount:
1798
+ type: integer
1799
+ minimum: -9007199254740991
1800
+ maximum: 9007199254740991
1801
+ readOnly: true
1802
+ description: (Read-only) Orders created today (UTC). Resets at midnight.
1803
+ pendingOrdersCount:
1804
+ type: integer
1805
+ minimum: -9007199254740991
1806
+ maximum: 9007199254740991
1807
+ readOnly: true
1808
+ description: (Read-only) Orders currently in PENDING status (all-time cumulative).
1809
+ Corrected by full recalc on order updates.
1810
+ orderCompletionRate30d:
1811
+ type: number
1812
+ readOnly: true
1813
+ description: (Read-only) Percentage of orders completed or delivered in
1814
+ the last 30 days. Always full recalc.
1815
+ todayPurchasesCount:
1816
+ type: integer
1817
+ minimum: -9007199254740991
1818
+ maximum: 9007199254740991
1819
+ readOnly: true
1820
+ description: (Read-only) Purchases created today (UTC). Resets at midnight.
1821
+ todayPurchasesSum:
1822
+ type: number
1823
+ readOnly: true
1824
+ description: (Read-only) Total value of purchases created today (UTC). Resets
1825
+ at midnight.
1826
+ averagePurchaseAmount:
1827
+ type: number
1828
+ readOnly: true
1829
+ description: (Read-only) Average purchase value across last 200 purchases
1830
+ (rolling, not time-windowed).
1831
+ monthlyRevenue:
1832
+ type: number
1833
+ readOnly: true
1834
+ description: (Read-only) Total purchase value in the current calendar month
1835
+ (UTC).
1836
+ monthlyPurchasesCount:
1837
+ type: integer
1838
+ minimum: -9007199254740991
1839
+ maximum: 9007199254740991
1840
+ readOnly: true
1841
+ description: (Read-only) Number of purchases in the current calendar month
1842
+ (UTC).
1843
+ todayBookingsCount:
1844
+ type: integer
1845
+ minimum: -9007199254740991
1846
+ maximum: 9007199254740991
1847
+ readOnly: true
1848
+ description: (Read-only) Bookings with date == today AND status in [PENDING,
1849
+ CONFIRMED]. Resets at midnight.
1850
+ bookingsCreatedToday:
1851
+ type: integer
1852
+ minimum: -9007199254740991
1853
+ maximum: 9007199254740991
1854
+ readOnly: true
1855
+ description: (Read-only) Bookings with createdAt today (all statuses). Resets
1856
+ at midnight.
1857
+ todayBookingsConfirmedAmount:
1858
+ type: number
1859
+ readOnly: true
1860
+ description: (Read-only) Sum of totalAmount for bookings created today with
1861
+ status CONFIRMED or COMPLETED. Resets at midnight.
1862
+ monthlyBookingsConfirmedAmount:
1863
+ type: number
1864
+ readOnly: true
1865
+ description: (Read-only) Sum of totalAmount for bookings created this month
1866
+ with status CONFIRMED or COMPLETED.
1867
+ todayCollectedAmount:
1868
+ type: number
1869
+ readOnly: true
1870
+ description: (Read-only) Sum of totalAmount for bookings where PAYMENT_PAID_AT
1871
+ is today. Resets at midnight.
1872
+ todayRevenue:
1873
+ type: number
1874
+ readOnly: true
1875
+ description: (Read-only) Sum of totalAmount for COMPLETED bookings where
1876
+ startDate == endDate == today (numeric YYYYMMDD). Resets at midnight.
1877
+ bookingsPendingPaymentVerification:
1878
+ type: integer
1879
+ minimum: -9007199254740991
1880
+ maximum: 9007199254740991
1881
+ readOnly: true
1882
+ description: (Read-only) Bookings with status PENDING/CANCELLATION_REQUESTED
1883
+ and a payment proof uploaded but not yet verified. Current state, always
1884
+ full recalc.
1885
+ bookingsPendingValidation:
1886
+ type: integer
1887
+ minimum: -9007199254740991
1888
+ maximum: 9007199254740991
1889
+ readOnly: true
1890
+ description: (Read-only) PENDING bookings with startDate within ±30 days
1891
+ of today. Rolling window, always full recalc.
1892
+ bookingsPendingValidation24h:
1893
+ type: integer
1894
+ minimum: -9007199254740991
1895
+ maximum: 9007199254740991
1896
+ readOnly: true
1897
+ description: (Read-only) PENDING bookings created in the last 24h with startDate
1898
+ >= tomorrow. Always full recalc.
1899
+ customersCount:
1900
+ type: integer
1901
+ minimum: -9007199254740991
1902
+ maximum: 9007199254740991
1903
+ readOnly: true
1904
+ description: (Read-only) Total customer count (all-time cumulative).
1905
+ newCustomersThisMonth:
1906
+ type: integer
1907
+ minimum: -9007199254740991
1908
+ maximum: 9007199254740991
1909
+ readOnly: true
1910
+ description: (Read-only) Customers with createdAt in the current calendar
1911
+ month (UTC).
1912
+ averageRating:
1913
+ type: number
1914
+ readOnly: true
1915
+ description: (Read-only) Average rating from the last 200 reviews (rolling).
1916
+ Always full recalc.
1917
+ lowStockItemsCount:
1918
+ type: integer
1919
+ minimum: -9007199254740991
1920
+ maximum: 9007199254740991
1921
+ readOnly: true
1922
+ description: (Read-only) Active stock items where currentQuantity <= minimumQuantity.
1923
+ Current state, always full recalc.
1924
+ activeRecurringPaymentsCount:
1925
+ type: integer
1926
+ minimum: -9007199254740991
1927
+ maximum: 9007199254740991
1928
+ readOnly: true
1929
+ description: (Read-only) Recurring payments with status ACTIVE. Current
1930
+ state, always full recalc.
1931
+ monthlyRecurringRevenue:
1932
+ type: number
1933
+ readOnly: true
1934
+ description: (Read-only) Sum of amount for ACTIVE + MONTHLY recurring payments.
1935
+ Always full recalc.
1936
+ computedForDay:
1937
+ type: string
1938
+ readOnly: true
1939
+ description: (Read-only) YYYY-MM-DD string indicating which day these metrics
1940
+ reflect. Used to detect day boundaries and trigger midnight resets.
1941
+ generatedAt:
1942
+ $ref: '#/components/schemas/FirestoreTimestamp'
1943
+ description: (Read-only) Server timestamp of the last metrics write.
1944
+ readOnly: true
1945
+ date:
1946
+ type: string
1947
+ readOnly: true
1948
+ description: (Read-only) YYYY-MM-DD document ID repeated as a field. Identifies
1949
+ the day this snapshot covers.
1950
+ required:
1951
+ - todayOrdersCount
1952
+ - pendingOrdersCount
1953
+ - orderCompletionRate30d
1954
+ - todayPurchasesCount
1955
+ - todayPurchasesSum
1956
+ - averagePurchaseAmount
1957
+ - monthlyRevenue
1958
+ - monthlyPurchasesCount
1959
+ - todayBookingsCount
1960
+ - bookingsCreatedToday
1961
+ - todayBookingsConfirmedAmount
1962
+ - monthlyBookingsConfirmedAmount
1963
+ - todayCollectedAmount
1964
+ - todayRevenue
1965
+ - bookingsPendingPaymentVerification
1966
+ - bookingsPendingValidation
1967
+ - bookingsPendingValidation24h
1968
+ - customersCount
1969
+ - newCustomersThisMonth
1970
+ - averageRating
1971
+ - lowStockItemsCount
1972
+ - activeRecurringPaymentsCount
1973
+ - monthlyRecurringRevenue
1974
+ - computedForDay
1975
+ - generatedAt
1976
+ - date
1977
+ additionalProperties: false
1978
+ description: 'Daily metrics snapshot. Collection: companies/{companyId}/metrics_daily/{YYYY-MM-DD}.
1979
+ Same fields as MetricsCurrent plus `date`. Written by computeDailyCompanyMetrics
1980
+ cron (02:00 UTC) and inline after full recalcs.'
1981
+ MetricsDailyCreate:
1982
+ allOf:
1983
+ - $ref: '#/components/schemas/MetricsDaily'
1984
+ description: Write payload for creating a new MetricsDaily document. Fields
1985
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
1986
+ may be set once at creation.
1987
+ MetricsDailyUpdate:
1988
+ allOf:
1989
+ - $ref: '#/components/schemas/MetricsDaily'
1990
+ description: Write payload for partial update (PATCH) of a MetricsDaily document.
1991
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
1992
+ sent.
1993
+ MetricsMonthly:
1994
+ type: object
1995
+ properties:
1996
+ todayOrdersCount:
1997
+ type: integer
1998
+ minimum: -9007199254740991
1999
+ maximum: 9007199254740991
2000
+ readOnly: true
2001
+ description: (Read-only) Orders created today (UTC). Resets at midnight.
2002
+ pendingOrdersCount:
2003
+ type: integer
2004
+ minimum: -9007199254740991
2005
+ maximum: 9007199254740991
2006
+ readOnly: true
2007
+ description: (Read-only) Orders currently in PENDING status (all-time cumulative).
2008
+ Corrected by full recalc on order updates.
2009
+ orderCompletionRate30d:
2010
+ type: number
2011
+ readOnly: true
2012
+ description: (Read-only) Percentage of orders completed or delivered in
2013
+ the last 30 days. Always full recalc.
2014
+ todayPurchasesCount:
2015
+ type: integer
2016
+ minimum: -9007199254740991
2017
+ maximum: 9007199254740991
2018
+ readOnly: true
2019
+ description: (Read-only) Purchases created today (UTC). Resets at midnight.
2020
+ todayPurchasesSum:
2021
+ type: number
2022
+ readOnly: true
2023
+ description: (Read-only) Total value of purchases created today (UTC). Resets
2024
+ at midnight.
2025
+ averagePurchaseAmount:
2026
+ type: number
2027
+ readOnly: true
2028
+ description: (Read-only) Average purchase value across last 200 purchases
2029
+ (rolling, not time-windowed).
2030
+ monthlyRevenue:
2031
+ type: number
2032
+ readOnly: true
2033
+ description: (Read-only) Total purchase value in the current calendar month
2034
+ (UTC).
2035
+ monthlyPurchasesCount:
2036
+ type: integer
2037
+ minimum: -9007199254740991
2038
+ maximum: 9007199254740991
2039
+ readOnly: true
2040
+ description: (Read-only) Number of purchases in the current calendar month
2041
+ (UTC).
2042
+ todayBookingsCount:
2043
+ type: integer
2044
+ minimum: -9007199254740991
2045
+ maximum: 9007199254740991
2046
+ readOnly: true
2047
+ description: (Read-only) Bookings with date == today AND status in [PENDING,
2048
+ CONFIRMED]. Resets at midnight.
2049
+ bookingsCreatedToday:
2050
+ type: integer
2051
+ minimum: -9007199254740991
2052
+ maximum: 9007199254740991
2053
+ readOnly: true
2054
+ description: (Read-only) Bookings with createdAt today (all statuses). Resets
2055
+ at midnight.
2056
+ todayBookingsConfirmedAmount:
2057
+ type: number
2058
+ readOnly: true
2059
+ description: (Read-only) Sum of totalAmount for bookings created today with
2060
+ status CONFIRMED or COMPLETED. Resets at midnight.
2061
+ monthlyBookingsConfirmedAmount:
2062
+ type: number
2063
+ readOnly: true
2064
+ description: (Read-only) Sum of totalAmount for bookings created this month
2065
+ with status CONFIRMED or COMPLETED.
2066
+ todayCollectedAmount:
2067
+ type: number
2068
+ readOnly: true
2069
+ description: (Read-only) Sum of totalAmount for bookings where PAYMENT_PAID_AT
2070
+ is today. Resets at midnight.
2071
+ todayRevenue:
2072
+ type: number
2073
+ readOnly: true
2074
+ description: (Read-only) Sum of totalAmount for COMPLETED bookings where
2075
+ startDate == endDate == today (numeric YYYYMMDD). Resets at midnight.
2076
+ bookingsPendingPaymentVerification:
2077
+ type: integer
2078
+ minimum: -9007199254740991
2079
+ maximum: 9007199254740991
2080
+ readOnly: true
2081
+ description: (Read-only) Bookings with status PENDING/CANCELLATION_REQUESTED
2082
+ and a payment proof uploaded but not yet verified. Current state, always
2083
+ full recalc.
2084
+ bookingsPendingValidation:
2085
+ type: integer
2086
+ minimum: -9007199254740991
2087
+ maximum: 9007199254740991
2088
+ readOnly: true
2089
+ description: (Read-only) PENDING bookings with startDate within ±30 days
2090
+ of today. Rolling window, always full recalc.
2091
+ bookingsPendingValidation24h:
2092
+ type: integer
2093
+ minimum: -9007199254740991
2094
+ maximum: 9007199254740991
2095
+ readOnly: true
2096
+ description: (Read-only) PENDING bookings created in the last 24h with startDate
2097
+ >= tomorrow. Always full recalc.
2098
+ customersCount:
2099
+ type: integer
2100
+ minimum: -9007199254740991
2101
+ maximum: 9007199254740991
2102
+ readOnly: true
2103
+ description: (Read-only) Total customer count (all-time cumulative).
2104
+ newCustomersThisMonth:
2105
+ type: integer
2106
+ minimum: -9007199254740991
2107
+ maximum: 9007199254740991
2108
+ readOnly: true
2109
+ description: (Read-only) Customers with createdAt in the current calendar
2110
+ month (UTC).
2111
+ averageRating:
2112
+ type: number
2113
+ readOnly: true
2114
+ description: (Read-only) Average rating from the last 200 reviews (rolling).
2115
+ Always full recalc.
2116
+ lowStockItemsCount:
2117
+ type: integer
2118
+ minimum: -9007199254740991
2119
+ maximum: 9007199254740991
2120
+ readOnly: true
2121
+ description: (Read-only) Active stock items where currentQuantity <= minimumQuantity.
2122
+ Current state, always full recalc.
2123
+ activeRecurringPaymentsCount:
2124
+ type: integer
2125
+ minimum: -9007199254740991
2126
+ maximum: 9007199254740991
2127
+ readOnly: true
2128
+ description: (Read-only) Recurring payments with status ACTIVE. Current
2129
+ state, always full recalc.
2130
+ monthlyRecurringRevenue:
2131
+ type: number
2132
+ readOnly: true
2133
+ description: (Read-only) Sum of amount for ACTIVE + MONTHLY recurring payments.
2134
+ Always full recalc.
2135
+ computedForDay:
2136
+ type: string
2137
+ readOnly: true
2138
+ description: (Read-only) YYYY-MM-DD string indicating which day these metrics
2139
+ reflect. Used to detect day boundaries and trigger midnight resets.
2140
+ generatedAt:
2141
+ $ref: '#/components/schemas/FirestoreTimestamp'
2142
+ description: (Read-only) Server timestamp of the last metrics write.
2143
+ readOnly: true
2144
+ month:
2145
+ type: string
2146
+ readOnly: true
2147
+ description: (Read-only) YYYY-MM document ID repeated as a field. Identifies
2148
+ the month this rollup covers.
2149
+ required:
2150
+ - todayOrdersCount
2151
+ - pendingOrdersCount
2152
+ - orderCompletionRate30d
2153
+ - todayPurchasesCount
2154
+ - todayPurchasesSum
2155
+ - averagePurchaseAmount
2156
+ - monthlyRevenue
2157
+ - monthlyPurchasesCount
2158
+ - todayBookingsCount
2159
+ - bookingsCreatedToday
2160
+ - todayBookingsConfirmedAmount
2161
+ - monthlyBookingsConfirmedAmount
2162
+ - todayCollectedAmount
2163
+ - todayRevenue
2164
+ - bookingsPendingPaymentVerification
2165
+ - bookingsPendingValidation
2166
+ - bookingsPendingValidation24h
2167
+ - customersCount
2168
+ - newCustomersThisMonth
2169
+ - averageRating
2170
+ - lowStockItemsCount
2171
+ - activeRecurringPaymentsCount
2172
+ - monthlyRecurringRevenue
2173
+ - computedForDay
2174
+ - generatedAt
2175
+ - month
2176
+ additionalProperties: false
2177
+ description: 'Monthly metrics rollup. Collection: companies/{companyId}/metrics_monthly/{YYYY-MM}.
2178
+ Same fields as MetricsCurrent plus `month`. Written alongside daily snapshots;
2179
+ reflects state at last full recalc within the month.'
2180
+ MetricsMonthlyCreate:
2181
+ allOf:
2182
+ - $ref: '#/components/schemas/MetricsMonthly'
2183
+ description: Write payload for creating a new MetricsMonthly document. Fields
2184
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
2185
+ may be set once at creation.
2186
+ MetricsMonthlyUpdate:
2187
+ allOf:
2188
+ - $ref: '#/components/schemas/MetricsMonthly'
2189
+ description: Write payload for partial update (PATCH) of a MetricsMonthly document.
2190
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
2191
+ sent.
2192
+ Order:
2193
+ type: object
2194
+ properties:
2195
+ id:
2196
+ type: string
2197
+ readOnly: true
2198
+ description: '(Read-only) Firestore document ID. Note: some models also
2199
+ have uid; see ID conventions.'
2200
+ uid:
2201
+ type: string
2202
+ readOnly: true
2203
+ description: (Read-only) Entity UID. Often mirrors id.
2204
+ companyId:
2205
+ type: string
2206
+ x-immutable: true
2207
+ description: (Immutable) FK → Company document ID. Scopes all queries.
2208
+ orderNumber:
2209
+ type: string
2210
+ readOnly: true
2211
+ description: (Read-only) Server-generated order number.
2212
+ status:
2213
+ $ref: '#/components/schemas/OrderStatus'
2214
+ description: Core lifecycle status (D34, MIG-11). See OrderStatus enum for
2215
+ the legacy value migration mapping.
2216
+ x-migration:
2217
+ source: Dashboard flat 20-value OrderStatus
2218
+ migration: MIG-11
2219
+ note: REFUND_PROCESSING and REFUNDED map to paymentStatus, not returnStatus
2220
+ — an order can be refunded without a physical return.
2221
+ mapping:
2222
+ - old: PENDING
2223
+ status: PENDING
2224
+ - old: CONFIRMED
2225
+ status: CONFIRMED
2226
+ - old: PROCESSING
2227
+ status: PROCESSING
2228
+ - old: READY
2229
+ status: READY
2230
+ - old: COMPLETED
2231
+ status: COMPLETED
2232
+ - old: CANCELLED
2233
+ status: CANCELLED
2234
+ - old: EXPIRED
2235
+ status: EXPIRED
2236
+ - old: AWAITING_PAYMENT
2237
+ status: PENDING
2238
+ paymentStatus: PENDING
2239
+ - old: SHIPPED
2240
+ status: PROCESSING
2241
+ fulfillmentStatus: SHIPPED
2242
+ - old: PARTIALLY_SHIPPED
2243
+ status: PROCESSING
2244
+ fulfillmentStatus: PARTIALLY_SHIPPED
2245
+ - old: ON_THE_WAY
2246
+ status: PROCESSING
2247
+ fulfillmentStatus: IN_TRANSIT
2248
+ - old: DELIVERED
2249
+ status: COMPLETED
2250
+ fulfillmentStatus: DELIVERED
2251
+ - old: PICKED_UP
2252
+ status: COMPLETED
2253
+ fulfillmentStatus: PICKED_UP
2254
+ - old: RETURN_REQUESTED
2255
+ status: COMPLETED
2256
+ returnStatus: RETURN_REQUESTED
2257
+ - old: RETURN_PROCESSING
2258
+ status: COMPLETED
2259
+ returnStatus: RETURN_PROCESSING
2260
+ - old: RETURNED
2261
+ status: COMPLETED
2262
+ returnStatus: RETURNED
2263
+ - old: EXCHANGE_REQUESTED
2264
+ status: COMPLETED
2265
+ returnStatus: EXCHANGE_REQUESTED
2266
+ - old: EXCHANGE_PROCESSING
2267
+ status: COMPLETED
2268
+ returnStatus: EXCHANGE_PROCESSING
2269
+ - old: EXCHANGE_COMPLETED
2270
+ status: COMPLETED
2271
+ returnStatus: EXCHANGE_COMPLETED
2272
+ - old: REFUND_PROCESSING
2273
+ status: COMPLETED
2274
+ paymentStatus: REFUND_PROCESSING
2275
+ - old: REFUNDED
2276
+ status: COMPLETED
2277
+ paymentStatus: REFUNDED
2278
+ paymentStatus:
2279
+ anyOf:
2280
+ - $ref: '#/components/schemas/PaymentStatus'
2281
+ - type: 'null'
2282
+ description: Payment lifecycle (D34). Null until payment is initiated.
2283
+ fulfillmentStatus:
2284
+ anyOf:
2285
+ - $ref: '#/components/schemas/FulfillmentStatus'
2286
+ - type: 'null'
2287
+ x-when: Null for ON_SITE and PICK_UP orders. Set when deliveryType is DELIVERY
2288
+ and a physical shipment or pickup is involved.
2289
+ x-see:
2290
+ decisions:
2291
+ - D34
2292
+ description: Delivery/fulfillment lifecycle (D34).
2293
+ returnStatus:
2294
+ anyOf:
2295
+ - $ref: '#/components/schemas/ReturnStatus'
2296
+ - type: 'null'
2297
+ description: Return/exchange lifecycle (D34). Null until a return or exchange
2298
+ is initiated.
2299
+ deliveryType:
2300
+ anyOf:
2301
+ - $ref: '#/components/schemas/DeliveryType'
2302
+ - type: 'null'
2303
+ x-note: Drives whether fulfillmentStatus is relevant. ON_SITE and PICK_UP
2304
+ orders typically have no fulfillmentStatus.
2305
+ description: Fulfillment channel for this order (ON_SITE, PICK_UP, DELIVERY).
2306
+ paymentMethod:
2307
+ anyOf:
2308
+ - $ref: '#/components/schemas/PaymentMethod'
2309
+ - type: 'null'
2310
+ description: Unified payment method set with African + global methods (D02).
2311
+ invoiceId:
2312
+ description: FK → Invoice document ID.
2313
+ type:
2314
+ - string
2315
+ - 'null'
2316
+ customerId:
2317
+ description: FK → Customer.id (Firestore doc ID). Used to resolve customer
2318
+ details.
2319
+ type:
2320
+ - string
2321
+ - 'null'
2322
+ customerName:
2323
+ denormalized: true
2324
+ description: (Denormalized) From Customer.name at write time.
2325
+ type:
2326
+ - string
2327
+ - 'null'
2328
+ customerEmail:
2329
+ denormalized: true
2330
+ description: (Denormalized) From Customer.email at write time. Canonical
2331
+ field per D24.
2332
+ type:
2333
+ - string
2334
+ - 'null'
2335
+ customerPhone:
2336
+ denormalized: true
2337
+ description: (Denormalized) From Customer.phone at write time. Canonical
2338
+ field per D24.
2339
+ type:
2340
+ - string
2341
+ - 'null'
2342
+ clientEmail:
2343
+ denormalized: true
2344
+ deprecated: true
2345
+ x-replaced-by: customerEmail
2346
+ description: (Denormalized) Legacy — use `customerEmail`. D24 standardized
2347
+ to customer* prefix.
2348
+ type:
2349
+ - string
2350
+ - 'null'
2351
+ clientPhoneNumber:
2352
+ denormalized: true
2353
+ deprecated: true
2354
+ x-replaced-by: customerPhone
2355
+ description: (Denormalized) Legacy — use `customerPhone`. D24 standardized
2356
+ to customer* prefix.
2357
+ type:
2358
+ - string
2359
+ - 'null'
2360
+ items:
2361
+ type:
2362
+ - array
2363
+ - 'null'
2364
+ items:
2365
+ $ref: '#/components/schemas/OrderItem'
2366
+ description: 'Line item within an Order or Sale/Purchase. Canonical fields:
2367
+ name, quantity, price, productId. Optional commerce fields (increment,
2368
+ variantId, supplierId, supplierName) and kitchen tracking fields (sentAt,
2369
+ startedCookingAt, readyAt, servedAt) are TBD/WIP pending cross-platform
2370
+ alignment.'
2371
+ amount:
2372
+ type: number
2373
+ description: Total order amount. Canonical field for the order total.
2374
+ amountPaid:
2375
+ description: Amount of `amount` paid to date. Derived from payment allocations.
2376
+ type:
2377
+ - number
2378
+ - 'null'
2379
+ total:
2380
+ deprecated: true
2381
+ x-replaced-by: amount
2382
+ x-note: Sent by Mobile only. Mirrors `amount`. Use `amount` for all new
2383
+ writes. Pending deduplication cleanup.
2384
+ description: Mobile-only legacy total field. Deprecated — use `amount`.
2385
+ type:
2386
+ - number
2387
+ - 'null'
2388
+ createdAt:
2389
+ $ref: '#/components/schemas/FirestoreTimestamp'
2390
+ description: (Read-only) Server-generated creation timestamp.
2391
+ readOnly: true
2392
+ orderDate:
2393
+ $ref: '#/components/schemas/FirestoreTimestamp'
2394
+ description: Firestore Timestamp serialized representation
2395
+ PROCESSING_ON:
2396
+ anyOf:
2397
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2398
+ - type: 'null'
2399
+ readOnly: true
2400
+ x-note: Never write this field directly. The server sets it automatically
2401
+ on status transition to PROCESSING.
2402
+ x-see:
2403
+ decisions:
2404
+ - D04
2405
+ migrations:
2406
+ - MIG-01
2407
+ description: (Read-only) Timestamp when order entered PROCESSING (D04 SCREAMING_SNAKE).
2408
+ MIG-01 renames from camelCase.
2409
+ COMPLETED_ON:
2410
+ anyOf:
2411
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2412
+ - type: 'null'
2413
+ readOnly: true
2414
+ description: (Read-only) Timestamp when order completed (D04 SCREAMING_SNAKE).
2415
+ MIG-01 renames from camelCase.
2416
+ CANCELLED_ON:
2417
+ anyOf:
2418
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2419
+ - type: 'null'
2420
+ readOnly: true
2421
+ description: (Read-only) Timestamp when order cancelled (D04 SCREAMING_SNAKE).
2422
+ MIG-01 renames from camelCase.
2423
+ cancellationReason:
2424
+ type:
2425
+ - string
2426
+ - 'null'
2427
+ shippingCarrier:
2428
+ type:
2429
+ - string
2430
+ - 'null'
2431
+ trackingNumber:
2432
+ type:
2433
+ - string
2434
+ - 'null'
2435
+ estimatedDeliveryDate:
2436
+ anyOf:
2437
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2438
+ - type: 'null'
2439
+ description: Firestore Timestamp serialized representation
2440
+ shippingCost:
2441
+ type:
2442
+ - number
2443
+ - 'null'
2444
+ paymentProofUrl:
2445
+ description: URL to uploaded payment proof image/document.
2446
+ type:
2447
+ - string
2448
+ - 'null'
2449
+ paymentProofStatus:
2450
+ anyOf:
2451
+ - $ref: '#/components/schemas/PaymentProofStatus'
2452
+ - type: 'null'
2453
+ description: Payment proof review status.
2454
+ paymentProofAddedAt:
2455
+ anyOf:
2456
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2457
+ - type: 'null'
2458
+ readOnly: true
2459
+ description: (Read-only) Timestamp when proof was uploaded.
2460
+ paymentProofAddedBy:
2461
+ description: FK → User/staff UID who uploaded the payment proof.
2462
+ type:
2463
+ - string
2464
+ - 'null'
2465
+ paymentProofReviewedAt:
2466
+ anyOf:
2467
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2468
+ - type: 'null'
2469
+ readOnly: true
2470
+ description: (Read-only) Timestamp when proof was reviewed.
2471
+ paymentProofReviewedBy:
2472
+ description: FK → User/staff UID who reviewed the payment proof.
2473
+ type:
2474
+ - string
2475
+ - 'null'
2476
+ paymentProofRejectionReason:
2477
+ type:
2478
+ - string
2479
+ - 'null'
2480
+ paymentStatusChangeReason:
2481
+ type:
2482
+ - string
2483
+ - 'null'
2484
+ paymentStatusChangedBy:
2485
+ description: FK → User/staff UID who changed payment status.
2486
+ type:
2487
+ - string
2488
+ - 'null'
2489
+ paymentStatusChangedAt:
2490
+ anyOf:
2491
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2492
+ - type: 'null'
2493
+ readOnly: true
2494
+ description: (Read-only) Timestamp of last payment status change.
2495
+ payments:
2496
+ x-note: Shape and sync rules are TBD pending IG-4 resolution. Do not build
2497
+ production logic against this field until IG-4 is closed.
2498
+ x-see:
2499
+ issues:
2500
+ - IG-4
2501
+ description: '[TBD/WIP — IG-4] Denormalized snapshots of CustomerPayments
2502
+ allocated to this order. Sync rules pending IG-4 resolution.'
2503
+ type:
2504
+ - array
2505
+ - 'null'
2506
+ items:
2507
+ type: object
2508
+ properties:
2509
+ paymentId:
2510
+ type: string
2511
+ x-immutable: true
2512
+ description: (Immutable) FK → CustomerPayment.id. Source document
2513
+ for this snapshot.
2514
+ amount:
2515
+ type: number
2516
+ description: Total payment amount.
2517
+ currency:
2518
+ type: string
2519
+ const: XOF
2520
+ description: Currency code. Locked to XOF.
2521
+ paymentMethod:
2522
+ $ref: '#/components/schemas/PaymentMethod'
2523
+ description: Payment method used (D02).
2524
+ paymentDate:
2525
+ $ref: '#/components/schemas/FirestoreTimestamp'
2526
+ description: Firestore Timestamp serialized representation
2527
+ referenceNumber:
2528
+ description: Payment reference (receipt number, transaction ID, etc.).
2529
+ type: string
2530
+ status:
2531
+ $ref: '#/components/schemas/CustomerPaymentStatus'
2532
+ description: Payment status at time of snapshot. May lag CustomerPayment.status
2533
+ until re-synced.
2534
+ required:
2535
+ - paymentId
2536
+ - amount
2537
+ - currency
2538
+ - paymentMethod
2539
+ - paymentDate
2540
+ - status
2541
+ additionalProperties: false
2542
+ description: '[TBD/WIP — IG-4] Denormalized payment snapshot. Subset of
2543
+ CustomerPayment (D22), written when a payment is allocated to this order.
2544
+ Sync rules pending IG-4 resolution.'
2545
+ totalOverridden:
2546
+ x-when: Set only by the mobile app when the user manually edits the order
2547
+ total. Dashboard and Cloud Functions should treat this field as read-only.
2548
+ x-see:
2549
+ decisions:
2550
+ - D14
2551
+ description: Mobile-only. When true, total was manually overridden by user
2552
+ (D14).
2553
+ type:
2554
+ - boolean
2555
+ - 'null'
2556
+ notes:
2557
+ type:
2558
+ - array
2559
+ - 'null'
2560
+ items:
2561
+ type: object
2562
+ properties:
2563
+ id:
2564
+ type: string
2565
+ readOnly: true
2566
+ description: (Read-only) Note ID. Server-generated.
2567
+ text:
2568
+ type: string
2569
+ createdAt:
2570
+ $ref: '#/components/schemas/FirestoreTimestamp'
2571
+ description: (Read-only) Firestore Timestamp serialized representation.
2572
+ readOnly: true
2573
+ createdBy:
2574
+ x-immutable: true
2575
+ description: (Immutable) FK → User/staff UID who created this note.
2576
+ type: string
2577
+ createdByName:
2578
+ denormalized: true
2579
+ description: (Denormalized) From User display name at write time.
2580
+ type: string
2581
+ required:
2582
+ - id
2583
+ - text
2584
+ - createdAt
2585
+ additionalProperties: false
2586
+ description: Timestamped note attached to an Order.
2587
+ additionalInfo:
2588
+ type:
2589
+ - string
2590
+ - 'null'
2591
+ appliedDiscountCode:
2592
+ type:
2593
+ - string
2594
+ - 'null'
2595
+ purchaseId:
2596
+ description: FK → Sale.id. Link to associated Sale document.
2597
+ type:
2598
+ - string
2599
+ - 'null'
2600
+ required:
2601
+ - id
2602
+ - uid
2603
+ - companyId
2604
+ - orderNumber
2605
+ - status
2606
+ - amount
2607
+ - createdAt
2608
+ - orderDate
2609
+ additionalProperties: false
2610
+ description: 'Order model (D34). Collection: companies/{companyId}/orders/{orderId}.
2611
+ Status decomposed into 4 orthogonal fields per D34: status (OrderStatus),
2612
+ paymentStatus, fulfillmentStatus, returnStatus. deliveryType determines the
2613
+ fulfillment channel. MIG-11 migrates the old flat 20-value enum.'
2614
+ OrderCreate:
2615
+ allOf:
2616
+ - $ref: '#/components/schemas/Order'
2617
+ description: Write payload for creating a new Order document. Fields marked
2618
+ `readOnly` are server-set and must not be included. Fields marked `x-immutable`
2619
+ may be set once at creation.
2620
+ required:
2621
+ - companyId
2622
+ - status
2623
+ - amount
2624
+ - orderDate
2625
+ OrderUpdate:
2626
+ allOf:
2627
+ - $ref: '#/components/schemas/Order'
2628
+ description: Write payload for partial update (PATCH) of a Order document. All
2629
+ fields optional. Fields marked `readOnly` or `x-immutable` must not be sent.
2630
+ OrderItem:
2631
+ type: object
2632
+ properties:
2633
+ name:
2634
+ type: string
2635
+ description: Display name of the product or service.
2636
+ quantity:
2637
+ type: number
2638
+ description: Number of units ordered.
2639
+ price:
2640
+ type: number
2641
+ description: Unit price. Canonical name per D11.
2642
+ productId:
2643
+ description: FK → Product document ID.
2644
+ type:
2645
+ - string
2646
+ - 'null'
2647
+ increment:
2648
+ x-note: TBD/WIP — Dashboard/Firebase only. Canonical status pending cross-platform
2649
+ alignment.
2650
+ description: Quantity step for bundle/bulk items (e.g. 5 for a 5-pack).
2651
+ Dashboard/Firebase only.
2652
+ type:
2653
+ - number
2654
+ - 'null'
2655
+ variantId:
2656
+ x-note: TBD/WIP — Dashboard/Firebase only. Canonical status pending cross-platform
2657
+ alignment.
2658
+ description: FK → ProductVariant ID within the product. Dashboard/Firebase
2659
+ only.
2660
+ type:
2661
+ - string
2662
+ - 'null'
2663
+ supplierId:
2664
+ x-note: TBD/WIP — B2B orders only. Canonical status pending cross-platform
2665
+ alignment.
2666
+ description: FK → Supplier document ID. B2B orders only.
2667
+ type:
2668
+ - string
2669
+ - 'null'
2670
+ supplierName:
2671
+ denormalized: true
2672
+ x-note: TBD/WIP — B2B orders only. Canonical status pending cross-platform
2673
+ alignment.
2674
+ description: (Denormalized) From Supplier.name at write time. B2B orders
2675
+ only.
2676
+ type:
2677
+ - string
2678
+ - 'null'
2679
+ sentAt:
2680
+ anyOf:
2681
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2682
+ - type: 'null'
2683
+ readOnly: true
2684
+ x-note: TBD/WIP — Originally from the archived Couchbase Lite (CBL) restaurant
2685
+ flow. Canonical inclusion in Firestore model is pending review.
2686
+ description: (Read-only) Timestamp when the item was sent to the kitchen.
2687
+ TBD/WIP — archived CBL flow.
2688
+ startedCookingAt:
2689
+ anyOf:
2690
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2691
+ - type: 'null'
2692
+ readOnly: true
2693
+ x-note: TBD/WIP — Originally from the archived Couchbase Lite (CBL) restaurant
2694
+ flow. Canonical inclusion in Firestore model is pending review.
2695
+ description: (Read-only) Timestamp when the kitchen started preparing this
2696
+ item. TBD/WIP — archived CBL flow.
2697
+ readyAt:
2698
+ anyOf:
2699
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2700
+ - type: 'null'
2701
+ readOnly: true
2702
+ x-note: TBD/WIP — Originally from the archived Couchbase Lite (CBL) restaurant
2703
+ flow. Canonical inclusion in Firestore model is pending review.
2704
+ description: (Read-only) Timestamp when the item was ready for pickup or
2705
+ service. TBD/WIP — archived CBL flow.
2706
+ servedAt:
2707
+ anyOf:
2708
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2709
+ - type: 'null'
2710
+ readOnly: true
2711
+ x-note: TBD/WIP — Originally from the archived Couchbase Lite (CBL) restaurant
2712
+ flow. Canonical inclusion in Firestore model is pending review.
2713
+ description: (Read-only) Timestamp when the item was served to the customer.
2714
+ TBD/WIP — archived CBL flow.
2715
+ required:
2716
+ - name
2717
+ - quantity
2718
+ - price
2719
+ additionalProperties: false
2720
+ description: 'Line item within an Order or Sale/Purchase. Canonical fields:
2721
+ name, quantity, price, productId. Optional commerce fields (increment, variantId,
2722
+ supplierId, supplierName) and kitchen tracking fields (sentAt, startedCookingAt,
2723
+ readyAt, servedAt) are TBD/WIP pending cross-platform alignment.'
2724
+ OrderItemCreate:
2725
+ allOf:
2726
+ - $ref: '#/components/schemas/OrderItem'
2727
+ description: Write payload for creating a new OrderItem document. Fields marked
2728
+ `readOnly` are server-set and must not be included. Fields marked `x-immutable`
2729
+ may be set once at creation.
2730
+ required:
2731
+ - name
2732
+ - quantity
2733
+ - price
2734
+ OrderItemUpdate:
2735
+ allOf:
2736
+ - $ref: '#/components/schemas/OrderItem'
2737
+ description: Write payload for partial update (PATCH) of a OrderItem document.
2738
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
2739
+ sent.
2740
+ Sale:
2741
+ type: object
2742
+ properties:
2743
+ id:
2744
+ readOnly: true
2745
+ description: '(Read-only) Firestore document ID. Note: optional in current
2746
+ schema — some legacy docs may lack this field.'
2747
+ type:
2748
+ - string
2749
+ - 'null'
2750
+ companyId:
2751
+ x-immutable: true
2752
+ description: '(Immutable) FK → Company document ID. Note: optional in current
2753
+ schema — should be required (see ID consistency audit).'
2754
+ type:
2755
+ - string
2756
+ - 'null'
2757
+ customerId:
2758
+ description: FK → Customer.id (Firestore doc ID).
2759
+ type:
2760
+ - string
2761
+ - 'null'
2762
+ customerName:
2763
+ denormalized: true
2764
+ description: (Denormalized) From Customer.name at write time.
2765
+ type:
2766
+ - string
2767
+ - 'null'
2768
+ amount:
2769
+ type:
2770
+ - number
2771
+ - 'null'
2772
+ items:
2773
+ description: Line items. Reuses Order item schema.
2774
+ type:
2775
+ - array
2776
+ - 'null'
2777
+ items:
2778
+ $ref: '#/components/schemas/OrderItem'
2779
+ description: 'Line item within an Order or Sale/Purchase. Canonical fields:
2780
+ name, quantity, price, productId. Optional commerce fields (increment,
2781
+ variantId, supplierId, supplierName) and kitchen tracking fields (sentAt,
2782
+ startedCookingAt, readyAt, servedAt) are TBD/WIP pending cross-platform
2783
+ alignment.'
2784
+ imageUrl:
2785
+ type:
2786
+ - string
2787
+ - 'null'
2788
+ notes:
2789
+ type:
2790
+ - string
2791
+ - 'null'
2792
+ orderId:
2793
+ description: FK → Order.id. Link to associated Order document.
2794
+ type:
2795
+ - string
2796
+ - 'null'
2797
+ bookingId:
2798
+ description: FK → Booking.id. Link to associated Booking document (IG-12).
2799
+ type:
2800
+ - string
2801
+ - 'null'
2802
+ purchaseDate:
2803
+ $ref: '#/components/schemas/FirestoreTimestamp'
2804
+ description: Firestore Timestamp serialized representation
2805
+ createdAt:
2806
+ anyOf:
2807
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2808
+ - type: 'null'
2809
+ readOnly: true
2810
+ description: (Read-only) Server-generated creation timestamp.
2811
+ required:
2812
+ - purchaseDate
2813
+ additionalProperties: false
2814
+ description: 'Sale/Purchase model (D06). Collection: companies/{companyId}/purchases/{purchaseId}.
2815
+ Code alias: SalesService. [Deprecated path: customers/{custId}/purchases/{purchaseId}
2816
+ — migrate to company-wide path per MIG-04/IG-5]'
2817
+ SaleCreate:
2818
+ allOf:
2819
+ - $ref: '#/components/schemas/Sale'
2820
+ description: Write payload for creating a new Sale document. Fields marked `readOnly`
2821
+ are server-set and must not be included. Fields marked `x-immutable` may be
2822
+ set once at creation.
2823
+ required:
2824
+ - purchaseDate
2825
+ SaleUpdate:
2826
+ allOf:
2827
+ - $ref: '#/components/schemas/Sale'
2828
+ description: Write payload for partial update (PATCH) of a Sale document. All
2829
+ fields optional. Fields marked `readOnly` or `x-immutable` must not be sent.
2830
+ Ticket:
2831
+ type: object
2832
+ properties:
2833
+ id:
2834
+ type: string
2835
+ readOnly: true
2836
+ description: '(Read-only) Firestore document ID. Note: Ticket does not have
2837
+ a uid field.'
2838
+ eventId:
2839
+ type: string
2840
+ x-immutable: true
2841
+ description: (Immutable) FK → Event.id. Parent event this ticket belongs
2842
+ to. Set at creation.
2843
+ companyId:
2844
+ type: string
2845
+ x-immutable: true
2846
+ denormalized: true
2847
+ description: (Immutable, Denormalized) FK → Company document ID. Denormalized
2848
+ from parent Event for direct queries.
2849
+ customerId:
2850
+ x-see:
2851
+ decisions:
2852
+ - D29
2853
+ x-note: Added additively per D29. Denormalized customerName/email/phone
2854
+ are kept for backwards compatibility. Null on tickets created before D29
2855
+ was applied.
2856
+ description: FK → Customer.id. Optional structured link to Customer document
2857
+ (D29). Added additively alongside denormalized fields.
2858
+ type:
2859
+ - string
2860
+ - 'null'
2861
+ customerName:
2862
+ denormalized: true
2863
+ description: (Denormalized) From Customer.name at write time.
2864
+ type:
2865
+ - string
2866
+ - 'null'
2867
+ customerEmail:
2868
+ denormalized: true
2869
+ description: (Denormalized) From Customer.email at write time.
2870
+ type:
2871
+ - string
2872
+ - 'null'
2873
+ customerPhone:
2874
+ denormalized: true
2875
+ description: (Denormalized) From Customer.phone at write time.
2876
+ type:
2877
+ - string
2878
+ - 'null'
2879
+ status:
2880
+ $ref: '#/components/schemas/TicketStatus'
2881
+ description: Ticket lifecycle (D32). SCREAMING_SNAKE per D04. MIG-10 migrates
2882
+ legacy lowercase values.
2883
+ usedAt:
2884
+ anyOf:
2885
+ - $ref: '#/components/schemas/FirestoreTimestamp'
2886
+ - type: 'null'
2887
+ readOnly: true
2888
+ description: (Read-only) Timestamp when ticket was scanned/used. Set by
2889
+ scan operation.
2890
+ usedBy:
2891
+ readOnly: true
2892
+ description: (Read-only) FK → User/staff UID who scanned the ticket.
2893
+ type:
2894
+ - string
2895
+ - 'null'
2896
+ usedByName:
2897
+ readOnly: true
2898
+ denormalized: true
2899
+ description: (Read-only, Denormalized) From User display name at scan time.
2900
+ type:
2901
+ - string
2902
+ - 'null'
2903
+ price:
2904
+ type:
2905
+ - number
2906
+ - 'null'
2907
+ notes:
2908
+ type:
2909
+ - string
2910
+ - 'null'
2911
+ createdAt:
2912
+ $ref: '#/components/schemas/FirestoreTimestamp'
2913
+ description: (Read-only) Server-generated creation timestamp.
2914
+ readOnly: true
2915
+ updatedAt:
2916
+ $ref: '#/components/schemas/FirestoreTimestamp'
2917
+ description: (Read-only) Server-generated update timestamp.
2918
+ readOnly: true
2919
+ createdBy:
2920
+ x-immutable: true
2921
+ description: (Immutable) FK → User/staff UID who created this ticket.
2922
+ type:
2923
+ - string
2924
+ - 'null'
2925
+ required:
2926
+ - id
2927
+ - eventId
2928
+ - companyId
2929
+ - status
2930
+ - createdAt
2931
+ - updatedAt
2932
+ additionalProperties: false
2933
+ description: 'Ticket model (D32). Collection: companies/{companyId}/events/{eventId}/tickets/{ticketId}.
2934
+ Mobile-only today; Dashboard in Wave 4.'
2935
+ TicketCreate:
2936
+ allOf:
2937
+ - $ref: '#/components/schemas/Ticket'
2938
+ description: Write payload for creating a new Ticket document. Fields marked
2939
+ `readOnly` are server-set and must not be included. Fields marked `x-immutable`
2940
+ may be set once at creation.
2941
+ required:
2942
+ - eventId
2943
+ - companyId
2944
+ - status
2945
+ TicketUpdate:
2946
+ allOf:
2947
+ - $ref: '#/components/schemas/Ticket'
2948
+ description: Write payload for partial update (PATCH) of a Ticket document.
2949
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
2950
+ sent.
2951
+ PaymentSummary:
2952
+ type: object
2953
+ properties:
2954
+ id:
2955
+ type: string
2956
+ readOnly: true
2957
+ description: (Read-only) Firestore document ID. Matches the period key (e.g.
2958
+ "2026-03-09" for DAILY, "2026-03" for MONTHLY).
2959
+ companyId:
2960
+ type: string
2961
+ x-immutable: true
2962
+ description: (Immutable) FK → Company document ID. Scopes all queries.
2963
+ period:
2964
+ type: string
2965
+ x-note: 'Document ID matches this value. Format: YYYY-MM-DD (DAILY), YYYY-Www
2966
+ (WEEKLY), YYYY-MM (MONTHLY).'
2967
+ description: Period key. Determines the document ID and the time window
2968
+ covered.
2969
+ periodType:
2970
+ type: string
2971
+ enum:
2972
+ - DAILY
2973
+ - WEEKLY
2974
+ - MONTHLY
2975
+ description: Granularity of this summary document.
2976
+ paymentsByMethod:
2977
+ type: object
2978
+ propertyNames:
2979
+ type: string
2980
+ enum:
2981
+ - CASH
2982
+ - CREDIT_CARD
2983
+ - ORANGE_MONEY
2984
+ - WAVE
2985
+ - MTN_MONEY
2986
+ - MOOV_MONEY
2987
+ - BANK_TRANSFER
2988
+ - PAYPAL
2989
+ - STRIPE
2990
+ - OTHER
2991
+ additionalProperties:
2992
+ type: object
2993
+ properties:
2994
+ total:
2995
+ type: number
2996
+ description: Sum of payment amounts for this method in the period.
2997
+ Updated via FieldValue.increment().
2998
+ count:
2999
+ type: integer
3000
+ minimum: -9007199254740991
3001
+ maximum: 9007199254740991
3002
+ description: Number of individual payments for this method in the
3003
+ period. Updated via FieldValue.increment().
3004
+ required:
3005
+ - total
3006
+ - count
3007
+ additionalProperties: false
3008
+ required:
3009
+ - CASH
3010
+ - CREDIT_CARD
3011
+ - ORANGE_MONEY
3012
+ - WAVE
3013
+ - MTN_MONEY
3014
+ - MOOV_MONEY
3015
+ - BANK_TRANSFER
3016
+ - PAYPAL
3017
+ - STRIPE
3018
+ - OTHER
3019
+ x-note: Keys are PaymentMethod enum values (CASH, WAVE, ORANGE_MONEY, etc.).
3020
+ Missing keys imply zero for that method. Updated incrementally via FieldValue.increment().
3021
+ x-see:
3022
+ decisions:
3023
+ - D02
3024
+ - D12
3025
+ description: Payment totals broken down by PaymentMethod. Each entry holds
3026
+ the total amount and transaction count for the period.
3027
+ grandTotal:
3028
+ type: number
3029
+ readOnly: true
3030
+ description: (Read-only) Sum of all paymentsByMethod[*].total. Server-calculated.
3031
+ totalCount:
3032
+ type: integer
3033
+ minimum: -9007199254740991
3034
+ maximum: 9007199254740991
3035
+ readOnly: true
3036
+ description: (Read-only) Sum of all paymentsByMethod[*].count. Server-calculated.
3037
+ currency:
3038
+ type: string
3039
+ const: XOF
3040
+ description: Currency code. Locked to XOF (West African CFA franc).
3041
+ lastUpdatedAt:
3042
+ readOnly: true
3043
+ description: (Read-only) Server timestamp of the last increment write.
3044
+ type: object
3045
+ properties:
3046
+ _seconds:
3047
+ type: integer
3048
+ minimum: -9007199254740991
3049
+ maximum: 9007199254740991
3050
+ _nanoseconds:
3051
+ type: integer
3052
+ minimum: -9007199254740991
3053
+ maximum: 9007199254740991
3054
+ required:
3055
+ - _seconds
3056
+ - _nanoseconds
3057
+ additionalProperties: false
3058
+ createdAt:
3059
+ type: object
3060
+ properties:
3061
+ _seconds:
3062
+ type: integer
3063
+ minimum: -9007199254740991
3064
+ maximum: 9007199254740991
3065
+ _nanoseconds:
3066
+ type: integer
3067
+ minimum: -9007199254740991
3068
+ maximum: 9007199254740991
3069
+ required:
3070
+ - _seconds
3071
+ - _nanoseconds
3072
+ additionalProperties: false
3073
+ description: (Read-only) Server-generated creation timestamp.
3074
+ readOnly: true
3075
+ required:
3076
+ - id
3077
+ - companyId
3078
+ - period
3079
+ - periodType
3080
+ - paymentsByMethod
3081
+ - grandTotal
3082
+ - totalCount
3083
+ - currency
3084
+ - createdAt
3085
+ additionalProperties: false
3086
+ description: '[PROPOSED] Aggregated payment totals by method for a given period.
3087
+ Collection: companies/{companyId}/paymentSummaries/{period}. Server-owned
3088
+ — all fields are set by Cloud Function triggers on Order.payments[] and CustomerPayment
3089
+ writes. Clients must never write to this collection.'
3090
+ x-status: proposed