square_sandbox_simulator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +176 -0
  4. data/bin/simulate +388 -0
  5. data/lib/square_sandbox_simulator/configuration.rb +193 -0
  6. data/lib/square_sandbox_simulator/data/cafe_bakery/categories.json +54 -0
  7. data/lib/square_sandbox_simulator/data/cafe_bakery/combos.json +33 -0
  8. data/lib/square_sandbox_simulator/data/cafe_bakery/coupon_codes.json +133 -0
  9. data/lib/square_sandbox_simulator/data/cafe_bakery/discounts.json +113 -0
  10. data/lib/square_sandbox_simulator/data/cafe_bakery/items.json +55 -0
  11. data/lib/square_sandbox_simulator/data/cafe_bakery/modifiers.json +73 -0
  12. data/lib/square_sandbox_simulator/data/cafe_bakery/tax_rates.json +26 -0
  13. data/lib/square_sandbox_simulator/data/cafe_bakery/tenders.json +41 -0
  14. data/lib/square_sandbox_simulator/data/restaurant/categories.json +54 -0
  15. data/lib/square_sandbox_simulator/data/restaurant/combos.json +265 -0
  16. data/lib/square_sandbox_simulator/data/restaurant/coupon_codes.json +266 -0
  17. data/lib/square_sandbox_simulator/data/restaurant/discounts.json +198 -0
  18. data/lib/square_sandbox_simulator/data/restaurant/gift_cards.json +82 -0
  19. data/lib/square_sandbox_simulator/data/restaurant/items.json +388 -0
  20. data/lib/square_sandbox_simulator/data/restaurant/modifiers.json +62 -0
  21. data/lib/square_sandbox_simulator/data/restaurant/tax_rates.json +38 -0
  22. data/lib/square_sandbox_simulator/data/restaurant/tenders.json +41 -0
  23. data/lib/square_sandbox_simulator/data/salon_spa/categories.json +24 -0
  24. data/lib/square_sandbox_simulator/data/salon_spa/combos.json +88 -0
  25. data/lib/square_sandbox_simulator/data/salon_spa/coupon_codes.json +96 -0
  26. data/lib/square_sandbox_simulator/data/salon_spa/discounts.json +93 -0
  27. data/lib/square_sandbox_simulator/data/salon_spa/gift_cards.json +47 -0
  28. data/lib/square_sandbox_simulator/data/salon_spa/items.json +100 -0
  29. data/lib/square_sandbox_simulator/data/salon_spa/modifiers.json +49 -0
  30. data/lib/square_sandbox_simulator/data/salon_spa/tax_rates.json +17 -0
  31. data/lib/square_sandbox_simulator/data/salon_spa/tenders.json +41 -0
  32. data/lib/square_sandbox_simulator/database.rb +224 -0
  33. data/lib/square_sandbox_simulator/db/factories/api_requests.rb +95 -0
  34. data/lib/square_sandbox_simulator/db/factories/business_types.rb +178 -0
  35. data/lib/square_sandbox_simulator/db/factories/categories.rb +379 -0
  36. data/lib/square_sandbox_simulator/db/factories/daily_summaries.rb +56 -0
  37. data/lib/square_sandbox_simulator/db/factories/items.rb +1526 -0
  38. data/lib/square_sandbox_simulator/db/factories/simulated_orders.rb +112 -0
  39. data/lib/square_sandbox_simulator/db/factories/simulated_payments.rb +61 -0
  40. data/lib/square_sandbox_simulator/db/migrate/20260312000000_enable_pgcrypto.rb +7 -0
  41. data/lib/square_sandbox_simulator/db/migrate/20260312000001_create_business_types.rb +18 -0
  42. data/lib/square_sandbox_simulator/db/migrate/20260312000002_create_categories.rb +18 -0
  43. data/lib/square_sandbox_simulator/db/migrate/20260312000003_create_items.rb +23 -0
  44. data/lib/square_sandbox_simulator/db/migrate/20260312000004_create_simulated_orders.rb +36 -0
  45. data/lib/square_sandbox_simulator/db/migrate/20260312000005_create_simulated_payments.rb +26 -0
  46. data/lib/square_sandbox_simulator/db/migrate/20260312000006_create_api_requests.rb +27 -0
  47. data/lib/square_sandbox_simulator/db/migrate/20260312000007_create_daily_summaries.rb +24 -0
  48. data/lib/square_sandbox_simulator/generators/data_loader.rb +202 -0
  49. data/lib/square_sandbox_simulator/generators/entity_generator.rb +248 -0
  50. data/lib/square_sandbox_simulator/generators/order_generator.rb +632 -0
  51. data/lib/square_sandbox_simulator/models/api_request.rb +43 -0
  52. data/lib/square_sandbox_simulator/models/business_type.rb +25 -0
  53. data/lib/square_sandbox_simulator/models/category.rb +18 -0
  54. data/lib/square_sandbox_simulator/models/daily_summary.rb +68 -0
  55. data/lib/square_sandbox_simulator/models/item.rb +33 -0
  56. data/lib/square_sandbox_simulator/models/record.rb +16 -0
  57. data/lib/square_sandbox_simulator/models/simulated_order.rb +42 -0
  58. data/lib/square_sandbox_simulator/models/simulated_payment.rb +28 -0
  59. data/lib/square_sandbox_simulator/seeder.rb +242 -0
  60. data/lib/square_sandbox_simulator/services/base_service.rb +253 -0
  61. data/lib/square_sandbox_simulator/services/square/catalog_service.rb +203 -0
  62. data/lib/square_sandbox_simulator/services/square/customer_service.rb +130 -0
  63. data/lib/square_sandbox_simulator/services/square/order_service.rb +121 -0
  64. data/lib/square_sandbox_simulator/services/square/payment_service.rb +136 -0
  65. data/lib/square_sandbox_simulator/services/square/services_manager.rb +68 -0
  66. data/lib/square_sandbox_simulator/services/square/team_service.rb +108 -0
  67. data/lib/square_sandbox_simulator/version.rb +5 -0
  68. data/lib/square_sandbox_simulator.rb +47 -0
  69. metadata +348 -0
@@ -0,0 +1,1526 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ~168 item traits across 9 business types and 38 categories.
4
+ # Each trait sets name, price (cents), sku, and associates with the correct
5
+ # category (which in turn associates with the correct business type).
6
+ #
7
+ # Usage:
8
+ # build(:item, :buffalo_wings) # auto-creates category + business_type
9
+ # create(:item, :buffalo_wings, category: my_cat) # override category
10
+
11
+ FactoryBot.define do
12
+ factory :item, class: "SquareSandboxSimulator::Models::Item" do
13
+ association :category
14
+ sequence(:name) { |n| "Item #{n}" }
15
+ price { 999 } # cents
16
+ active { true }
17
+ variants { [] }
18
+ metadata { {} }
19
+
20
+ # ═══════════════════════════════════════════════════════════
21
+ # RESTAURANT (5 categories, 25 items)
22
+ # ═══════════════════════════════════════════════════════════
23
+
24
+ # ── Appetizers ─────────────────────────────────────────────
25
+
26
+ trait :buffalo_wings do
27
+ name { "Buffalo Wings" }
28
+ price { 1299 }
29
+ sku { "REST-APP-001" }
30
+ association :category, factory: %i[category appetizers]
31
+ end
32
+
33
+ trait :mozzarella_sticks do
34
+ name { "Mozzarella Sticks" }
35
+ price { 999 }
36
+ sku { "REST-APP-002" }
37
+ association :category, factory: %i[category appetizers]
38
+ end
39
+
40
+ trait :loaded_nachos do
41
+ name { "Loaded Nachos" }
42
+ price { 1199 }
43
+ sku { "REST-APP-003" }
44
+ association :category, factory: %i[category appetizers]
45
+ end
46
+
47
+ trait :spinach_artichoke_dip do
48
+ name { "Spinach Artichoke Dip" }
49
+ price { 1099 }
50
+ sku { "REST-APP-004" }
51
+ association :category, factory: %i[category appetizers]
52
+ end
53
+
54
+ trait :calamari do
55
+ name { "Calamari" }
56
+ price { 1399 }
57
+ sku { "REST-APP-005" }
58
+ association :category, factory: %i[category appetizers]
59
+ end
60
+
61
+ # ── Entrees ────────────────────────────────────────────────
62
+
63
+ trait :grilled_salmon do
64
+ name { "Grilled Salmon" }
65
+ price { 2299 }
66
+ sku { "REST-ENT-001" }
67
+ association :category, factory: %i[category entrees]
68
+ end
69
+
70
+ trait :ny_strip_steak do
71
+ name { "NY Strip Steak" }
72
+ price { 2899 }
73
+ sku { "REST-ENT-002" }
74
+ association :category, factory: %i[category entrees]
75
+ end
76
+
77
+ trait :chicken_parmesan do
78
+ name { "Chicken Parmesan" }
79
+ price { 1899 }
80
+ sku { "REST-ENT-003" }
81
+ association :category, factory: %i[category entrees]
82
+ end
83
+
84
+ trait :pasta_primavera do
85
+ name { "Pasta Primavera" }
86
+ price { 1599 }
87
+ sku { "REST-ENT-004" }
88
+ association :category, factory: %i[category entrees]
89
+ end
90
+
91
+ trait :herb_roasted_chicken do
92
+ name { "Herb Roasted Chicken" }
93
+ price { 1999 }
94
+ sku { "REST-ENT-005" }
95
+ association :category, factory: %i[category entrees]
96
+ end
97
+
98
+ # ── Sides ──────────────────────────────────────────────────
99
+
100
+ trait :french_fries do
101
+ name { "French Fries" }
102
+ price { 599 }
103
+ sku { "REST-SID-001" }
104
+ association :category, factory: %i[category sides]
105
+ end
106
+
107
+ trait :coleslaw do
108
+ name { "Coleslaw" }
109
+ price { 499 }
110
+ sku { "REST-SID-002" }
111
+ association :category, factory: %i[category sides]
112
+ end
113
+
114
+ trait :mashed_potatoes do
115
+ name { "Mashed Potatoes" }
116
+ price { 699 }
117
+ sku { "REST-SID-003" }
118
+ association :category, factory: %i[category sides]
119
+ end
120
+
121
+ trait :side_salad do
122
+ name { "Side Salad" }
123
+ price { 549 }
124
+ sku { "REST-SID-004" }
125
+ association :category, factory: %i[category sides]
126
+ end
127
+
128
+ trait :onion_rings do
129
+ name { "Onion Rings" }
130
+ price { 699 }
131
+ sku { "REST-SID-005" }
132
+ association :category, factory: %i[category sides]
133
+ end
134
+
135
+ # ── Desserts ───────────────────────────────────────────────
136
+
137
+ trait :chocolate_lava_cake do
138
+ name { "Chocolate Lava Cake" }
139
+ price { 999 }
140
+ sku { "REST-DES-001" }
141
+ association :category, factory: %i[category desserts]
142
+ end
143
+
144
+ trait :ny_cheesecake do
145
+ name { "New York Cheesecake" }
146
+ price { 899 }
147
+ sku { "REST-DES-002" }
148
+ association :category, factory: %i[category desserts]
149
+ end
150
+
151
+ trait :restaurant_tiramisu do
152
+ name { "Tiramisu" }
153
+ price { 1099 }
154
+ sku { "REST-DES-003" }
155
+ association :category, factory: %i[category desserts]
156
+ end
157
+
158
+ trait :apple_pie do
159
+ name { "Apple Pie a la Mode" }
160
+ price { 799 }
161
+ sku { "REST-DES-004" }
162
+ association :category, factory: %i[category desserts]
163
+ end
164
+
165
+ trait :ice_cream_sundae do
166
+ name { "Ice Cream Sundae" }
167
+ price { 699 }
168
+ sku { "REST-DES-005" }
169
+ association :category, factory: %i[category desserts]
170
+ end
171
+
172
+ # ── Beverages ──────────────────────────────────────────────
173
+
174
+ trait :soft_drink do
175
+ name { "Soft Drink" }
176
+ price { 299 }
177
+ sku { "REST-BEV-001" }
178
+ association :category, factory: %i[category beverages]
179
+ end
180
+
181
+ trait :iced_tea do
182
+ name { "Iced Tea" }
183
+ price { 299 }
184
+ sku { "REST-BEV-002" }
185
+ association :category, factory: %i[category beverages]
186
+ end
187
+
188
+ trait :lemonade do
189
+ name { "Lemonade" }
190
+ price { 349 }
191
+ sku { "REST-BEV-003" }
192
+ association :category, factory: %i[category beverages]
193
+ end
194
+
195
+ trait :sparkling_water do
196
+ name { "Sparkling Water" }
197
+ price { 399 }
198
+ sku { "REST-BEV-004" }
199
+ association :category, factory: %i[category beverages]
200
+ end
201
+
202
+ trait :restaurant_coffee do
203
+ name { "Coffee" }
204
+ price { 249 }
205
+ sku { "REST-BEV-005" }
206
+ association :category, factory: %i[category beverages]
207
+ end
208
+
209
+ # ═══════════════════════════════════════════════════════════
210
+ # CAFE & BAKERY (5 categories, 21 items)
211
+ # ═══════════════════════════════════════════════════════════
212
+
213
+ # ── Coffee & Espresso ──────────────────────────────────────
214
+
215
+ trait :house_drip_coffee do
216
+ name { "House Drip Coffee" }
217
+ price { 299 }
218
+ sku { "CAFE-COF-001" }
219
+ association :category, factory: %i[category coffee_espresso]
220
+ end
221
+
222
+ trait :espresso do
223
+ name { "Espresso" }
224
+ price { 349 }
225
+ sku { "CAFE-COF-002" }
226
+ association :category, factory: %i[category coffee_espresso]
227
+ end
228
+
229
+ trait :cappuccino do
230
+ name { "Cappuccino" }
231
+ price { 499 }
232
+ sku { "CAFE-COF-003" }
233
+ association :category, factory: %i[category coffee_espresso]
234
+ end
235
+
236
+ trait :latte do
237
+ name { "Latte" }
238
+ price { 549 }
239
+ sku { "CAFE-COF-004" }
240
+ association :category, factory: %i[category coffee_espresso]
241
+ end
242
+
243
+ trait :cold_brew do
244
+ name { "Cold Brew" }
245
+ price { 499 }
246
+ sku { "CAFE-COF-005" }
247
+ association :category, factory: %i[category coffee_espresso]
248
+ end
249
+
250
+ # ── Pastries ───────────────────────────────────────────────
251
+
252
+ trait :croissant do
253
+ name { "Butter Croissant" }
254
+ price { 399 }
255
+ sku { "CAFE-PAS-001" }
256
+ association :category, factory: %i[category pastries]
257
+ end
258
+
259
+ trait :blueberry_muffin do
260
+ name { "Blueberry Muffin" }
261
+ price { 349 }
262
+ sku { "CAFE-PAS-002" }
263
+ association :category, factory: %i[category pastries]
264
+ end
265
+
266
+ trait :cinnamon_roll do
267
+ name { "Cinnamon Roll" }
268
+ price { 449 }
269
+ sku { "CAFE-PAS-003" }
270
+ association :category, factory: %i[category pastries]
271
+ end
272
+
273
+ trait :chocolate_chip_cookie do
274
+ name { "Chocolate Chip Cookie" }
275
+ price { 299 }
276
+ sku { "CAFE-PAS-004" }
277
+ association :category, factory: %i[category pastries]
278
+ end
279
+
280
+ # ── Breakfast ──────────────────────────────────────────────
281
+
282
+ trait :avocado_toast do
283
+ name { "Avocado Toast" }
284
+ price { 999 }
285
+ sku { "CAFE-BRK-001" }
286
+ association :category, factory: %i[category breakfast]
287
+ end
288
+
289
+ trait :breakfast_burrito do
290
+ name { "Breakfast Burrito" }
291
+ price { 899 }
292
+ sku { "CAFE-BRK-002" }
293
+ association :category, factory: %i[category breakfast]
294
+ end
295
+
296
+ trait :acai_bowl do
297
+ name { "Acai Bowl" }
298
+ price { 1199 }
299
+ sku { "CAFE-BRK-003" }
300
+ association :category, factory: %i[category breakfast]
301
+ end
302
+
303
+ trait :yogurt_parfait do
304
+ name { "Yogurt Parfait" }
305
+ price { 699 }
306
+ sku { "CAFE-BRK-004" }
307
+ association :category, factory: %i[category breakfast]
308
+ end
309
+
310
+ # ── Sandwiches ─────────────────────────────────────────────
311
+
312
+ trait :turkey_club do
313
+ name { "Turkey Club" }
314
+ price { 1099 }
315
+ sku { "CAFE-SAN-001" }
316
+ association :category, factory: %i[category sandwiches]
317
+ end
318
+
319
+ trait :caprese_panini do
320
+ name { "Caprese Panini" }
321
+ price { 999 }
322
+ sku { "CAFE-SAN-002" }
323
+ association :category, factory: %i[category sandwiches]
324
+ end
325
+
326
+ trait :chicken_caesar_wrap do
327
+ name { "Chicken Caesar Wrap" }
328
+ price { 1049 }
329
+ sku { "CAFE-SAN-003" }
330
+ association :category, factory: %i[category sandwiches]
331
+ end
332
+
333
+ trait :blt do
334
+ name { "BLT" }
335
+ price { 899 }
336
+ sku { "CAFE-SAN-004" }
337
+ association :category, factory: %i[category sandwiches]
338
+ end
339
+
340
+ # ── Smoothies & Juices ─────────────────────────────────────
341
+
342
+ trait :berry_blast_smoothie do
343
+ name { "Berry Blast Smoothie" }
344
+ price { 699 }
345
+ sku { "CAFE-SMO-001" }
346
+ association :category, factory: %i[category smoothies]
347
+ end
348
+
349
+ trait :green_detox_juice do
350
+ name { "Green Detox Juice" }
351
+ price { 749 }
352
+ sku { "CAFE-SMO-002" }
353
+ association :category, factory: %i[category smoothies]
354
+ end
355
+
356
+ trait :mango_tango_smoothie do
357
+ name { "Mango Tango Smoothie" }
358
+ price { 699 }
359
+ sku { "CAFE-SMO-003" }
360
+ association :category, factory: %i[category smoothies]
361
+ end
362
+
363
+ trait :fresh_oj do
364
+ name { "Fresh-Squeezed OJ" }
365
+ price { 499 }
366
+ sku { "CAFE-SMO-004" }
367
+ association :category, factory: %i[category smoothies]
368
+ end
369
+
370
+ # ═══════════════════════════════════════════════════════════
371
+ # CAFE & BAKERY — DUPLICATES (mirrors real Square messiness)
372
+ # Same item names appearing in multiple categories, same names
373
+ # with different prices, items cross-listed in overlapping categories.
374
+ # ═══════════════════════════════════════════════════════════
375
+
376
+ # ── Duplicate Coffee & Espresso (2nd category, same item names, different prices) ──
377
+
378
+ trait :house_drip_coffee_dup do
379
+ name { "House Drip Coffee" } # Same name as original!
380
+ price { 350 } # Different price ($3.50 vs $2.99)
381
+ sku { "CAFE-COF2-001" }
382
+ association :category, factory: %i[category coffee_espresso_dup]
383
+ end
384
+
385
+ trait :latte_dup do
386
+ name { "Latte" } # Same name as original!
387
+ price { 599 } # Different price ($5.99 vs $5.49)
388
+ sku { "CAFE-COF2-002" }
389
+ association :category, factory: %i[category coffee_espresso_dup]
390
+ end
391
+
392
+ trait :iced_latte do
393
+ name { "Iced Latte" } # New item only in duplicate category
394
+ price { 599 }
395
+ sku { "CAFE-COF2-003" }
396
+ association :category, factory: %i[category coffee_espresso_dup]
397
+ end
398
+
399
+ trait :matcha_latte do
400
+ name { "Matcha Latte" }
401
+ price { 649 }
402
+ sku { "CAFE-COF2-004" }
403
+ association :category, factory: %i[category coffee_espresso_dup]
404
+ end
405
+
406
+ # ── Duplicate Pastries (2nd category, some same item names) ──
407
+
408
+ trait :croissant_dup do
409
+ name { "Butter Croissant" } # Same name as original!
410
+ price { 450 } # Higher price ($4.50 vs $3.99)
411
+ sku { "CAFE-PAS2-001" }
412
+ association :category, factory: %i[category pastries_dup]
413
+ end
414
+
415
+ trait :blueberry_muffin_dup do
416
+ name { "Blueberry Muffin" } # Same name as original!
417
+ price { 349 } # Same price
418
+ sku { "CAFE-PAS2-002" }
419
+ association :category, factory: %i[category pastries_dup]
420
+ end
421
+
422
+ trait :almond_croissant do
423
+ name { "Almond Croissant" } # New item
424
+ price { 499 }
425
+ sku { "CAFE-PAS2-003" }
426
+ association :category, factory: %i[category pastries_dup]
427
+ end
428
+
429
+ trait :banana_bread do
430
+ name { "Banana Bread" }
431
+ price { 399 }
432
+ sku { "CAFE-PAS2-004" }
433
+ association :category, factory: %i[category pastries_dup]
434
+ end
435
+
436
+ # ── Duplicate Breakfast (2nd category, same + different items) ──
437
+
438
+ trait :avocado_toast_dup do
439
+ name { "Avocado Toast" } # Same name as original!
440
+ price { 1099 } # Higher price ($10.99 vs $9.99)
441
+ sku { "CAFE-BRK2-001" }
442
+ association :category, factory: %i[category breakfast_dup]
443
+ end
444
+
445
+ trait :eggs_benedict do
446
+ name { "Eggs Benedict" }
447
+ price { 1399 }
448
+ sku { "CAFE-BRK2-002" }
449
+ association :category, factory: %i[category breakfast_dup]
450
+ end
451
+
452
+ trait :french_toast do
453
+ name { "French Toast" }
454
+ price { 1199 }
455
+ sku { "CAFE-BRK2-003" }
456
+ association :category, factory: %i[category breakfast_dup]
457
+ end
458
+
459
+ # ── Drinks (catch-all that overlaps Coffee & Smoothies) ──
460
+
461
+ trait :cafe_drip_coffee do
462
+ name { "House Drip Coffee" } # Same name AGAIN (3rd occurrence!)
463
+ price { 275 } # Yet another price ($2.75)
464
+ sku { "CAFE-DRK-001" }
465
+ association :category, factory: %i[category cafe_drinks]
466
+ end
467
+
468
+ trait :cafe_iced_tea do
469
+ name { "Iced Tea" }
470
+ price { 349 }
471
+ sku { "CAFE-DRK-002" }
472
+ association :category, factory: %i[category cafe_drinks]
473
+ end
474
+
475
+ trait :cafe_lemonade do
476
+ name { "Lemonade" }
477
+ price { 399 }
478
+ sku { "CAFE-DRK-003" }
479
+ association :category, factory: %i[category cafe_drinks]
480
+ end
481
+
482
+ trait :cafe_hot_chocolate do
483
+ name { "Hot Chocolate" }
484
+ price { 449 }
485
+ sku { "CAFE-DRK-004" }
486
+ association :category, factory: %i[category cafe_drinks]
487
+ end
488
+
489
+ trait :berry_blast_smoothie_dup do
490
+ name { "Berry Blast Smoothie" } # Same name as in Smoothies!
491
+ price { 749 } # Different price ($7.49 vs $6.99)
492
+ sku { "CAFE-DRK-005" }
493
+ association :category, factory: %i[category cafe_drinks]
494
+ end
495
+
496
+ # ── Grab & Go (cross-listed items from other categories) ──
497
+
498
+ trait :gg_croissant do
499
+ name { "Butter Croissant" } # 3rd occurrence of this item name!
500
+ price { 399 }
501
+ sku { "CAFE-GNG-001" }
502
+ association :category, factory: %i[category cafe_grab_and_go]
503
+ end
504
+
505
+ trait :gg_muffin do
506
+ name { "Blueberry Muffin" } # 3rd occurrence
507
+ price { 399 }
508
+ sku { "CAFE-GNG-002" }
509
+ association :category, factory: %i[category cafe_grab_and_go]
510
+ end
511
+
512
+ trait :gg_blt do
513
+ name { "BLT" } # Same as in Sandwiches
514
+ price { 899 }
515
+ sku { "CAFE-GNG-003" }
516
+ association :category, factory: %i[category cafe_grab_and_go]
517
+ end
518
+
519
+ trait :gg_yogurt_parfait do
520
+ name { "Yogurt Parfait" } # Same as in Breakfast
521
+ price { 599 } # Lower price ($5.99 vs $6.99)
522
+ sku { "CAFE-GNG-004" }
523
+ association :category, factory: %i[category cafe_grab_and_go]
524
+ end
525
+
526
+ trait :gg_cold_brew do
527
+ name { "Cold Brew" } # Same as in Coffee & Espresso
528
+ price { 499 }
529
+ sku { "CAFE-GNG-005" }
530
+ association :category, factory: %i[category cafe_grab_and_go]
531
+ end
532
+
533
+ # ═══════════════════════════════════════════════════════════
534
+ # BAR & NIGHTCLUB (5 categories, 20 items)
535
+ # ═══════════════════════════════════════════════════════════
536
+
537
+ # ── Draft Beer ─────────────────────────────────────────────
538
+
539
+ trait :house_lager do
540
+ name { "House Lager" }
541
+ price { 600 }
542
+ sku { "BAR-DRF-001" }
543
+ association :category, factory: %i[category draft_beer]
544
+ end
545
+
546
+ trait :ipa do
547
+ name { "IPA" }
548
+ price { 700 }
549
+ sku { "BAR-DRF-002" }
550
+ association :category, factory: %i[category draft_beer]
551
+ end
552
+
553
+ trait :stout do
554
+ name { "Stout" }
555
+ price { 750 }
556
+ sku { "BAR-DRF-003" }
557
+ association :category, factory: %i[category draft_beer]
558
+ end
559
+
560
+ trait :wheat_beer do
561
+ name { "Wheat Beer" }
562
+ price { 650 }
563
+ sku { "BAR-DRF-004" }
564
+ association :category, factory: %i[category draft_beer]
565
+ end
566
+
567
+ # ── Cocktails ──────────────────────────────────────────────
568
+
569
+ trait :margarita do
570
+ name { "Margarita" }
571
+ price { 1200 }
572
+ sku { "BAR-CTL-001" }
573
+ association :category, factory: %i[category cocktails]
574
+ end
575
+
576
+ trait :old_fashioned do
577
+ name { "Old Fashioned" }
578
+ price { 1400 }
579
+ sku { "BAR-CTL-002" }
580
+ association :category, factory: %i[category cocktails]
581
+ end
582
+
583
+ trait :mojito do
584
+ name { "Mojito" }
585
+ price { 1300 }
586
+ sku { "BAR-CTL-003" }
587
+ association :category, factory: %i[category cocktails]
588
+ end
589
+
590
+ trait :espresso_martini do
591
+ name { "Espresso Martini" }
592
+ price { 1500 }
593
+ sku { "BAR-CTL-004" }
594
+ association :category, factory: %i[category cocktails]
595
+ end
596
+
597
+ # ── Spirits ────────────────────────────────────────────────
598
+
599
+ trait :whiskey_neat do
600
+ name { "Whiskey Neat" }
601
+ price { 800 }
602
+ sku { "BAR-SPR-001" }
603
+ association :category, factory: %i[category spirits]
604
+ end
605
+
606
+ trait :vodka_soda do
607
+ name { "Vodka Soda" }
608
+ price { 700 }
609
+ sku { "BAR-SPR-002" }
610
+ association :category, factory: %i[category spirits]
611
+ end
612
+
613
+ trait :tequila_shot do
614
+ name { "Tequila Shot" }
615
+ price { 900 }
616
+ sku { "BAR-SPR-003" }
617
+ association :category, factory: %i[category spirits]
618
+ end
619
+
620
+ trait :rum_and_coke do
621
+ name { "Rum & Coke" }
622
+ price { 800 }
623
+ sku { "BAR-SPR-004" }
624
+ association :category, factory: %i[category spirits]
625
+ end
626
+
627
+ # ── Wine ───────────────────────────────────────────────────
628
+
629
+ trait :house_red do
630
+ name { "House Red (Glass)" }
631
+ price { 1000 }
632
+ sku { "BAR-WIN-001" }
633
+ association :category, factory: %i[category wine]
634
+ end
635
+
636
+ trait :house_white do
637
+ name { "House White (Glass)" }
638
+ price { 1000 }
639
+ sku { "BAR-WIN-002" }
640
+ association :category, factory: %i[category wine]
641
+ end
642
+
643
+ trait :prosecco do
644
+ name { "Prosecco (Glass)" }
645
+ price { 1200 }
646
+ sku { "BAR-WIN-003" }
647
+ association :category, factory: %i[category wine]
648
+ end
649
+
650
+ trait :rose do
651
+ name { "Rose (Glass)" }
652
+ price { 1100 }
653
+ sku { "BAR-WIN-004" }
654
+ association :category, factory: %i[category wine]
655
+ end
656
+
657
+ # ── Bar Snacks ─────────────────────────────────────────────
658
+
659
+ trait :bar_loaded_fries do
660
+ name { "Loaded Fries" }
661
+ price { 999 }
662
+ sku { "BAR-SNK-001" }
663
+ association :category, factory: %i[category bar_snacks]
664
+ end
665
+
666
+ trait :sliders do
667
+ name { "Sliders (3pc)" }
668
+ price { 1299 }
669
+ sku { "BAR-SNK-002" }
670
+ association :category, factory: %i[category bar_snacks]
671
+ end
672
+
673
+ trait :bar_wings do
674
+ name { "Chicken Wings" }
675
+ price { 1199 }
676
+ sku { "BAR-SNK-003" }
677
+ association :category, factory: %i[category bar_snacks]
678
+ end
679
+
680
+ trait :pretzel_bites do
681
+ name { "Pretzel Bites" }
682
+ price { 899 }
683
+ sku { "BAR-SNK-004" }
684
+ association :category, factory: %i[category bar_snacks]
685
+ end
686
+
687
+ # ═══════════════════════════════════════════════════════════
688
+ # FOOD TRUCK (3 categories, 14 items)
689
+ # ═══════════════════════════════════════════════════════════
690
+
691
+ # ── Tacos ──────────────────────────────────────────────────
692
+
693
+ trait :carne_asada_taco do
694
+ name { "Carne Asada Taco" }
695
+ price { 499 }
696
+ sku { "FT-TAC-001" }
697
+ association :category, factory: %i[category tacos]
698
+ end
699
+
700
+ trait :al_pastor_taco do
701
+ name { "Al Pastor Taco" }
702
+ price { 449 }
703
+ sku { "FT-TAC-002" }
704
+ association :category, factory: %i[category tacos]
705
+ end
706
+
707
+ trait :fish_taco do
708
+ name { "Fish Taco" }
709
+ price { 549 }
710
+ sku { "FT-TAC-003" }
711
+ association :category, factory: %i[category tacos]
712
+ end
713
+
714
+ trait :veggie_taco do
715
+ name { "Veggie Taco" }
716
+ price { 399 }
717
+ sku { "FT-TAC-004" }
718
+ association :category, factory: %i[category tacos]
719
+ end
720
+
721
+ trait :birria_taco do
722
+ name { "Birria Taco" }
723
+ price { 599 }
724
+ sku { "FT-TAC-005" }
725
+ association :category, factory: %i[category tacos]
726
+ end
727
+
728
+ # ── Burritos & Bowls ───────────────────────────────────────
729
+
730
+ trait :classic_burrito do
731
+ name { "Classic Burrito" }
732
+ price { 1099 }
733
+ sku { "FT-BUR-001" }
734
+ association :category, factory: %i[category burritos_bowls]
735
+ end
736
+
737
+ trait :burrito_bowl do
738
+ name { "Burrito Bowl" }
739
+ price { 1149 }
740
+ sku { "FT-BUR-002" }
741
+ association :category, factory: %i[category burritos_bowls]
742
+ end
743
+
744
+ trait :quesadilla do
745
+ name { "Quesadilla" }
746
+ price { 899 }
747
+ sku { "FT-BUR-003" }
748
+ association :category, factory: %i[category burritos_bowls]
749
+ end
750
+
751
+ trait :truck_nachos do
752
+ name { "Loaded Nachos" }
753
+ price { 999 }
754
+ sku { "FT-BUR-004" }
755
+ association :category, factory: %i[category burritos_bowls]
756
+ end
757
+
758
+ # ── Sides & Drinks ─────────────────────────────────────────
759
+
760
+ trait :chips_and_guac do
761
+ name { "Chips & Guacamole" }
762
+ price { 499 }
763
+ sku { "FT-SDE-001" }
764
+ association :category, factory: %i[category truck_sides_drinks]
765
+ end
766
+
767
+ trait :elote do
768
+ name { "Elote (Street Corn)" }
769
+ price { 399 }
770
+ sku { "FT-SDE-002" }
771
+ association :category, factory: %i[category truck_sides_drinks]
772
+ end
773
+
774
+ trait :rice_and_beans do
775
+ name { "Rice & Beans" }
776
+ price { 349 }
777
+ sku { "FT-SDE-003" }
778
+ association :category, factory: %i[category truck_sides_drinks]
779
+ end
780
+
781
+ trait :horchata do
782
+ name { "Horchata" }
783
+ price { 399 }
784
+ sku { "FT-SDE-004" }
785
+ association :category, factory: %i[category truck_sides_drinks]
786
+ end
787
+
788
+ trait :jarritos do
789
+ name { "Jarritos" }
790
+ price { 299 }
791
+ sku { "FT-SDE-005" }
792
+ association :category, factory: %i[category truck_sides_drinks]
793
+ end
794
+
795
+ # ═══════════════════════════════════════════════════════════
796
+ # FINE DINING (3 categories, 15 items)
797
+ # ═══════════════════════════════════════════════════════════
798
+
799
+ # ── First Course ───────────────────────────────────────────
800
+
801
+ trait :seared_foie_gras do
802
+ name { "Seared Foie Gras" }
803
+ price { 2400 }
804
+ sku { "FD-FST-001" }
805
+ association :category, factory: %i[category first_course]
806
+ end
807
+
808
+ trait :lobster_bisque do
809
+ name { "Lobster Bisque" }
810
+ price { 1800 }
811
+ sku { "FD-FST-002" }
812
+ association :category, factory: %i[category first_course]
813
+ end
814
+
815
+ trait :tuna_tartare do
816
+ name { "Tuna Tartare" }
817
+ price { 2200 }
818
+ sku { "FD-FST-003" }
819
+ association :category, factory: %i[category first_course]
820
+ end
821
+
822
+ trait :burrata_salad do
823
+ name { "Burrata Salad" }
824
+ price { 1900 }
825
+ sku { "FD-FST-004" }
826
+ association :category, factory: %i[category first_course]
827
+ end
828
+
829
+ trait :oysters_half_dozen do
830
+ name { "Oysters (Half Dozen)" }
831
+ price { 2800 }
832
+ sku { "FD-FST-005" }
833
+ association :category, factory: %i[category first_course]
834
+ end
835
+
836
+ # ── Main Course ────────────────────────────────────────────
837
+
838
+ trait :wagyu_ribeye do
839
+ name { "Wagyu Ribeye" }
840
+ price { 6500 }
841
+ sku { "FD-MAN-001" }
842
+ association :category, factory: %i[category main_course]
843
+ end
844
+
845
+ trait :chilean_sea_bass do
846
+ name { "Pan-Seared Chilean Sea Bass" }
847
+ price { 4800 }
848
+ sku { "FD-MAN-002" }
849
+ association :category, factory: %i[category main_course]
850
+ end
851
+
852
+ trait :rack_of_lamb do
853
+ name { "Rack of Lamb" }
854
+ price { 5200 }
855
+ sku { "FD-MAN-003" }
856
+ association :category, factory: %i[category main_course]
857
+ end
858
+
859
+ trait :duck_breast do
860
+ name { "Duck Breast" }
861
+ price { 4200 }
862
+ sku { "FD-MAN-004" }
863
+ association :category, factory: %i[category main_course]
864
+ end
865
+
866
+ trait :truffle_risotto do
867
+ name { "Truffle Risotto" }
868
+ price { 3800 }
869
+ sku { "FD-MAN-005" }
870
+ association :category, factory: %i[category main_course]
871
+ end
872
+
873
+ # ── Desserts & Petit Fours ─────────────────────────────────
874
+
875
+ trait :creme_brulee do
876
+ name { "Creme Brulee" }
877
+ price { 1600 }
878
+ sku { "FD-DES-001" }
879
+ association :category, factory: %i[category fine_desserts]
880
+ end
881
+
882
+ trait :chocolate_souffle do
883
+ name { "Chocolate Souffle" }
884
+ price { 1800 }
885
+ sku { "FD-DES-002" }
886
+ association :category, factory: %i[category fine_desserts]
887
+ end
888
+
889
+ trait :tasting_plate do
890
+ name { "Tasting Plate" }
891
+ price { 2200 }
892
+ sku { "FD-DES-003" }
893
+ association :category, factory: %i[category fine_desserts]
894
+ end
895
+
896
+ trait :cheese_board do
897
+ name { "Cheese Board" }
898
+ price { 2400 }
899
+ sku { "FD-DES-004" }
900
+ association :category, factory: %i[category fine_desserts]
901
+ end
902
+
903
+ trait :fine_tiramisu do
904
+ name { "Tiramisu" }
905
+ price { 1600 }
906
+ sku { "FD-DES-005" }
907
+ association :category, factory: %i[category fine_desserts]
908
+ end
909
+
910
+ # ═══════════════════════════════════════════════════════════
911
+ # PIZZERIA (3 categories, 16 items)
912
+ # ═══════════════════════════════════════════════════════════
913
+
914
+ # ── Pizzas ─────────────────────────────────────────────────
915
+
916
+ trait :margherita do
917
+ name { "Margherita" }
918
+ price { 1499 }
919
+ sku { "PIZ-PIZ-001" }
920
+ association :category, factory: %i[category pizzas]
921
+ end
922
+
923
+ trait :pepperoni do
924
+ name { "Pepperoni" }
925
+ price { 1599 }
926
+ sku { "PIZ-PIZ-002" }
927
+ association :category, factory: %i[category pizzas]
928
+ end
929
+
930
+ trait :supreme do
931
+ name { "Supreme" }
932
+ price { 1899 }
933
+ sku { "PIZ-PIZ-003" }
934
+ association :category, factory: %i[category pizzas]
935
+ end
936
+
937
+ trait :hawaiian do
938
+ name { "Hawaiian" }
939
+ price { 1699 }
940
+ sku { "PIZ-PIZ-004" }
941
+ association :category, factory: %i[category pizzas]
942
+ end
943
+
944
+ trait :bbq_chicken_pizza do
945
+ name { "BBQ Chicken Pizza" }
946
+ price { 1999 }
947
+ sku { "PIZ-PIZ-005" }
948
+ association :category, factory: %i[category pizzas]
949
+ end
950
+
951
+ trait :meat_lovers do
952
+ name { "Meat Lovers" }
953
+ price { 2099 }
954
+ sku { "PIZ-PIZ-006" }
955
+ association :category, factory: %i[category pizzas]
956
+ end
957
+
958
+ # ── Calzones & Stromboli ───────────────────────────────────
959
+
960
+ trait :classic_calzone do
961
+ name { "Classic Calzone" }
962
+ price { 1399 }
963
+ sku { "PIZ-CAL-001" }
964
+ association :category, factory: %i[category calzones]
965
+ end
966
+
967
+ trait :meat_calzone do
968
+ name { "Meat Calzone" }
969
+ price { 1599 }
970
+ sku { "PIZ-CAL-002" }
971
+ association :category, factory: %i[category calzones]
972
+ end
973
+
974
+ trait :stromboli do
975
+ name { "Stromboli" }
976
+ price { 1499 }
977
+ sku { "PIZ-CAL-003" }
978
+ association :category, factory: %i[category calzones]
979
+ end
980
+
981
+ trait :spinach_calzone do
982
+ name { "Spinach & Ricotta Calzone" }
983
+ price { 1399 }
984
+ sku { "PIZ-CAL-004" }
985
+ association :category, factory: %i[category calzones]
986
+ end
987
+
988
+ # ── Sides & Drinks ─────────────────────────────────────────
989
+
990
+ trait :garlic_bread do
991
+ name { "Garlic Bread" }
992
+ price { 599 }
993
+ sku { "PIZ-SDE-001" }
994
+ association :category, factory: %i[category pizza_sides_drinks]
995
+ end
996
+
997
+ trait :garden_salad do
998
+ name { "Garden Salad" }
999
+ price { 799 }
1000
+ sku { "PIZ-SDE-002" }
1001
+ association :category, factory: %i[category pizza_sides_drinks]
1002
+ end
1003
+
1004
+ trait :caesar_salad do
1005
+ name { "Caesar Salad" }
1006
+ price { 899 }
1007
+ sku { "PIZ-SDE-003" }
1008
+ association :category, factory: %i[category pizza_sides_drinks]
1009
+ end
1010
+
1011
+ trait :garlic_knots do
1012
+ name { "Garlic Knots (6pc)" }
1013
+ price { 699 }
1014
+ sku { "PIZ-SDE-004" }
1015
+ association :category, factory: %i[category pizza_sides_drinks]
1016
+ end
1017
+
1018
+ trait :fountain_drink do
1019
+ name { "Fountain Drink" }
1020
+ price { 249 }
1021
+ sku { "PIZ-SDE-005" }
1022
+ association :category, factory: %i[category pizza_sides_drinks]
1023
+ end
1024
+
1025
+ trait :cannoli do
1026
+ name { "Cannoli" }
1027
+ price { 599 }
1028
+ sku { "PIZ-SDE-006" }
1029
+ association :category, factory: %i[category pizza_sides_drinks]
1030
+ end
1031
+
1032
+ # ═══════════════════════════════════════════════════════════
1033
+ # RETAIL CLOTHING (5 categories, 21 items)
1034
+ # — items include `variants` jsonb with sizes/colors
1035
+ # ═══════════════════════════════════════════════════════════
1036
+
1037
+ # ── Tops ───────────────────────────────────────────────────
1038
+
1039
+ trait :classic_tshirt do
1040
+ name { "Classic T-Shirt" }
1041
+ price { 2499 }
1042
+ sku { "RCL-TOP-001" }
1043
+ association :category, factory: %i[category tops]
1044
+ variants { [{ sizes: %w[S M L XL], colors: %w[Black White Navy] }] }
1045
+ end
1046
+
1047
+ trait :button_down_shirt do
1048
+ name { "Button-Down Shirt" }
1049
+ price { 4999 }
1050
+ sku { "RCL-TOP-002" }
1051
+ association :category, factory: %i[category tops]
1052
+ variants { [{ sizes: %w[S M L XL], colors: %w[White Blue Striped] }] }
1053
+ end
1054
+
1055
+ trait :polo_shirt do
1056
+ name { "Polo Shirt" }
1057
+ price { 3999 }
1058
+ sku { "RCL-TOP-003" }
1059
+ association :category, factory: %i[category tops]
1060
+ variants { [{ sizes: %w[S M L XL], colors: %w[Black White Red] }] }
1061
+ end
1062
+
1063
+ trait :hoodie do
1064
+ name { "Hoodie" }
1065
+ price { 5499 }
1066
+ sku { "RCL-TOP-004" }
1067
+ association :category, factory: %i[category tops]
1068
+ variants { [{ sizes: %w[S M L XL], colors: %w[Black Gray Navy] }] }
1069
+ end
1070
+
1071
+ trait :tank_top do
1072
+ name { "Tank Top" }
1073
+ price { 1999 }
1074
+ sku { "RCL-TOP-005" }
1075
+ association :category, factory: %i[category tops]
1076
+ variants { [{ sizes: %w[S M L XL], colors: %w[White Black] }] }
1077
+ end
1078
+
1079
+ # ── Bottoms ────────────────────────────────────────────────
1080
+
1081
+ trait :slim_fit_jeans do
1082
+ name { "Slim Fit Jeans" }
1083
+ price { 5999 }
1084
+ sku { "RCL-BTM-001" }
1085
+ association :category, factory: %i[category bottoms]
1086
+ variants { [{ sizes: %w[28 30 32 34 36], colors: %w[Indigo Black Light] }] }
1087
+ end
1088
+
1089
+ trait :chino_pants do
1090
+ name { "Chino Pants" }
1091
+ price { 4499 }
1092
+ sku { "RCL-BTM-002" }
1093
+ association :category, factory: %i[category bottoms]
1094
+ variants { [{ sizes: %w[28 30 32 34 36], colors: %w[Khaki Navy Olive] }] }
1095
+ end
1096
+
1097
+ trait :joggers do
1098
+ name { "Joggers" }
1099
+ price { 3999 }
1100
+ sku { "RCL-BTM-003" }
1101
+ association :category, factory: %i[category bottoms]
1102
+ variants { [{ sizes: %w[S M L XL], colors: %w[Black Gray] }] }
1103
+ end
1104
+
1105
+ trait :shorts do
1106
+ name { "Shorts" }
1107
+ price { 3499 }
1108
+ sku { "RCL-BTM-004" }
1109
+ association :category, factory: %i[category bottoms]
1110
+ variants { [{ sizes: %w[S M L XL], colors: %w[Navy Khaki Black] }] }
1111
+ end
1112
+
1113
+ # ── Outerwear ──────────────────────────────────────────────
1114
+
1115
+ trait :denim_jacket do
1116
+ name { "Denim Jacket" }
1117
+ price { 7999 }
1118
+ sku { "RCL-OUT-001" }
1119
+ association :category, factory: %i[category outerwear]
1120
+ variants { [{ sizes: %w[S M L XL], colors: %w[Indigo Light] }] }
1121
+ end
1122
+
1123
+ trait :bomber_jacket do
1124
+ name { "Bomber Jacket" }
1125
+ price { 8999 }
1126
+ sku { "RCL-OUT-002" }
1127
+ association :category, factory: %i[category outerwear]
1128
+ variants { [{ sizes: %w[S M L XL], colors: %w[Black Olive] }] }
1129
+ end
1130
+
1131
+ trait :puffer_vest do
1132
+ name { "Puffer Vest" }
1133
+ price { 6999 }
1134
+ sku { "RCL-OUT-003" }
1135
+ association :category, factory: %i[category outerwear]
1136
+ variants { [{ sizes: %w[S M L XL], colors: %w[Black Navy Red] }] }
1137
+ end
1138
+
1139
+ trait :rain_jacket do
1140
+ name { "Rain Jacket" }
1141
+ price { 6499 }
1142
+ sku { "RCL-OUT-004" }
1143
+ association :category, factory: %i[category outerwear]
1144
+ variants { [{ sizes: %w[S M L XL], colors: %w[Yellow Navy Black] }] }
1145
+ end
1146
+
1147
+ # ── Accessories ────────────────────────────────────────────
1148
+
1149
+ trait :baseball_cap do
1150
+ name { "Baseball Cap" }
1151
+ price { 2499 }
1152
+ sku { "RCL-ACC-001" }
1153
+ association :category, factory: %i[category accessories]
1154
+ variants { [{ colors: %w[Black White Navy Red] }] }
1155
+ end
1156
+
1157
+ trait :beanie do
1158
+ name { "Beanie" }
1159
+ price { 1999 }
1160
+ sku { "RCL-ACC-002" }
1161
+ association :category, factory: %i[category accessories]
1162
+ variants { [{ colors: %w[Black Gray Burgundy] }] }
1163
+ end
1164
+
1165
+ trait :canvas_belt do
1166
+ name { "Canvas Belt" }
1167
+ price { 2999 }
1168
+ sku { "RCL-ACC-003" }
1169
+ association :category, factory: %i[category accessories]
1170
+ variants { [{ sizes: %w[S M L], colors: %w[Black Brown Tan] }] }
1171
+ end
1172
+
1173
+ trait :scarf do
1174
+ name { "Scarf" }
1175
+ price { 3499 }
1176
+ sku { "RCL-ACC-004" }
1177
+ association :category, factory: %i[category accessories]
1178
+ variants { [{ colors: ["Plaid", "Solid Gray", "Solid Black"] }] }
1179
+ end
1180
+
1181
+ # ── Footwear ───────────────────────────────────────────────
1182
+
1183
+ trait :canvas_sneakers do
1184
+ name { "Canvas Sneakers" }
1185
+ price { 4999 }
1186
+ sku { "RCL-FTW-001" }
1187
+ association :category, factory: %i[category footwear]
1188
+ variants { [{ sizes: %w[7 8 9 10 11 12], colors: %w[White Black Navy] }] }
1189
+ end
1190
+
1191
+ trait :leather_boots do
1192
+ name { "Leather Boots" }
1193
+ price { 12_999 }
1194
+ sku { "RCL-FTW-002" }
1195
+ association :category, factory: %i[category footwear]
1196
+ variants { [{ sizes: %w[7 8 9 10 11 12], colors: %w[Brown Black] }] }
1197
+ end
1198
+
1199
+ trait :sandals do
1200
+ name { "Sandals" }
1201
+ price { 3499 }
1202
+ sku { "RCL-FTW-003" }
1203
+ association :category, factory: %i[category footwear]
1204
+ variants { [{ sizes: %w[7 8 9 10 11 12], colors: %w[Tan Black] }] }
1205
+ end
1206
+
1207
+ trait :running_shoes do
1208
+ name { "Running Shoes" }
1209
+ price { 8999 }
1210
+ sku { "RCL-FTW-004" }
1211
+ association :category, factory: %i[category footwear]
1212
+ variants { [{ sizes: %w[7 8 9 10 11 12], colors: %w[Black White Red] }] }
1213
+ end
1214
+
1215
+ # ═══════════════════════════════════════════════════════════
1216
+ # RETAIL GENERAL (5 categories, 20 items)
1217
+ # ═══════════════════════════════════════════════════════════
1218
+
1219
+ # ── Electronics ────────────────────────────────────────────
1220
+
1221
+ trait :wireless_earbuds do
1222
+ name { "Wireless Earbuds" }
1223
+ price { 7999 }
1224
+ sku { "RGN-ELC-001" }
1225
+ association :category, factory: %i[category electronics]
1226
+ end
1227
+
1228
+ trait :phone_charger do
1229
+ name { "Phone Charger" }
1230
+ price { 1999 }
1231
+ sku { "RGN-ELC-002" }
1232
+ association :category, factory: %i[category electronics]
1233
+ end
1234
+
1235
+ trait :bluetooth_speaker do
1236
+ name { "Bluetooth Speaker" }
1237
+ price { 4999 }
1238
+ sku { "RGN-ELC-003" }
1239
+ association :category, factory: %i[category electronics]
1240
+ end
1241
+
1242
+ trait :power_bank do
1243
+ name { "Power Bank" }
1244
+ price { 3499 }
1245
+ sku { "RGN-ELC-004" }
1246
+ association :category, factory: %i[category electronics]
1247
+ end
1248
+
1249
+ # ── Home & Kitchen ─────────────────────────────────────────
1250
+
1251
+ trait :scented_candle do
1252
+ name { "Scented Candle" }
1253
+ price { 2499 }
1254
+ sku { "RGN-HMK-001" }
1255
+ association :category, factory: %i[category home_kitchen]
1256
+ end
1257
+
1258
+ trait :throw_pillow do
1259
+ name { "Throw Pillow" }
1260
+ price { 2999 }
1261
+ sku { "RGN-HMK-002" }
1262
+ association :category, factory: %i[category home_kitchen]
1263
+ end
1264
+
1265
+ trait :kitchen_towel_set do
1266
+ name { "Kitchen Towel Set" }
1267
+ price { 1499 }
1268
+ sku { "RGN-HMK-003" }
1269
+ association :category, factory: %i[category home_kitchen]
1270
+ end
1271
+
1272
+ trait :ceramic_mug do
1273
+ name { "Ceramic Mug" }
1274
+ price { 1299 }
1275
+ sku { "RGN-HMK-004" }
1276
+ association :category, factory: %i[category home_kitchen]
1277
+ end
1278
+
1279
+ # ── Personal Care ──────────────────────────────────────────
1280
+
1281
+ trait :hand_soap do
1282
+ name { "Hand Soap" }
1283
+ price { 699 }
1284
+ sku { "RGN-PRC-001" }
1285
+ association :category, factory: %i[category personal_care]
1286
+ end
1287
+
1288
+ trait :body_lotion do
1289
+ name { "Body Lotion" }
1290
+ price { 1499 }
1291
+ sku { "RGN-PRC-002" }
1292
+ association :category, factory: %i[category personal_care]
1293
+ end
1294
+
1295
+ trait :lip_balm do
1296
+ name { "Lip Balm" }
1297
+ price { 499 }
1298
+ sku { "RGN-PRC-003" }
1299
+ association :category, factory: %i[category personal_care]
1300
+ end
1301
+
1302
+ trait :sunscreen do
1303
+ name { "Sunscreen SPF 50" }
1304
+ price { 1299 }
1305
+ sku { "RGN-PRC-004" }
1306
+ association :category, factory: %i[category personal_care]
1307
+ end
1308
+
1309
+ # ── Office Supplies ────────────────────────────────────────
1310
+
1311
+ trait :notebook do
1312
+ name { "Notebook" }
1313
+ price { 999 }
1314
+ sku { "RGN-OFC-001" }
1315
+ association :category, factory: %i[category office_supplies]
1316
+ end
1317
+
1318
+ trait :pen_set do
1319
+ name { "Ballpoint Pen Set" }
1320
+ price { 799 }
1321
+ sku { "RGN-OFC-002" }
1322
+ association :category, factory: %i[category office_supplies]
1323
+ end
1324
+
1325
+ trait :desk_organizer do
1326
+ name { "Desk Organizer" }
1327
+ price { 1999 }
1328
+ sku { "RGN-OFC-003" }
1329
+ association :category, factory: %i[category office_supplies]
1330
+ end
1331
+
1332
+ trait :sticky_notes do
1333
+ name { "Sticky Notes" }
1334
+ price { 499 }
1335
+ sku { "RGN-OFC-004" }
1336
+ association :category, factory: %i[category office_supplies]
1337
+ end
1338
+
1339
+ # ── Snacks & Beverages ─────────────────────────────────────
1340
+
1341
+ trait :granola_bar_3pack do
1342
+ name { "Granola Bar (3-Pack)" }
1343
+ price { 599 }
1344
+ sku { "RGN-SNK-001" }
1345
+ association :category, factory: %i[category snacks_beverages]
1346
+ end
1347
+
1348
+ trait :retail_sparkling_water do
1349
+ name { "Sparkling Water" }
1350
+ price { 249 }
1351
+ sku { "RGN-SNK-002" }
1352
+ association :category, factory: %i[category snacks_beverages]
1353
+ end
1354
+
1355
+ trait :trail_mix do
1356
+ name { "Trail Mix" }
1357
+ price { 699 }
1358
+ sku { "RGN-SNK-003" }
1359
+ association :category, factory: %i[category snacks_beverages]
1360
+ end
1361
+
1362
+ trait :dark_chocolate_bar do
1363
+ name { "Dark Chocolate Bar" }
1364
+ price { 499 }
1365
+ sku { "RGN-SNK-004" }
1366
+ association :category, factory: %i[category snacks_beverages]
1367
+ end
1368
+
1369
+ # ═══════════════════════════════════════════════════════════
1370
+ # SALON & SPA (4 categories, 16 items)
1371
+ # — services use unit: "session", metadata with duration
1372
+ # ═══════════════════════════════════════════════════════════
1373
+
1374
+ # ── Haircuts ───────────────────────────────────────────────
1375
+
1376
+ trait :womens_haircut do
1377
+ name { "Women's Haircut" }
1378
+ price { 5500 }
1379
+ sku { "SPA-HAR-001" }
1380
+ unit { "session" }
1381
+ metadata { { "duration_minutes" => 45 } }
1382
+ association :category, factory: %i[category haircuts]
1383
+ end
1384
+
1385
+ trait :mens_haircut do
1386
+ name { "Men's Haircut" }
1387
+ price { 3000 }
1388
+ sku { "SPA-HAR-002" }
1389
+ unit { "session" }
1390
+ metadata { { "duration_minutes" => 30 } }
1391
+ association :category, factory: %i[category haircuts]
1392
+ end
1393
+
1394
+ trait :childrens_haircut do
1395
+ name { "Children's Haircut" }
1396
+ price { 2000 }
1397
+ sku { "SPA-HAR-003" }
1398
+ unit { "session" }
1399
+ metadata { { "duration_minutes" => 20 } }
1400
+ association :category, factory: %i[category haircuts]
1401
+ end
1402
+
1403
+ trait :bang_trim do
1404
+ name { "Bang Trim" }
1405
+ price { 1500 }
1406
+ sku { "SPA-HAR-004" }
1407
+ unit { "session" }
1408
+ metadata { { "duration_minutes" => 15 } }
1409
+ association :category, factory: %i[category haircuts]
1410
+ end
1411
+
1412
+ # ── Color Services ─────────────────────────────────────────
1413
+
1414
+ trait :full_color do
1415
+ name { "Full Color" }
1416
+ price { 12_000 }
1417
+ sku { "SPA-CLR-001" }
1418
+ unit { "session" }
1419
+ metadata { { "duration_minutes" => 120 } }
1420
+ association :category, factory: %i[category color_services]
1421
+ end
1422
+
1423
+ trait :partial_highlights do
1424
+ name { "Highlights (Partial)" }
1425
+ price { 9000 }
1426
+ sku { "SPA-CLR-002" }
1427
+ unit { "session" }
1428
+ metadata { { "duration_minutes" => 90 } }
1429
+ association :category, factory: %i[category color_services]
1430
+ end
1431
+
1432
+ trait :full_highlights do
1433
+ name { "Highlights (Full)" }
1434
+ price { 15_000 }
1435
+ sku { "SPA-CLR-003" }
1436
+ unit { "session" }
1437
+ metadata { { "duration_minutes" => 120 } }
1438
+ association :category, factory: %i[category color_services]
1439
+ end
1440
+
1441
+ trait :balayage do
1442
+ name { "Balayage" }
1443
+ price { 18_000 }
1444
+ sku { "SPA-CLR-004" }
1445
+ unit { "session" }
1446
+ metadata { { "duration_minutes" => 150 } }
1447
+ association :category, factory: %i[category color_services]
1448
+ end
1449
+
1450
+ # ── Spa Treatments ─────────────────────────────────────────
1451
+
1452
+ trait :swedish_massage do
1453
+ name { "Swedish Massage (60min)" }
1454
+ price { 9500 }
1455
+ sku { "SPA-SPA-001" }
1456
+ unit { "hour" }
1457
+ metadata { { "duration_minutes" => 60 } }
1458
+ association :category, factory: %i[category spa_treatments]
1459
+ end
1460
+
1461
+ trait :deep_tissue_massage do
1462
+ name { "Deep Tissue Massage (60min)" }
1463
+ price { 11_000 }
1464
+ sku { "SPA-SPA-002" }
1465
+ unit { "hour" }
1466
+ metadata { { "duration_minutes" => 60 } }
1467
+ association :category, factory: %i[category spa_treatments]
1468
+ end
1469
+
1470
+ trait :hot_stone_massage do
1471
+ name { "Hot Stone Massage (60min)" }
1472
+ price { 12_000 }
1473
+ sku { "SPA-SPA-003" }
1474
+ unit { "hour" }
1475
+ metadata { { "duration_minutes" => 60 } }
1476
+ association :category, factory: %i[category spa_treatments]
1477
+ end
1478
+
1479
+ trait :facial do
1480
+ name { "Facial (60min)" }
1481
+ price { 8500 }
1482
+ sku { "SPA-SPA-004" }
1483
+ unit { "hour" }
1484
+ metadata { { "duration_minutes" => 60 } }
1485
+ association :category, factory: %i[category spa_treatments]
1486
+ end
1487
+
1488
+ # ── Nail Services ──────────────────────────────────────────
1489
+
1490
+ trait :manicure do
1491
+ name { "Manicure" }
1492
+ price { 2500 }
1493
+ sku { "SPA-NAL-001" }
1494
+ unit { "session" }
1495
+ metadata { { "duration_minutes" => 30 } }
1496
+ association :category, factory: %i[category nail_services]
1497
+ end
1498
+
1499
+ trait :pedicure do
1500
+ name { "Pedicure" }
1501
+ price { 3500 }
1502
+ sku { "SPA-NAL-002" }
1503
+ unit { "session" }
1504
+ metadata { { "duration_minutes" => 45 } }
1505
+ association :category, factory: %i[category nail_services]
1506
+ end
1507
+
1508
+ trait :gel_manicure do
1509
+ name { "Gel Manicure" }
1510
+ price { 4000 }
1511
+ sku { "SPA-NAL-003" }
1512
+ unit { "session" }
1513
+ metadata { { "duration_minutes" => 45 } }
1514
+ association :category, factory: %i[category nail_services]
1515
+ end
1516
+
1517
+ trait :acrylic_full_set do
1518
+ name { "Acrylic Full Set" }
1519
+ price { 5500 }
1520
+ sku { "SPA-NAL-004" }
1521
+ unit { "session" }
1522
+ metadata { { "duration_minutes" => 60 } }
1523
+ association :category, factory: %i[category nail_services]
1524
+ end
1525
+ end
1526
+ end