@ingenx-io/valets-schema-mcp-server 0.1.4 → 0.1.6

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 (54) hide show
  1. package/data/docs/collections/firestore-paths.md +42 -8
  2. package/data/docs/enums/attention-status.md +1 -1
  3. package/data/docs/enums/booking-status.md +1 -1
  4. package/data/docs/enums/customer-payment-status.md +1 -1
  5. package/data/docs/enums/customer-payment-target-type.md +1 -1
  6. package/data/docs/enums/delivery-type.md +1 -1
  7. package/data/docs/enums/deployment-link-type.md +26 -0
  8. package/data/docs/enums/event-status.md +2 -2
  9. package/data/docs/enums/fulfillment-status.md +2 -2
  10. package/data/docs/enums/loyalty-transaction-type.md +2 -2
  11. package/data/docs/enums/order-status.md +2 -2
  12. package/data/docs/enums/payment-method.md +2 -2
  13. package/data/docs/enums/payment-proof-status.md +2 -2
  14. package/data/docs/enums/payment-status.md +2 -2
  15. package/data/docs/enums/pending-issue.md +2 -2
  16. package/data/docs/enums/return-status.md +2 -2
  17. package/data/docs/enums/session-status.md +2 -2
  18. package/data/docs/enums/site-status.md +24 -0
  19. package/data/docs/enums/stocktake-frequency.md +24 -0
  20. package/data/docs/enums/stocktake-item-status.md +24 -0
  21. package/data/docs/enums/stocktake-status.md +24 -0
  22. package/data/docs/enums/ticket-status.md +2 -2
  23. package/data/docs/index.md +14 -3
  24. package/data/docs/models/allowed-user.md +1 -1
  25. package/data/docs/models/analytics-backfill.md +398 -0
  26. package/data/docs/models/analytics-daily.md +351 -0
  27. package/data/docs/models/analytics-event.md +2 -2
  28. package/data/docs/models/analytics-hourly.md +372 -0
  29. package/data/docs/models/booking-version.md +2 -2
  30. package/data/docs/models/booking.md +2 -2
  31. package/data/docs/models/customer-payment-allocation.md +2 -2
  32. package/data/docs/models/customer-payment.md +2 -2
  33. package/data/docs/models/customer.md +2 -2
  34. package/data/docs/models/event.md +2 -2
  35. package/data/docs/models/loyalty-config.md +2 -2
  36. package/data/docs/models/loyalty-reward.md +2 -2
  37. package/data/docs/models/loyalty-status.md +2 -2
  38. package/data/docs/models/loyalty-transaction.md +2 -2
  39. package/data/docs/models/magic-link-request.md +2 -2
  40. package/data/docs/models/metrics-current.md +2 -2
  41. package/data/docs/models/metrics-daily.md +2 -2
  42. package/data/docs/models/metrics-monthly.md +2 -2
  43. package/data/docs/models/order-item.md +2 -2
  44. package/data/docs/models/order.md +248 -220
  45. package/data/docs/models/sale.md +2 -2
  46. package/data/docs/models/site-payment.md +2 -2
  47. package/data/docs/models/site.md +561 -0
  48. package/data/docs/models/stocktake-item.md +500 -0
  49. package/data/docs/models/stocktake.md +649 -0
  50. package/data/docs/models/ticket.md +2 -2
  51. package/data/static/llms.txt +309 -2
  52. package/data/static/openapi.yaml +972 -0
  53. package/data/static/schemas.json +1249 -77
  54. package/package.json +1 -1
@@ -69,6 +69,17 @@ components:
69
69
  comes to the business (ON_SITE), collects their order themselves (PICK_UP),
70
70
  or receives a physical delivery (DELIVERY). Drives whether fulfillmentStatus
71
71
  is relevant.
72
+ DeploymentLinkType:
73
+ type: string
74
+ enum:
75
+ - web
76
+ - mobile
77
+ - pwa
78
+ - app-store
79
+ - play-store
80
+ - other
81
+ description: Category of a Site deployment link (D41). Used for UI icon/labelling
82
+ and deep-link handling.
72
83
  EventStatus:
73
84
  type: string
74
85
  enum:
@@ -184,6 +195,39 @@ components:
184
195
  - CANCELLED
185
196
  description: Per-date/per-slot booking session status (D19). Dashboard is sole
186
197
  writer; Mobile is read-only.
198
+ SiteStatus:
199
+ type: string
200
+ enum:
201
+ - ACTIVE
202
+ - INACTIVE
203
+ - EXPIRED
204
+ - ARCHIVED
205
+ description: Lifecycle status for a Site (D41). Drives whether the site is reachable
206
+ and whether analytics/payments flow.
207
+ StocktakeFrequency:
208
+ type: string
209
+ enum:
210
+ - DAILY
211
+ - WEEKLY
212
+ - MONTHLY
213
+ - AD_HOC
214
+ description: Recurrence cadence of a Stocktake session (GH#29 §4).
215
+ StocktakeItemStatus:
216
+ type: string
217
+ enum:
218
+ - PENDING
219
+ - COUNTED
220
+ - VERIFIED
221
+ - ADJUSTED
222
+ description: Per-line workflow status inside a Stocktake (GH#29 §4, audit Q21).
223
+ StocktakeStatus:
224
+ type: string
225
+ enum:
226
+ - PENDING
227
+ - IN_PROGRESS
228
+ - COMPLETED
229
+ - CANCELLED
230
+ description: Lifecycle status of a Stocktake session (GH#29 §3, §4).
187
231
  TicketStatus:
188
232
  type: string
189
233
  enum:
@@ -281,6 +325,260 @@ components:
281
325
  description: Write payload for partial update (PATCH) of a AllowedUser document.
282
326
  All fields optional. Fields marked `readOnly` or `x-immutable` must not be
283
327
  sent.
328
+ AnalyticsBackfill:
329
+ type: object
330
+ properties:
331
+ id:
332
+ readOnly: true
333
+ description: (Read-only) Firestore document ID, auto-generated.
334
+ type:
335
+ - string
336
+ - 'null'
337
+ companyId:
338
+ type: string
339
+ x-immutable: true
340
+ description: (Immutable) FK → Company document ID.
341
+ siteId:
342
+ type: string
343
+ x-immutable: true
344
+ description: (Immutable) FK → Site document ID (D40).
345
+ status:
346
+ type: string
347
+ enum:
348
+ - pending
349
+ - running
350
+ - completed
351
+ - failed
352
+ - cancelled
353
+ description: Run status.
354
+ from:
355
+ type: string
356
+ x-immutable: true
357
+ description: (Immutable) Inclusive start date (`YYYY-MM-DD`, UTC).
358
+ to:
359
+ type: string
360
+ x-immutable: true
361
+ description: (Immutable) Inclusive end date (`YYYY-MM-DD`, UTC).
362
+ dryRun:
363
+ type: boolean
364
+ x-immutable: true
365
+ description: (Immutable) When true, rollup writes are skipped — only source
366
+ counts are returned for diffing.
367
+ processedDates:
368
+ type: array
369
+ items:
370
+ type: string
371
+ description: Ordered list of `YYYY-MM-DD` dates already processed by this
372
+ run.
373
+ errors:
374
+ type: array
375
+ items:
376
+ type: object
377
+ properties:
378
+ date:
379
+ type: string
380
+ description: '`YYYY-MM-DD` date that failed.'
381
+ message:
382
+ type: string
383
+ description: Error message captured at failure.
384
+ at:
385
+ $ref: '#/components/schemas/FirestoreTimestamp'
386
+ description: When the error occurred.
387
+ required:
388
+ - date
389
+ - message
390
+ - at
391
+ additionalProperties: false
392
+ description: Per-date errors collected during the run.
393
+ triggeredBy:
394
+ type: string
395
+ x-immutable: true
396
+ description: (Immutable) UID of the admin who triggered the backfill.
397
+ startedAt:
398
+ $ref: '#/components/schemas/FirestoreTimestamp'
399
+ description: (Read-only) When the run actually started.
400
+ readOnly: true
401
+ completedAt:
402
+ anyOf:
403
+ - $ref: '#/components/schemas/FirestoreTimestamp'
404
+ - type: 'null'
405
+ readOnly: true
406
+ description: (Read-only) When the run reached a terminal status.
407
+ required:
408
+ - companyId
409
+ - siteId
410
+ - status
411
+ - from
412
+ - to
413
+ - dryRun
414
+ - processedDates
415
+ - errors
416
+ - triggeredBy
417
+ - startedAt
418
+ additionalProperties: false
419
+ description: 'AnalyticsBackfill run (D42 / ING-304). Collection: companies/{companyId}/sites/{siteId}/analytics_backfills/{runId}.
420
+ Tracks admin-triggered rollup backfill jobs; supports dry-run.'
421
+ AnalyticsBackfillCreate:
422
+ allOf:
423
+ - $ref: '#/components/schemas/AnalyticsBackfill'
424
+ description: Write payload for creating a new AnalyticsBackfill document. Fields
425
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
426
+ may be set once at creation.
427
+ required:
428
+ - companyId
429
+ - siteId
430
+ - status
431
+ - from
432
+ - to
433
+ - dryRun
434
+ - processedDates
435
+ - errors
436
+ - triggeredBy
437
+ AnalyticsBackfillUpdate:
438
+ allOf:
439
+ - $ref: '#/components/schemas/AnalyticsBackfill'
440
+ description: Write payload for partial update (PATCH) of a AnalyticsBackfill
441
+ document. All fields optional. Fields marked `readOnly` or `x-immutable` must
442
+ not be sent.
443
+ AnalyticsDaily:
444
+ type: object
445
+ properties:
446
+ totalEvents:
447
+ type: integer
448
+ minimum: -9007199254740991
449
+ maximum: 9007199254740991
450
+ description: Total event count within the bucket.
451
+ pageViews:
452
+ type: integer
453
+ minimum: -9007199254740991
454
+ maximum: 9007199254740991
455
+ description: Count of `page_view` + `screen_view` events.
456
+ sessions:
457
+ type: integer
458
+ minimum: -9007199254740991
459
+ maximum: 9007199254740991
460
+ description: Distinct session count within the bucket (by `sessionId`).
461
+ uniqueUsers:
462
+ type: integer
463
+ minimum: -9007199254740991
464
+ maximum: 9007199254740991
465
+ description: Distinct identified `userId` count. Anonymous sessions (userId=null)
466
+ are excluded.
467
+ anonymousSessions:
468
+ type: integer
469
+ minimum: -9007199254740991
470
+ maximum: 9007199254740991
471
+ description: Distinct session count where `userId` was null for the entire
472
+ session.
473
+ orders:
474
+ type: integer
475
+ minimum: -9007199254740991
476
+ maximum: 9007199254740991
477
+ description: Count of `order_submitted` events (or Order documents scoped
478
+ to this site, D43) within the bucket.
479
+ paymentsCompleted:
480
+ type: integer
481
+ minimum: -9007199254740991
482
+ maximum: 9007199254740991
483
+ description: Count of `payment_completed` events within the bucket.
484
+ paymentsFailed:
485
+ type: integer
486
+ minimum: -9007199254740991
487
+ maximum: 9007199254740991
488
+ description: Count of `payment_failed` events within the bucket.
489
+ errors:
490
+ type: integer
491
+ minimum: -9007199254740991
492
+ maximum: 9007199254740991
493
+ description: Count of `error_occurred` + `exception_caught` events within
494
+ the bucket.
495
+ eventCounts:
496
+ type: object
497
+ propertyNames:
498
+ type: string
499
+ additionalProperties:
500
+ type: integer
501
+ minimum: -9007199254740991
502
+ maximum: 9007199254740991
503
+ description: Per-event-name counts — key is the event name (canonical or
504
+ custom), value is the count within the bucket.
505
+ id:
506
+ readOnly: true
507
+ description: (Read-only) Firestore document ID = `YYYY-MM-DD`.
508
+ type:
509
+ - string
510
+ - 'null'
511
+ companyId:
512
+ type: string
513
+ x-immutable: true
514
+ description: (Immutable) FK → Company document ID.
515
+ siteId:
516
+ type: string
517
+ x-immutable: true
518
+ description: (Immutable) FK → Site document ID (D40).
519
+ date:
520
+ type: string
521
+ x-immutable: true
522
+ description: (Immutable) `YYYY-MM-DD` UTC — matches document ID.
523
+ computedAt:
524
+ $ref: '#/components/schemas/FirestoreTimestamp'
525
+ description: (Read-only) When this rollup was last (re)computed. Updated
526
+ on every set/merge.
527
+ readOnly: true
528
+ sourceEventCount:
529
+ readOnly: true
530
+ description: (Read-only, Optional) Total source events scanned when producing
531
+ this rollup. Useful for dry-run diffing.
532
+ type:
533
+ - integer
534
+ - 'null'
535
+ minimum: -9007199254740991
536
+ maximum: 9007199254740991
537
+ required:
538
+ - totalEvents
539
+ - pageViews
540
+ - sessions
541
+ - uniqueUsers
542
+ - anonymousSessions
543
+ - orders
544
+ - paymentsCompleted
545
+ - paymentsFailed
546
+ - errors
547
+ - eventCounts
548
+ - companyId
549
+ - siteId
550
+ - date
551
+ - computedAt
552
+ additionalProperties: false
553
+ description: 'AnalyticsDaily rollup (D42 / ING-304). Collection: companies/{companyId}/sites/{siteId}/analytics_daily/{YYYY-MM-DD}.
554
+ Idempotent set/merge — reruns overwrite. Fall back to raw analytics_events
555
+ for uncovered slices.'
556
+ AnalyticsDailyCreate:
557
+ allOf:
558
+ - $ref: '#/components/schemas/AnalyticsDaily'
559
+ description: Write payload for creating a new AnalyticsDaily document. Fields
560
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
561
+ may be set once at creation.
562
+ required:
563
+ - totalEvents
564
+ - pageViews
565
+ - sessions
566
+ - uniqueUsers
567
+ - anonymousSessions
568
+ - orders
569
+ - paymentsCompleted
570
+ - paymentsFailed
571
+ - errors
572
+ - eventCounts
573
+ - companyId
574
+ - siteId
575
+ - date
576
+ AnalyticsDailyUpdate:
577
+ allOf:
578
+ - $ref: '#/components/schemas/AnalyticsDaily'
579
+ description: Write payload for partial update (PATCH) of a AnalyticsDaily document.
580
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
581
+ sent.
284
582
  AnalyticsEvent:
285
583
  type: object
286
584
  properties:
@@ -448,6 +746,151 @@ components:
448
746
  description: Write payload for partial update (PATCH) of a AnalyticsEvent document.
449
747
  All fields optional. Fields marked `readOnly` or `x-immutable` must not be
450
748
  sent.
749
+ AnalyticsHourly:
750
+ type: object
751
+ properties:
752
+ totalEvents:
753
+ type: integer
754
+ minimum: -9007199254740991
755
+ maximum: 9007199254740991
756
+ description: Total event count within the bucket.
757
+ pageViews:
758
+ type: integer
759
+ minimum: -9007199254740991
760
+ maximum: 9007199254740991
761
+ description: Count of `page_view` + `screen_view` events.
762
+ sessions:
763
+ type: integer
764
+ minimum: -9007199254740991
765
+ maximum: 9007199254740991
766
+ description: Distinct session count within the bucket (by `sessionId`).
767
+ uniqueUsers:
768
+ type: integer
769
+ minimum: -9007199254740991
770
+ maximum: 9007199254740991
771
+ description: Distinct identified `userId` count. Anonymous sessions (userId=null)
772
+ are excluded.
773
+ anonymousSessions:
774
+ type: integer
775
+ minimum: -9007199254740991
776
+ maximum: 9007199254740991
777
+ description: Distinct session count where `userId` was null for the entire
778
+ session.
779
+ orders:
780
+ type: integer
781
+ minimum: -9007199254740991
782
+ maximum: 9007199254740991
783
+ description: Count of `order_submitted` events (or Order documents scoped
784
+ to this site, D43) within the bucket.
785
+ paymentsCompleted:
786
+ type: integer
787
+ minimum: -9007199254740991
788
+ maximum: 9007199254740991
789
+ description: Count of `payment_completed` events within the bucket.
790
+ paymentsFailed:
791
+ type: integer
792
+ minimum: -9007199254740991
793
+ maximum: 9007199254740991
794
+ description: Count of `payment_failed` events within the bucket.
795
+ errors:
796
+ type: integer
797
+ minimum: -9007199254740991
798
+ maximum: 9007199254740991
799
+ description: Count of `error_occurred` + `exception_caught` events within
800
+ the bucket.
801
+ eventCounts:
802
+ type: object
803
+ propertyNames:
804
+ type: string
805
+ additionalProperties:
806
+ type: integer
807
+ minimum: -9007199254740991
808
+ maximum: 9007199254740991
809
+ description: Per-event-name counts — key is the event name (canonical or
810
+ custom), value is the count within the bucket.
811
+ id:
812
+ readOnly: true
813
+ description: (Read-only) Firestore document ID = `YYYY-MM-DD-HH`.
814
+ type:
815
+ - string
816
+ - 'null'
817
+ companyId:
818
+ type: string
819
+ x-immutable: true
820
+ description: (Immutable) FK → Company document ID.
821
+ siteId:
822
+ type: string
823
+ x-immutable: true
824
+ description: (Immutable) FK → Site document ID (D40).
825
+ date:
826
+ type: string
827
+ x-immutable: true
828
+ description: (Immutable) `YYYY-MM-DD` UTC for the bucket day.
829
+ hour:
830
+ type: integer
831
+ minimum: -9007199254740991
832
+ maximum: 9007199254740991
833
+ x-immutable: true
834
+ description: (Immutable) UTC hour of day, 0–23.
835
+ computedAt:
836
+ $ref: '#/components/schemas/FirestoreTimestamp'
837
+ description: (Read-only) When this rollup was last (re)computed.
838
+ readOnly: true
839
+ sourceEventCount:
840
+ readOnly: true
841
+ description: (Read-only, Optional) Total source events scanned when producing
842
+ this rollup.
843
+ type:
844
+ - integer
845
+ - 'null'
846
+ minimum: -9007199254740991
847
+ maximum: 9007199254740991
848
+ required:
849
+ - totalEvents
850
+ - pageViews
851
+ - sessions
852
+ - uniqueUsers
853
+ - anonymousSessions
854
+ - orders
855
+ - paymentsCompleted
856
+ - paymentsFailed
857
+ - errors
858
+ - eventCounts
859
+ - companyId
860
+ - siteId
861
+ - date
862
+ - hour
863
+ - computedAt
864
+ additionalProperties: false
865
+ description: 'AnalyticsHourly rollup (D42 / ING-304). Collection: companies/{companyId}/sites/{siteId}/analytics_hourly/{YYYY-MM-DD-HH}.
866
+ Scheduled-CF writer; idempotent set/merge.'
867
+ AnalyticsHourlyCreate:
868
+ allOf:
869
+ - $ref: '#/components/schemas/AnalyticsHourly'
870
+ description: Write payload for creating a new AnalyticsHourly document. Fields
871
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
872
+ may be set once at creation.
873
+ required:
874
+ - totalEvents
875
+ - pageViews
876
+ - sessions
877
+ - uniqueUsers
878
+ - anonymousSessions
879
+ - orders
880
+ - paymentsCompleted
881
+ - paymentsFailed
882
+ - errors
883
+ - eventCounts
884
+ - companyId
885
+ - siteId
886
+ - date
887
+ - hour
888
+ AnalyticsHourlyUpdate:
889
+ allOf:
890
+ - $ref: '#/components/schemas/AnalyticsHourly'
891
+ description: Write payload for partial update (PATCH) of a AnalyticsHourly document.
892
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
893
+ sent.
451
894
  Booking:
452
895
  type: object
453
896
  properties:
@@ -2579,6 +3022,23 @@ components:
2579
3022
  type: string
2580
3023
  x-immutable: true
2581
3024
  description: (Immutable) FK → Company document ID. Scopes all queries.
3025
+ siteId:
3026
+ x-immutable: true
3027
+ x-note: 'D43 / ADR-003: optional site attribution. Absent/null means company-wide
3028
+ (legacy). When set, the order is attributed to a specific site for analytics
3029
+ and site-scoped dashboards. No migration — existing orders keep null.
3030
+ Flat path; Order continues to live at `companies/{companyId}/orders/{orderId}`.'
3031
+ x-see:
3032
+ decisions:
3033
+ - D43
3034
+ x-when: 'Set at order creation when the client knows which site the order
3035
+ originated from (e.g. SR Single, Lifesense). Leave null for legacy/company-wide
3036
+ orders. Composite index: (companyId, siteId, createdAt).'
3037
+ description: (Immutable, Optional) FK → Site document ID (D43 / ADR-003).
3038
+ null = company-wide.
3039
+ type:
3040
+ - string
3041
+ - 'null'
2582
3042
  orderNumber:
2583
3043
  type: string
2584
3044
  readOnly: true
@@ -3201,6 +3661,187 @@ components:
3201
3661
  - $ref: '#/components/schemas/Sale'
3202
3662
  description: Write payload for partial update (PATCH) of a Sale document. All
3203
3663
  fields optional. Fields marked `readOnly` or `x-immutable` must not be sent.
3664
+ Site:
3665
+ type: object
3666
+ properties:
3667
+ id:
3668
+ readOnly: true
3669
+ description: (Read-only) Firestore document ID, auto-generated.
3670
+ type:
3671
+ - string
3672
+ - 'null'
3673
+ companyId:
3674
+ type: string
3675
+ x-immutable: true
3676
+ description: (Immutable) FK → Company document ID. Scopes all site sub-collections.
3677
+ name:
3678
+ type: string
3679
+ description: Human-readable site name shown in dashboards.
3680
+ description:
3681
+ description: Optional freeform description.
3682
+ type:
3683
+ - string
3684
+ - 'null'
3685
+ status:
3686
+ $ref: '#/components/schemas/SiteStatus'
3687
+ description: Lifecycle status (D41). Clients filter by ACTIVE.
3688
+ x-note: ACTIVE = live and serving traffic. INACTIVE = intentionally disabled
3689
+ by operators. EXPIRED = past expiresAt (derived when isExpired is true;
3690
+ may also be stored explicitly). ARCHIVED = soft-deleted; retained for
3691
+ audit but not listed by default.
3692
+ x-see:
3693
+ decisions:
3694
+ - D41
3695
+ x-when: Written by operators via dashboard or by server triggers (expiration).
3696
+ Clients filter by ACTIVE.
3697
+ deploymentLinks:
3698
+ type: array
3699
+ items:
3700
+ type: object
3701
+ properties:
3702
+ label:
3703
+ type: string
3704
+ description: Human-readable label shown in dashboards (e.g. "Production",
3705
+ "Staging").
3706
+ url:
3707
+ type: string
3708
+ description: Absolute URL or store link.
3709
+ type:
3710
+ $ref: '#/components/schemas/DeploymentLinkType'
3711
+ description: Link category — drives icon/handler selection.
3712
+ x-note: Lowercase by convention — deployment link types are display-oriented
3713
+ labels, not lifecycle states.
3714
+ x-see:
3715
+ decisions:
3716
+ - D41
3717
+ isPrimary:
3718
+ description: If true, this link is used as the canonical deployment
3719
+ URL for the Site.
3720
+ type: boolean
3721
+ required:
3722
+ - label
3723
+ - url
3724
+ - type
3725
+ additionalProperties: false
3726
+ description: Deployment link entry embedded on Site.deploymentLinks[]
3727
+ (D41).
3728
+ description: Ordered list of deployment URLs (web, mobile, PWA, store links).
3729
+ expiresAt:
3730
+ anyOf:
3731
+ - $ref: '#/components/schemas/FirestoreTimestamp'
3732
+ - type: 'null'
3733
+ description: Optional expiration timestamp. When set and elapsed, `isExpired`
3734
+ flips true and status typically moves to EXPIRED.
3735
+ isExpired:
3736
+ readOnly: true
3737
+ description: (Read-only) Derived — true when `expiresAt` is in the past.
3738
+ Maintained by server trigger.
3739
+ type:
3740
+ - boolean
3741
+ - 'null'
3742
+ createdAt:
3743
+ anyOf:
3744
+ - $ref: '#/components/schemas/FirestoreTimestamp'
3745
+ - type: 'null'
3746
+ readOnly: true
3747
+ description: (Read-only) Server-generated creation timestamp.
3748
+ updatedAt:
3749
+ anyOf:
3750
+ - $ref: '#/components/schemas/FirestoreTimestamp'
3751
+ - type: 'null'
3752
+ readOnly: true
3753
+ description: (Read-only) Server-generated update timestamp.
3754
+ createdBy:
3755
+ type: string
3756
+ x-immutable: true
3757
+ description: (Immutable) FK → User/staff UID who created the site.
3758
+ analyticsEnabled:
3759
+ type: boolean
3760
+ description: Feature flag — when false, clients should not emit analytics
3761
+ events for this site.
3762
+ lastAnalyticsSync:
3763
+ anyOf:
3764
+ - $ref: '#/components/schemas/FirestoreTimestamp'
3765
+ - type: 'null'
3766
+ readOnly: true
3767
+ description: (Read-only) Last time the analytics rollup pipeline refreshed
3768
+ `cachedMetrics`.
3769
+ cachedMetrics:
3770
+ readOnly: true
3771
+ denormalized: true
3772
+ x-note: Updated by the rollup pipeline (D42). Readers should treat this
3773
+ as a hint; authoritative counts live in analytics_daily/analytics_hourly.
3774
+ x-see:
3775
+ decisions:
3776
+ - D41
3777
+ - D42
3778
+ description: (Read-only, Denormalized) Cached metrics snapshot for quick
3779
+ dashboard rendering.
3780
+ type:
3781
+ - object
3782
+ - 'null'
3783
+ properties:
3784
+ totalEvents:
3785
+ type: integer
3786
+ minimum: -9007199254740991
3787
+ maximum: 9007199254740991
3788
+ description: All-time event count cached on the Site.
3789
+ totalPageViews:
3790
+ type: integer
3791
+ minimum: -9007199254740991
3792
+ maximum: 9007199254740991
3793
+ description: All-time `page_view` + `screen_view` count.
3794
+ totalSessions:
3795
+ type: integer
3796
+ minimum: -9007199254740991
3797
+ maximum: 9007199254740991
3798
+ description: All-time distinct session count.
3799
+ totalOrders:
3800
+ type: integer
3801
+ minimum: -9007199254740991
3802
+ maximum: 9007199254740991
3803
+ description: All-time order count attributed to this site (via `Order.siteId`,
3804
+ D43).
3805
+ lastEventAt:
3806
+ $ref: '#/components/schemas/FirestoreTimestamp'
3807
+ description: Timestamp of the most recent analytics event seen for this
3808
+ site.
3809
+ required:
3810
+ - totalEvents
3811
+ - totalPageViews
3812
+ - totalSessions
3813
+ - totalOrders
3814
+ additionalProperties: false
3815
+ required:
3816
+ - companyId
3817
+ - name
3818
+ - status
3819
+ - deploymentLinks
3820
+ - createdBy
3821
+ - analyticsEnabled
3822
+ additionalProperties: false
3823
+ description: 'Site model (D41 / ING-304). Collection: companies/{companyId}/sites/{siteId}.
3824
+ Per-company product surface. Sub-collections (magic_link_requests, allowed_users,
3825
+ payments, analytics_events, analytics_daily, analytics_hourly, analytics_backfills)
3826
+ live under this document per D40/D42.'
3827
+ SiteCreate:
3828
+ allOf:
3829
+ - $ref: '#/components/schemas/Site'
3830
+ description: Write payload for creating a new Site document. Fields marked `readOnly`
3831
+ are server-set and must not be included. Fields marked `x-immutable` may be
3832
+ set once at creation.
3833
+ required:
3834
+ - companyId
3835
+ - name
3836
+ - status
3837
+ - deploymentLinks
3838
+ - createdBy
3839
+ - analyticsEnabled
3840
+ SiteUpdate:
3841
+ allOf:
3842
+ - $ref: '#/components/schemas/Site'
3843
+ description: Write payload for partial update (PATCH) of a Site document. All
3844
+ fields optional. Fields marked `readOnly` or `x-immutable` must not be sent.
3204
3845
  SitePayment:
3205
3846
  type: object
3206
3847
  properties:
@@ -3278,6 +3919,337 @@ components:
3278
3919
  description: Write payload for partial update (PATCH) of a SitePayment document.
3279
3920
  All fields optional. Fields marked `readOnly` or `x-immutable` must not be
3280
3921
  sent.
3922
+ Stocktake:
3923
+ type: object
3924
+ properties:
3925
+ id:
3926
+ readOnly: true
3927
+ description: (Read-only) Firestore document ID, auto-generated.
3928
+ type:
3929
+ - string
3930
+ - 'null'
3931
+ companyId:
3932
+ type: string
3933
+ x-immutable: true
3934
+ description: (Immutable) FK → Company document ID. Tenant scope.
3935
+ stocktakeNumber:
3936
+ type: string
3937
+ description: Human-readable session number, e.g. "STK-2026-001". Embedded
3938
+ as `referenceNumber` on the StockMovement rows the session emits, so the
3939
+ ledger remains traceable to the originating stocktake.
3940
+ stocktakeDate:
3941
+ $ref: '#/components/schemas/FirestoreTimestamp'
3942
+ description: Effective date of the count. Distinct from createdAt; can be
3943
+ backdated.
3944
+ x-note: The date the count is FOR. Can be backdated (stocktakeDate < createdAt)
3945
+ — analytics buckets by stocktakeDate for "what period was counted" and
3946
+ by completedAt for "when adjustments hit the ledger" (GH#29 Q24).
3947
+ warehouseId:
3948
+ description: FK → Warehouse document ID. Null = whole-company stocktake
3949
+ (no warehouse partition).
3950
+ anyOf:
3951
+ - type: string
3952
+ - type: 'null'
3953
+ frequency:
3954
+ anyOf:
3955
+ - $ref: '#/components/schemas/StocktakeFrequency'
3956
+ - type: 'null'
3957
+ description: Recurrence cadence. Optional — leave unset for ad-hoc sessions,
3958
+ or set to indicate this stocktake is part of a recurring schedule.
3959
+ x-note: Optional today (dashboard does not currently track recurrence).
3960
+ Frequency is a property of the record, not a separate type — a daily stocktake
3961
+ is a Stocktake with frequency=DAILY, not a distinct DailyStocktake model.
3962
+ If the session was emitted from a recurring schedule, set Stocktake.scheduledStocktakeId;
3963
+ ad-hoc sessions leave it null.
3964
+ x-see:
3965
+ issues:
3966
+ - gh#29
3967
+ scheduledStocktakeId:
3968
+ description: FK to a recurring stocktake schedule, if this session was emitted
3969
+ from one. Null for ad-hoc sessions.
3970
+ anyOf:
3971
+ - type: string
3972
+ - type: 'null'
3973
+ status:
3974
+ $ref: '#/components/schemas/StocktakeStatus'
3975
+ description: Lifecycle status. Transitions PENDING → IN_PROGRESS → COMPLETED
3976
+ are server-owned (GH#29 §3 / Q25).
3977
+ x-note: PENDING = session created, no items counted yet. IN_PROGRESS = at
3978
+ least one item has been counted. COMPLETED = all items counted and the
3979
+ session was closed; on this transition the server emits one StockMovement(type=INVENTORY)
3980
+ per non-zero delta. CANCELLED = session abandoned without applying adjustments.
3981
+ Status transitions PENDING → IN_PROGRESS → COMPLETED are server-owned
3982
+ (Q25) — clients PATCH item status; the server transitions session status
3983
+ to avoid the concurrent-edit race documented in stockService.ts:1671.
3984
+ x-see:
3985
+ issues:
3986
+ - gh#29
3987
+ x-when: Server-managed. Clients write item updates; the trigger transitions
3988
+ session status.
3989
+ adjustmentsApplied:
3990
+ type: boolean
3991
+ x-note: Records whether the COMPLETED transition actually mutated stock
3992
+ or was record-only. The dashboard's completeInventory(applyAdjustments)
3993
+ flag was not persisted; this field closes that gap (GH#29 audit Q22).
3994
+ description: True if completing the session actually emitted StockMovement
3995
+ adjustments. False for record-only counts.
3996
+ notes:
3997
+ description: Free-text session-level notes.
3998
+ anyOf:
3999
+ - type: string
4000
+ - type: 'null'
4001
+ totalItemsCount:
4002
+ type: integer
4003
+ minimum: -9007199254740991
4004
+ maximum: 9007199254740991
4005
+ readOnly: true
4006
+ x-note: Server-derived. Recomputed by trigger on each StocktakeItem write;
4007
+ not safe to write client-side under concurrent edits (GH#29 audit Q23).
4008
+ description: (Read-only) Total number of StocktakeItem rows under this session.
4009
+ totalDiscrepancies:
4010
+ type: integer
4011
+ minimum: -9007199254740991
4012
+ maximum: 9007199254740991
4013
+ readOnly: true
4014
+ x-note: Server-derived = sum of |item.delta| across COUNTED items. Updated
4015
+ by trigger.
4016
+ description: (Read-only) Sum of absolute deltas across counted items.
4017
+ totalDeltaValue:
4018
+ type: number
4019
+ readOnly: true
4020
+ x-note: Server-derived = sum of (item.delta × item.costPerUnit) across COUNTED
4021
+ items, in XOF integer minor units (D18). Updated by trigger.
4022
+ description: (Read-only) Net valuation impact of the count, in XOF integer
4023
+ minor units.
4024
+ startedAt:
4025
+ $ref: '#/components/schemas/FirestoreTimestamp'
4026
+ description: When the counting session physically opened. Typically equal
4027
+ to createdAt for ad-hoc sessions.
4028
+ completedAt:
4029
+ anyOf:
4030
+ - $ref: '#/components/schemas/FirestoreTimestamp'
4031
+ - type: 'null'
4032
+ readOnly: true
4033
+ x-note: Server-set on transition to COMPLETED. Distinct from stocktakeDate
4034
+ (the period counted) — analytics buckets by completedAt to answer "when
4035
+ did the ledger hit?".
4036
+ description: (Read-only) When the session transitioned to COMPLETED.
4037
+ createdAt:
4038
+ anyOf:
4039
+ - $ref: '#/components/schemas/FirestoreTimestamp'
4040
+ - type: 'null'
4041
+ readOnly: true
4042
+ description: (Read-only) Server-generated creation timestamp.
4043
+ createdBy:
4044
+ type: string
4045
+ x-immutable: true
4046
+ description: (Immutable) FK → User UID who created the session.
4047
+ createdByName:
4048
+ type: string
4049
+ denormalized: true
4050
+ x-note: Read-time hint snapshot of the creator's display name (GH#29 §3
4051
+ / Q10).
4052
+ description: (Denormalized) Snapshot of the creator's display name at session
4053
+ creation.
4054
+ completedBy:
4055
+ readOnly: true
4056
+ description: (Read-only) FK → User UID who completed the session, set by
4057
+ server on COMPLETED.
4058
+ anyOf:
4059
+ - type: string
4060
+ - type: 'null'
4061
+ completedByName:
4062
+ denormalized: true
4063
+ x-note: Read-time hint snapshot of the completer's display name (GH#29 §3
4064
+ / Q10).
4065
+ description: (Denormalized) Snapshot of the completer's display name.
4066
+ anyOf:
4067
+ - type: string
4068
+ - type: 'null'
4069
+ updatedAt:
4070
+ anyOf:
4071
+ - $ref: '#/components/schemas/FirestoreTimestamp'
4072
+ - type: 'null'
4073
+ readOnly: true
4074
+ description: (Read-only) Server-generated update timestamp.
4075
+ required:
4076
+ - companyId
4077
+ - stocktakeNumber
4078
+ - stocktakeDate
4079
+ - status
4080
+ - adjustmentsApplied
4081
+ - totalItemsCount
4082
+ - totalDiscrepancies
4083
+ - totalDeltaValue
4084
+ - startedAt
4085
+ - createdBy
4086
+ - createdByName
4087
+ additionalProperties: false
4088
+ description: 'Stocktake (GH#29 §4). Collection: companies/{companyId}/stocktakes/{stocktakeId}.
4089
+ A recorded session of physically counting all SKUs at a point in time. Replaces
4090
+ the dashboard''s Inventory model; items live in a sub-collection (replaces
4091
+ InventoryItem[] embedded array); InventoryEvolution is dropped — derivable
4092
+ from the sub-collection. Status transitions and aggregate fields (totalItemsCount/Discrepancies/totalDeltaValue)
4093
+ are server-owned per GH#29 §3.'
4094
+ StocktakeCreate:
4095
+ allOf:
4096
+ - $ref: '#/components/schemas/Stocktake'
4097
+ description: Write payload for creating a new Stocktake document. Fields marked
4098
+ `readOnly` are server-set and must not be included. Fields marked `x-immutable`
4099
+ may be set once at creation.
4100
+ required:
4101
+ - companyId
4102
+ - stocktakeNumber
4103
+ - stocktakeDate
4104
+ - status
4105
+ - adjustmentsApplied
4106
+ - startedAt
4107
+ - createdBy
4108
+ - createdByName
4109
+ StocktakeUpdate:
4110
+ allOf:
4111
+ - $ref: '#/components/schemas/Stocktake'
4112
+ description: Write payload for partial update (PATCH) of a Stocktake document.
4113
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
4114
+ sent.
4115
+ StocktakeItem:
4116
+ type: object
4117
+ properties:
4118
+ stockItemId:
4119
+ type: string
4120
+ description: FK → StockItem document ID. Doubles as the item document ID.
4121
+ stockItemName:
4122
+ type: string
4123
+ denormalized: true
4124
+ x-note: Read-time hint snapshot of StockItem.name. Authoritative value lives
4125
+ at the StockItem record; do not refresh on rename — the historical name
4126
+ at session time is what the auditor wrote (GH#29 §3 / Q10).
4127
+ description: (Denormalized) Snapshot of StockItem.name at session time.
4128
+ theoreticalQuantity:
4129
+ type: number
4130
+ x-immutable: true
4131
+ description: (Immutable) System belief at session start, snapshotted when
4132
+ this item row was created.
4133
+ actualQuantity:
4134
+ description: Physical count entered by the counter. Null until counted;
4135
+ set when the line moves to status COUNTED.
4136
+ anyOf:
4137
+ - type: number
4138
+ - type: 'null'
4139
+ delta:
4140
+ type: number
4141
+ readOnly: true
4142
+ x-note: Server-derived = actualQuantity − theoreticalQuantity. Recomputed
4143
+ on every actualQuantity write; null/zero before counting.
4144
+ description: (Read-only) Signed difference between actual and theoretical.
4145
+ Drives the StockMovement(type=INVENTORY) emitted on session COMPLETED.
4146
+ unit:
4147
+ type: string
4148
+ x-immutable: true
4149
+ description: (Immutable) Snapshot of StockItem.unit at session start. Locks
4150
+ the unit against later catalog changes.
4151
+ costPerUnit:
4152
+ type: number
4153
+ x-immutable: true
4154
+ x-note: Snapshot at session time — locks valuation against future cost changes.
4155
+ Lock to XOF integer minor units per D18 (GH#29 Q16).
4156
+ description: (Immutable) Snapshot of StockItem.costPerUnit at session start,
4157
+ in XOF integer minor units.
4158
+ totalValue:
4159
+ type: number
4160
+ readOnly: true
4161
+ x-note: Server-derived = actualQuantity × costPerUnit. Recomputed when actualQuantity
4162
+ is written.
4163
+ description: (Read-only) Per-line valuation in XOF integer minor units.
4164
+ status:
4165
+ $ref: '#/components/schemas/StocktakeItemStatus'
4166
+ description: Per-line workflow status. Server transitions ADJUSTED on parent
4167
+ COMPLETED.
4168
+ x-note: PENDING = line not yet counted. COUNTED = a counter recorded actualQuantity.
4169
+ VERIFIED = a second pair of eyes signed off (verifiedBy fields populated;
4170
+ Q21 — kept nullable until tenants opt into stricter QA via policy). ADJUSTED
4171
+ = the resulting StockMovement was emitted (server-set on Stocktake.status
4172
+ → COMPLETED).
4173
+ x-see:
4174
+ issues:
4175
+ - gh#29
4176
+ notes:
4177
+ description: Optional per-line note from the counter (e.g. "damaged carton,
4178
+ 3 unsellable").
4179
+ anyOf:
4180
+ - type: string
4181
+ - type: 'null'
4182
+ countedBy:
4183
+ description: UID of the staff member who entered actualQuantity.
4184
+ anyOf:
4185
+ - type: string
4186
+ - type: 'null'
4187
+ countedByName:
4188
+ denormalized: true
4189
+ x-note: Read-time hint snapshot of the counter's display name (GH#29 §3
4190
+ / Q10).
4191
+ description: (Denormalized) Snapshot of the counter's display name at count
4192
+ time.
4193
+ anyOf:
4194
+ - type: string
4195
+ - type: 'null'
4196
+ countedAt:
4197
+ anyOf:
4198
+ - $ref: '#/components/schemas/FirestoreTimestamp'
4199
+ - type: 'null'
4200
+ description: When the line was counted.
4201
+ verifiedBy:
4202
+ description: UID of a second staff member who verified the count. Nullable
4203
+ per GH#29 Q21.
4204
+ anyOf:
4205
+ - type: string
4206
+ - type: 'null'
4207
+ verifiedByName:
4208
+ denormalized: true
4209
+ x-note: Read-time hint snapshot of the verifier's display name (GH#29 §3
4210
+ / Q10).
4211
+ description: (Denormalized) Snapshot of the verifier's display name.
4212
+ anyOf:
4213
+ - type: string
4214
+ - type: 'null'
4215
+ verifiedAt:
4216
+ anyOf:
4217
+ - $ref: '#/components/schemas/FirestoreTimestamp'
4218
+ - type: 'null'
4219
+ description: When the line was verified, if a second-eye flow was used.
4220
+ required:
4221
+ - stockItemId
4222
+ - stockItemName
4223
+ - theoreticalQuantity
4224
+ - delta
4225
+ - unit
4226
+ - costPerUnit
4227
+ - totalValue
4228
+ - status
4229
+ additionalProperties: false
4230
+ description: 'StocktakeItem (GH#29 §4). Sub-collection path: companies/{companyId}/stocktakes/{stocktakeId}/items/{stockItemId}.
4231
+ Replaces the embedded `InventoryItem[]` array on the dashboard model. Server
4232
+ emits one StockMovement(type=INVENTORY) per non-zero delta when the parent
4233
+ Stocktake transitions to COMPLETED.'
4234
+ StocktakeItemCreate:
4235
+ allOf:
4236
+ - $ref: '#/components/schemas/StocktakeItem'
4237
+ description: Write payload for creating a new StocktakeItem document. Fields
4238
+ marked `readOnly` are server-set and must not be included. Fields marked `x-immutable`
4239
+ may be set once at creation.
4240
+ required:
4241
+ - stockItemId
4242
+ - stockItemName
4243
+ - theoreticalQuantity
4244
+ - unit
4245
+ - costPerUnit
4246
+ - status
4247
+ StocktakeItemUpdate:
4248
+ allOf:
4249
+ - $ref: '#/components/schemas/StocktakeItem'
4250
+ description: Write payload for partial update (PATCH) of a StocktakeItem document.
4251
+ All fields optional. Fields marked `readOnly` or `x-immutable` must not be
4252
+ sent.
3281
4253
  Ticket:
3282
4254
  type: object
3283
4255
  properties: