@bash-app/bash-common 30.117.0 → 30.118.1

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 (31) hide show
  1. package/dist/extendedSchemas.d.ts +54 -0
  2. package/dist/extendedSchemas.d.ts.map +1 -1
  3. package/dist/extendedSchemas.js +10 -1
  4. package/dist/extendedSchemas.js.map +1 -1
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/utils/__tests__/paymentUtils.test.d.ts +6 -0
  9. package/dist/utils/__tests__/paymentUtils.test.d.ts.map +1 -0
  10. package/dist/utils/__tests__/paymentUtils.test.js +77 -0
  11. package/dist/utils/__tests__/paymentUtils.test.js.map +1 -0
  12. package/dist/utils/discountEngine/__tests__/bestPriceResolver.test.d.ts +2 -0
  13. package/dist/utils/discountEngine/__tests__/bestPriceResolver.test.d.ts.map +1 -0
  14. package/dist/utils/discountEngine/__tests__/bestPriceResolver.test.js +457 -0
  15. package/dist/utils/discountEngine/__tests__/bestPriceResolver.test.js.map +1 -0
  16. package/dist/utils/discountEngine/__tests__/eligibilityValidator.test.d.ts +2 -0
  17. package/dist/utils/discountEngine/__tests__/eligibilityValidator.test.d.ts.map +1 -0
  18. package/dist/utils/discountEngine/__tests__/eligibilityValidator.test.js +480 -0
  19. package/dist/utils/discountEngine/__tests__/eligibilityValidator.test.js.map +1 -0
  20. package/package.json +2 -2
  21. package/prisma/COMPREHENSIVE-MIGRATION-README.md +295 -0
  22. package/prisma/MIGRATION-FILES-GUIDE.md +76 -0
  23. package/prisma/comprehensive-migration-20260120.sql +5751 -0
  24. package/prisma/delta-migration-20260120.sql +302 -0
  25. package/prisma/schema.prisma +486 -147
  26. package/prisma/verify-migration.sql +132 -0
  27. package/src/extendedSchemas.ts +10 -1
  28. package/src/index.ts +4 -0
  29. package/src/utils/__tests__/paymentUtils.test.ts +95 -0
  30. package/src/utils/discountEngine/__tests__/bestPriceResolver.test.ts +558 -0
  31. package/src/utils/discountEngine/__tests__/eligibilityValidator.test.ts +655 -0
@@ -0,0 +1,302 @@
1
+ -- ============================================================================
2
+ -- DELTA MIGRATION - January 20, 2026
3
+ -- New columns and tables added since last commit
4
+ -- ============================================================================
5
+
6
+ -- 1. BashFeedPost: Make bashId optional, add serviceId
7
+ -- ============================================================================
8
+ ALTER TABLE "BashFeedPost" ALTER COLUMN "bashId" DROP NOT NULL;
9
+ ALTER TABLE "BashFeedPost" ADD COLUMN IF NOT EXISTS "serviceId" TEXT;
10
+ ALTER TABLE "BashFeedPost" ADD CONSTRAINT "BashFeedPost_serviceId_fkey"
11
+ FOREIGN KEY ("serviceId") REFERENCES "Service"("id") ON DELETE CASCADE ON UPDATE CASCADE;
12
+ CREATE INDEX IF NOT EXISTS "BashFeedPost_serviceId_idx" ON "BashFeedPost"("serviceId");
13
+
14
+ -- 2. BashEvent: Add isAutoApprovable and organization fields
15
+ -- ============================================================================
16
+ ALTER TABLE "BashEvent" ADD COLUMN IF NOT EXISTS "isAutoApprovable" BOOLEAN NOT NULL DEFAULT false;
17
+ ALTER TABLE "BashEvent" ADD COLUMN IF NOT EXISTS "organizationId" TEXT;
18
+ ALTER TABLE "BashEvent" ADD CONSTRAINT "BashEvent_organizationId_fkey"
19
+ FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE SET NULL ON UPDATE CASCADE;
20
+ CREATE INDEX IF NOT EXISTS "BashEvent_organizationId_idx" ON "BashEvent"("organizationId");
21
+
22
+ -- 3. Service: Add verification and certification fields
23
+ -- ============================================================================
24
+ ALTER TABLE "Service" ADD COLUMN IF NOT EXISTS "isVerified" BOOLEAN NOT NULL DEFAULT false;
25
+ ALTER TABLE "Service" ADD COLUMN IF NOT EXISTS "isLicensed" BOOLEAN NOT NULL DEFAULT false;
26
+ ALTER TABLE "Service" ADD COLUMN IF NOT EXISTS "verifiedAt" TIMESTAMP(3);
27
+ ALTER TABLE "Service" ADD COLUMN IF NOT EXISTS "licenseNumber" TEXT;
28
+ ALTER TABLE "Service" ADD COLUMN IF NOT EXISTS "certifications" TEXT[] DEFAULT ARRAY[]::TEXT[];
29
+ CREATE INDEX IF NOT EXISTS "Service_isVerified_idx" ON "Service"("isVerified");
30
+ CREATE INDEX IF NOT EXISTS "Service_isLicensed_idx" ON "Service"("isLicensed");
31
+
32
+ -- 4. User: Add organization relations (these are handled by Organization tables below)
33
+ -- Note: These are just relation fields in Prisma, no actual columns needed in User table
34
+
35
+ -- 5. Organization: Create complete organization system
36
+ -- ============================================================================
37
+
38
+ -- Create OrganizationType enum if it doesn't exist
39
+ DO $$ BEGIN
40
+ CREATE TYPE "OrganizationType" AS ENUM (
41
+ 'Nonprofit',
42
+ 'ForProfit',
43
+ 'Religious',
44
+ 'Educational',
45
+ 'Government',
46
+ 'Club',
47
+ 'Association',
48
+ 'Fraternity',
49
+ 'Sorority',
50
+ 'Union',
51
+ 'Cooperative',
52
+ 'Other'
53
+ );
54
+ EXCEPTION
55
+ WHEN duplicate_object THEN null;
56
+ END $$;
57
+
58
+ -- Create Organization table
59
+ CREATE TABLE IF NOT EXISTS "Organization" (
60
+ "id" TEXT NOT NULL PRIMARY KEY,
61
+ "name" TEXT NOT NULL,
62
+ "slug" TEXT NOT NULL UNIQUE,
63
+ "organizationType" "OrganizationType"[],
64
+ "bio" TEXT,
65
+ "mission" TEXT,
66
+ "pocName" TEXT,
67
+ "pocEmail" TEXT,
68
+ "pocPhone" TEXT,
69
+ "street" TEXT,
70
+ "city" TEXT,
71
+ "state" TEXT,
72
+ "zipCode" TEXT,
73
+ "country" TEXT DEFAULT 'US',
74
+ "coverPhoto" TEXT,
75
+ "logoUrl" TEXT,
76
+ "websiteUrl" TEXT,
77
+ "socialLinks" JSONB,
78
+ "goodsOrServices" TEXT[],
79
+ "venueTypes" TEXT[],
80
+ "secondaryVenueTypes" TEXT[] DEFAULT ARRAY[]::TEXT[],
81
+ "baseMembershipPriceCents" INTEGER,
82
+ "premiumMembershipPriceCents" INTEGER,
83
+ "allowGuestsByDefault" BOOLEAN DEFAULT false,
84
+ "defaultGuestLimit" INTEGER DEFAULT 0,
85
+ "visibility" TEXT DEFAULT 'Public',
86
+ "requiresApproval" BOOLEAN DEFAULT true,
87
+ "memberAutoApproval" BOOLEAN DEFAULT false,
88
+ "ownerId" TEXT NOT NULL,
89
+ "parentOrganizationId" TEXT,
90
+ "departmentName" TEXT,
91
+ "hierarchyLevel" INTEGER DEFAULT 0,
92
+ "subscriptionTier" TEXT,
93
+ "subscriptionStatus" TEXT,
94
+ "stripeSubscriptionId" TEXT UNIQUE,
95
+ "stripeCustomerId" TEXT,
96
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
97
+ "updatedAt" TIMESTAMP(3) NOT NULL,
98
+ CONSTRAINT "Organization_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE,
99
+ CONSTRAINT "Organization_parentOrganizationId_fkey" FOREIGN KEY ("parentOrganizationId") REFERENCES "Organization"("id") ON DELETE SET NULL ON UPDATE CASCADE
100
+ );
101
+
102
+ CREATE INDEX IF NOT EXISTS "Organization_ownerId_idx" ON "Organization"("ownerId");
103
+ CREATE INDEX IF NOT EXISTS "Organization_slug_idx" ON "Organization"("slug");
104
+ CREATE INDEX IF NOT EXISTS "Organization_parentOrganizationId_idx" ON "Organization"("parentOrganizationId");
105
+
106
+ -- Create OrganizationMember table
107
+ CREATE TABLE IF NOT EXISTS "OrganizationMember" (
108
+ "id" TEXT NOT NULL PRIMARY KEY,
109
+ "organizationId" TEXT NOT NULL,
110
+ "userId" TEXT NOT NULL,
111
+ "role" TEXT DEFAULT 'Member',
112
+ "membershipTier" TEXT DEFAULT 'Base',
113
+ "status" TEXT DEFAULT 'Pending',
114
+ "joinedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
115
+ "approvedAt" TIMESTAMP(3),
116
+ "approvedBy" TEXT,
117
+ "paidUntil" TIMESTAMP(3),
118
+ "paymentStatus" TEXT DEFAULT 'Current',
119
+ "guestLimit" INTEGER,
120
+ "canInvite" BOOLEAN DEFAULT true,
121
+ "canCreateEvents" BOOLEAN DEFAULT false,
122
+ "canManageBudgets" BOOLEAN DEFAULT false,
123
+ CONSTRAINT "OrganizationMember_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE,
124
+ CONSTRAINT "OrganizationMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE,
125
+ CONSTRAINT "OrganizationMember_organizationId_userId_key" UNIQUE ("organizationId", "userId")
126
+ );
127
+
128
+ CREATE INDEX IF NOT EXISTS "OrganizationMember_organizationId_idx" ON "OrganizationMember"("organizationId");
129
+ CREATE INDEX IF NOT EXISTS "OrganizationMember_userId_idx" ON "OrganizationMember"("userId");
130
+ CREATE INDEX IF NOT EXISTS "OrganizationMember_status_idx" ON "OrganizationMember"("status");
131
+
132
+ -- Create Department table
133
+ CREATE TABLE IF NOT EXISTS "Department" (
134
+ "id" TEXT NOT NULL PRIMARY KEY,
135
+ "organizationId" TEXT NOT NULL,
136
+ "name" TEXT NOT NULL,
137
+ "description" TEXT,
138
+ "parentDeptId" TEXT,
139
+ "headUserId" TEXT,
140
+ "budgetCents" INTEGER,
141
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
142
+ "updatedAt" TIMESTAMP(3) NOT NULL,
143
+ CONSTRAINT "Department_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE,
144
+ CONSTRAINT "Department_parentDeptId_fkey" FOREIGN KEY ("parentDeptId") REFERENCES "Department"("id") ON DELETE SET NULL ON UPDATE CASCADE,
145
+ CONSTRAINT "Department_headUserId_fkey" FOREIGN KEY ("headUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE
146
+ );
147
+
148
+ CREATE INDEX IF NOT EXISTS "Department_organizationId_idx" ON "Department"("organizationId");
149
+ CREATE INDEX IF NOT EXISTS "Department_parentDeptId_idx" ON "Department"("parentDeptId");
150
+
151
+ -- Create OrganizationEvent table
152
+ CREATE TABLE IF NOT EXISTS "OrganizationEvent" (
153
+ "id" TEXT NOT NULL PRIMARY KEY,
154
+ "organizationId" TEXT NOT NULL,
155
+ "title" TEXT NOT NULL,
156
+ "description" TEXT,
157
+ "eventType" TEXT,
158
+ "startTime" TIMESTAMP(3) NOT NULL,
159
+ "endTime" TIMESTAMP(3),
160
+ "location" TEXT,
161
+ "maxAttendees" INTEGER,
162
+ "requiresRSVP" BOOLEAN DEFAULT true,
163
+ "allowGuests" BOOLEAN DEFAULT false,
164
+ "guestLimit" INTEGER DEFAULT 0,
165
+ "creatorId" TEXT NOT NULL,
166
+ "departmentId" TEXT,
167
+ "isPublic" BOOLEAN DEFAULT false,
168
+ "budgetCents" INTEGER,
169
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
170
+ "updatedAt" TIMESTAMP(3) NOT NULL,
171
+ CONSTRAINT "OrganizationEvent_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE,
172
+ CONSTRAINT "OrganizationEvent_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE,
173
+ CONSTRAINT "OrganizationEvent_departmentId_fkey" FOREIGN KEY ("departmentId") REFERENCES "Department"("id") ON DELETE SET NULL ON UPDATE CASCADE
174
+ );
175
+
176
+ CREATE INDEX IF NOT EXISTS "OrganizationEvent_organizationId_idx" ON "OrganizationEvent"("organizationId");
177
+ CREATE INDEX IF NOT EXISTS "OrganizationEvent_creatorId_idx" ON "OrganizationEvent"("creatorId");
178
+ CREATE INDEX IF NOT EXISTS "OrganizationEvent_startTime_idx" ON "OrganizationEvent"("startTime");
179
+
180
+ -- Create EventRSVP table
181
+ CREATE TABLE IF NOT EXISTS "EventRSVP" (
182
+ "id" TEXT NOT NULL PRIMARY KEY,
183
+ "eventId" TEXT NOT NULL,
184
+ "memberId" TEXT NOT NULL,
185
+ "userId" TEXT NOT NULL,
186
+ "status" TEXT DEFAULT 'Pending',
187
+ "guestCount" INTEGER DEFAULT 0,
188
+ "respondedAt" TIMESTAMP(3),
189
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
190
+ CONSTRAINT "EventRSVP_eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "OrganizationEvent"("id") ON DELETE CASCADE ON UPDATE CASCADE,
191
+ CONSTRAINT "EventRSVP_memberId_fkey" FOREIGN KEY ("memberId") REFERENCES "OrganizationMember"("id") ON DELETE CASCADE ON UPDATE CASCADE,
192
+ CONSTRAINT "EventRSVP_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE,
193
+ CONSTRAINT "EventRSVP_eventId_memberId_key" UNIQUE ("eventId", "memberId")
194
+ );
195
+
196
+ CREATE INDEX IF NOT EXISTS "EventRSVP_eventId_idx" ON "EventRSVP"("eventId");
197
+ CREATE INDEX IF NOT EXISTS "EventRSVP_memberId_idx" ON "EventRSVP"("memberId");
198
+
199
+ -- Create OrganizationBudget table
200
+ CREATE TABLE IF NOT EXISTS "OrganizationBudget" (
201
+ "id" TEXT NOT NULL PRIMARY KEY,
202
+ "organizationId" TEXT NOT NULL,
203
+ "name" TEXT NOT NULL,
204
+ "description" TEXT,
205
+ "totalCents" INTEGER NOT NULL,
206
+ "spentCents" INTEGER DEFAULT 0,
207
+ "fiscalYear" INTEGER,
208
+ "startDate" TIMESTAMP(3) NOT NULL,
209
+ "endDate" TIMESTAMP(3) NOT NULL,
210
+ "approvedBy" TEXT,
211
+ "approvedAt" TIMESTAMP(3),
212
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
213
+ "updatedAt" TIMESTAMP(3) NOT NULL,
214
+ CONSTRAINT "OrganizationBudget_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE,
215
+ CONSTRAINT "OrganizationBudget_approvedBy_fkey" FOREIGN KEY ("approvedBy") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE
216
+ );
217
+
218
+ CREATE INDEX IF NOT EXISTS "OrganizationBudget_organizationId_idx" ON "OrganizationBudget"("organizationId");
219
+
220
+ -- Create BudgetExpense table
221
+ CREATE TABLE IF NOT EXISTS "BudgetExpense" (
222
+ "id" TEXT NOT NULL PRIMARY KEY,
223
+ "budgetId" TEXT NOT NULL,
224
+ "eventId" TEXT,
225
+ "description" TEXT NOT NULL,
226
+ "amountCents" INTEGER NOT NULL,
227
+ "category" TEXT,
228
+ "submittedBy" TEXT NOT NULL,
229
+ "approvedBy" TEXT,
230
+ "approvedAt" TIMESTAMP(3),
231
+ "rejectedAt" TIMESTAMP(3),
232
+ "receiptUrl" TEXT,
233
+ "status" TEXT DEFAULT 'Pending',
234
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
235
+ CONSTRAINT "BudgetExpense_budgetId_fkey" FOREIGN KEY ("budgetId") REFERENCES "OrganizationBudget"("id") ON DELETE CASCADE ON UPDATE CASCADE,
236
+ "eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "OrganizationEvent"("id") ON DELETE SET NULL ON UPDATE CASCADE,
237
+ CONSTRAINT "BudgetExpense_submittedBy_fkey" FOREIGN KEY ("submittedBy") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE,
238
+ CONSTRAINT "BudgetExpense_approvedBy_fkey" FOREIGN KEY ("approvedBy") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE
239
+ );
240
+
241
+ CREATE INDEX IF NOT EXISTS "BudgetExpense_budgetId_idx" ON "BudgetExpense"("budgetId");
242
+ CREATE INDEX IF NOT EXISTS "BudgetExpense_eventId_idx" ON "BudgetExpense"("eventId");
243
+
244
+ -- Create EventProposal table
245
+ CREATE TABLE IF NOT EXISTS "EventProposal" (
246
+ "id" TEXT NOT NULL PRIMARY KEY,
247
+ "organizationId" TEXT NOT NULL,
248
+ "title" TEXT NOT NULL,
249
+ "description" TEXT NOT NULL,
250
+ "proposedDate" TIMESTAMP(3),
251
+ "estimatedBudgetCents" INTEGER,
252
+ "creatorId" TEXT NOT NULL,
253
+ "status" TEXT DEFAULT 'Pending',
254
+ "votingDeadline" TIMESTAMP(3),
255
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
256
+ "updatedAt" TIMESTAMP(3) NOT NULL,
257
+ CONSTRAINT "EventProposal_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE,
258
+ CONSTRAINT "EventProposal_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE
259
+ );
260
+
261
+ CREATE INDEX IF NOT EXISTS "EventProposal_organizationId_idx" ON "EventProposal"("organizationId");
262
+ CREATE INDEX IF NOT EXISTS "EventProposal_status_idx" ON "EventProposal"("status");
263
+
264
+ -- Create ProposalVote table
265
+ CREATE TABLE IF NOT EXISTS "ProposalVote" (
266
+ "id" TEXT NOT NULL PRIMARY KEY,
267
+ "proposalId" TEXT NOT NULL,
268
+ "voterId" TEXT NOT NULL,
269
+ "vote" TEXT NOT NULL,
270
+ "comment" TEXT,
271
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
272
+ CONSTRAINT "ProposalVote_proposalId_fkey" FOREIGN KEY ("proposalId") REFERENCES "EventProposal"("id") ON DELETE CASCADE ON UPDATE CASCADE,
273
+ CONSTRAINT "ProposalVote_voterId_fkey" FOREIGN KEY ("voterId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE,
274
+ CONSTRAINT "ProposalVote_proposalId_voterId_key" UNIQUE ("proposalId", "voterId")
275
+ );
276
+
277
+ CREATE INDEX IF NOT EXISTS "ProposalVote_proposalId_idx" ON "ProposalVote"("proposalId");
278
+
279
+ -- Create _FavoriteOrganizations join table
280
+ CREATE TABLE IF NOT EXISTS "_FavoriteOrganizations" (
281
+ "A" TEXT NOT NULL,
282
+ "B" TEXT NOT NULL,
283
+ CONSTRAINT "_FavoriteOrganizations_A_fkey" FOREIGN KEY ("A") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE,
284
+ CONSTRAINT "_FavoriteOrganizations_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE
285
+ );
286
+
287
+ CREATE UNIQUE INDEX IF NOT EXISTS "_FavoriteOrganizations_AB_unique" ON "_FavoriteOrganizations"("A", "B");
288
+ CREATE INDEX IF NOT EXISTS "_FavoriteOrganizations_B_index" ON "_FavoriteOrganizations"("B");
289
+
290
+ -- ============================================================================
291
+ -- VERIFICATION QUERIES
292
+ -- ============================================================================
293
+
294
+ -- Run these after migration to verify:
295
+ -- SELECT column_name FROM information_schema.columns WHERE table_name = 'BashFeedPost' AND column_name = 'serviceId';
296
+ -- SELECT column_name FROM information_schema.columns WHERE table_name = 'BashEvent' AND column_name = 'isAutoApprovable';
297
+ -- SELECT column_name FROM information_schema.columns WHERE table_name = 'Service' AND column_name = 'isVerified';
298
+ -- SELECT table_name FROM information_schema.tables WHERE table_name = 'Organization';
299
+
300
+ -- ============================================================================
301
+ -- END OF DELTA MIGRATION
302
+ -- ============================================================================