@fire-spark/cli 0.0.0-stage → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/firespark.mjs +14 -0
- package/package.json +37 -3
- package/src/commands/doctor/client/catalog.json +1586 -0
- package/src/commands/doctor/client/catalog.ts +179 -0
- package/src/commands/doctor/client/command.ts +246 -0
- package/src/commands/doctor/client/config.ts +346 -0
- package/src/commands/doctor/client/copy.ts +228 -0
- package/src/commands/doctor/client/manifests.ts +161 -0
- package/src/commands/doctor/client/options.ts +201 -0
- package/src/commands/doctor/client/order.ts +261 -0
- package/src/commands/doctor/client/runner.ts +162 -0
- package/src/commands/doctor/client/yaml.ts +179 -0
- package/src/commands/doctor/live/http.ts +89 -0
- package/src/commands/doctor/live/live.session.ts +243 -0
- package/src/commands/doctor/live/order.ts +95 -0
- package/src/commands/doctor/live/realtime.ts +161 -0
- package/src/commands/doctor/live/result.ts +76 -0
- package/src/commands/index.ts +8 -0
- package/src/core/command.types.ts +25 -0
- package/src/core/help.ts +85 -0
- package/src/core/i18n.ts +49 -0
- package/src/core/options.ts +131 -0
- package/src/core/output.ts +47 -0
- package/src/core/redact.ts +71 -0
- package/src/index.ts +4 -0
- package/src/main.ts +113 -0
- package/README.md +0 -3
|
@@ -0,0 +1,1586 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "PAY-01",
|
|
4
|
+
"description": "Catálogo configurable de métodos de pago, con targeting por canal/tienda/marca/fulfillment/labels, vía el canal y API.",
|
|
5
|
+
"acceptance": "Se puede crear y desactivar un payment y variar su scope; los cambios se ven en el canal y API.",
|
|
6
|
+
"channels": ["server", "dashboard"],
|
|
7
|
+
"platforms": ["dashboard"],
|
|
8
|
+
"fixable": true,
|
|
9
|
+
"required": true,
|
|
10
|
+
"area": "payments",
|
|
11
|
+
"notes": "",
|
|
12
|
+
"verification": "deterministic",
|
|
13
|
+
"howTo": "Maestro en Dashboard: crear y desactivar un payment, variar su scope y comprobar que el canal y la API lo reflejan."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "PAY-02",
|
|
17
|
+
"description": "El evento payments.changed marca el canal como dirty y provoca una descarga entre sesiones, no a mitad de una compra.",
|
|
18
|
+
"acceptance": "Tras un cambio de payments, el canal no actualiza a mitad de compra; al iniciar una nueva sesión descarga el catálogo actualizado.",
|
|
19
|
+
"channels": ["kiosk"],
|
|
20
|
+
"platforms": ["web", "android", "ios"],
|
|
21
|
+
"fixable": true,
|
|
22
|
+
"required": true,
|
|
23
|
+
"area": "payments",
|
|
24
|
+
"notes": "se aplica entre sesiones, no a mitad de compra",
|
|
25
|
+
"verification": "deterministic",
|
|
26
|
+
"howTo": "Maestro: iniciar una compra, cambiar payments en el dashboard y afirmar que el checkout no cambia; al abrir una sesión nueva se descarga el catálogo actualizado."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "PAY-03",
|
|
30
|
+
"description": "El canal selecciona el método de pago según los payments del fulfillment, las condiciones y overrides.disabled_payment_ids del device. REDEMPTION no se elige como método de dinero.",
|
|
31
|
+
"acceptance": "Con payments filtrados y disabled_payment_ids, solo aparecen los métodos permitidos; REDEMPTION no figura como método de pago.",
|
|
32
|
+
"channels": ["kiosk"],
|
|
33
|
+
"platforms": ["web", "android", "ios"],
|
|
34
|
+
"fixable": true,
|
|
35
|
+
"required": true,
|
|
36
|
+
"area": "payments",
|
|
37
|
+
"notes": "",
|
|
38
|
+
"verification": "deterministic",
|
|
39
|
+
"howTo": "Maestro: aplicar filtros y disabled_payment_ids; afirmar los métodos visibles y que REDEMPTION no aparece como método de dinero."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "PAY-04",
|
|
43
|
+
"description": "El canal muestra instrucciones según el método de pago: CARD/PIN_PAD → card; BANK_TRANSFER → transfer; WALLET → wallet; el efectivo no muestra instrucciones (se paga en caja).",
|
|
44
|
+
"acceptance": "Al elegir cada método de pago se ve el flujo esperado; el efectivo no muestra instrucciones (se paga en caja).",
|
|
45
|
+
"channels": ["kiosk"],
|
|
46
|
+
"platforms": ["web", "android", "ios"],
|
|
47
|
+
"fixable": true,
|
|
48
|
+
"required": true,
|
|
49
|
+
"area": "payments",
|
|
50
|
+
"notes": "",
|
|
51
|
+
"verification": "deterministic",
|
|
52
|
+
"howTo": "Maestro: elegir cada método y afirmar el flujo de instrucciones (card, transfer, wallet); el efectivo no muestra instrucciones."
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "PAY-05",
|
|
56
|
+
"description": "En los canales hay UI de alta, edición, baja y preferido de efectivo y tarjetas; el efectivo admite importe exacto o entregado.",
|
|
57
|
+
"acceptance": "En cada canal se completa alta/edición/baja/preferido y el flujo de efectivo acepta monto exacto o entregado.",
|
|
58
|
+
"channels": ["web", "app", "chat", "call-center"],
|
|
59
|
+
"platforms": ["web", "android", "ios"],
|
|
60
|
+
"fixable": true,
|
|
61
|
+
"required": true,
|
|
62
|
+
"area": "payments",
|
|
63
|
+
"notes": "",
|
|
64
|
+
"verification": "deterministic",
|
|
65
|
+
"howTo": "Maestro: completar alta, edición, baja y preferido de efectivo y tarjetas; el efectivo acepta monto exacto o entregado."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "PAY-07",
|
|
69
|
+
"description": "Al hacer place se puede armar el payload y el flujo de efectivo. Los helpers aceptan los métodos de pago configurados, pero el stock solo tiene payments-cash.",
|
|
70
|
+
"acceptance": "Un place con CASH genera el payload correcto y el flujo de efectivo se completa en local.",
|
|
71
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "server"],
|
|
72
|
+
"platforms": ["web", "android", "ios"],
|
|
73
|
+
"fixable": true,
|
|
74
|
+
"required": true,
|
|
75
|
+
"area": "payments",
|
|
76
|
+
"notes": "templates no registran setPaymentAdapters; stock solo payments-cash; el canal registra redemptions",
|
|
77
|
+
"verification": "deterministic",
|
|
78
|
+
"howTo": "Maestro completa un place con CASH y un script local (HAR o proxy en tu máquina) afirma que el payload de la request es el esperado. Sin cobro real."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "PAY-08",
|
|
82
|
+
"description": "Si el merchant tiene adapter y proveedor configurados, se completa un cobro de prueba (sandbox).",
|
|
83
|
+
"acceptance": "Con adapter y proveedor configurados, un cobro de prueba en sandbox se completa correctamente.",
|
|
84
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "server"],
|
|
85
|
+
"platforms": ["web", "android", "ios"],
|
|
86
|
+
"fixable": true,
|
|
87
|
+
"required": false,
|
|
88
|
+
"area": "payments",
|
|
89
|
+
"notes": "solo si existe adapter/proveedor; no uses cobro real en producción",
|
|
90
|
+
"verification": "deterministic",
|
|
91
|
+
"howTo": "Maestro: si hay adapter y proveedor, completar un cobro de prueba en sandbox y afirmar éxito. Nunca usar cobro real. Si no hay adapter, se omite (Requerido No)."
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": "PAY-09",
|
|
95
|
+
"description": "En el canal se puede vender con efectivo (importe entregado y cambio) si CASH está habilitado y no bloqueado por device.",
|
|
96
|
+
"acceptance": "Con CASH habilitado se cobra, se ingresa entregado y se calcula el cambio; si está deshabilitado o bloqueado, no se puede.",
|
|
97
|
+
"channels": ["pos"],
|
|
98
|
+
"platforms": ["web"],
|
|
99
|
+
"fixable": true,
|
|
100
|
+
"required": false,
|
|
101
|
+
"area": "payments",
|
|
102
|
+
"notes": "",
|
|
103
|
+
"verification": "deterministic",
|
|
104
|
+
"howTo": "Maestro en POS: con CASH habilitado, cobrar, ingresar entregado y afirmar el cambio; con CASH deshabilitado o bloqueado, afirmar que no se puede."
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"id": "PAY-10",
|
|
108
|
+
"description": "En el canal, split entre efectivo y otro método: líneas a efectivo, resto a tarjeta, fees al tender de tarjeta.",
|
|
109
|
+
"acceptance": "El total se reparte: las líneas marcadas van a efectivo, el resto a tarjeta y los fees al tender de tarjeta.",
|
|
110
|
+
"channels": ["pos"],
|
|
111
|
+
"platforms": ["web"],
|
|
112
|
+
"fixable": true,
|
|
113
|
+
"required": false,
|
|
114
|
+
"area": "payments",
|
|
115
|
+
"notes": "el código trata la tarjeta como ya aprobada en pinpad",
|
|
116
|
+
"verification": "deterministic",
|
|
117
|
+
"howTo": "Maestro en POS: marcar líneas a efectivo y el resto a tarjeta; afirmar la distribución y que los fees van al tender de tarjeta."
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"id": "PAY-12",
|
|
121
|
+
"description": "Pagos mixtos opcionales (preferences.payments.mixed no nulo, ≥2 métodos, workflow compatible) en los canales: distribución, intent bloqueado, retry, reemplazo de tender, cancelar/reembolsar.",
|
|
122
|
+
"acceptance": "Con mixed habilitado se distribuye el pago, el intent queda bloqueado, y se puede reintentar, reemplazar tender y cancelar/reembolsar.",
|
|
123
|
+
"channels": ["kiosk", "web", "app"],
|
|
124
|
+
"platforms": ["web", "android", "ios"],
|
|
125
|
+
"fixable": true,
|
|
126
|
+
"required": false,
|
|
127
|
+
"area": "payments",
|
|
128
|
+
"notes": "",
|
|
129
|
+
"verification": "deterministic",
|
|
130
|
+
"howTo": "Maestro: con mixed habilitado, distribuir el pago, afirmar el intent bloqueado, reintentar, reemplazar tender y cancelar o reembolsar."
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"id": "PAY-13",
|
|
134
|
+
"description": "En el canal: Payments review y refundTender/cancelOrderPayments (permiso orders:orders:write).",
|
|
135
|
+
"acceptance": "Un usuario con orders:orders:write puede reembolsar un tender y cancelar pagos de una orden; sin el permiso, no.",
|
|
136
|
+
"channels": ["dashboard"],
|
|
137
|
+
"platforms": ["dashboard"],
|
|
138
|
+
"fixable": true,
|
|
139
|
+
"required": true,
|
|
140
|
+
"area": "payments",
|
|
141
|
+
"notes": "",
|
|
142
|
+
"verification": "deterministic",
|
|
143
|
+
"howTo": "Maestro en Dashboard: un usuario con orders:orders:write reembolsa un tender y cancela pagos; sin el permiso, no puede."
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"id": "ORD-01",
|
|
147
|
+
"description": "Carrito, validación y place order en los canales (Order.placeOrder o services.pos.createOrder, según el canal).",
|
|
148
|
+
"acceptance": "En cada canal se arma el carrito, un carrito inválido queda bloqueado y el place crea la orden.",
|
|
149
|
+
"channels": ["kiosk", "pos", "web", "app", "chat", "call-center"],
|
|
150
|
+
"platforms": ["web", "android", "ios"],
|
|
151
|
+
"fixable": true,
|
|
152
|
+
"required": true,
|
|
153
|
+
"area": "orders",
|
|
154
|
+
"notes": "",
|
|
155
|
+
"verification": "deterministic",
|
|
156
|
+
"howTo": "Maestro: armar el carrito, afirmar que uno inválido queda bloqueado y que el place crea la orden en cada canal aplicable."
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "ORD-02",
|
|
160
|
+
"description": "Overlay de checkout: éxito con checkout_state === released, fallo con failed, timeout 60 s; se actualiza con order.changed y snapshot al suscribirse.",
|
|
161
|
+
"acceptance": "Al released se muestra éxito; al failed, fallo; si no hay actualización en 60 s, timeout; order.changed y el snapshot al suscribirse actualizan el overlay.",
|
|
162
|
+
"channels": ["kiosk", "web", "app"],
|
|
163
|
+
"platforms": ["web", "android", "ios"],
|
|
164
|
+
"fixable": true,
|
|
165
|
+
"required": true,
|
|
166
|
+
"area": "orders",
|
|
167
|
+
"notes": "",
|
|
168
|
+
"verification": "deterministic",
|
|
169
|
+
"howTo": "Maestro: forzar checkout_state released (éxito), failed (fallo) y 60 s sin actualización (timeout); afirmar el overlay. order.changed y el snapshot al suscribirse deben actualizarlo."
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"id": "ORD-03",
|
|
173
|
+
"description": "Tracking de orden/workflow (estados, milestones). Los canales usan order.changed o postgres_changes de order_fulfillments, según corresponda.",
|
|
174
|
+
"acceptance": "Los cambios de estado y milestones se ven en los canales vía order.changed o postgres_changes, según corresponda.",
|
|
175
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
176
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
177
|
+
"fixable": true,
|
|
178
|
+
"required": true,
|
|
179
|
+
"area": "orders",
|
|
180
|
+
"notes": "",
|
|
181
|
+
"verification": "deterministic",
|
|
182
|
+
"howTo": "Maestro: avanzar el fulfillment y afirmar que estados y milestones se ven en el canal (order.changed o postgres_changes, según el canal)."
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"id": "ORD-04",
|
|
186
|
+
"description": "Editor de workflows en el canal (estados, joins, checkout_release, webhooks).",
|
|
187
|
+
"acceptance": "Se pueden crear/editar estados, joins, checkout_release y webhooks, y se persisten.",
|
|
188
|
+
"channels": ["dashboard"],
|
|
189
|
+
"platforms": ["dashboard"],
|
|
190
|
+
"fixable": true,
|
|
191
|
+
"required": true,
|
|
192
|
+
"area": "orders",
|
|
193
|
+
"notes": "",
|
|
194
|
+
"verification": "deterministic",
|
|
195
|
+
"howTo": "Maestro en Dashboard: crear o editar estados, joins, checkout_release y webhooks; recargar y afirmar que persistieron."
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"id": "ORD-05",
|
|
199
|
+
"description": "Inyección externa vía server-template (/api/injection, /api/fulfillment). El destino debe ser un receptor o URL de callback que controle el cliente (el destino stock es example-integration.invalid).",
|
|
200
|
+
"acceptance": "Con un receptor controlado por el cliente, una inyección por /api/injection o /api/fulfillment crea o avanza la orden (no implica levantar FireSpark en local).",
|
|
201
|
+
"channels": ["server"],
|
|
202
|
+
"platforms": ["web"],
|
|
203
|
+
"fixable": true,
|
|
204
|
+
"required": false,
|
|
205
|
+
"area": "orders",
|
|
206
|
+
"notes": "destino stock es example-integration.invalid; usar un receptor/URL que controle el cliente, no un endpoint de FireSpark local",
|
|
207
|
+
"verification": "deterministic",
|
|
208
|
+
"howTo": "Script local: levantar un receptor HTTP en tu máquina, apuntar la inyección del cliente a esa URL y afirmar que /api/injection o /api/fulfillment crea o avanza la orden y que el receptor recibe el callback. No se levanta FireSpark en local."
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"id": "ORD-06",
|
|
212
|
+
"description": "Saldar en efectivo una orden de otro canal (tender_id para la parte CASH de una mixta). Online, no offline.",
|
|
213
|
+
"acceptance": "Una orden de otro canal se salda en efectivo (y la parte CASH de una mixta vía tender_id) estando online; no está disponible offline.",
|
|
214
|
+
"channels": ["kiosk", "pos"],
|
|
215
|
+
"platforms": ["web", "android", "ios"],
|
|
216
|
+
"fixable": true,
|
|
217
|
+
"required": false,
|
|
218
|
+
"area": "orders",
|
|
219
|
+
"notes": "online, no offline",
|
|
220
|
+
"verification": "deterministic",
|
|
221
|
+
"howTo": "Maestro: estando online, saldar en efectivo una orden de otro canal y la parte CASH de una mixta (tender_id); afirmar que no está disponible offline."
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"id": "MEN-01",
|
|
225
|
+
"description": "CRUD y publicación de menú en el canal (permisos menus:menus). No hay emit directo al guardar CRUD.",
|
|
226
|
+
"acceptance": "Con permiso menus:menus se crea, edita, elimina y publica un menú; los cambios se ven al refetch, no por un emit inmediato al guardar.",
|
|
227
|
+
"channels": ["dashboard"],
|
|
228
|
+
"platforms": ["dashboard"],
|
|
229
|
+
"fixable": true,
|
|
230
|
+
"required": true,
|
|
231
|
+
"area": "menu",
|
|
232
|
+
"notes": "no hay emit directo al guardar CRUD; verificar por refetch",
|
|
233
|
+
"verification": "deterministic",
|
|
234
|
+
"howTo": "Maestro en Dashboard: con permiso menus:menus, crear, editar, eliminar y publicar; afirmar los cambios al refetch del menú (el criterio visible no exige inspeccionar el emit)."
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"id": "MEN-02",
|
|
238
|
+
"description": "Sync de menú: drafts, publish auto/manual, cancelar, rollback. El canal sigue menu_syncs por postgres_changes; finalize puede emitir menu.changed.",
|
|
239
|
+
"acceptance": "Se puede crear un draft, publicar (auto y manual), cancelar y hacer rollback; el canal refleja menu_syncs en realtime; al finalize con reemplazo se emite menu.changed.",
|
|
240
|
+
"channels": ["dashboard"],
|
|
241
|
+
"platforms": ["dashboard"],
|
|
242
|
+
"fixable": true,
|
|
243
|
+
"required": true,
|
|
244
|
+
"area": "menu",
|
|
245
|
+
"notes": "menu.changed solo si replaced_menu_uids.length > 0",
|
|
246
|
+
"verification": "deterministic",
|
|
247
|
+
"howTo": "Maestro: crear un draft, publicar en auto y manual, cancelar y hacer rollback; afirmar que el canal refleja el sync y que, si hubo reemplazo, el menú live cambia."
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"id": "MEN-04",
|
|
251
|
+
"description": "Alta, baja, precio y disponibilidad de producto (idioma, imagen, pricing, agotado, horarios). El efecto realtime solo ocurre si el sync reemplaza el menú live.",
|
|
252
|
+
"acceptance": "Los cambios de producto se persisten; el canal solo los ve en realtime si el sync reemplazó el menú live.",
|
|
253
|
+
"channels": ["kiosk", "dashboard"],
|
|
254
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
255
|
+
"fixable": true,
|
|
256
|
+
"required": true,
|
|
257
|
+
"area": "menu",
|
|
258
|
+
"notes": "",
|
|
259
|
+
"verification": "deterministic",
|
|
260
|
+
"howTo": "Maestro: cambiar precio, imagen, idioma, agotado u horario de un producto; afirmar que persiste y que el canal lo ve tras un sync que reemplaza el menú live, o al refetch."
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"id": "MEN-05",
|
|
264
|
+
"description": "En el canal, el realtime marca dirty el catálogo y lo aplica entre sesiones, no a mitad de una compra. Full sync en boot, relink y reconexión.",
|
|
265
|
+
"acceptance": "Un menu.changed durante una compra no cambia el catálogo hasta la siguiente sesión; hay full sync al boot, relink y reconexión.",
|
|
266
|
+
"channels": ["kiosk"],
|
|
267
|
+
"platforms": ["web", "android", "ios"],
|
|
268
|
+
"fixable": true,
|
|
269
|
+
"required": true,
|
|
270
|
+
"area": "menu",
|
|
271
|
+
"notes": "se aplica entre sesiones, no a mitad de compra",
|
|
272
|
+
"verification": "deterministic",
|
|
273
|
+
"howTo": "Maestro: provocar un menu.changed a mitad de una compra y afirmar que el catálogo no cambia hasta la siguiente sesión; afirmar full sync al boot, relink y reconexión."
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"id": "MEN-06",
|
|
277
|
+
"description": "El canal hace poll del menú cada 5 minutos y refetch al montar; no tiene listener de realtime.",
|
|
278
|
+
"acceptance": "Al montar el canal refresca el menú; un cambio se refleja como máximo a los 5 minutos o al remontar, no por evento realtime.",
|
|
279
|
+
"channels": ["pos"],
|
|
280
|
+
"platforms": ["web"],
|
|
281
|
+
"fixable": true,
|
|
282
|
+
"required": true,
|
|
283
|
+
"area": "menu",
|
|
284
|
+
"notes": "poll 5 min; sin listener de menu.changed",
|
|
285
|
+
"verification": "deterministic",
|
|
286
|
+
"howTo": "Maestro en POS: publicar un cambio de menú y afirmar que no aparece al instante (no hay listener); sí aparece al remontar o, como máximo, tras el poll de 5 minutos."
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"id": "MEN-07",
|
|
290
|
+
"description": "Los canales tienen listenOrder, pero no se encontró listener de menu.changed. staleTime típico de 15 min.",
|
|
291
|
+
"acceptance": "Un cambio de menú no llega por menu.changed; se ve al refetch, al navegar o al recargar (staleTime ~15 min).",
|
|
292
|
+
"channels": ["web", "app", "chat", "call-center"],
|
|
293
|
+
"platforms": ["web", "android", "ios"],
|
|
294
|
+
"fixable": true,
|
|
295
|
+
"required": true,
|
|
296
|
+
"area": "menu",
|
|
297
|
+
"notes": "check comportamental (refetch/navegación/recarga)",
|
|
298
|
+
"verification": "deterministic",
|
|
299
|
+
"howTo": "Maestro: igual que el check comportamental de refetch, navegación o recarga (staleTime ~15 min); un cambio no llega como evento inmediato."
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"id": "OVR-01",
|
|
303
|
+
"description": "Overrides por scope (merchant/env/brand/store/channel/fulfillment) con REORDER, DELETE, INSERT, UPDATE en PRODUCT, CATEGORY, MENU, más excludes. Se aplican al volver a pedir el menú; sin broadcast al guardar.",
|
|
304
|
+
"acceptance": "Tras guardar un override, el menú pedido de nuevo refleja REORDER/DELETE/INSERT/UPDATE y excludes; no hay broadcast inmediato al guardar.",
|
|
305
|
+
"channels": ["dashboard"],
|
|
306
|
+
"platforms": ["dashboard"],
|
|
307
|
+
"fixable": true,
|
|
308
|
+
"required": false,
|
|
309
|
+
"area": "overrides",
|
|
310
|
+
"notes": "sin broadcast al guardar; se aplican al refetch del menú",
|
|
311
|
+
"verification": "deterministic",
|
|
312
|
+
"howTo": "Maestro: guardar un override y volver a pedir el menú; afirmar REORDER, DELETE, INSERT, UPDATE y excludes. No se exige un broadcast inmediato al guardar."
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"id": "OVR-02",
|
|
316
|
+
"description": "Menu preview: edición visual, drag/reorder, guardar y descartar.",
|
|
317
|
+
"acceptance": "Se puede reordenar por drag, guardar los cambios y descartarlos sin persistir.",
|
|
318
|
+
"channels": ["dashboard"],
|
|
319
|
+
"platforms": ["dashboard"],
|
|
320
|
+
"fixable": true,
|
|
321
|
+
"required": false,
|
|
322
|
+
"area": "overrides",
|
|
323
|
+
"notes": "",
|
|
324
|
+
"verification": "deterministic",
|
|
325
|
+
"howTo": "Maestro: reordenar por drag, guardar y afirmar que persistió; descartar en otro intento y afirmar que no persistió."
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"id": "AVA-01",
|
|
329
|
+
"description": "Cerrar merchant/tienda o cambiar horario emite availability.changed. El canal que aplica cambios entre sesiones invalida preferences; los demás canales hacen refetch con availability.next_change_at. No es un evento de stock por producto.",
|
|
330
|
+
"acceptance": "Al cerrar o cambiar horario se emite availability.changed; el canal que aplica cambios entre sesiones aplica preferences en la siguiente sesión; los demás canales hacen refetch según next_change_at; un cambio de stock de producto no dispara este evento.",
|
|
331
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
332
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
333
|
+
"fixable": true,
|
|
334
|
+
"required": true,
|
|
335
|
+
"area": "availability",
|
|
336
|
+
"notes": "no es evento de stock por producto; el canal aplica cambios entre sesiones",
|
|
337
|
+
"verification": "deterministic",
|
|
338
|
+
"howTo": "Maestro: cerrar o cambiar horario y afirmar el efecto (siguiente sesión o refetch según el canal). Cambiar stock de un producto no debe disparar la pantalla de disponibilidad."
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"id": "AVA-02",
|
|
342
|
+
"description": "Direcciones, geolocalización y coverage zones en los canales.",
|
|
343
|
+
"acceptance": "Se puede crear/editar una dirección con geolocalización y validar si está dentro o fuera de una coverage zone.",
|
|
344
|
+
"channels": ["web", "app", "chat", "call-center", "dashboard"],
|
|
345
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
346
|
+
"fixable": true,
|
|
347
|
+
"required": false,
|
|
348
|
+
"area": "availability",
|
|
349
|
+
"notes": "",
|
|
350
|
+
"verification": "deterministic",
|
|
351
|
+
"howTo": "Maestro: crear o editar una dirección con coordenadas conocidas dentro y fuera de una coverage zone y afirmar el resultado."
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
"id": "AVA-03",
|
|
355
|
+
"description": "Shipping y billing se muestran como required, optional o hidden según preferences.",
|
|
356
|
+
"acceptance": "Con cada valor de preference, el checkout muestra u oculta shipping/billing y exige o no el dato.",
|
|
357
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center"],
|
|
358
|
+
"platforms": ["web", "android", "ios"],
|
|
359
|
+
"fixable": true,
|
|
360
|
+
"required": false,
|
|
361
|
+
"area": "availability",
|
|
362
|
+
"notes": "",
|
|
363
|
+
"verification": "deterministic",
|
|
364
|
+
"howTo": "Maestro: poner shipping/billing en required, optional y hidden y afirmar que el checkout muestra, oculta o exige el dato."
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"id": "AVA-04",
|
|
368
|
+
"description": "Fees y tips con reglas de tipo fijo, porcentaje, distancia y tags. No hay evento fees.changed. No está validado que todos los templates tengan UI de propina.",
|
|
369
|
+
"acceptance": "Las reglas de fee se calculan según tipo (fijo/porcentaje/distancia/tags) al armar el checkout. La UI de propina solo se valida donde exista.",
|
|
370
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
371
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
372
|
+
"fixable": true,
|
|
373
|
+
"required": false,
|
|
374
|
+
"area": "availability",
|
|
375
|
+
"notes": "no hay fees.changed; UI de tip solo donde exista",
|
|
376
|
+
"verification": "deterministic",
|
|
377
|
+
"howTo": "Maestro: con reglas de fee conocidas (fijo, porcentaje, distancia, tags), afirmar el cálculo en el checkout. La UI de propina solo se afirma donde exista."
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
"id": "RT-02",
|
|
381
|
+
"description": "kiosk.command refresh_cache marca dirty menus, preferences, cms, fulfillments y payments, y sincroniza entre sesiones. No borra imágenes (eso es resetKioskCache local).",
|
|
382
|
+
"acceptance": "Tras refresh_cache desde un canal, el canal receptor marca dirty esos recursos y los sincroniza en la siguiente sesión; las imágenes en caché no se borran.",
|
|
383
|
+
"channels": ["kiosk", "dashboard"],
|
|
384
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
385
|
+
"fixable": true,
|
|
386
|
+
"required": true,
|
|
387
|
+
"area": "cache",
|
|
388
|
+
"notes": "no borra imágenes; sync entre sesiones",
|
|
389
|
+
"verification": "deterministic",
|
|
390
|
+
"howTo": "Maestro: mandar refresh_cache; en la siguiente sesión el canal sincroniza menús, preferences, cms, fulfillments y payments; las imágenes en caché no se borran."
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"id": "RT-03",
|
|
394
|
+
"description": "Reset local de caché del canal: borra queries e imágenes; el siguiente full sync las repuebla.",
|
|
395
|
+
"acceptance": "Tras resetKioskCache, queries e imágenes quedan vacías y el siguiente full sync las vuelve a cargar.",
|
|
396
|
+
"channels": ["kiosk"],
|
|
397
|
+
"platforms": ["web", "android", "ios"],
|
|
398
|
+
"fixable": true,
|
|
399
|
+
"required": true,
|
|
400
|
+
"area": "cache",
|
|
401
|
+
"notes": "",
|
|
402
|
+
"verification": "deterministic",
|
|
403
|
+
"howTo": "Maestro: ejecutar resetKioskCache; afirmar que queries e imágenes quedan vacías y que el siguiente full sync las vuelve a cargar."
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
"id": "RT-04",
|
|
407
|
+
"description": "kiosk.command reload navega a /(kiosk)/(home) y hace full sync (bumpReloadNonce). No es un reboot de Android.",
|
|
408
|
+
"acceptance": "Al mandar Reload desde Devices, el canal vuelve a home y hace full sync.",
|
|
409
|
+
"channels": ["kiosk", "dashboard"],
|
|
410
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
411
|
+
"fixable": true,
|
|
412
|
+
"required": true,
|
|
413
|
+
"area": "cache",
|
|
414
|
+
"notes": "no es reboot de Android",
|
|
415
|
+
"verification": "deterministic",
|
|
416
|
+
"howTo": "Maestro: mandar Reload desde Devices y afirmar que el canal vuelve a home y hace full sync (no es un reboot de Android)."
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
"id": "RT-05",
|
|
420
|
+
"description": "kiosk.command check_ota hace fetch y apply de una actualización OTA.",
|
|
421
|
+
"acceptance": "No se puede validar fetch+apply sin un update OTA local preparado.",
|
|
422
|
+
"channels": ["kiosk", "dashboard"],
|
|
423
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
424
|
+
"fixable": false,
|
|
425
|
+
"required": false,
|
|
426
|
+
"area": "cache",
|
|
427
|
+
"notes": "dependencia de despliegue OTA; hace falta un update OTA local preparado",
|
|
428
|
+
"verification": "deterministic",
|
|
429
|
+
"howTo": "Si hay un update OTA preparado en local: Maestro dispara check_ota y afirma que se aplica la versión. Si no hay paquete, se omite (Requerido No, Fixable No). Sin cloud ni servicios de pago."
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
"id": "RT-06",
|
|
433
|
+
"description": "kiosk.command unlink borra la identidad local y el query cache.",
|
|
434
|
+
"acceptance": "Al desvincular, el device pierde el pairing y hay que volver a enlazar.",
|
|
435
|
+
"channels": ["kiosk", "dashboard"],
|
|
436
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
437
|
+
"fixable": true,
|
|
438
|
+
"required": true,
|
|
439
|
+
"area": "cache",
|
|
440
|
+
"notes": "",
|
|
441
|
+
"verification": "deterministic",
|
|
442
|
+
"howTo": "Maestro: desvincular el device y afirmar que pierde el pairing y hay que volver a enlazar."
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
"id": "RT-07",
|
|
446
|
+
"description": "Reboot del dispositivo vía AMAPI REBOOT, no vía kiosk.command. Requiere amapi_device_name enrolado; sin enrolamiento, precondition failed.",
|
|
447
|
+
"acceptance": "Con un device enrolado (amapi_device_name), el reboot se dispara por AMAPI; sin enrolamiento, falla con precondition failed. No usa kiosk.command.",
|
|
448
|
+
"channels": ["kiosk", "dashboard"],
|
|
449
|
+
"platforms": ["android", "dashboard"],
|
|
450
|
+
"fixable": false,
|
|
451
|
+
"required": false,
|
|
452
|
+
"area": "cache",
|
|
453
|
+
"notes": "requiere AMAPI/MDM y device enrolado",
|
|
454
|
+
"verification": "deterministic",
|
|
455
|
+
"howTo": "Si el device está enrolado en AMAPI: disparar el reboot y observar el reinicio. Sin enrolamiento, afirmar precondition failed. Si no hay device enrolado, se omite."
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
"id": "LOG-01",
|
|
459
|
+
"description": "En el canal se gestionan cuentas, equipos, roles y permisos por recurso (admin \"*\").",
|
|
460
|
+
"acceptance": "Un miembro con solo lectura no puede escribir; una API con scope insuficiente falla.",
|
|
461
|
+
"channels": ["dashboard"],
|
|
462
|
+
"platforms": ["dashboard"],
|
|
463
|
+
"fixable": true,
|
|
464
|
+
"required": true,
|
|
465
|
+
"area": "login",
|
|
466
|
+
"notes": "",
|
|
467
|
+
"verification": "deterministic",
|
|
468
|
+
"howTo": "Maestro: un miembro con solo lectura no puede escribir; una API con scope insuficiente falla."
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
"id": "LOG-02",
|
|
472
|
+
"description": "Los canales usan Firebase email/password y Google (Google en el canal que lo requiere necesita client ID). El canal con inicio anónimo tiene login/registro.",
|
|
473
|
+
"acceptance": "En los canales se puede entrar con email/password y con Google (si hay client ID). En el canal con inicio anónimo se puede hacer login/registro.",
|
|
474
|
+
"channels": ["kiosk", "web", "app"],
|
|
475
|
+
"platforms": ["web", "android", "ios"],
|
|
476
|
+
"fixable": true,
|
|
477
|
+
"required": true,
|
|
478
|
+
"area": "login",
|
|
479
|
+
"notes": "Google puede requerir config local",
|
|
480
|
+
"verification": "mixed",
|
|
481
|
+
"howTo": "Maestro prueba email/password de forma determinística. Google: un agente completa el consentimiento OAuth si el selector de cuentas o el captcha no se pueden automatizar en local."
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
"id": "LOG-03",
|
|
485
|
+
"description": "Pairing del canal (código/secret), gesto admin y código admin; idle y warning configurables.",
|
|
486
|
+
"acceptance": "Se puede enlazar el canal con código/secret, entrar al modo admin con el gesto y el código, y se respetan idle y warning configurados.",
|
|
487
|
+
"channels": ["kiosk", "dashboard"],
|
|
488
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
489
|
+
"fixable": true,
|
|
490
|
+
"required": true,
|
|
491
|
+
"area": "login",
|
|
492
|
+
"notes": "Aquí se vincula la identidad del equipo: el código mostrado en el dispositivo se registra en la administración con tienda y alias; el secret se gestiona en el dispositivo. Iniciar sesión como cliente no reemplaza el vínculo ni habilita la administración del equipo.",
|
|
493
|
+
"verification": "deterministic",
|
|
494
|
+
"howTo": "Maestro: enlazar con código y secret, entrar al modo admin con el gesto y el código, y afirmar que se respetan idle y warning."
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
"id": "LOG-04",
|
|
498
|
+
"description": "En el canal: operadores por tienda, PIN verificado server-side, login requiere conexión, sesión persistente, lock/unlock.",
|
|
499
|
+
"acceptance": "Un operador de la tienda inicia sesión con PIN (verificado en el server) estando online; la sesión persiste y se puede bloquear/desbloquear.",
|
|
500
|
+
"channels": ["pos", "dashboard"],
|
|
501
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
502
|
+
"fixable": true,
|
|
503
|
+
"required": true,
|
|
504
|
+
"area": "login",
|
|
505
|
+
"notes": "",
|
|
506
|
+
"verification": "deterministic",
|
|
507
|
+
"howTo": "Maestro: un operador inicia sesión con PIN estando online; la sesión persiste y se puede bloquear y desbloquear."
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
"id": "LOG-05",
|
|
511
|
+
"description": "En el canal, biometría opcional del mismo operador, solo nativa.",
|
|
512
|
+
"acceptance": "En un device o emulador compatible, el mismo operador puede habilitar y usar biometría para desbloquear.",
|
|
513
|
+
"channels": ["pos"],
|
|
514
|
+
"platforms": ["android", "ios"],
|
|
515
|
+
"fixable": true,
|
|
516
|
+
"required": false,
|
|
517
|
+
"area": "login",
|
|
518
|
+
"notes": "solo nativa; no aplica en Web; hace falta device/emulador compatible",
|
|
519
|
+
"verification": "mixed",
|
|
520
|
+
"howTo": "Maestro inicia el flujo de biometría en un device o emulador compatible; un agente completa el prompt biométrico del sistema. No aplica en Web."
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"id": "LOG-06",
|
|
524
|
+
"description": "En el canal: el agente se configura por canal; se verifica cada 60 s y expulsa con 401/403; cambiar de customer implica navegación completa.",
|
|
525
|
+
"acceptance": "Un agente configurado mantiene la sesión si la verificación cada 60 s es válida; un 401/403 lo expulsa. Al cambiar de customer se recarga la navegación completa.",
|
|
526
|
+
"channels": ["call-center", "dashboard"],
|
|
527
|
+
"platforms": ["web", "dashboard"],
|
|
528
|
+
"fixable": true,
|
|
529
|
+
"required": true,
|
|
530
|
+
"area": "login",
|
|
531
|
+
"notes": "",
|
|
532
|
+
"verification": "deterministic",
|
|
533
|
+
"howTo": "Maestro: una sesión de agente válida se mantiene; al provocar un 401 o 403 se expulsa; al cambiar de customer se recarga la navegación completa."
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
"id": "LOG-07",
|
|
537
|
+
"description": "En el canal: sesión por handoff (token server-owned).",
|
|
538
|
+
"acceptance": "Un enlace de handoff válido abre sesión; uno vencido no.",
|
|
539
|
+
"channels": ["chat"],
|
|
540
|
+
"platforms": ["web", "android", "ios"],
|
|
541
|
+
"fixable": true,
|
|
542
|
+
"required": true,
|
|
543
|
+
"area": "login",
|
|
544
|
+
"notes": "",
|
|
545
|
+
"verification": "deterministic",
|
|
546
|
+
"howTo": "Maestro: un enlace de handoff válido abre sesión; uno vencido no."
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
"id": "CMS-01",
|
|
550
|
+
"description": "Blocks tipados TEXT, IMAGE, VIDEO, DOCUMENT, LIST (máx. 25 items / 10 hijos). La DB emite cms.changed por nombre; el canal consumidor lo recibe (otros canales por query).",
|
|
551
|
+
"acceptance": "Crear o editar un block usado por el template y ver el cambio en el canal en la siguiente sync.",
|
|
552
|
+
"channels": ["kiosk", "dashboard"],
|
|
553
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
554
|
+
"fixable": true,
|
|
555
|
+
"required": false,
|
|
556
|
+
"area": "cms",
|
|
557
|
+
"notes": "otros canales se actualizan por query, no por cms.changed; LIST máx. 25 items / 10 hijos",
|
|
558
|
+
"verification": "deterministic",
|
|
559
|
+
"howTo": "Maestro: crear o editar un block usado por el template y afirmar el cambio en el canal en la siguiente sync."
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
"id": "CMS-02",
|
|
563
|
+
"description": "Home por canal: video principal + fallback de imagen; bloques home-hero-video-app y home-hero-banners-web, según la configuración del canal.",
|
|
564
|
+
"acceptance": "Cada canal muestra su home configurado (video con fallback de imagen o bloques home-hero-video-app y home-hero-banners-web, según corresponda).",
|
|
565
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center"],
|
|
566
|
+
"platforms": ["web", "android", "ios"],
|
|
567
|
+
"fixable": true,
|
|
568
|
+
"required": false,
|
|
569
|
+
"area": "cms",
|
|
570
|
+
"notes": "los bloques nombrados cubren los canales correspondientes",
|
|
571
|
+
"verification": "deterministic",
|
|
572
|
+
"howTo": "Maestro: afirmar que cada canal muestra su home configurado (video con fallback de imagen, o los bloques hero/banners que le correspondan) según los assets configurados."
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
"id": "IMG-01",
|
|
576
|
+
"description": "Biblioteca multimedia en el canal (imagen 5 MB, video 50 MB, documento 10 MB). No hay multimedia.changed; el consumidor se actualiza al publicar o al refetch de menú o CMS.",
|
|
577
|
+
"acceptance": "Se puede subir una imagen de hasta 5 MB, un video de hasta 50 MB y un documento de hasta 10 MB; el consumidor refleja el cambio al publicar o al refetch de menú/CMS, no por un evento multimedia.changed.",
|
|
578
|
+
"channels": ["dashboard"],
|
|
579
|
+
"platforms": ["dashboard"],
|
|
580
|
+
"fixable": true,
|
|
581
|
+
"required": true,
|
|
582
|
+
"area": "images",
|
|
583
|
+
"notes": "no hay multimedia.changed",
|
|
584
|
+
"verification": "deterministic",
|
|
585
|
+
"howTo": "Maestro: subir una imagen de hasta 5 MB, un video de hasta 50 MB y un documento de hasta 10 MB; el consumidor lo refleja al publicar o al refetch, no por un evento multimedia.changed."
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
"id": "IMG-02",
|
|
589
|
+
"description": "Precarga offline de imágenes en los canales: batches de 8 con tolerancia 10% o precarga del catálogo, según el canal.",
|
|
590
|
+
"acceptance": "Cargar online, desconectar y seguir viendo el catálogo.",
|
|
591
|
+
"channels": ["kiosk", "pos"],
|
|
592
|
+
"platforms": ["web", "android", "ios"],
|
|
593
|
+
"fixable": true,
|
|
594
|
+
"required": false,
|
|
595
|
+
"area": "images",
|
|
596
|
+
"notes": "el canal usa batches de 8 con tolerancia 10%",
|
|
597
|
+
"verification": "deterministic",
|
|
598
|
+
"howTo": "Maestro: cargar el catálogo online, desconectar la red y afirmar que las imágenes precargadas siguen visibles."
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
"id": "HW-01",
|
|
602
|
+
"description": "Pagers en el canal: settings enabled/required/side/payment_ids/range_start/range_end + overrides de device; captura del número en checkout (kiosk.config.changed).",
|
|
603
|
+
"acceptance": "Se pueden habilitar pagers, restringir payment y rango, e ingresar un número válido y uno inválido.",
|
|
604
|
+
"channels": ["kiosk", "dashboard"],
|
|
605
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
606
|
+
"fixable": true,
|
|
607
|
+
"required": false,
|
|
608
|
+
"area": "hardware",
|
|
609
|
+
"notes": "",
|
|
610
|
+
"verification": "deterministic",
|
|
611
|
+
"howTo": "Maestro: habilitar pagers, restringir payment y rango, e ingresar un número válido y uno inválido."
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
"id": "HW-03",
|
|
615
|
+
"description": "El canal en la capa Web: teclado físico en keypad (dígitos, Backspace, Enter; excluye input/textarea). Solo Platform.OS === \"web\".",
|
|
616
|
+
"acceptance": "En el canal, dentro de la capa Web, el teclado físico escribe dígitos y responde a Backspace y Enter en el keypad, excepto cuando el foco está en un input o textarea.",
|
|
617
|
+
"channels": ["pos"],
|
|
618
|
+
"platforms": ["web"],
|
|
619
|
+
"fixable": true,
|
|
620
|
+
"required": false,
|
|
621
|
+
"area": "hardware",
|
|
622
|
+
"notes": "solo Platform.OS === \"web\"",
|
|
623
|
+
"verification": "deterministic",
|
|
624
|
+
"howTo": "Maestro en la capa Web del POS: enviar dígitos, Backspace y Enter al keypad y afirmar que no se capturan si el foco está en un input o textarea."
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
"id": "PRT-01",
|
|
628
|
+
"description": "Impresión en el canal de tickets on_open, on_count, on_close y on_withdrawal. Nativo expo-print; capa Web: iframe + window.print.",
|
|
629
|
+
"acceptance": "Al abrir, contar, cerrar o retirar caja se genera el ticket; en nativo usa expo-print y en la capa Web, iframe + window.print.",
|
|
630
|
+
"channels": ["pos"],
|
|
631
|
+
"platforms": ["web", "android", "ios"],
|
|
632
|
+
"fixable": true,
|
|
633
|
+
"required": false,
|
|
634
|
+
"area": "printing",
|
|
635
|
+
"notes": "",
|
|
636
|
+
"verification": "mixed",
|
|
637
|
+
"howTo": "Maestro dispara la impresión en apertura, conteo, cierre y retiro; un agente confirma el diálogo o preview (iframe + window.print en Web, o expo-print en nativo)."
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
"id": "CAJ-01",
|
|
641
|
+
"description": "Ciclo de caja del canal: apertura, venta, conteo, retiro, cierre, drawer-no-sale, fin de turno e historial. Sync cada 30 s; PIN/roles por acción.",
|
|
642
|
+
"acceptance": "Se puede abrir caja, vender, contar, retirar, cerrar, registrar drawer-no-sale, terminar el turno y ver el historial, con PIN/roles por acción y sync cada 30 s.",
|
|
643
|
+
"channels": ["pos", "dashboard"],
|
|
644
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
645
|
+
"fixable": true,
|
|
646
|
+
"required": false,
|
|
647
|
+
"area": "pos",
|
|
648
|
+
"notes": "El kick físico del cajón no forma parte de este check.",
|
|
649
|
+
"verification": "deterministic",
|
|
650
|
+
"howTo": "Maestro: abrir caja, vender, contar, retirar, cerrar, registrar drawer-no-sale, terminar el turno y ver el historial, con PIN o roles por acción. El kick físico del cajón no forma parte de este check."
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
"id": "CAJ-02",
|
|
654
|
+
"description": "Venta offline solo 100% CASH, dedupe por client_id; mixed/card necesita API. Al reconectar se envía una sola vez.",
|
|
655
|
+
"acceptance": "Desconectar, vender CASH, reconectar y ver una sola venta; mixed offline no debe encolarse.",
|
|
656
|
+
"channels": ["pos"],
|
|
657
|
+
"platforms": ["web", "android", "ios"],
|
|
658
|
+
"fixable": true,
|
|
659
|
+
"required": false,
|
|
660
|
+
"area": "pos",
|
|
661
|
+
"notes": "dedupe por client_id; mixed/card necesita API",
|
|
662
|
+
"verification": "deterministic",
|
|
663
|
+
"howTo": "Maestro: desconectar, vender 100% CASH, reconectar y afirmar una sola venta (dedupe por client_id); una venta mixed offline no debe encolarse."
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
"id": "CAJ-03",
|
|
667
|
+
"description": "Alertas de venta offline rechazada al reconciliar (por ejemplo si cambió disponibilidad o payment antes de reconectar).",
|
|
668
|
+
"acceptance": "Si al reconectar una venta offline CASH es rechazada (por cambio de disponibilidad o payment), el canal muestra la alerta.",
|
|
669
|
+
"channels": ["pos"],
|
|
670
|
+
"platforms": ["web", "android", "ios"],
|
|
671
|
+
"fixable": true,
|
|
672
|
+
"required": false,
|
|
673
|
+
"area": "pos",
|
|
674
|
+
"notes": "",
|
|
675
|
+
"verification": "deterministic",
|
|
676
|
+
"howTo": "Maestro: vender CASH offline, cambiar disponibilidad o payment antes de reconectar, y afirmar que el canal muestra la alerta de rechazo."
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
"id": "CHT-01",
|
|
680
|
+
"description": "Configurar en el canal el bot de WhatsApp (nodos start, message, question, handoff, summary, order_placed, end; hasta 3 opciones por pregunta).",
|
|
681
|
+
"acceptance": "Se puede armar un flujo con esos nodos, con hasta 3 opciones por pregunta, y se persiste.",
|
|
682
|
+
"channels": ["chat", "dashboard"],
|
|
683
|
+
"platforms": ["web", "dashboard"],
|
|
684
|
+
"fixable": true,
|
|
685
|
+
"required": false,
|
|
686
|
+
"area": "chat",
|
|
687
|
+
"notes": "",
|
|
688
|
+
"verification": "deterministic",
|
|
689
|
+
"howTo": "Maestro en Dashboard: armar un flujo con nodos start, message, question, handoff, summary, order_placed y end, con hasta 3 opciones por pregunta; persistir y recargar."
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
"id": "CHT-03",
|
|
693
|
+
"description": "Gestión de conversaciones y agentes (providers/flow/conversations; handoff y cliente atendido).",
|
|
694
|
+
"acceptance": "Se puede ver y gestionar conversaciones, hacer handoff y marcar un cliente como atendido.",
|
|
695
|
+
"channels": ["chat", "call-center", "dashboard"],
|
|
696
|
+
"platforms": ["web", "dashboard"],
|
|
697
|
+
"fixable": true,
|
|
698
|
+
"required": false,
|
|
699
|
+
"area": "chat",
|
|
700
|
+
"notes": "",
|
|
701
|
+
"verification": "deterministic",
|
|
702
|
+
"howTo": "Maestro: ver y gestionar conversaciones, hacer handoff y marcar un cliente como atendido."
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
"id": "MKT-01",
|
|
706
|
+
"description": "Upsell, downsell y relacionados según datos del menú, en los canales y en el preview del canal correspondiente.",
|
|
707
|
+
"acceptance": "Con productos configurados como upsell, downsell o relacionados, se muestran en los canales y en el preview del canal correspondiente.",
|
|
708
|
+
"channels": ["kiosk", "pos", "dashboard"],
|
|
709
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
710
|
+
"fixable": true,
|
|
711
|
+
"required": false,
|
|
712
|
+
"area": "marketing",
|
|
713
|
+
"notes": "",
|
|
714
|
+
"verification": "deterministic",
|
|
715
|
+
"howTo": "Maestro: con productos de prueba configurados como upsell, downsell o relacionados, afirmar que se muestran en los canales y en el preview correspondiente."
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
"id": "MKT-02",
|
|
719
|
+
"description": "Páginas de cupones y loyalty vía Masivo (product=coupons|loyalty).",
|
|
720
|
+
"acceptance": "Se pueden abrir y configurar las páginas de cupones y loyalty (product=coupons o loyalty) en el canal.",
|
|
721
|
+
"channels": ["dashboard"],
|
|
722
|
+
"platforms": ["dashboard"],
|
|
723
|
+
"fixable": true,
|
|
724
|
+
"required": false,
|
|
725
|
+
"area": "marketing",
|
|
726
|
+
"notes": "el canje real queda en MKT-03",
|
|
727
|
+
"verification": "deterministic",
|
|
728
|
+
"howTo": "Maestro: abrir y afirmar que se pueden configurar las páginas de cupones y loyalty (product=coupons o loyalty). El canje real queda en MKT-03."
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
"id": "MKT-03",
|
|
732
|
+
"description": "Si el merchant tiene Masivo (sandbox o producción), un canje de cupón/loyalty se aplica al total.",
|
|
733
|
+
"acceptance": "Con Masivo configurado (sandbox o producción), un canje de cupón o loyalty se aplica al total.",
|
|
734
|
+
"channels": ["dashboard"],
|
|
735
|
+
"platforms": ["dashboard"],
|
|
736
|
+
"fixable": true,
|
|
737
|
+
"required": false,
|
|
738
|
+
"area": "marketing",
|
|
739
|
+
"notes": "solo si existe Masivo",
|
|
740
|
+
"verification": "deterministic",
|
|
741
|
+
"howTo": "Maestro: si hay Masivo (sandbox o producción), canjear un cupón o loyalty y afirmar que se aplica al total. Si no hay Masivo, se omite."
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
"id": "MKT-04",
|
|
745
|
+
"description": "Redemptions en orden (en los canales): puede cubrir parte o todo el total; si falta payment REDEMPTION, error REDEEM_PAYMENT_METHOD_NOT_CONFIGURED.",
|
|
746
|
+
"acceptance": "Con REDEMPTION configurado, un redemption cubre parte o todo el total. Sin ese payment method, el place falla con REDEEM_PAYMENT_METHOD_NOT_CONFIGURED.",
|
|
747
|
+
"channels": ["kiosk", "web", "app"],
|
|
748
|
+
"platforms": ["web", "android", "ios"],
|
|
749
|
+
"fixable": true,
|
|
750
|
+
"required": false,
|
|
751
|
+
"area": "marketing",
|
|
752
|
+
"notes": "",
|
|
753
|
+
"verification": "deterministic",
|
|
754
|
+
"howTo": "Maestro: con REDEMPTION configurado, un redemption cubre parte o todo el total; sin ese payment method, el place falla con REDEEM_PAYMENT_METHOD_NOT_CONFIGURED."
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
"id": "MKT-05",
|
|
758
|
+
"description": "Analytics: presencia de tracking plans y destinos de analítica en los canales (cobertura no confirmada en todos los canales).",
|
|
759
|
+
"acceptance": "Se puede configurar tracking plans y destinos de analítica para los canales. El envío real a destinos externos no se valida en este check.",
|
|
760
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
761
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
762
|
+
"fixable": true,
|
|
763
|
+
"required": false,
|
|
764
|
+
"area": "marketing",
|
|
765
|
+
"notes": "Fixable Sí para la config; el envío real a destinos externos es No. Cobertura no confirmada en todos los canales",
|
|
766
|
+
"verification": "deterministic",
|
|
767
|
+
"howTo": "Maestro en Dashboard: configurar tracking plans y destinos. No se valida el envío real a GA4, Meta u otros destinos externos."
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
"id": "ADM-01",
|
|
771
|
+
"description": "CRUD de merchants, organizaciones, ambientes, tiendas, marcas, canales y labels, con permisos y filtro por scope.",
|
|
772
|
+
"acceptance": "Se puede crear una entidad de prueba y filtrar su scope.",
|
|
773
|
+
"channels": ["dashboard"],
|
|
774
|
+
"platforms": ["dashboard"],
|
|
775
|
+
"fixable": true,
|
|
776
|
+
"required": true,
|
|
777
|
+
"area": "admin",
|
|
778
|
+
"notes": "",
|
|
779
|
+
"verification": "deterministic",
|
|
780
|
+
"howTo": "Maestro en Dashboard: crear una entidad de prueba (merchant, org, ambiente, tienda, marca, canal o label) y filtrar por scope, respetando permisos. Usar un ambiente de prueba."
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
"id": "ADM-02",
|
|
784
|
+
"description": "Webhooks/callbacks de workflow contra un receptor que controle el cliente (URL de callback que el merchant pueda apuntar): order.voided, order.fulfilled, order.refunded, order.reversed, order.status.changed, order.cancelled.",
|
|
785
|
+
"acceptance": "Un receptor controlado por el cliente recibe el evento al avanzar, cancelar o reembolsar (no implica levantar FireSpark en local).",
|
|
786
|
+
"channels": ["server", "dashboard"],
|
|
787
|
+
"platforms": ["web", "dashboard"],
|
|
788
|
+
"fixable": true,
|
|
789
|
+
"required": false,
|
|
790
|
+
"area": "admin",
|
|
791
|
+
"notes": "",
|
|
792
|
+
"verification": "deterministic",
|
|
793
|
+
"howTo": "Script local con un receptor HTTP en tu máquina: avanzar, cancelar o reembolsar y afirmar que llegan order.voided, fulfilled, refunded, reversed, status.changed y cancelled. El destino es una URL que controle el cliente, no FireSpark local."
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
"id": "ADM-03",
|
|
797
|
+
"description": "Importaciones, migraciones (customers/orders), exports y audit logs.",
|
|
798
|
+
"acceptance": "Se puede importar un dataset mínimo, seguir errores/progreso, exportar y consultar auditoría.",
|
|
799
|
+
"channels": ["dashboard"],
|
|
800
|
+
"platforms": ["dashboard"],
|
|
801
|
+
"fixable": true,
|
|
802
|
+
"required": false,
|
|
803
|
+
"area": "admin",
|
|
804
|
+
"notes": "",
|
|
805
|
+
"verification": "deterministic",
|
|
806
|
+
"howTo": "Maestro: importar un dataset mínimo, seguir errores y progreso, exportar y consultar audit logs."
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
"id": "ADM-04",
|
|
810
|
+
"description": "Channel Builder: elegir template, generar canal y observar build.",
|
|
811
|
+
"acceptance": "Se puede elegir un template, generar un canal y observar el build.",
|
|
812
|
+
"channels": ["dashboard"],
|
|
813
|
+
"platforms": ["dashboard"],
|
|
814
|
+
"fixable": true,
|
|
815
|
+
"required": false,
|
|
816
|
+
"area": "admin",
|
|
817
|
+
"notes": "Concreto del repo: packages/channel-builder/src/generate.ts copia templates/{folder} a apps/{slug}, siempre genera el server (no se pide como template), escribe channel-manifest.json y channel-manifest.ts, .env.example y .env.local, parcha app.json en Expo, espeja los kits de UI standalone del SDK en packages/, y arma el package.json raíz, pnpm-workspace, turbo, CI y README. Orchestrator: apps/api/src/workflows/builderBuild/builderBuild.workflow.ts (generate, diseño opcional, verify, PR). UI: apps/dashboard/.../builder/.",
|
|
818
|
+
"verification": "deterministic",
|
|
819
|
+
"howTo": "Maestro en Dashboard: elegir un template, generar un canal y afirmar el estado visible del build (éxito o error en Builder)."
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
"id": "SDK-01",
|
|
823
|
+
"description": "Los canales del cliente usan la última versión publicada del SDK de FireSpark.",
|
|
824
|
+
"acceptance": "En Builder, con el repo del cliente conectado, cada canal aparece al día (sdkVersion igual a latestSdkVersion, sin cambios de SDK pendientes). En el repo del canal, channel-manifest.json tiene sdkVersion igual a la versión latest de npm y ningún @fire-spark/* usa workspace:* (salvo @fire-spark/rn-ui).",
|
|
825
|
+
"channels": [
|
|
826
|
+
"kiosk",
|
|
827
|
+
"pos",
|
|
828
|
+
"web",
|
|
829
|
+
"app",
|
|
830
|
+
"chat",
|
|
831
|
+
"call-center",
|
|
832
|
+
"server",
|
|
833
|
+
"dashboard"
|
|
834
|
+
],
|
|
835
|
+
"platforms": ["dashboard"],
|
|
836
|
+
"fixable": true,
|
|
837
|
+
"required": true,
|
|
838
|
+
"area": "sdk",
|
|
839
|
+
"notes": "Inspección estática del repo del canal del cliente o vía Builder. No hay header de versión en runtime: SDK_VERSION de analítica está fijo en 0.0.1 y no sirve. Latest al 6-nov-2026: 0.13.1.",
|
|
840
|
+
"verification": "deterministic",
|
|
841
|
+
"howTo": "Script local o Maestro en Builder: cada canal tiene sdkVersion igual a la latest de npm y ningún @fire-spark/* usa workspace:* (salvo @fire-spark/rn-ui). No usar el header de analítica (está fijo en 0.0.1)."
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
"id": "SDK-02",
|
|
845
|
+
"description": "Los canales tienen aplicados los cambios pendientes de su plantilla.",
|
|
846
|
+
"acceptance": "En Builder, ningún canal muestra cambios de plantilla pendientes (templateVersion igual a la última versión del changelog de su plantilla). Si hay pendientes, se listan con versión, título y descripción.",
|
|
847
|
+
"channels": [
|
|
848
|
+
"kiosk",
|
|
849
|
+
"pos",
|
|
850
|
+
"web",
|
|
851
|
+
"app",
|
|
852
|
+
"chat",
|
|
853
|
+
"call-center",
|
|
854
|
+
"server",
|
|
855
|
+
"dashboard"
|
|
856
|
+
],
|
|
857
|
+
"platforms": ["dashboard"],
|
|
858
|
+
"fixable": true,
|
|
859
|
+
"required": true,
|
|
860
|
+
"area": "sdk",
|
|
861
|
+
"notes": "Mismo origen que SDK-01. Las correcciones de las plantillas llegan por aquí.",
|
|
862
|
+
"verification": "deterministic",
|
|
863
|
+
"howTo": "Maestro en Builder: ningún canal muestra cambios de plantilla pendientes (templateVersion al día). Si hay pendientes, se listan con versión, título y descripción."
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
"id": "SDK-03",
|
|
867
|
+
"description": "Las tiendas que ofrece el canal vienen de la API. No están quemadas en el código ni en variables de entorno.",
|
|
868
|
+
"acceptance": "El selector o la lista de tiendas coincide con las tiendas activas del canal en el dashboard (GET /api/storefront/v1/stores o stores.nearby). En los canales enlazados por device, la tienda es la asignada al device. Si creas o desactivas una tienda de prueba, el canal lo refleja tras el refetch. El deep link ?store_id=… selecciona esa tienda. Ningún ID de tienda aparece en el bundle ni en el entorno desplegado.",
|
|
869
|
+
"channels": [
|
|
870
|
+
"kiosk",
|
|
871
|
+
"pos",
|
|
872
|
+
"web",
|
|
873
|
+
"app",
|
|
874
|
+
"chat",
|
|
875
|
+
"call-center",
|
|
876
|
+
"dashboard"
|
|
877
|
+
],
|
|
878
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
879
|
+
"fixable": true,
|
|
880
|
+
"required": true,
|
|
881
|
+
"area": "sdk",
|
|
882
|
+
"notes": "La única variable de scope de la plantilla es el channel ID. Tienda, fulfillment y marca se descubren mientras se ordena.",
|
|
883
|
+
"verification": "deterministic",
|
|
884
|
+
"howTo": "Maestro: la lista de tiendas coincide con la API y el dashboard; ?store_id= selecciona esa tienda; crear o desactivar una de prueba se refleja al refetch. Un script local afirma que ningún ID de tienda está quemado en el bundle ni en el env."
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
"id": "SDK-04",
|
|
888
|
+
"description": "Los fulfillments del canal vienen de la API según el canal. No están quemados.",
|
|
889
|
+
"acceptance": "Los fulfillments ofrecidos coinciden con los asignados al canal en el dashboard (GET /api/storefront/v1/fulfillment con channel_id). Si quitas el canal de un fulfillment, desaparece tras el refetch. Si el fulfillment guardado deja de estar disponible, se muestra el aviso y se pide elegir otro. El deep link ?fulfillment_id=… funciona.",
|
|
890
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
891
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
892
|
+
"fixable": true,
|
|
893
|
+
"required": true,
|
|
894
|
+
"area": "sdk",
|
|
895
|
+
"notes": "",
|
|
896
|
+
"verification": "deterministic",
|
|
897
|
+
"howTo": "Maestro: los fulfillments coinciden con la API; al quitar el canal de uno, desaparece al refetch; si el guardado deja de estar disponible, se pide elegir otro; ?fulfillment_id= funciona."
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
"id": "SDK-05",
|
|
901
|
+
"description": "Los métodos de pago del checkout vienen de la API para la orden, no de una lista quemada.",
|
|
902
|
+
"acceptance": "Los métodos que muestra el checkout coinciden con la respuesta de GET /api/storefront/v1/payments?order_id=… y con los payments activos en el dashboard para ese scope. Si desactivas uno de prueba, desaparece en la siguiente carga del checkout. Ningún payment_id aparece quemado en el bundle.",
|
|
903
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
904
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
905
|
+
"fixable": true,
|
|
906
|
+
"required": true,
|
|
907
|
+
"area": "sdk",
|
|
908
|
+
"notes": "Complementa PAY-01 (dashboard/API) y PAY-03 (filtrado por device).",
|
|
909
|
+
"verification": "deterministic",
|
|
910
|
+
"howTo": "Maestro: los métodos del checkout coinciden con GET /payments?order_id=…; al desactivar uno de prueba, desaparece en la siguiente carga. Un script local afirma que no hay payment_id quemado en el bundle."
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
"id": "SDK-06",
|
|
914
|
+
"description": "Los IDs de orden los genera el SDK (genOrderId/genId), no el canal a mano.",
|
|
915
|
+
"acceptance": "Toda orden nueva creada desde el canal tiene un id con formato ord-<store_id>-<hash> (12 caracteres [0-9a-z] por defecto). Si el merchant lo personalizó, el id cumple isGenId. Ningún id es UUID, timestamp ni secuencia inventada. El mismo id se ve en la request POST /api/storefront/v1/orders y en Órdenes del dashboard.",
|
|
916
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
917
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
918
|
+
"fixable": true,
|
|
919
|
+
"required": true,
|
|
920
|
+
"area": "sdk",
|
|
921
|
+
"notes": "En el canal que crea órdenes con services.pos.createOrder, el server genera POS-<client_id> por diseño (dedupe offline), así que este check no aplica ahí. Si se reabre con un order_id inválido, el server responde ORDER_ID_INVALID (400) u ORDER_ID_EXISTS (409).",
|
|
922
|
+
"verification": "deterministic",
|
|
923
|
+
"howTo": "Maestro: crear una orden y afirmar que el id cumple ord-<store_id>-<hash> (o isGenId si el merchant lo personalizó). En el canal de POS con createOrder, el id POS-<client_id> es válido por diseño."
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
"id": "SDK-07",
|
|
927
|
+
"description": "El canal no muestra datos demo de la plantilla: tienda de muestra en carrito y checkout, ni textos legales de muestra.",
|
|
928
|
+
"acceptance": "El nombre, la dirección y la imagen de la tienda en carrito y checkout son los de la tienda seleccionada. No aparece \"Demo Kitchen\", \"Av. Isla Floreana E5-44\" ni la imagen de muestra. Las páginas legales (términos, privacidad, reembolsos, entregas) muestran el contenido del merchant, no textos como \"storefront demo\".",
|
|
929
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center"],
|
|
930
|
+
"platforms": ["web", "android", "ios"],
|
|
931
|
+
"fixable": true,
|
|
932
|
+
"required": true,
|
|
933
|
+
"area": "sdk",
|
|
934
|
+
"notes": "En las plantillas stock, DEMO_STORE y LEGAL_DOCS están quemados, así que un canal sin personalizar falla este check. Es un defecto real que ve el cliente final y se puede corregir.",
|
|
935
|
+
"verification": "deterministic",
|
|
936
|
+
"howTo": "Maestro: afirmar que no aparecen Demo Kitchen, Av. Isla Floreana E5-44 ni textos legales de muestra; nombre, dirección e imagen son los de la tienda del merchant."
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
"id": "SDK-08",
|
|
940
|
+
"description": "El build desplegado no expone secretos de servidor.",
|
|
941
|
+
"acceptance": "En el JS público del canal no aparecen FIRESPARK_CLIENT_SECRET, FIRESPARK_WEBHOOK_SECRET ni otras credenciales de servidor. Solo hay variables públicas (prefijos NEXT_PUBLIC_ y EXPO_PUBLIC_: Firebase, channel ID, Maps).",
|
|
942
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center"],
|
|
943
|
+
"platforms": ["web", "android", "ios"],
|
|
944
|
+
"fixable": true,
|
|
945
|
+
"required": true,
|
|
946
|
+
"area": "sdk",
|
|
947
|
+
"notes": "En Android/iOS se inspecciona el bundle JS del build.",
|
|
948
|
+
"verification": "deterministic",
|
|
949
|
+
"howTo": "Script local: inspeccionar el JS público o el bundle y afirmar que no aparecen FIRESPARK_CLIENT_SECRET, FIRESPARK_WEBHOOK_SECRET ni otras credenciales de servidor."
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
"id": "SDK-09",
|
|
953
|
+
"description": "El canal apunta al API y al channel correctos del merchant.",
|
|
954
|
+
"acceptance": "Las requests van al API de FireSpark configurado (por defecto https://firespark.cloud), nunca a localhost. El channel_id de las requests es el del canal en el dashboard y ese canal está activo. Con un channel ID inválido, el canal muestra el estado de storefront no encontrado y no un menú vacío.",
|
|
955
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center"],
|
|
956
|
+
"platforms": ["web", "android", "ios"],
|
|
957
|
+
"fixable": true,
|
|
958
|
+
"required": true,
|
|
959
|
+
"area": "sdk",
|
|
960
|
+
"notes": "",
|
|
961
|
+
"verification": "deterministic",
|
|
962
|
+
"howTo": "Script local: las requests van al API configurado (por defecto firespark.cloud), nunca a localhost, con el channel_id correcto. Maestro: un channel ID inválido muestra storefront no encontrado, no un menú vacío."
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
"id": "LOG-08",
|
|
966
|
+
"description": "Sesión de invitado: sin cuenta se crea una sesión anónima que permite ver el menú y pedir. Las secciones de cliente identificado quedan restringidas.",
|
|
967
|
+
"acceptance": "Al abrir el canal sin login se crea una sesión anónima y puedes armar el carrito y hacer checkout como invitado. Historial, direcciones, facturación y notificaciones redirigen a la cuenta o piden iniciar sesión.",
|
|
968
|
+
"channels": ["kiosk", "web", "app"],
|
|
969
|
+
"platforms": ["web", "android", "ios"],
|
|
970
|
+
"fixable": true,
|
|
971
|
+
"required": true,
|
|
972
|
+
"area": "login",
|
|
973
|
+
"notes": "LOG-02 cubre el login/registro desde la sesión anónima. Este check cubre el uso como invitado.",
|
|
974
|
+
"verification": "deterministic",
|
|
975
|
+
"howTo": "Maestro: abrir el canal sin login, afirmar sesión anónima, armar carrito y checkout de invitado; historial, direcciones, facturación y notificaciones piden cuenta."
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
"id": "LOG-09",
|
|
979
|
+
"description": "Rutas protegidas y registro completo. Sin sesión se redirige a login. Con cuenta del proveedor pero sin customer se pide completar el registro.",
|
|
980
|
+
"acceptance": "Sin sesión, abrir menú, checkout o perfil lleva a login. Con login en el proveedor y sin customer identificado, lleva a completar el registro; al terminar, entras al menú. Con sesión identificada, la pantalla de login redirige al menú. Si hay desync del customer, el canal no queda en un estado intermedio (409 desync).",
|
|
981
|
+
"channels": ["web", "app"],
|
|
982
|
+
"platforms": ["web", "android", "ios"],
|
|
983
|
+
"fixable": true,
|
|
984
|
+
"required": true,
|
|
985
|
+
"area": "login",
|
|
986
|
+
"notes": "",
|
|
987
|
+
"verification": "deterministic",
|
|
988
|
+
"howTo": "Maestro: sin sesión, menú, checkout o perfil llevan a login; con login del proveedor y sin customer, piden completar registro; con sesión identificada, login redirige al menú; no queda un 409 de desync."
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
"id": "LOG-10",
|
|
992
|
+
"description": "La sesión persiste al recargar y cerrar sesión la elimina.",
|
|
993
|
+
"acceptance": "Recargar o reabrir el canal mantiene la sesión. Al cerrar sesión se borra la cookie o el token local y la sesión del proveedor, y volver a una ruta protegida pide login. Una sesión vencida o malformada se trata como sin sesión.",
|
|
994
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center"],
|
|
995
|
+
"platforms": ["web", "android", "ios"],
|
|
996
|
+
"fixable": true,
|
|
997
|
+
"required": true,
|
|
998
|
+
"area": "login",
|
|
999
|
+
"notes": "",
|
|
1000
|
+
"verification": "deterministic",
|
|
1001
|
+
"howTo": "Maestro: recargar o reabrir mantiene la sesión; al cerrar sesión se borra el token y una ruta protegida vuelve a pedir login. Una sesión vencida se trata como sin sesión."
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
"id": "LOG-11",
|
|
1005
|
+
"description": "Tras iniciar sesión, el token del proveedor de identidad se canjea por un token de storefront del merchant.",
|
|
1006
|
+
"acceptance": "Después del login, POST /api/storefront/v1/oauth/exchange responde 200 con access_token y las llamadas siguientes van con Bearer. Si el proyecto de identidad es compartido, la audiencia coincide con el alias del OAuth app del dashboard. Sin OAuth app configurado, el canje falla y el canal no carga datos.",
|
|
1007
|
+
"channels": ["kiosk", "web", "app", "call-center", "dashboard"],
|
|
1008
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1009
|
+
"fixable": true,
|
|
1010
|
+
"required": true,
|
|
1011
|
+
"area": "login",
|
|
1012
|
+
"notes": "El SDK reusa el token hasta expires_in y vuelve a canjear solo.",
|
|
1013
|
+
"verification": "deterministic",
|
|
1014
|
+
"howTo": "Maestro hace el login y un script local afirma que POST /api/storefront/v1/oauth/exchange responde 200 con access_token y que las llamadas siguientes van con Bearer."
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
"id": "LOG-12",
|
|
1018
|
+
"description": "El cliente puede eliminar su cuenta desde el perfil.",
|
|
1019
|
+
"acceptance": "Al confirmar la eliminación se borra el customer, se cierra la sesión y el canal vuelve al menú. El customer queda eliminado en Clientes del dashboard.",
|
|
1020
|
+
"channels": ["kiosk", "web", "app", "dashboard"],
|
|
1021
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1022
|
+
"fixable": true,
|
|
1023
|
+
"required": true,
|
|
1024
|
+
"area": "login",
|
|
1025
|
+
"notes": "",
|
|
1026
|
+
"verification": "deterministic",
|
|
1027
|
+
"howTo": "Maestro: confirmar la eliminación de una cuenta de prueba; se borra el customer, se cierra la sesión y el canal vuelve al menú. El customer queda eliminado en Clientes del dashboard."
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
"id": "LOG-13",
|
|
1031
|
+
"description": "Acceso al dashboard: iniciar sesión, registrarse, recuperar y restablecer la contraseña.",
|
|
1032
|
+
"acceptance": "Entras con credenciales válidas y las inválidas se rechazan. El flujo de recuperar contraseña permite restablecerla y entrar con la nueva.",
|
|
1033
|
+
"channels": ["dashboard"],
|
|
1034
|
+
"platforms": ["dashboard"],
|
|
1035
|
+
"fixable": true,
|
|
1036
|
+
"required": true,
|
|
1037
|
+
"area": "login",
|
|
1038
|
+
"notes": "Recuperar la contraseña requiere leer el correo de prueba.",
|
|
1039
|
+
"verification": "mixed",
|
|
1040
|
+
"howTo": "Maestro prueba login válido e inválido de forma determinística. Recuperar y restablecer la contraseña: el agente lee el correo de prueba (Gmail u otra bandeja que ya tengas) y completa el reset."
|
|
1041
|
+
},
|
|
1042
|
+
{
|
|
1043
|
+
"id": "LOG-14",
|
|
1044
|
+
"description": "MFA en el dashboard: enrolamiento, step-up y exigencia por merchant (mfa_required).",
|
|
1045
|
+
"acceptance": "Un usuario puede enrolar MFA y entrar con código. Si el merchant activa mfa_required, un miembro sin MFA queda bloqueado hasta enrolar.",
|
|
1046
|
+
"channels": ["dashboard"],
|
|
1047
|
+
"platforms": ["dashboard"],
|
|
1048
|
+
"fixable": true,
|
|
1049
|
+
"required": false,
|
|
1050
|
+
"area": "login",
|
|
1051
|
+
"notes": "Solo si el merchant exige o usa MFA.",
|
|
1052
|
+
"verification": "mixed",
|
|
1053
|
+
"howTo": "Maestro llega a la pantalla de enrolamiento MFA. Un agente lee el QR o usa el secreto TOTP para completar el step-up y afirmar que, con mfa_required, un miembro sin MFA queda bloqueado."
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
"id": "MEN-08",
|
|
1057
|
+
"description": "Render de descuentos: los productos con anchor_price o anchor_price_percentage muestran la etiqueta -X% y el precio anterior tachado. El ahorro se ve en el carrito.",
|
|
1058
|
+
"acceptance": "Con un producto en descuento, la card y el detalle muestran -X% y el precio anterior tachado. Al agregarlo, la línea y el total usan el precio con descuento y el carrito muestra el ahorro (discount_total).",
|
|
1059
|
+
"channels": ["kiosk", "pos", "web", "app", "chat", "call-center"],
|
|
1060
|
+
"platforms": ["web", "android", "ios"],
|
|
1061
|
+
"fixable": true,
|
|
1062
|
+
"required": true,
|
|
1063
|
+
"area": "menu",
|
|
1064
|
+
"notes": "",
|
|
1065
|
+
"verification": "deterministic",
|
|
1066
|
+
"howTo": "Maestro: un producto con descuento muestra la etiqueta -X% y el precio anterior tachado; al agregarlo, la línea y el total usan el precio con descuento y el carrito muestra el ahorro."
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
"id": "MEN-09",
|
|
1070
|
+
"description": "Render de agotado: un producto OUT_OF_STOCK se muestra en gris con \"Agotado\" o \"Agotado hasta …\" (out_of_stock_until) y no se puede agregar.",
|
|
1071
|
+
"acceptance": "Un producto OUT_OF_STOCK se ve deshabilitado y con la etiqueta. Con out_of_stock_until, muestra el tiempo relativo. No se puede agregar. Si ya estaba en el carrito, la validación lo marca con OUT_OF_STOCK.",
|
|
1072
|
+
"channels": ["kiosk", "pos", "web", "app", "chat", "call-center"],
|
|
1073
|
+
"platforms": ["web", "android", "ios"],
|
|
1074
|
+
"fixable": true,
|
|
1075
|
+
"required": true,
|
|
1076
|
+
"area": "menu",
|
|
1077
|
+
"notes": "Para provocarlo, agota el producto desde el dashboard (MEN-04).",
|
|
1078
|
+
"verification": "deterministic",
|
|
1079
|
+
"howTo": "Maestro: un producto OUT_OF_STOCK se ve deshabilitado y con la etiqueta (o Agotado hasta …); no se puede agregar. Si ya estaba en el carrito, la validación devuelve OUT_OF_STOCK."
|
|
1080
|
+
},
|
|
1081
|
+
{
|
|
1082
|
+
"id": "MEN-10",
|
|
1083
|
+
"description": "Disponibilidad por horario: los productos y categorías fuera de su schedule muestran \"Fuera de horario\" o \"Disponible en …\" y no se pueden pedir. Un producto HIDDEN no se puede agregar.",
|
|
1084
|
+
"acceptance": "Con un producto o categoría fuera de horario se ve la etiqueta con el próximo inicio y el botón de agregar deshabilitado. Un producto HIDDEN no se puede agregar. Si quedó en el carrito, la validación devuelve OUTSIDE_HOURS o HIDDEN.",
|
|
1085
|
+
"channels": ["kiosk", "pos", "web", "app", "chat", "call-center"],
|
|
1086
|
+
"platforms": ["web", "android", "ios"],
|
|
1087
|
+
"fixable": true,
|
|
1088
|
+
"required": true,
|
|
1089
|
+
"area": "menu",
|
|
1090
|
+
"notes": "Es el \"unavailable until\" por horario. El agotado con fecha está en MEN-09.",
|
|
1091
|
+
"verification": "deterministic",
|
|
1092
|
+
"howTo": "Maestro: un producto o categoría fuera de horario muestra la etiqueta y no se puede pedir; un HIDDEN no se agrega. Si quedó en el carrito, la validación devuelve OUTSIDE_HOURS o HIDDEN."
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
"id": "MEN-11",
|
|
1096
|
+
"description": "Variantes: grupos de variantes seleccionables, precio \"desde\" y variantes agotadas deshabilitadas.",
|
|
1097
|
+
"acceptance": "Un producto con variantes muestra \"Desde $X\" (starting_price) en la card. En el detalle, elegir otra variante cambia el precio. Una variante agotada aparece deshabilitada con \"Agotado\". La base no ordenable no se puede agregar sola.",
|
|
1098
|
+
"channels": ["kiosk", "pos", "web", "app", "chat", "call-center"],
|
|
1099
|
+
"platforms": ["web", "android", "ios"],
|
|
1100
|
+
"fixable": true,
|
|
1101
|
+
"required": true,
|
|
1102
|
+
"area": "menu",
|
|
1103
|
+
"notes": "Errores de validación: VARIANT_NOT_OFFERED, VARIANT_BASE_NOT_ORDERABLE.",
|
|
1104
|
+
"verification": "deterministic",
|
|
1105
|
+
"howTo": "Maestro: la card muestra Desde $X; al elegir otra variante cambia el precio; una variante agotada aparece deshabilitada. La base no ordenable no se agrega sola."
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
"id": "MEN-12",
|
|
1109
|
+
"description": "Modificadores: reglas de mínimo y máximo por grupo, obligatorios, anidados y precio adicional.",
|
|
1110
|
+
"acceptance": "Cada grupo muestra su regla (elige 1, hasta N, entre N y M) y si es obligatorio. No se puede agregar hasta que la selección sea VALID ni pasar del máximo. Un modificador con precio suma +$X a la línea y al total. Los anidados exigen completar su propio grupo.",
|
|
1111
|
+
"channels": ["kiosk", "pos", "web", "app", "chat", "call-center"],
|
|
1112
|
+
"platforms": ["web", "android", "ios"],
|
|
1113
|
+
"fixable": true,
|
|
1114
|
+
"required": true,
|
|
1115
|
+
"area": "menu",
|
|
1116
|
+
"notes": "Si la regla se rompe, la validación devuelve MODIFIER_RULES_INVALID.",
|
|
1117
|
+
"verification": "deterministic",
|
|
1118
|
+
"howTo": "Maestro: cada grupo muestra su regla (elige 1, hasta N, entre N y M); no se puede agregar si es inválido ni pasar del máximo; un modificador con precio suma al total; los anidados exigen su propio grupo."
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
"id": "MEN-13",
|
|
1122
|
+
"description": "Búsqueda de productos en el menú.",
|
|
1123
|
+
"acceptance": "Buscar por nombre devuelve productos del menú actual, con sus etiquetas de agotado y horario, y abrir un resultado lleva al detalle. Sin resultados, muestra el estado vacío.",
|
|
1124
|
+
"channels": ["kiosk", "pos", "web", "app", "chat", "call-center"],
|
|
1125
|
+
"platforms": ["web", "android", "ios"],
|
|
1126
|
+
"fixable": true,
|
|
1127
|
+
"required": true,
|
|
1128
|
+
"area": "menu",
|
|
1129
|
+
"notes": "",
|
|
1130
|
+
"verification": "deterministic",
|
|
1131
|
+
"howTo": "Maestro: buscar por nombre devuelve productos del menú actual con sus etiquetas; abrir un resultado lleva al detalle; sin resultados, se ve el estado vacío."
|
|
1132
|
+
},
|
|
1133
|
+
{
|
|
1134
|
+
"id": "MEN-14",
|
|
1135
|
+
"description": "Cambio de idioma de la interfaz y del contenido del menú.",
|
|
1136
|
+
"acceptance": "Al cambiar de idioma, la interfaz y los textos del menú (nombres y descripciones) se muestran en el idioma elegido cuando el merchant los cargó.",
|
|
1137
|
+
"channels": ["kiosk", "pos", "web", "app", "chat", "call-center"],
|
|
1138
|
+
"platforms": ["web", "android", "ios"],
|
|
1139
|
+
"fixable": true,
|
|
1140
|
+
"required": false,
|
|
1141
|
+
"area": "menu",
|
|
1142
|
+
"notes": "Solo si el merchant es multi-idioma.",
|
|
1143
|
+
"verification": "deterministic",
|
|
1144
|
+
"howTo": "Maestro: cambiar el idioma y afirmar que la interfaz y los textos del menú pasan al idioma elegido, si el merchant los cargó. Si no es multi-idioma, se omite."
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
"id": "MEN-15",
|
|
1148
|
+
"description": "Menu builder: productos, categorías, grupos de modificadores, grupos de variantes y listas de precios, con publicación por scope.",
|
|
1149
|
+
"acceptance": "Creas en el builder un producto con variantes y modificadores, lo publicas a un scope y aparece en el canal tras la sincronización. La publicación queda en el historial de releases y sync.",
|
|
1150
|
+
"channels": [
|
|
1151
|
+
"kiosk",
|
|
1152
|
+
"pos",
|
|
1153
|
+
"web",
|
|
1154
|
+
"app",
|
|
1155
|
+
"chat",
|
|
1156
|
+
"call-center",
|
|
1157
|
+
"dashboard"
|
|
1158
|
+
],
|
|
1159
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1160
|
+
"fixable": true,
|
|
1161
|
+
"required": false,
|
|
1162
|
+
"area": "menu",
|
|
1163
|
+
"notes": "Solo si el merchant arma el menú con el builder y no por integración. MEN-01/02 cubren el CRUD y sync de menús anteriores.",
|
|
1164
|
+
"verification": "deterministic",
|
|
1165
|
+
"howTo": "Maestro: crear en el builder un producto con variantes y modificadores, publicarlo a un scope y afirmar que aparece en el canal tras la sync. Solo si el merchant usa el builder."
|
|
1166
|
+
},
|
|
1167
|
+
{
|
|
1168
|
+
"id": "MEN-16",
|
|
1169
|
+
"description": "Menús golden y scoped: un cambio en una entidad usada por un menú scoped lo marca como desactualizado, y se puede comparar y aplicar.",
|
|
1170
|
+
"acceptance": "Al editar o borrar una entidad usada por un menú scoped, ese menú aparece como desactualizado. Puedes ver la comparación y aplicar o descartar el cambio.",
|
|
1171
|
+
"channels": ["dashboard"],
|
|
1172
|
+
"platforms": ["dashboard"],
|
|
1173
|
+
"fixable": true,
|
|
1174
|
+
"required": false,
|
|
1175
|
+
"area": "menu",
|
|
1176
|
+
"notes": "Solo si el merchant usa el builder con menús scoped.",
|
|
1177
|
+
"verification": "deterministic",
|
|
1178
|
+
"howTo": "Maestro: editar o borrar una entidad usada por un menú scoped y afirmar que queda desactualizado; se puede ver la comparación y aplicar o descartar. Solo si usa menús scoped."
|
|
1179
|
+
},
|
|
1180
|
+
{
|
|
1181
|
+
"id": "CHK-01",
|
|
1182
|
+
"description": "Checkout completo: del carrito a la confirmación de la orden pagando en efectivo.",
|
|
1183
|
+
"acceptance": "Con un producto disponible vas al checkout, completas los datos requeridos, eliges efectivo, haces place y llegas a la pantalla de éxito. La orden aparece en Órdenes del dashboard con el canal, la tienda y el fulfillment correctos y, si el cliente está identificado, en su historial.",
|
|
1184
|
+
"channels": [
|
|
1185
|
+
"kiosk",
|
|
1186
|
+
"pos",
|
|
1187
|
+
"web",
|
|
1188
|
+
"app",
|
|
1189
|
+
"chat",
|
|
1190
|
+
"call-center",
|
|
1191
|
+
"dashboard"
|
|
1192
|
+
],
|
|
1193
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1194
|
+
"fixable": true,
|
|
1195
|
+
"required": true,
|
|
1196
|
+
"area": "checkout",
|
|
1197
|
+
"notes": "Usa efectivo para no generar cobros reales. Complementa ORD-01/ORD-02 con el recorrido de punta a punta.",
|
|
1198
|
+
"verification": "deterministic",
|
|
1199
|
+
"howTo": "Maestro, punta a punta con efectivo (sin cobro real): producto disponible, datos requeridos, place y pantalla de éxito. La orden aparece en el dashboard con canal, tienda y fulfillment correctos."
|
|
1200
|
+
},
|
|
1201
|
+
{
|
|
1202
|
+
"id": "CHK-02",
|
|
1203
|
+
"description": "La orden se valida antes de entrar al checkout.",
|
|
1204
|
+
"acceptance": "Al pulsar \"Ir a pagar\", el canal llama POST /api/storefront/v1/orders/validate. En el canal con validación local, corre validateOrderAgainstMenu antes de cobrar. Si hay issues, no navega al checkout y muestra el error por línea (OUT_OF_STOCK, PRICE_MISMATCH, MODIFIER_RULES_INVALID…) o de orden (EMPTY_ORDER, MENU_CLOSED). Al corregirlo, navega.",
|
|
1205
|
+
"channels": ["kiosk", "pos", "web", "app", "chat", "call-center"],
|
|
1206
|
+
"platforms": ["web", "android", "ios"],
|
|
1207
|
+
"fixable": true,
|
|
1208
|
+
"required": true,
|
|
1209
|
+
"area": "checkout",
|
|
1210
|
+
"notes": "Para provocarlo, agota en el dashboard un producto que ya está en el carrito o cierra el menú.",
|
|
1211
|
+
"verification": "deterministic",
|
|
1212
|
+
"howTo": "Maestro: al ir a pagar se llama validate (o validateOrderAgainstMenu). Provocar OUT_OF_STOCK o MENU_CLOSED y afirmar que no navega al checkout y muestra el error; al corregirlo, sí navega."
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
"id": "CHK-03",
|
|
1216
|
+
"description": "Reglas de orden del merchant: mínimo y máximo de ítems y de total, y límite diario de órdenes.",
|
|
1217
|
+
"acceptance": "Con min_total o min_items configurados, un carrito por debajo no pasa la validación (TOTAL_BELOW_MINIMUM / ITEMS_BELOW_MINIMUM) y el canal explica el motivo. Por encima del máximo devuelve ITEMS_ABOVE_MAXIMUM / TOTAL_ABOVE_MAXIMUM. Al alcanzar el límite diario devuelve DAILY_ORDER_LIMIT_REACHED.",
|
|
1218
|
+
"channels": [
|
|
1219
|
+
"kiosk",
|
|
1220
|
+
"pos",
|
|
1221
|
+
"web",
|
|
1222
|
+
"app",
|
|
1223
|
+
"chat",
|
|
1224
|
+
"call-center",
|
|
1225
|
+
"dashboard"
|
|
1226
|
+
],
|
|
1227
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1228
|
+
"fixable": true,
|
|
1229
|
+
"required": false,
|
|
1230
|
+
"area": "checkout",
|
|
1231
|
+
"notes": "Solo si el merchant configuró reglas distintas a las de defecto.",
|
|
1232
|
+
"verification": "deterministic",
|
|
1233
|
+
"howTo": "Maestro: con min/max de ítems o total, un carrito fuera de rango no pasa y explica el motivo (TOTAL_BELOW_MINIMUM, etc.). Al alcanzar el límite diario, DAILY_ORDER_LIMIT_REACHED. Si no hay reglas custom, se omite."
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
"id": "CHK-04",
|
|
1237
|
+
"description": "Datos de contacto del invitado en el checkout según preferences: required, optional o hidden, y campos personalizados.",
|
|
1238
|
+
"acceptance": "En un pedido anónimo, el checkout muestra los campos de contacto configurados. Un campo required vacío o inválido bloquea el place (CUSTOMER_FIELD_REQUIRED). Los campos personalizados se guardan en la orden.",
|
|
1239
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
1240
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1241
|
+
"fixable": true,
|
|
1242
|
+
"required": false,
|
|
1243
|
+
"area": "checkout",
|
|
1244
|
+
"notes": "Solo si el merchant permite pedidos anónimos. AVA-03 cubre shipping y billing.",
|
|
1245
|
+
"verification": "deterministic",
|
|
1246
|
+
"howTo": "Maestro: en un pedido anónimo, afirmar campos required, optional o hidden y los personalizados; un required vacío bloquea el place con CUSTOMER_FIELD_REQUIRED. Si no hay pedidos anónimos, se omite."
|
|
1247
|
+
},
|
|
1248
|
+
{
|
|
1249
|
+
"id": "CHK-05",
|
|
1250
|
+
"description": "Cancelar una orden que espera liberación desde el checkout la reabre sin perder el carrito.",
|
|
1251
|
+
"acceptance": "Mientras la orden espera checkout_release (pendiente o demorada) aparece la opción de cancelar. Al usarla se anulan los pagos y se abre una orden OPEN con el mismo contenido y un id nuevo. Si un pago ya fue capturado, se muestra el aviso ORDER_PAYMENT_CAPTURED y no cambia nada.",
|
|
1252
|
+
"channels": ["kiosk"],
|
|
1253
|
+
"platforms": ["web", "android", "ios"],
|
|
1254
|
+
"fixable": true,
|
|
1255
|
+
"required": true,
|
|
1256
|
+
"area": "checkout",
|
|
1257
|
+
"notes": "",
|
|
1258
|
+
"verification": "deterministic",
|
|
1259
|
+
"howTo": "Maestro en kiosk: cancelar una orden que espera checkout_release; se anulan pagos y se abre una orden OPEN con el mismo contenido y un id nuevo. Si un pago ya fue capturado, se muestra ORDER_PAYMENT_CAPTURED y no cambia nada."
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
"id": "ORD-07",
|
|
1263
|
+
"description": "El carrito se recupera entre sesiones del mismo cliente (orden OPEN), salvo en el canal que inicia cada sesión con carrito nuevo.",
|
|
1264
|
+
"acceptance": "Agregas productos, recargas o reabres el canal y ves el mismo carrito (la misma orden OPEN para el scope). En el canal con reuseOpen: false, cada sesión nueva empieza con el carrito vacío.",
|
|
1265
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center"],
|
|
1266
|
+
"platforms": ["web", "android", "ios"],
|
|
1267
|
+
"fixable": true,
|
|
1268
|
+
"required": true,
|
|
1269
|
+
"area": "orders",
|
|
1270
|
+
"notes": "",
|
|
1271
|
+
"verification": "deterministic",
|
|
1272
|
+
"howTo": "Maestro: agregar productos, recargar o reabrir y afirmar el mismo carrito (misma orden OPEN). En el canal con reuseOpen false, cada sesión nueva empieza vacía."
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
"id": "ORD-08",
|
|
1276
|
+
"description": "Historial y detalle de las órdenes del cliente identificado.",
|
|
1277
|
+
"acceptance": "El perfil lista las órdenes del cliente. El detalle muestra líneas, pagos, facturación, entrega y el progreso de milestones.",
|
|
1278
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center"],
|
|
1279
|
+
"platforms": ["web", "android", "ios"],
|
|
1280
|
+
"fixable": true,
|
|
1281
|
+
"required": true,
|
|
1282
|
+
"area": "orders",
|
|
1283
|
+
"notes": "ORD-03 cubre el tracking en tiempo real.",
|
|
1284
|
+
"verification": "deterministic",
|
|
1285
|
+
"howTo": "Maestro: el perfil lista las órdenes del cliente; el detalle muestra líneas, pagos, facturación, entrega y el progreso de milestones."
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
"id": "ORD-09",
|
|
1289
|
+
"description": "Repetir una orden anterior (reorder) reemplazando o mezclando el carrito, con aviso de diferencias.",
|
|
1290
|
+
"acceptance": "Desde una orden pasada puedes repetirla reemplazando o mezclando el carrito. Si algo cambió (agotado, precio, ya no existe), se muestra el modal de diferencias y el carrito queda válido.",
|
|
1291
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center"],
|
|
1292
|
+
"platforms": ["web", "android", "ios"],
|
|
1293
|
+
"fixable": true,
|
|
1294
|
+
"required": true,
|
|
1295
|
+
"area": "orders",
|
|
1296
|
+
"notes": "",
|
|
1297
|
+
"verification": "deterministic",
|
|
1298
|
+
"howTo": "Maestro: repetir una orden anterior reemplazando y mezclando; si algo cambió (agotado, precio, ya no existe), se muestra el modal de diferencias y el carrito queda válido."
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
"id": "ORD-10",
|
|
1302
|
+
"description": "Desde el dashboard se avanza manualmente el fulfillment de una orden.",
|
|
1303
|
+
"acceptance": "En el detalle de la orden solo se ofrecen los estados siguientes permitidos. Al avanzar uno, el cambio se refleja en el tracking del canal.",
|
|
1304
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
1305
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1306
|
+
"fixable": true,
|
|
1307
|
+
"required": true,
|
|
1308
|
+
"area": "orders",
|
|
1309
|
+
"notes": "",
|
|
1310
|
+
"verification": "deterministic",
|
|
1311
|
+
"howTo": "Maestro: en el detalle de la orden del dashboard solo se ofrecen los estados siguientes permitidos; al avanzar uno, el tracking del canal se actualiza."
|
|
1312
|
+
},
|
|
1313
|
+
{
|
|
1314
|
+
"id": "ORD-11",
|
|
1315
|
+
"description": "Los endpoints del server exigen la firma x-firespark-signature.",
|
|
1316
|
+
"acceptance": "Una request sin firma o con firma inválida a /api/injection o /api/fulfillment responde 401 invalid_signature. Con JSON inválido responde 400 y con un cuerpo que no es un evento, 422.",
|
|
1317
|
+
"channels": ["server"],
|
|
1318
|
+
"platforms": ["web"],
|
|
1319
|
+
"fixable": true,
|
|
1320
|
+
"required": false,
|
|
1321
|
+
"area": "orders",
|
|
1322
|
+
"notes": "Solo si el merchant usa el canal server (ORD-05).",
|
|
1323
|
+
"verification": "deterministic",
|
|
1324
|
+
"howTo": "Script local con curl: una request a /api/injection o /api/fulfillment sin firma o con firma inválida responde 401 invalid_signature; con JSON inválido, 400; con un cuerpo que no es un evento, 422."
|
|
1325
|
+
},
|
|
1326
|
+
{
|
|
1327
|
+
"id": "AVA-05",
|
|
1328
|
+
"description": "Fuera de servicio por canal: la regla de disponibilidad del canal (activada, título y descripción) muestra la pantalla de no disponible.",
|
|
1329
|
+
"acceptance": "Al activar la regla del canal en el dashboard, el canal muestra la pantalla de no disponible con ese título y descripción y no deja pedir. Al desactivarla, vuelve a funcionar.",
|
|
1330
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
1331
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1332
|
+
"fixable": true,
|
|
1333
|
+
"required": true,
|
|
1334
|
+
"area": "availability",
|
|
1335
|
+
"notes": "Complementa AVA-01 (cierre de merchant/tienda y availability.changed).",
|
|
1336
|
+
"verification": "deterministic",
|
|
1337
|
+
"howTo": "Maestro: activar la regla de no disponible del canal y afirmar la pantalla con ese título y descripción, sin poder pedir; al desactivarla, vuelve a funcionar."
|
|
1338
|
+
},
|
|
1339
|
+
{
|
|
1340
|
+
"id": "AVA-06",
|
|
1341
|
+
"description": "Versión mínima del canal nativo: si min_version es mayor que la instalada, se muestra la pantalla de actualización requerida.",
|
|
1342
|
+
"acceptance": "Al configurar en Releases una min_version mayor, el canal muestra la pantalla de actualización con el título, la descripción y el enlace a la tienda de apps configurados. Con una versión igual o menor, no aparece.",
|
|
1343
|
+
"channels": ["kiosk", "app", "dashboard"],
|
|
1344
|
+
"platforms": ["android", "ios", "dashboard"],
|
|
1345
|
+
"fixable": true,
|
|
1346
|
+
"required": false,
|
|
1347
|
+
"area": "availability",
|
|
1348
|
+
"notes": "Solo si el merchant usa versión mínima.",
|
|
1349
|
+
"verification": "deterministic",
|
|
1350
|
+
"howTo": "Maestro: configurar una min_version mayor y afirmar la pantalla de actualización con título, descripción y enlace a la tienda; con versión igual o menor, no aparece. Si el merchant no usa min_version, se omite."
|
|
1351
|
+
},
|
|
1352
|
+
{
|
|
1353
|
+
"id": "RT-08",
|
|
1354
|
+
"description": "Salud de devices: heartbeat, muestras de salud y logs del device visibles en el dashboard.",
|
|
1355
|
+
"acceptance": "Un device enlazado aparece en línea con su última conexión. Sus muestras de salud y logs aparecen en las tablas del device en el dashboard.",
|
|
1356
|
+
"channels": ["kiosk", "pos", "dashboard"],
|
|
1357
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1358
|
+
"fixable": true,
|
|
1359
|
+
"required": false,
|
|
1360
|
+
"area": "cache",
|
|
1361
|
+
"notes": "",
|
|
1362
|
+
"verification": "deterministic",
|
|
1363
|
+
"howTo": "Maestro en Dashboard: un device enlazado aparece en línea con su última conexión; sus muestras de salud y logs se ven en las tablas del device."
|
|
1364
|
+
},
|
|
1365
|
+
{
|
|
1366
|
+
"id": "RT-09",
|
|
1367
|
+
"description": "Checklist de configuración del canal en el overview del dashboard.",
|
|
1368
|
+
"acceptance": "En el overview del canal todos los pasos aparecen completos: device enlazado, tiendas, en línea y primera orden; o registro, pairing, operadores y sesión de caja, según el canal.",
|
|
1369
|
+
"channels": ["kiosk", "pos", "dashboard"],
|
|
1370
|
+
"platforms": ["dashboard"],
|
|
1371
|
+
"fixable": true,
|
|
1372
|
+
"required": false,
|
|
1373
|
+
"area": "cache",
|
|
1374
|
+
"notes": "",
|
|
1375
|
+
"verification": "deterministic",
|
|
1376
|
+
"howTo": "Maestro en Dashboard: en el overview del canal todos los pasos del checklist aparecen completos según el tipo de canal."
|
|
1377
|
+
},
|
|
1378
|
+
{
|
|
1379
|
+
"id": "MES-01",
|
|
1380
|
+
"description": "Plano del local: los pisos, zonas y mesas editados en el dashboard se ven en el canal.",
|
|
1381
|
+
"acceptance": "Creas o editas un piso con zonas y mesas en el editor del plano, y el canal muestra el mismo plano con su ocupación.",
|
|
1382
|
+
"channels": ["pos", "dashboard"],
|
|
1383
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1384
|
+
"fixable": true,
|
|
1385
|
+
"required": false,
|
|
1386
|
+
"area": "tables",
|
|
1387
|
+
"notes": "Funcionalidad nueva desde el 25-oct-2026 (PR #167). No es AUS-02, que se borró cuando no existía.",
|
|
1388
|
+
"verification": "mixed",
|
|
1389
|
+
"howTo": "Maestro afirma que los pisos, zonas y mesas creados en el editor existen en el canal con su ocupación. Un agente compara que la disposición visual del plano coincide."
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
"id": "MES-02",
|
|
1393
|
+
"description": "Operación de mesas en el canal: sentar, unir, dividir, cerrar y vender asociado a una mesa.",
|
|
1394
|
+
"acceptance": "Sientas un grupo con su tamaño, asocias la venta a la mesa, unes y divides mesas y cierras la mesa. Una venta de mesa no se encola offline.",
|
|
1395
|
+
"channels": ["pos"],
|
|
1396
|
+
"platforms": ["web", "android", "ios"],
|
|
1397
|
+
"fixable": true,
|
|
1398
|
+
"required": false,
|
|
1399
|
+
"area": "tables",
|
|
1400
|
+
"notes": "Requiere conexión (la venta de mesa no va al outbox).",
|
|
1401
|
+
"verification": "deterministic",
|
|
1402
|
+
"howTo": "Maestro en POS: sentar un grupo, asociar la venta a la mesa, unir, dividir y cerrar. Una venta de mesa no se encola offline."
|
|
1403
|
+
},
|
|
1404
|
+
{
|
|
1405
|
+
"id": "MES-03",
|
|
1406
|
+
"description": "Reservas: se crean en el dashboard y se sientan en el canal.",
|
|
1407
|
+
"acceptance": "Una reserva creada en el dashboard aparece en el canal y se puede sentar en una mesa. Su estado cambia en el dashboard.",
|
|
1408
|
+
"channels": ["pos", "dashboard"],
|
|
1409
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1410
|
+
"fixable": true,
|
|
1411
|
+
"required": false,
|
|
1412
|
+
"area": "tables",
|
|
1413
|
+
"notes": "",
|
|
1414
|
+
"verification": "deterministic",
|
|
1415
|
+
"howTo": "Maestro: crear una reserva en el dashboard, sentarla en una mesa del canal y afirmar que su estado cambia en el dashboard."
|
|
1416
|
+
},
|
|
1417
|
+
{
|
|
1418
|
+
"id": "ADM-05",
|
|
1419
|
+
"description": "Clientes del dashboard: el cliente registrado en un canal aparece y se puede ver y editar.",
|
|
1420
|
+
"acceptance": "Tras registrarse en un canal, el cliente aparece en Clientes con sus datos, y puedes ver su detalle y editarlo.",
|
|
1421
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
1422
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1423
|
+
"fixable": true,
|
|
1424
|
+
"required": true,
|
|
1425
|
+
"area": "admin",
|
|
1426
|
+
"notes": "ADM-03 cubre importaciones y exports.",
|
|
1427
|
+
"verification": "deterministic",
|
|
1428
|
+
"howTo": "Maestro: registrar un cliente de prueba en un canal y afirmar que aparece en Clientes con sus datos, y que se puede ver y editar."
|
|
1429
|
+
},
|
|
1430
|
+
{
|
|
1431
|
+
"id": "ADM-06",
|
|
1432
|
+
"description": "Los logs de los canales llegan a Monitoreo.",
|
|
1433
|
+
"acceptance": "Un error provocado en un canal llega a /api/storefront/v1/logs (o /pos/logs) y aparece en Monitoreo > Logs con su nivel y channel_id. Los filtros por nivel y canal funcionan.",
|
|
1434
|
+
"channels": [
|
|
1435
|
+
"kiosk",
|
|
1436
|
+
"pos",
|
|
1437
|
+
"web",
|
|
1438
|
+
"app",
|
|
1439
|
+
"chat",
|
|
1440
|
+
"call-center",
|
|
1441
|
+
"dashboard"
|
|
1442
|
+
],
|
|
1443
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1444
|
+
"fixable": true,
|
|
1445
|
+
"required": true,
|
|
1446
|
+
"area": "admin",
|
|
1447
|
+
"notes": "Los errores de validación de orden no se registran como error, por diseño (isOrderValidationError).",
|
|
1448
|
+
"verification": "deterministic",
|
|
1449
|
+
"howTo": "Maestro: provocar un error en un canal y afirmar que llega a Monitoreo > Logs con su nivel y channel_id; los filtros por nivel y canal funcionan."
|
|
1450
|
+
},
|
|
1451
|
+
{
|
|
1452
|
+
"id": "MKT-07",
|
|
1453
|
+
"description": "Los canales envían eventos de analítica a FireSpark y estos aparecen en el tracking plan.",
|
|
1454
|
+
"acceptance": "Al navegar y comprar, el canal hace POST /api/storefront/v1/analytics/events con menu_viewed, product_viewed, add_to_cart y purchase, con respuesta 2xx. El tracking plan del dashboard muestra muestras o cobertura de esos eventos para el canal.",
|
|
1455
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center", "dashboard"],
|
|
1456
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1457
|
+
"fixable": true,
|
|
1458
|
+
"required": true,
|
|
1459
|
+
"area": "marketing",
|
|
1460
|
+
"notes": "Cubre solo el envío a FireSpark. La entrega a destinos externos no se valida; por eso se borró MKT-06.",
|
|
1461
|
+
"verification": "deterministic",
|
|
1462
|
+
"howTo": "Maestro recorre navegación y compra; un script local afirma POST /api/storefront/v1/analytics/events 2xx para menu_viewed, product_viewed, add_to_cart y purchase. El tracking plan muestra muestras de esos eventos. No se valida la entrega a destinos externos."
|
|
1463
|
+
},
|
|
1464
|
+
{
|
|
1465
|
+
"id": "MKT-08",
|
|
1466
|
+
"description": "Preferencias de notificaciones y consentimiento de marketing del cliente.",
|
|
1467
|
+
"acceptance": "En el perfil cambias marketing, email y push. Se guardan en el customer (consent y notification_preferences) y siguen así al recargar.",
|
|
1468
|
+
"channels": ["kiosk", "web", "app", "chat", "call-center"],
|
|
1469
|
+
"platforms": ["web", "android", "ios"],
|
|
1470
|
+
"fixable": true,
|
|
1471
|
+
"required": true,
|
|
1472
|
+
"area": "marketing",
|
|
1473
|
+
"notes": "",
|
|
1474
|
+
"verification": "deterministic",
|
|
1475
|
+
"howTo": "Maestro: en el perfil cambiar marketing, email y push; afirmar que se guardan en el customer y siguen así al recargar."
|
|
1476
|
+
},
|
|
1477
|
+
{
|
|
1478
|
+
"id": "RT-10",
|
|
1479
|
+
"description": "Realtime limitado a checkout y detalle de la orden, sin listeners globales ni suscripciones que queden abiertas al salir.",
|
|
1480
|
+
"acceptance": "Aquí, al navegar por inicio, menú, carrito, login y perfil general, el canal no abre ni mantiene suscripciones realtime. Solo las abre durante checkout o en el detalle de una orden cuando necesita actualizaciones. Al salir, cambiar de pestaña, reemplazar la orden o terminar el seguimiento, cierra la suscripción anterior; volver a entrar no duplica listeners.",
|
|
1481
|
+
"channels": ["web", "app", "chat", "call-center"],
|
|
1482
|
+
"platforms": ["web", "android", "ios"],
|
|
1483
|
+
"fixable": true,
|
|
1484
|
+
"required": true,
|
|
1485
|
+
"area": "cache",
|
|
1486
|
+
"notes": "Verifica los joins/leaves y sockets del cliente, no solo que la UI deje de actualizarse. Los canales de dispositivos pueden mantener suscripciones propias del equipo; esta excepción no habilita listeners globales en los canales de esta fila. La limpieza debe ocurrir también si la pantalla pierde foco pero sigue montada.",
|
|
1487
|
+
"verification": "mixed",
|
|
1488
|
+
"howTo": "Maestro navega inicio, menú, carrito, login y perfil (sin abrir realtime) y luego checkout o detalle de orden (sí lo necesita). Un agente, apoyado en las herramientas de red del navegador o un script local de sockets, confirma joins y leaves: sin listeners globales, cierra al salir y no duplica suscripciones."
|
|
1489
|
+
},
|
|
1490
|
+
{
|
|
1491
|
+
"id": "LOG-15",
|
|
1492
|
+
"description": "Vinculación y revinculación del dispositivo antes de operar, con identidad propia y persistente del equipo.",
|
|
1493
|
+
"acceptance": "Aquí, un equipo sin vínculo muestra el flujo de vinculación y no deja operar con una cuenta de cliente como reemplazo. El código generado desde la administración vincula el equipo con la tienda y el canal correctos; un código inválido se rechaza. Al reiniciar conserva el vínculo. La revinculación válida recupera el mismo equipo y su número fiscal, sin crear un duplicado.",
|
|
1494
|
+
"channels": ["pos", "dashboard"],
|
|
1495
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1496
|
+
"fixable": true,
|
|
1497
|
+
"required": true,
|
|
1498
|
+
"area": "login",
|
|
1499
|
+
"notes": "Complementa el pairing ya cubierto para otros canales en LOG-03. No repitas aquí PIN, lock/unlock o biometría de LOG-04/05. Usa un equipo de prueba y un código de vinculación o revinculación autorizado; no desvincules equipos en uso.",
|
|
1500
|
+
"verification": "deterministic",
|
|
1501
|
+
"howTo": "Maestro: un equipo sin vínculo muestra el flujo de vinculación y no opera con una cuenta de cliente como reemplazo; un código válido enlaza con la tienda y el canal correctos; uno inválido se rechaza; al reiniciar conserva el vínculo; la revinculación no crea un duplicado."
|
|
1502
|
+
},
|
|
1503
|
+
{
|
|
1504
|
+
"id": "LOG-16",
|
|
1505
|
+
"description": "Las operaciones del dispositivo usan su autenticación propia y no aceptan la sesión pública del cliente como sustituto.",
|
|
1506
|
+
"acceptance": "Aquí, con el equipo vinculado, las operaciones propias del dispositivo se autentican con device_uid y device_secret mediante los métodos del SDK correspondientes. Un token de cliente por sí solo, sin la credencial válida del equipo, no habilita configuración, administración ni las operaciones protegidas del dispositivo. Las suscripciones privadas del equipo obtienen su token por el endpoint propio del dispositivo y respetan su scope.",
|
|
1507
|
+
"channels": ["kiosk", "pos"],
|
|
1508
|
+
"platforms": ["web", "android", "ios"],
|
|
1509
|
+
"fixable": true,
|
|
1510
|
+
"required": true,
|
|
1511
|
+
"area": "login",
|
|
1512
|
+
"notes": "Separa identidad del equipo, sesión del operador e identidad del cliente. La autenticación de cliente que usa el storefront no demuestra autenticación del dispositivo ni la reemplaza. El diseño actual admite ambas identidades; no inventes un device OAuth exchange ni una audiencia pública distinta. Valida el rechazo de credenciales ausentes o inválidas con un equipo de prueba y no expongas secrets en evidencia.",
|
|
1513
|
+
"verification": "deterministic",
|
|
1514
|
+
"howTo": "Script local: las operaciones del dispositivo con device_uid y device_secret se aceptan; un token de cliente solo, sin la credencial del equipo, se rechaza. No registrar secrets en la evidencia. Usar un equipo de prueba."
|
|
1515
|
+
},
|
|
1516
|
+
{
|
|
1517
|
+
"id": "SDK-10",
|
|
1518
|
+
"description": "Los canales construyen su UI de compra con la lib de UI del SDK que corresponde a su plataforma.",
|
|
1519
|
+
"acceptance": "En el repo del canal, package.json declara la lib que le corresponde y el código la importa en las superficies de compra: menú y categorías, card de producto, detalle con modificadores, carrito y checkout. No se exige usar todos los exports ni copiar las pantallas de la plantilla. Envolver las primitivas en componentes de marca es válido. Falla si la lib solo aparece en package.json o si alguna de esas superficies se reconstruyó por completo sin ella.",
|
|
1520
|
+
"channels": ["kiosk", "pos", "web", "app", "chat", "call-center"],
|
|
1521
|
+
"platforms": ["web", "android", "ios"],
|
|
1522
|
+
"fixable": true,
|
|
1523
|
+
"required": true,
|
|
1524
|
+
"area": "sdk",
|
|
1525
|
+
"notes": "Aquí se revisa estáticamente el repo del canal (Builder o GitHub), igual que SDK-01. @fire-spark/react-ui corresponde a la plataforma web y @fire-spark/rn-ui a la nativa. @fire-spark/rn-ui se consume como workspace:* y es válido. El dashboard usa react-ui solo en el preview y no es parte de este check.",
|
|
1526
|
+
"verification": "mixed",
|
|
1527
|
+
"howTo": "Un script local afirma que package.json declara @fire-spark/react-ui o @fire-spark/rn-ui según la plataforma. El Investigador revisa el repo del canal y confirma que menú, card, detalle, carrito y checkout importan esa lib. Falla si solo está en package.json o si alguna superficie se reconstruyó por completo sin ella."
|
|
1528
|
+
},
|
|
1529
|
+
{
|
|
1530
|
+
"id": "MEN-17",
|
|
1531
|
+
"description": "Si el menú tiene recomendaciones, el canal muestra cada tipo presente (UPSELL, CROSS_SELL, DOWNSELL) en su superficie, y no muestra secciones vacías de tipos ausentes.",
|
|
1532
|
+
"acceptance": "Con un producto que tiene recomendaciones vinculadas (pricing.recommendations_ids): UPSELL aparece en el detalle como personalización o cambio sugerido; CROSS_SELL aparece como relacionados en el detalle o en el carrito; DOWNSELL aparece como oferta al abrir o agregar el producto. Solo se revisan los tipos que existen en el menú. Si un tipo no está o su producto destino está agotado, inactivo o fuera de horario, no se muestra nada de ese tipo. Agregar desde la recomendación pone el producto destino en el carrito.",
|
|
1533
|
+
"channels": ["kiosk", "pos", "web", "app", "chat", "call-center"],
|
|
1534
|
+
"platforms": ["web", "android", "ios"],
|
|
1535
|
+
"fixable": true,
|
|
1536
|
+
"required": false,
|
|
1537
|
+
"area": "menu",
|
|
1538
|
+
"notes": "Solo si el menú del merchant tiene recomendaciones. Complementa MKT-01, que cubre la operación y el preview. Tipos válidos: Recommendation.type en @fire-spark/types. La ubicación exacta puede variar mientras el tipo sea visible y se pueda agregar.",
|
|
1539
|
+
"verification": "deterministic",
|
|
1540
|
+
"howTo": "Maestro: con recomendaciones conocidas, afirmar por el nombre del producto destino que se ve cada tipo presente (UPSELL, CROSS_SELL, DOWNSELL) y que no hay sección vacía de un tipo ausente o no disponible; agregar desde la recomendación suma al carrito. La ubicación exacta puede variar."
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
"id": "SDK-11",
|
|
1544
|
+
"description": "Los canales generados se identifican por channel_id en apps/*/src/channel-manifest.json, no por el tipo de plantilla ni por el nombre de la carpeta.",
|
|
1545
|
+
"acceptance": "En el repo del canal del cliente, cada app generada tiene apps/<slug>/src/channel-manifest.json (channel-manifest.ts es el manifiesto de runtime y, si no hay JSON, el formato legado). El JSON trae channel_id, name, template, templateVersion y sdkVersion (semver). El server vive en apps/server con channel_id null. Cualquier otro canal tiene un channel_id string igual al del dashboard, y ese valor no se repite. Si hay dos canales del mismo template, están en carpetas distintas (el builder arma el slug a partir del name y agrega -2, -3 si choca) y se emparejan solo por channel_id: la carpeta y el package name se ignoran a propósito.",
|
|
1546
|
+
"channels": [
|
|
1547
|
+
"kiosk",
|
|
1548
|
+
"pos",
|
|
1549
|
+
"web",
|
|
1550
|
+
"app",
|
|
1551
|
+
"chat",
|
|
1552
|
+
"call-center",
|
|
1553
|
+
"server",
|
|
1554
|
+
"dashboard"
|
|
1555
|
+
],
|
|
1556
|
+
"platforms": ["dashboard"],
|
|
1557
|
+
"fixable": true,
|
|
1558
|
+
"required": true,
|
|
1559
|
+
"area": "sdk",
|
|
1560
|
+
"notes": "Concreto del repo: packages/channel-builder/src/detect.ts (detectRepoChannels: matched later by channel_id only), generate.ts (planApps + uniqueSlug), manifest.record.ts (campos del JSON) y manifest.context.ts (empareja channel.id del build con found.channelId; no toca canales de otro ambiente). El doctor del CLI valida el mismo JSON en packages/cli/src/commands/doctor/groups/channels.group.ts. Campos opcionales del JSON: organization_id, merchant_id, environment_id y design (figma/custom/none, más url/file_key/node_id si es Figma). El .ts de runtime además guarda slug, port, platforms, locales y, según el template, web o native.",
|
|
1561
|
+
"verification": "deterministic",
|
|
1562
|
+
"howTo": "Script local sobre el repo del canal del cliente (no el monorepo de FireSpark): listar apps/*/src/channel-manifest.json, validar los campos y que no se repita channel_id, y que dos del mismo template no compartan ni channel_id ni carpeta. En Builder, Maestro puede listar los canales conectados y afirmar el mismo emparejamiento por channel_id."
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
"id": "CLI-01",
|
|
1566
|
+
"description": "Si un canal marcado como desplegado no tiene un dato operativo requerido, la CLI falla limpio sin correr la suite.",
|
|
1567
|
+
"acceptance": "Al invocar firespark doctor run contra uno o más canales marcados como desplegados, la CLI valida antes de empezar que existan los datos operativos requeridos: URL pública del canal, URL del dashboard, URL de la API, store-id, fulfillment-id, modo de pago (efectivo o sandbox), URL del receptor de webhooks, ruta de evidencia y credenciales de prueba (por entorno, no en el comando). Si el archivo YAML de config no existe, no falla así: auto-invoca init, pide lo que falte y crea el archivo. Este error aplica cuando el YAML ya existe y aun así falta un dato: termina con error sin ejecutar ningún check, y el mensaje nombra exactamente el campo que falta y el canal al que pertenece. No adivina ni completa con valores por defecto. Un canal que nunca se desplegó, y solo tiene la identidad, no exige esos campos y no genera este error.",
|
|
1568
|
+
"channels": [
|
|
1569
|
+
"kiosk",
|
|
1570
|
+
"pos",
|
|
1571
|
+
"web",
|
|
1572
|
+
"app",
|
|
1573
|
+
"chat",
|
|
1574
|
+
"call-center",
|
|
1575
|
+
"server",
|
|
1576
|
+
"dashboard"
|
|
1577
|
+
],
|
|
1578
|
+
"platforms": ["web", "android", "ios", "dashboard"],
|
|
1579
|
+
"fixable": false,
|
|
1580
|
+
"required": true,
|
|
1581
|
+
"area": "sdk",
|
|
1582
|
+
"notes": "Los datos operativos viven en el YAML de config (por ejemplo .bembos.doctor.yml) o en las flags equivalentes, no en el channel-manifest. El manifest cubre la identidad del canal. Un canal se trata como desplegado solo si está marcado así en esa config; tener manifest no implica que ya se desplegó. Si el YAML no existe al arrancar, la CLI auto-invoca init y no dispara este error. Fixable es No para que --fix no invente URLs ni IDs.",
|
|
1583
|
+
"verification": "deterministic",
|
|
1584
|
+
"howTo": "Script local: invocar la CLI con un canal marcado como desplegado y sin URL pública (y por separado sin store-id, fulfillment-id o credenciales) y afirmar que el código de salida no es 0, que no se corrió ningún check y que el error nombra el campo y el canal. Repetir con un canal nunca desplegado, solo con identidad, y afirmar que no aparece este error por falta de URLs u otros datos operativos."
|
|
1585
|
+
}
|
|
1586
|
+
]
|