@12-apps/prisma 6.5.0 → 6.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/prisma",
3
- "version": "6.5.0",
3
+ "version": "6.6.0",
4
4
  "sideEffects": false,
5
5
  "description": "Prisma host: the multi-file schema folder, the plugin migration seam, and the shared PrismaClient singleton with its audit / append-only extensions",
6
6
  "main": "dist/index.js",
@@ -69,7 +69,7 @@
69
69
  "devDependencies": {
70
70
  "@12-apps/audit": "^5.7.1",
71
71
  "@12-apps/auth": "^2.19.0",
72
- "@12-apps/discounts": "^1.12.0",
72
+ "@12-apps/discounts": "^1.13.0",
73
73
  "@12-apps/entitlements": "^3.10.0",
74
74
  "@12-apps/entity-lifecycle": "^4.13.0",
75
75
  "@12-apps/eslint-config": "^1.22.0",
@@ -77,11 +77,11 @@
77
77
  "@12-apps/i18n": "^1.3.0",
78
78
  "@12-apps/jobs": "^4.5.0",
79
79
  "@12-apps/mcp": "^3.16.0",
80
- "@12-apps/notifications": "^4.8.0",
80
+ "@12-apps/notifications": "^4.9.0",
81
81
  "@12-apps/onboarding": "^2.7.0",
82
- "@12-apps/payments-backend": "^4.24.0",
82
+ "@12-apps/payments-backend": "^4.26.0",
83
83
  "@12-apps/product-research": "^2.10.0",
84
- "@12-apps/rbac": "^4.13.0",
84
+ "@12-apps/rbac": "^4.14.0",
85
85
  "@12-apps/realtime": "^2.6.0",
86
86
  "@12-apps/report-builder": "^5.14.0",
87
87
  "@12-apps/shift": "^3.4.0",
@@ -0,0 +1,40 @@
1
+ -- @12-apps/discounts: the WEEKLY schedule (FUT-996).
2
+ --
3
+ -- "Toda sexta, das 16:00 às 20:00" — a recurring window INSIDE the campaign
4
+ -- `[starts_at, ends_at)`, which until now was the only scheduling a rule had.
5
+ --
6
+ -- ── PURELY ADDITIVE, AND THAT IS THE POINT ────────────────────────────────
7
+ -- One nullable column, no backfill, no CHECK to widen, nothing dropped. NULL
8
+ -- means "always, within the campaign window", which is exactly what every
9
+ -- existing row already means — so this migration cannot change the price of
10
+ -- anything, and the release carrying it needs none of the expand/contract
11
+ -- ceremony a destructive DDL would (see the host's CLAUDE.md on why a
12
+ -- `DROP COLUMN` takes production down for the length of a healthcheck).
13
+ --
14
+ -- Replay-safe like its predecessor: `IF NOT EXISTS` throughout.
15
+ --
16
+ -- ── WHY JSON RATHER THAN A FOURTH TABLE ───────────────────────────────────
17
+ -- The recurrence is not SQL-queryable in any useful way. The admin grid's
18
+ -- vigência filter already punts on comparing two nullable timestamps against
19
+ -- `now()` and rides as a separate parameter the store resolves, so a schedule
20
+ -- table would buy queryability that nothing asks for — while adding a third
21
+ -- `include` to `loadDiscountRules`, which is on the storefront's hottest read
22
+ -- (a whole menu page's badge preview walks it once per request).
23
+ --
24
+ -- The trade is stated plainly: the database cannot check this column's SHAPE.
25
+ -- `assertSchedule` in `src/server/validate.ts` is the only gate in front of it,
26
+ -- and the evaluator is deliberately tolerant of a row that got past it — a
27
+ -- malformed window covers NOTHING rather than everything, so the failure mode
28
+ -- of a bad blob is a promotion that does not fire, never a cart priced at zero.
29
+ --
30
+ -- Shape, for a reader with only the database in front of them:
31
+ --
32
+ -- {"windows":[{"days":[4],"from":"16:00","to":"20:00"}]}
33
+ --
34
+ -- * `days` — Monday-first, 0..6. NOT `Date#getDay()`'s Sunday-first axis.
35
+ -- * `from`/`to` — "HH:MM", 24-hour, in the STORE's timezone, half-open
36
+ -- [from, to) exactly as [starts_at, ends_at) is.
37
+ -- * `to` < `from` means the window runs past midnight (a bar shutting at
38
+ -- 02:00), which is the most common happy hour there is.
39
+
40
+ ALTER TABLE "discounts" ADD COLUMN IF NOT EXISTS "schedule" JSONB;
@@ -37,6 +37,7 @@
37
37
  "20260821120000_shift_kind_host_vocabulary",
38
38
  "20260821140000_discounts_package_owned",
39
39
  "20260824120000_payments_pending_verification",
40
- "20260826170000_locale_preferences"
40
+ "20260826170000_locale_preferences",
41
+ "20260830120000_discount_weekly_schedule"
41
42
  ]
42
43
  }
@@ -79,6 +79,29 @@ model Discount {
79
79
  // either side = open-ended.
80
80
  startsAt DateTime? @map("starts_at")
81
81
  endsAt DateTime? @map("ends_at")
82
+ // The WEEKLY schedule INSIDE that window (FUT-996) — "toda sexta, das 16:00
83
+ // às 20:00", "segunda e terça à tarde". NULL = always, within
84
+ // [starts_at, ends_at), which is what every row predating the feature means.
85
+ //
86
+ // {"windows":[{"days":[4],"from":"16:00","to":"20:00"}]}
87
+ //
88
+ // `days` is Monday-first 0..6 — the axis every hours surface uses, and NOT
89
+ // `Date#getDay()`'s Sunday-first one. `from`/`to` are "HH:MM" in the STORE's
90
+ // timezone, half-open [from, to) exactly as the campaign window is, and a
91
+ // `to` earlier than `from` runs past midnight rather than being invalid.
92
+ //
93
+ // JSON rather than a fourth table because the recurrence is not usefully
94
+ // SQL-queryable (the grid's vigência filter already cannot express the
95
+ // campaign window either), and a fourth table would put a third `include` on
96
+ // the storefront's hottest read. The cost is that the DATABASE cannot check
97
+ // this shape: `assertSchedule` is the only gate, and the evaluator treats a
98
+ // malformed window as covering NOTHING, so a bad blob is a promotion that
99
+ // does not fire rather than a cart priced wrong.
100
+ //
101
+ // A recurring window is a WALL-CLOCK fact, so it is only meaningful against
102
+ // the store's own timezone. The engine never reads one: a host resolves the
103
+ // instant into a weekday and a minute-of-day and passes those in.
104
+ schedule Json? @map("schedule")
82
105
  // Minimum PRE-DISCOUNT cart subtotal, in cents. Always compared against the
83
106
  // untouched subtotal, never a running total — which is what makes the result
84
107
  // independent of the order the discounts are applied in.