sinfactura-types 1.10.261 → 1.10.262

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.
@@ -191,6 +191,56 @@ declare global {
191
191
  lastReminderAt?: number;
192
192
  marketing?: CustomerMarketing;
193
193
  minBuy?: number;
194
+ /**
195
+ * How many appointments this customer has been marked `NO_SHOW` for, so a
196
+ * booking-time policy can be evaluated SERVER-SIDE against a durable number
197
+ * instead of against whatever date window a browser happened to read. Two
198
+ * operators with different date filters derive different counts for the same
199
+ * customer; this field is the one both of them share.
200
+ *
201
+ * ⚠️ **NOT A HISTORICAL TOTAL — it counts from the day it shipped.** This
202
+ * repo is forward-only and never backfills, so a customer with years of
203
+ * prior no-shows starts ABSENT and reads exactly like one who has never
204
+ * missed an appointment. A policy that auto-flags at 4+ will treat a
205
+ * long-standing offender as clean until they accumulate four MORE. That is a
206
+ * known consequence, not an oversight: what to do about it (a grace window,
207
+ * an operator override, showing the client-derived range count beside this
208
+ * one) is the app's decision — but it must not be a surprise.
209
+ *
210
+ * ⚠️ **Absence means "nothing recorded since this shipped"**, never "never
211
+ * no-showed". Coerce with `noShowCount ?? 0` for arithmetic, but do not
212
+ * render that fallback as a measured zero.
213
+ *
214
+ * ⚠️ **A LOWER BOUND.** The bump is issued after the appointment's own
215
+ * status write has already committed, and is deliberately best-effort: an
216
+ * appointment must still be markable `NO_SHOW` when the customer row has
217
+ * since been deleted. A lost bump undercounts by one and can never
218
+ * overcount — so a threshold that has been crossed was really crossed.
219
+ *
220
+ * ⚠️ **MONOTONIC, because `NO_SHOW` is TERMINAL — there is no decrement
221
+ * path.** `stacks/services/appointments.ts`'s transition table gives
222
+ * `NO_SHOW` an empty successor list, and the two writers that could move a
223
+ * terminal row (`cancelAppointment`, `rescheduleAppointment`) both condition
224
+ * on the status still being one of the four non-terminal ones. A booking
225
+ * marked `NO_SHOW` in error is corrected by creating a new appointment, not
226
+ * by reverting the old one. If a revert is ever added it owes a
227
+ * floor-guarded decrement in the SAME change, and the app must stop
228
+ * treating this as monotonic on the same day.
229
+ *
230
+ * ⚠️ **Written by an atomic `ADD noShowCount :one`**, like the `ADD balance`
231
+ * deltas on this same row. Never read-modify-write it: two operators
232
+ * clearing the same morning's board would both read the same value and one
233
+ * of the two events would vanish silently.
234
+ *
235
+ * ⚠️ Deliberately absent from `CUSTOMER_LIST_DISPLAY_FIELDS`, and therefore
236
+ * from `CUSTOMER_OPERATOR_BROADCAST_FIELDS`, which is DEFINED as that list
237
+ * plus frame mechanics. Behavioural data about a named person (Ley 25.326)
238
+ * does not belong on a store-wide list served to every staff-tier token, and
239
+ * a customer must not receive their own tally on a socket. It is served by
240
+ * the capability-gated `?customerId=` point read — the surface a booking
241
+ * screen already uses.
242
+ */
243
+ noShowCount?: number;
194
244
  /**
195
245
  * Unsettled debits denormalised onto this row so the accounts report can
196
246
  * bucket them by age without a per-customer query — the same
package/dist/order.d.ts CHANGED
@@ -97,8 +97,58 @@ declare global {
97
97
  * drift. The APPOINTMENT is authoritative: it is the row the scheduler
98
98
  * writes under a conditional update, and a stale pointer here must be
99
99
  * treated as absent rather than followed.
100
+ *
101
+ * ⚠️ **This field, {@link Order.appointmentDate} and
102
+ * {@link Order.appointmentTime} are ONE stamp.** Every writer sets all
103
+ * three in a single `UpdateExpression` on this row (`SET` when a booking
104
+ * is made or moved, `REMOVE` when it is cancelled), so they cannot drift
105
+ * apart from ONE ANOTHER: all three present, or all three absent — the
106
+ * one exception being a row written before the date/time existed, which
107
+ * carries this pointer alone and is never backfilled. What can still lag
108
+ * is the stamp AS A WHOLE, because the write follows an already-committed
109
+ * appointment and is best-effort; that is the drift the paragraph above
110
+ * is about, and it is why the appointment stays authoritative.
100
111
  */
101
112
  appointmentId?: string;
113
+ /**
114
+ * The booked slot's LOCAL date, `YYYYMMDD` in the store's (Buenos Aires)
115
+ * time — a copy of `Appointment.date`, denormalised so a page of orders
116
+ * can render, sort and filter a «Turno» column without reading the
117
+ * appointments table at all. The three alternatives it replaces are each
118
+ * wrong in a different way: a range read over appointments misses any
119
+ * booking outside the window, and renders "sin turno" on exactly the
120
+ * orders an operator is hunting for; a per-row lookup is N+1 with no
121
+ * `orderId` filter to collapse it; and neither can sort a grid by a key
122
+ * that is not on the row.
123
+ *
124
+ * ⚠️ **NOT a Unix timestamp.** `20260315` is the fifteenth of March, not
125
+ * an instant. It copies the AUTHORITATIVE local pair the appointment row
126
+ * is keyed by (`${date}#${startTime}#${appointmentId}`) rather than the
127
+ * derived `Appointment.startTimestamp`, so this row and that key can
128
+ * never denote different slots.
129
+ *
130
+ * ⚠️ **Absent means "nothing to render", and that includes an order
131
+ * written before this field existed.** The platform is forward-only and
132
+ * nothing backfills, so a row may carry {@link Order.appointmentId} with
133
+ * no date or time: render no turno rather than inventing one, and follow
134
+ * the pointer only from a detail view that can afford the read. The next
135
+ * booking or reschedule on such an order stamps all three.
136
+ *
137
+ * ⚠️ A COPY, for display and ordering. The APPOINTMENT stays
138
+ * authoritative for whether the slot is still live — a cancel clears the
139
+ * stamp, but a clear that failed leaves it stale, so nothing that charges
140
+ * money or promises a customer a time may read this without the
141
+ * appointment.
142
+ */
143
+ appointmentDate?: number;
144
+ /**
145
+ * The booked slot's LOCAL start time, `HHmm` — a copy of
146
+ * `Appointment.startTime`, written and cleared by the same single update
147
+ * as {@link Order.appointmentDate} and under exactly the same rules:
148
+ * absent means nothing to render, and the appointment remains
149
+ * authoritative for whether the booking still stands.
150
+ */
151
+ appointmentTime?: string;
102
152
  /**
103
153
  * OPTIONAL. The eleven readers that forced this to be required have all
104
154
  * been closed, and the widening was gated on a measured zero-error
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sinfactura-types",
3
- "version": "1.10.261",
3
+ "version": "1.10.262",
4
4
  "description": "Shared wire and entity contracts for the SINFACTURA platform.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",