@open-mercato/core 0.4.2-canary-07dbc98202 → 0.4.2-canary-11e19dfb84
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/dist/generated/entities.ids.generated.js +59 -63
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +0 -2
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/api_docs/frontend/docs/api/page.js +2 -3
- package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
- package/dist/modules/auth/api/admin/nav.js +3 -4
- package/dist/modules/auth/api/admin/nav.js.map +2 -2
- package/dist/modules/auth/api/reset/confirm.js +2 -25
- package/dist/modules/auth/api/reset/confirm.js.map +2 -2
- package/dist/modules/auth/api/reset.js +0 -23
- package/dist/modules/auth/api/reset.js.map +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js +9 -14
- package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
- package/dist/modules/auth/commands/users.js +0 -55
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/lib/setup-app.js +0 -1
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/auth/services/authService.js +3 -3
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/configs/cli.js +0 -6
- package/dist/modules/configs/cli.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +0 -31
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +0 -53
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/payments.js +0 -26
- package/dist/modules/sales/commands/payments.js.map +2 -2
- package/dist/modules/staff/commands/leave-requests.js +0 -79
- package/dist/modules/staff/commands/leave-requests.js.map +2 -2
- package/generated/entities.ids.generated.ts +59 -63
- package/generated/entity-fields-registry.ts +0 -2
- package/package.json +2 -2
- package/src/modules/api_docs/frontend/docs/api/page.tsx +2 -3
- package/src/modules/auth/api/admin/nav.ts +6 -10
- package/src/modules/auth/api/reset/confirm.ts +2 -25
- package/src/modules/auth/api/reset.ts +0 -23
- package/src/modules/auth/api/sidebar/preferences/route.ts +12 -21
- package/src/modules/auth/commands/users.ts +0 -68
- package/src/modules/auth/i18n/de.json +1 -29
- package/src/modules/auth/i18n/en.json +1 -29
- package/src/modules/auth/i18n/es.json +1 -29
- package/src/modules/auth/i18n/pl.json +1 -29
- package/src/modules/auth/lib/setup-app.ts +0 -1
- package/src/modules/auth/services/authService.ts +4 -4
- package/src/modules/business_rules/i18n/en.json +1 -3
- package/src/modules/catalog/i18n/en.json +1 -3
- package/src/modules/configs/cli.ts +0 -6
- package/src/modules/customers/commands/deals.ts +0 -39
- package/src/modules/customers/i18n/en.json +1 -5
- package/src/modules/sales/commands/documents.ts +0 -65
- package/src/modules/sales/commands/payments.ts +0 -33
- package/src/modules/sales/i18n/de.json +0 -20
- package/src/modules/sales/i18n/en.json +1 -25
- package/src/modules/sales/i18n/es.json +0 -20
- package/src/modules/sales/i18n/pl.json +0 -20
- package/src/modules/staff/commands/leave-requests.ts +0 -94
- package/src/modules/staff/i18n/de.json +0 -4
- package/src/modules/staff/i18n/en.json +1 -9
- package/src/modules/staff/i18n/es.json +0 -4
- package/src/modules/staff/i18n/pl.json +0 -4
- package/src/modules/workflows/i18n/en.json +1 -3
- package/dist/generated/entities/notification/index.js +0 -57
- package/dist/generated/entities/notification/index.js.map +0 -7
- package/dist/modules/auth/api/profile/route.js +0 -155
- package/dist/modules/auth/api/profile/route.js.map +0 -7
- package/dist/modules/auth/backend/auth/profile/page.js +0 -99
- package/dist/modules/auth/backend/auth/profile/page.js.map +0 -7
- package/dist/modules/auth/backend/auth/profile/page.meta.js +0 -12
- package/dist/modules/auth/backend/auth/profile/page.meta.js.map +0 -7
- package/dist/modules/auth/notifications.js +0 -112
- package/dist/modules/auth/notifications.js.map +0 -7
- package/dist/modules/business_rules/notifications.js +0 -28
- package/dist/modules/business_rules/notifications.js.map +0 -7
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +0 -37
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +0 -7
- package/dist/modules/catalog/notifications.js +0 -28
- package/dist/modules/catalog/notifications.js.map +0 -7
- package/dist/modules/catalog/subscribers/low-stock-notification.js +0 -38
- package/dist/modules/catalog/subscribers/low-stock-notification.js.map +0 -7
- package/dist/modules/customers/notifications.js +0 -48
- package/dist/modules/customers/notifications.js.map +0 -7
- package/dist/modules/notifications/acl.js +0 -11
- package/dist/modules/notifications/acl.js.map +0 -7
- package/dist/modules/notifications/api/[id]/action/route.js +0 -74
- package/dist/modules/notifications/api/[id]/action/route.js.map +0 -7
- package/dist/modules/notifications/api/[id]/dismiss/route.js +0 -15
- package/dist/modules/notifications/api/[id]/dismiss/route.js.map +0 -7
- package/dist/modules/notifications/api/[id]/read/route.js +0 -15
- package/dist/modules/notifications/api/[id]/read/route.js.map +0 -7
- package/dist/modules/notifications/api/[id]/restore/route.js +0 -53
- package/dist/modules/notifications/api/[id]/restore/route.js.map +0 -7
- package/dist/modules/notifications/api/batch/route.js +0 -17
- package/dist/modules/notifications/api/batch/route.js.map +0 -7
- package/dist/modules/notifications/api/feature/route.js +0 -17
- package/dist/modules/notifications/api/feature/route.js.map +0 -7
- package/dist/modules/notifications/api/mark-all-read/route.js +0 -35
- package/dist/modules/notifications/api/mark-all-read/route.js.map +0 -7
- package/dist/modules/notifications/api/openapi.js +0 -76
- package/dist/modules/notifications/api/openapi.js.map +0 -7
- package/dist/modules/notifications/api/role/route.js +0 -17
- package/dist/modules/notifications/api/role/route.js.map +0 -7
- package/dist/modules/notifications/api/route.js +0 -85
- package/dist/modules/notifications/api/route.js.map +0 -7
- package/dist/modules/notifications/api/settings/route.js +0 -155
- package/dist/modules/notifications/api/settings/route.js.map +0 -7
- package/dist/modules/notifications/api/unread-count/route.js +0 -38
- package/dist/modules/notifications/api/unread-count/route.js.map +0 -7
- package/dist/modules/notifications/backend/config/notifications/page.js +0 -10
- package/dist/modules/notifications/backend/config/notifications/page.js.map +0 -7
- package/dist/modules/notifications/backend/config/notifications/page.meta.js +0 -24
- package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +0 -7
- package/dist/modules/notifications/cli.js +0 -16
- package/dist/modules/notifications/cli.js.map +0 -7
- package/dist/modules/notifications/data/entities.js +0 -112
- package/dist/modules/notifications/data/entities.js.map +0 -7
- package/dist/modules/notifications/data/validators.js +0 -94
- package/dist/modules/notifications/data/validators.js.map +0 -7
- package/dist/modules/notifications/di.js +0 -13
- package/dist/modules/notifications/di.js.map +0 -7
- package/dist/modules/notifications/emails/NotificationEmail.js +0 -58
- package/dist/modules/notifications/emails/NotificationEmail.js.map +0 -7
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +0 -44
- package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +0 -7
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +0 -219
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +0 -7
- package/dist/modules/notifications/index.js +0 -14
- package/dist/modules/notifications/index.js.map +0 -7
- package/dist/modules/notifications/lib/deliveryConfig.js +0 -105
- package/dist/modules/notifications/lib/deliveryConfig.js.map +0 -7
- package/dist/modules/notifications/lib/events.js +0 -12
- package/dist/modules/notifications/lib/events.js.map +0 -7
- package/dist/modules/notifications/lib/notificationBuilder.js +0 -66
- package/dist/modules/notifications/lib/notificationBuilder.js.map +0 -7
- package/dist/modules/notifications/lib/notificationFactory.js +0 -54
- package/dist/modules/notifications/lib/notificationFactory.js.map +0 -7
- package/dist/modules/notifications/lib/notificationMapper.js +0 -34
- package/dist/modules/notifications/lib/notificationMapper.js.map +0 -7
- package/dist/modules/notifications/lib/notificationRecipients.js +0 -35
- package/dist/modules/notifications/lib/notificationRecipients.js.map +0 -7
- package/dist/modules/notifications/lib/notificationService.js +0 -279
- package/dist/modules/notifications/lib/notificationService.js.map +0 -7
- package/dist/modules/notifications/lib/routeHelpers.js +0 -101
- package/dist/modules/notifications/lib/routeHelpers.js.map +0 -7
- package/dist/modules/notifications/lib/safeHref.js +0 -24
- package/dist/modules/notifications/lib/safeHref.js.map +0 -7
- package/dist/modules/notifications/migrations/Migration20260123000001.js +0 -70
- package/dist/modules/notifications/migrations/Migration20260123000001.js.map +0 -7
- package/dist/modules/notifications/migrations/Migration20260126150000.js +0 -37
- package/dist/modules/notifications/migrations/Migration20260126150000.js.map +0 -7
- package/dist/modules/notifications/subscribers/deliver-notification.js +0 -139
- package/dist/modules/notifications/subscribers/deliver-notification.js.map +0 -7
- package/dist/modules/notifications/workers/create-notification.worker.js +0 -70
- package/dist/modules/notifications/workers/create-notification.worker.js.map +0 -7
- package/dist/modules/sales/notifications.client.js +0 -51
- package/dist/modules/sales/notifications.client.js.map +0 -7
- package/dist/modules/sales/notifications.js +0 -88
- package/dist/modules/sales/notifications.js.map +0 -7
- package/dist/modules/sales/subscribers/quote-expiring-notification.js +0 -38
- package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +0 -137
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +0 -137
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/index.js +0 -7
- package/dist/modules/sales/widgets/notifications/index.js.map +0 -7
- package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +0 -60
- package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +0 -7
- package/dist/modules/staff/notifications.js +0 -75
- package/dist/modules/staff/notifications.js.map +0 -7
- package/dist/modules/workflows/notifications.js +0 -28
- package/dist/modules/workflows/notifications.js.map +0 -7
- package/dist/modules/workflows/subscribers/task-assigned-notification.js +0 -38
- package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +0 -7
- package/generated/entities/notification/index.ts +0 -27
- package/src/modules/auth/api/profile/route.ts +0 -160
- package/src/modules/auth/backend/auth/profile/page.meta.ts +0 -8
- package/src/modules/auth/backend/auth/profile/page.tsx +0 -127
- package/src/modules/auth/notifications.ts +0 -109
- package/src/modules/business_rules/notifications.ts +0 -25
- package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +0 -50
- package/src/modules/catalog/notifications.ts +0 -25
- package/src/modules/catalog/subscribers/low-stock-notification.ts +0 -52
- package/src/modules/customers/notifications.ts +0 -44
- package/src/modules/notifications/acl.ts +0 -7
- package/src/modules/notifications/api/[id]/action/route.ts +0 -75
- package/src/modules/notifications/api/[id]/dismiss/route.ts +0 -12
- package/src/modules/notifications/api/[id]/read/route.ts +0 -12
- package/src/modules/notifications/api/[id]/restore/route.ts +0 -53
- package/src/modules/notifications/api/batch/route.ts +0 -14
- package/src/modules/notifications/api/feature/route.ts +0 -14
- package/src/modules/notifications/api/mark-all-read/route.ts +0 -34
- package/src/modules/notifications/api/openapi.ts +0 -76
- package/src/modules/notifications/api/role/route.ts +0 -14
- package/src/modules/notifications/api/route.ts +0 -92
- package/src/modules/notifications/api/settings/route.ts +0 -157
- package/src/modules/notifications/api/unread-count/route.ts +0 -38
- package/src/modules/notifications/backend/config/notifications/page.meta.ts +0 -22
- package/src/modules/notifications/backend/config/notifications/page.tsx +0 -12
- package/src/modules/notifications/cli.ts +0 -18
- package/src/modules/notifications/data/entities.ts +0 -99
- package/src/modules/notifications/data/validators.ts +0 -110
- package/src/modules/notifications/di.ts +0 -11
- package/src/modules/notifications/emails/NotificationEmail.tsx +0 -98
- package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +0 -42
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +0 -231
- package/src/modules/notifications/i18n/de.json +0 -50
- package/src/modules/notifications/i18n/en.json +0 -50
- package/src/modules/notifications/i18n/es.json +0 -50
- package/src/modules/notifications/i18n/pl.json +0 -50
- package/src/modules/notifications/index.ts +0 -12
- package/src/modules/notifications/lib/deliveryConfig.ts +0 -145
- package/src/modules/notifications/lib/events.ts +0 -48
- package/src/modules/notifications/lib/notificationBuilder.ts +0 -121
- package/src/modules/notifications/lib/notificationFactory.ts +0 -76
- package/src/modules/notifications/lib/notificationMapper.ts +0 -33
- package/src/modules/notifications/lib/notificationRecipients.ts +0 -83
- package/src/modules/notifications/lib/notificationService.ts +0 -414
- package/src/modules/notifications/lib/routeHelpers.ts +0 -151
- package/src/modules/notifications/lib/safeHref.ts +0 -29
- package/src/modules/notifications/migrations/.snapshot-open-mercato.json +0 -300
- package/src/modules/notifications/migrations/Migration20260123000001.ts +0 -73
- package/src/modules/notifications/migrations/Migration20260126150000.ts +0 -39
- package/src/modules/notifications/subscribers/deliver-notification.ts +0 -175
- package/src/modules/notifications/workers/create-notification.worker.ts +0 -122
- package/src/modules/sales/notifications.client.ts +0 -65
- package/src/modules/sales/notifications.ts +0 -82
- package/src/modules/sales/subscribers/quote-expiring-notification.ts +0 -53
- package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +0 -156
- package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +0 -156
- package/src/modules/sales/widgets/notifications/index.ts +0 -2
- package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +0 -81
- package/src/modules/staff/notifications.ts +0 -71
- package/src/modules/workflows/notifications.ts +0 -25
- package/src/modules/workflows/subscribers/task-assigned-notification.ts +0 -53
|
@@ -64,16 +64,12 @@ export async function GET(req: Request) {
|
|
|
64
64
|
{ tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },
|
|
65
65
|
) ?? false
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
organizationId: auth.orgId ?? null,
|
|
74
|
-
locale,
|
|
75
|
-
})
|
|
76
|
-
: null
|
|
67
|
+
const settings = await loadSidebarPreference(em, {
|
|
68
|
+
userId: auth.sub,
|
|
69
|
+
tenantId: auth.tenantId ?? null,
|
|
70
|
+
organizationId: auth.orgId ?? null,
|
|
71
|
+
locale,
|
|
72
|
+
})
|
|
77
73
|
|
|
78
74
|
let rolesPayload: Array<{ id: string; name: string; hasPreference: boolean }> = []
|
|
79
75
|
if (canApplyToRoles) {
|
|
@@ -96,11 +92,11 @@ export async function GET(req: Request) {
|
|
|
96
92
|
return NextResponse.json({
|
|
97
93
|
locale,
|
|
98
94
|
settings: {
|
|
99
|
-
version: settings
|
|
100
|
-
groupOrder: settings
|
|
101
|
-
groupLabels: settings
|
|
102
|
-
itemLabels: settings
|
|
103
|
-
hiddenItems: settings
|
|
95
|
+
version: settings.version ?? SIDEBAR_PREFERENCES_VERSION,
|
|
96
|
+
groupOrder: settings.groupOrder ?? [],
|
|
97
|
+
groupLabels: settings.groupLabels ?? {},
|
|
98
|
+
itemLabels: settings.itemLabels ?? {},
|
|
99
|
+
hiddenItems: settings.hiddenItems ?? [],
|
|
104
100
|
},
|
|
105
101
|
canApplyToRoles,
|
|
106
102
|
roles: rolesPayload,
|
|
@@ -110,11 +106,6 @@ export async function GET(req: Request) {
|
|
|
110
106
|
export async function PUT(req: Request) {
|
|
111
107
|
const auth = await getAuthFromRequest(req)
|
|
112
108
|
if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
113
|
-
// For API key auth, use userId (the actual user) if available
|
|
114
|
-
const effectiveUserId = auth.isApiKey ? auth.userId : auth.sub
|
|
115
|
-
if (!effectiveUserId) {
|
|
116
|
-
return NextResponse.json({ error: 'Cannot save preferences: no user associated with this API key' }, { status: 403 })
|
|
117
|
-
}
|
|
118
109
|
|
|
119
110
|
let parsedBody: unknown
|
|
120
111
|
try {
|
|
@@ -191,7 +182,7 @@ export async function PUT(req: Request) {
|
|
|
191
182
|
}
|
|
192
183
|
|
|
193
184
|
const settings = await saveSidebarPreference(em, {
|
|
194
|
-
userId:
|
|
185
|
+
userId: auth.sub,
|
|
195
186
|
tenantId: auth.tenantId ?? null,
|
|
196
187
|
organizationId: auth.orgId ?? null,
|
|
197
188
|
locale,
|
|
@@ -27,9 +27,6 @@ import {
|
|
|
27
27
|
import { normalizeTenantId } from '@open-mercato/core/modules/auth/lib/tenantAccess'
|
|
28
28
|
import { computeEmailHash } from '@open-mercato/core/modules/auth/lib/emailHash'
|
|
29
29
|
import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
30
|
-
import { buildNotificationFromType } from '@open-mercato/core/modules/notifications/lib/notificationBuilder'
|
|
31
|
-
import { resolveNotificationService } from '@open-mercato/core/modules/notifications/lib/notificationService'
|
|
32
|
-
import notificationTypes from '@open-mercato/core/modules/auth/notifications'
|
|
33
30
|
|
|
34
31
|
type SerializedUser = {
|
|
35
32
|
email: string
|
|
@@ -108,46 +105,6 @@ export const userCrudIndexer: CrudIndexerConfig = {
|
|
|
108
105
|
}),
|
|
109
106
|
}
|
|
110
107
|
|
|
111
|
-
async function notifyRoleChanges(
|
|
112
|
-
ctx: CommandRuntimeContext,
|
|
113
|
-
user: User,
|
|
114
|
-
assignedRoles: string[],
|
|
115
|
-
revokedRoles: string[],
|
|
116
|
-
): Promise<void> {
|
|
117
|
-
const tenantId = user.tenantId ? String(user.tenantId) : null
|
|
118
|
-
if (!tenantId) return
|
|
119
|
-
const organizationId = user.organizationId ? String(user.organizationId) : null
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
const notificationService = resolveNotificationService(ctx.container)
|
|
123
|
-
if (assignedRoles.length) {
|
|
124
|
-
const assignedType = notificationTypes.find((type) => type.type === 'auth.role.assigned')
|
|
125
|
-
if (assignedType) {
|
|
126
|
-
const notificationInput = buildNotificationFromType(assignedType, {
|
|
127
|
-
recipientUserId: String(user.id),
|
|
128
|
-
sourceEntityType: 'auth:user',
|
|
129
|
-
sourceEntityId: String(user.id),
|
|
130
|
-
})
|
|
131
|
-
await notificationService.create(notificationInput, { tenantId, organizationId })
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (revokedRoles.length) {
|
|
136
|
-
const revokedType = notificationTypes.find((type) => type.type === 'auth.role.revoked')
|
|
137
|
-
if (revokedType) {
|
|
138
|
-
const notificationInput = buildNotificationFromType(revokedType, {
|
|
139
|
-
recipientUserId: String(user.id),
|
|
140
|
-
sourceEntityType: 'auth:user',
|
|
141
|
-
sourceEntityId: String(user.id),
|
|
142
|
-
})
|
|
143
|
-
await notificationService.create(notificationInput, { tenantId, organizationId })
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
} catch (err) {
|
|
147
|
-
console.error('[auth.users.roles] Failed to create notification:', err)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
108
|
const createUserCommand: CommandHandler<Record<string, unknown>, User> = {
|
|
152
109
|
id: 'auth.users.create',
|
|
153
110
|
async execute(rawInput, ctx) {
|
|
@@ -190,10 +147,8 @@ const createUserCommand: CommandHandler<Record<string, unknown>, User> = {
|
|
|
190
147
|
throw error
|
|
191
148
|
}
|
|
192
149
|
|
|
193
|
-
let assignedRoles: string[] = []
|
|
194
150
|
if (Array.isArray(parsed.roles) && parsed.roles.length) {
|
|
195
151
|
await syncUserRoles(em, user, parsed.roles, tenantId)
|
|
196
|
-
assignedRoles = await loadUserRoleNames(em, String(user.id))
|
|
197
152
|
}
|
|
198
153
|
|
|
199
154
|
await setCustomFieldsIfAny({
|
|
@@ -218,10 +173,6 @@ const createUserCommand: CommandHandler<Record<string, unknown>, User> = {
|
|
|
218
173
|
indexer: userCrudIndexer,
|
|
219
174
|
})
|
|
220
175
|
|
|
221
|
-
if (assignedRoles.length) {
|
|
222
|
-
await notifyRoleChanges(ctx, user, assignedRoles, [])
|
|
223
|
-
}
|
|
224
|
-
|
|
225
176
|
return user
|
|
226
177
|
},
|
|
227
178
|
captureAfter: async (_input, result, ctx) => {
|
|
@@ -337,9 +288,6 @@ const updateUserCommand: CommandHandler<Record<string, unknown>, User> = {
|
|
|
337
288
|
async execute(rawInput, ctx) {
|
|
338
289
|
const { parsed, custom } = parseWithCustomFields(updateSchema, rawInput)
|
|
339
290
|
const em = (ctx.container.resolve('em') as EntityManager)
|
|
340
|
-
const rolesBefore = Array.isArray(parsed.roles)
|
|
341
|
-
? await loadUserRoleNames(em, parsed.id)
|
|
342
|
-
: null
|
|
343
291
|
|
|
344
292
|
if (parsed.email !== undefined) {
|
|
345
293
|
const emailHash = computeEmailHash(parsed.email)
|
|
@@ -429,14 +377,6 @@ const updateUserCommand: CommandHandler<Record<string, unknown>, User> = {
|
|
|
429
377
|
indexer: userCrudIndexer,
|
|
430
378
|
})
|
|
431
379
|
|
|
432
|
-
if (Array.isArray(parsed.roles) && rolesBefore) {
|
|
433
|
-
const rolesAfter = await loadUserRoleNames(em, String(user.id))
|
|
434
|
-
const { assigned, revoked } = diffRoleChanges(rolesBefore, rolesAfter)
|
|
435
|
-
if (assigned.length || revoked.length) {
|
|
436
|
-
await notifyRoleChanges(ctx, user, assigned, revoked)
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
380
|
await invalidateUserCache(ctx, parsed.id)
|
|
441
381
|
|
|
442
382
|
return user
|
|
@@ -832,14 +772,6 @@ async function invalidateUserCache(ctx: CommandRuntimeContext, userId: string) {
|
|
|
832
772
|
}
|
|
833
773
|
}
|
|
834
774
|
|
|
835
|
-
function diffRoleChanges(before: string[], after: string[]) {
|
|
836
|
-
const beforeSet = new Set(before)
|
|
837
|
-
const afterSet = new Set(after)
|
|
838
|
-
const assigned = after.filter((role) => !beforeSet.has(role))
|
|
839
|
-
const revoked = before.filter((role) => !afterSet.has(role))
|
|
840
|
-
return { assigned, revoked }
|
|
841
|
-
}
|
|
842
|
-
|
|
843
775
|
function arrayEquals(left: string[] | undefined, right: string[]): boolean {
|
|
844
776
|
if (!left) return false
|
|
845
777
|
if (left.length !== right.length) return false
|
|
@@ -80,19 +80,6 @@
|
|
|
80
80
|
"auth.users.form.errors.load": "Benutzerdaten konnten nicht geladen werden",
|
|
81
81
|
"auth.users.form.errors.aclUpdate": "Aktualisierung der Benutzerberechtigungen fehlgeschlagen",
|
|
82
82
|
"auth.users.form.errors.delete": "Benutzer konnte nicht gelöscht werden",
|
|
83
|
-
"auth.profile.title": "Profil",
|
|
84
|
-
"auth.profile.form.email": "E-Mail",
|
|
85
|
-
"auth.profile.form.password": "Neues Passwort",
|
|
86
|
-
"auth.profile.form.confirmPassword": "Neues Passwort bestätigen",
|
|
87
|
-
"auth.profile.form.save": "Änderungen speichern",
|
|
88
|
-
"auth.profile.form.loading": "Profil wird geladen...",
|
|
89
|
-
"auth.profile.form.errors.load": "Profil konnte nicht geladen werden.",
|
|
90
|
-
"auth.profile.form.errors.save": "Profil konnte nicht aktualisiert werden.",
|
|
91
|
-
"auth.profile.form.errors.invalid": "Ungültige Profilaktualisierung.",
|
|
92
|
-
"auth.profile.form.errors.passwordMismatch": "Die Passwörter stimmen nicht überein.",
|
|
93
|
-
"auth.profile.form.errors.noChanges": "Keine Änderungen zu speichern.",
|
|
94
|
-
"auth.profile.form.errors.emailRequired": "E-Mail ist erforderlich.",
|
|
95
|
-
"auth.profile.form.success": "Profil aktualisiert.",
|
|
96
83
|
"auth.users.list.error.load": "Benutzer konnten nicht geladen werden",
|
|
97
84
|
"auth.users.list.error.delete": "Benutzer konnte nicht gelöscht werden",
|
|
98
85
|
"auth.users.flash.created": "Benutzer erstellt",
|
|
@@ -108,20 +95,5 @@
|
|
|
108
95
|
"auth.email.resetPassword.title": "Passwort zurücksetzen",
|
|
109
96
|
"auth.email.resetPassword.body": "Klicken Sie auf den Link unten, um ein neues Passwort festzulegen. Dieser Link läuft in 60 Minuten ab.",
|
|
110
97
|
"auth.email.resetPassword.cta": "Neues Passwort festlegen",
|
|
111
|
-
"auth.email.resetPassword.hint": "Wenn Sie dies nicht angefordert haben, können Sie diese E-Mail ignorieren."
|
|
112
|
-
"auth.notifications.passwordReset.requested.title": "Passwort-Zurücksetzung angefordert",
|
|
113
|
-
"auth.notifications.passwordReset.requested.body": "Ein Link zum Zurücksetzen des Passworts wurde an Ihre E-Mail gesendet",
|
|
114
|
-
"auth.notifications.passwordReset.completed.title": "Passwort erfolgreich geändert",
|
|
115
|
-
"auth.notifications.passwordReset.completed.body": "Ihr Passwort wurde erfolgreich aktualisiert",
|
|
116
|
-
"auth.notifications.account.locked.title": "Konto gesperrt",
|
|
117
|
-
"auth.notifications.account.locked.body": "Ihr Konto wurde aus Sicherheitsgründen gesperrt. Bitte wenden Sie sich an den Support.",
|
|
118
|
-
"auth.notifications.login.newDevice.title": "Neues Gerät erkannt",
|
|
119
|
-
"auth.notifications.login.newDevice.body": "Es wurde eine Anmeldung von einem unbekannten Gerät für Ihr Konto erkannt",
|
|
120
|
-
"auth.notifications.role.assigned.title": "Neue Rolle zugewiesen",
|
|
121
|
-
"auth.notifications.role.assigned.body": "Ihnen wurde eine neue Rolle mit zusätzlichen Berechtigungen zugewiesen",
|
|
122
|
-
"auth.notifications.role.revoked.title": "Rolle entfernt",
|
|
123
|
-
"auth.notifications.role.revoked.body": "Eine Rolle wurde von Ihrem Konto entfernt",
|
|
124
|
-
"auth.actions.contactSupport": "Support kontaktieren",
|
|
125
|
-
"auth.actions.viewSessions": "Sitzungen anzeigen",
|
|
126
|
-
"auth.actions.viewPermissions": "Berechtigungen anzeigen"
|
|
98
|
+
"auth.email.resetPassword.hint": "Wenn Sie dies nicht angefordert haben, können Sie diese E-Mail ignorieren."
|
|
127
99
|
}
|
|
@@ -80,19 +80,6 @@
|
|
|
80
80
|
"auth.users.form.errors.load": "Failed to load user data",
|
|
81
81
|
"auth.users.form.errors.aclUpdate": "Failed to update user access control",
|
|
82
82
|
"auth.users.form.errors.delete": "Failed to delete user",
|
|
83
|
-
"auth.profile.title": "Profile",
|
|
84
|
-
"auth.profile.form.email": "Email",
|
|
85
|
-
"auth.profile.form.password": "New password",
|
|
86
|
-
"auth.profile.form.confirmPassword": "Confirm new password",
|
|
87
|
-
"auth.profile.form.save": "Save changes",
|
|
88
|
-
"auth.profile.form.loading": "Loading profile...",
|
|
89
|
-
"auth.profile.form.errors.load": "Failed to load profile.",
|
|
90
|
-
"auth.profile.form.errors.save": "Failed to update profile.",
|
|
91
|
-
"auth.profile.form.errors.invalid": "Invalid profile update.",
|
|
92
|
-
"auth.profile.form.errors.passwordMismatch": "Passwords do not match.",
|
|
93
|
-
"auth.profile.form.errors.noChanges": "No changes to save.",
|
|
94
|
-
"auth.profile.form.errors.emailRequired": "Email is required.",
|
|
95
|
-
"auth.profile.form.success": "Profile updated.",
|
|
96
83
|
"auth.users.list.error.load": "Failed to load users",
|
|
97
84
|
"auth.users.list.error.delete": "Failed to delete user",
|
|
98
85
|
"auth.users.flash.created": "User created",
|
|
@@ -108,20 +95,5 @@
|
|
|
108
95
|
"auth.email.resetPassword.title": "Reset your password",
|
|
109
96
|
"auth.email.resetPassword.body": "Click the link below to set a new password. This link will expire in 60 minutes.",
|
|
110
97
|
"auth.email.resetPassword.cta": "Set a new password",
|
|
111
|
-
"auth.email.resetPassword.hint": "If you didn't request this, you can safely ignore this email."
|
|
112
|
-
"auth.notifications.passwordReset.requested.title": "Password reset requested",
|
|
113
|
-
"auth.notifications.passwordReset.requested.body": "A password reset link has been sent to your email",
|
|
114
|
-
"auth.notifications.passwordReset.completed.title": "Password successfully changed",
|
|
115
|
-
"auth.notifications.passwordReset.completed.body": "Your password has been updated successfully",
|
|
116
|
-
"auth.notifications.account.locked.title": "Account locked",
|
|
117
|
-
"auth.notifications.account.locked.body": "Your account has been locked due to security reasons. Please contact support.",
|
|
118
|
-
"auth.notifications.login.newDevice.title": "New device login detected",
|
|
119
|
-
"auth.notifications.login.newDevice.body": "A new login from an unrecognized device was detected on your account",
|
|
120
|
-
"auth.notifications.role.assigned.title": "New role assigned",
|
|
121
|
-
"auth.notifications.role.assigned.body": "You have been assigned a new role with additional permissions",
|
|
122
|
-
"auth.notifications.role.revoked.title": "Role removed",
|
|
123
|
-
"auth.notifications.role.revoked.body": "A role has been removed from your account",
|
|
124
|
-
"auth.actions.contactSupport": "Contact Support",
|
|
125
|
-
"auth.actions.viewSessions": "View Sessions",
|
|
126
|
-
"auth.actions.viewPermissions": "View Permissions"
|
|
98
|
+
"auth.email.resetPassword.hint": "If you didn't request this, you can safely ignore this email."
|
|
127
99
|
}
|
|
@@ -80,19 +80,6 @@
|
|
|
80
80
|
"auth.users.form.errors.load": "No se pudieron cargar los datos del usuario",
|
|
81
81
|
"auth.users.form.errors.aclUpdate": "No se pudo actualizar el control de acceso del usuario",
|
|
82
82
|
"auth.users.form.errors.delete": "No se pudo eliminar el usuario",
|
|
83
|
-
"auth.profile.title": "Perfil",
|
|
84
|
-
"auth.profile.form.email": "Correo electrónico",
|
|
85
|
-
"auth.profile.form.password": "Nueva contraseña",
|
|
86
|
-
"auth.profile.form.confirmPassword": "Confirmar nueva contraseña",
|
|
87
|
-
"auth.profile.form.save": "Guardar cambios",
|
|
88
|
-
"auth.profile.form.loading": "Cargando perfil...",
|
|
89
|
-
"auth.profile.form.errors.load": "No se pudo cargar el perfil.",
|
|
90
|
-
"auth.profile.form.errors.save": "No se pudo actualizar el perfil.",
|
|
91
|
-
"auth.profile.form.errors.invalid": "Actualización de perfil inválida.",
|
|
92
|
-
"auth.profile.form.errors.passwordMismatch": "Las contraseñas no coinciden.",
|
|
93
|
-
"auth.profile.form.errors.noChanges": "No hay cambios para guardar.",
|
|
94
|
-
"auth.profile.form.errors.emailRequired": "El correo electrónico es obligatorio.",
|
|
95
|
-
"auth.profile.form.success": "Perfil actualizado.",
|
|
96
83
|
"auth.users.list.error.load": "No se pudieron cargar los usuarios",
|
|
97
84
|
"auth.users.list.error.delete": "No se pudo eliminar el usuario",
|
|
98
85
|
"auth.users.flash.created": "Usuario creado",
|
|
@@ -108,20 +95,5 @@
|
|
|
108
95
|
"auth.email.resetPassword.title": "Restablecer tu contraseña",
|
|
109
96
|
"auth.email.resetPassword.body": "Haz clic en el siguiente enlace para establecer una nueva contraseña. Este enlace caducará en 60 minutos.",
|
|
110
97
|
"auth.email.resetPassword.cta": "Establecer nueva contraseña",
|
|
111
|
-
"auth.email.resetPassword.hint": "Si no solicitaste esto, puedes ignorar este correo de forma segura."
|
|
112
|
-
"auth.notifications.passwordReset.requested.title": "Solicitud de restablecimiento de contraseña",
|
|
113
|
-
"auth.notifications.passwordReset.requested.body": "Se ha enviado un enlace de restablecimiento de contraseña a tu correo electrónico",
|
|
114
|
-
"auth.notifications.passwordReset.completed.title": "Contraseña cambiada correctamente",
|
|
115
|
-
"auth.notifications.passwordReset.completed.body": "Tu contraseña se actualizó correctamente",
|
|
116
|
-
"auth.notifications.account.locked.title": "Cuenta bloqueada",
|
|
117
|
-
"auth.notifications.account.locked.body": "Tu cuenta ha sido bloqueada por razones de seguridad. Ponte en contacto con soporte.",
|
|
118
|
-
"auth.notifications.login.newDevice.title": "Nuevo inicio de sesión detectado",
|
|
119
|
-
"auth.notifications.login.newDevice.body": "Se detectó un inicio de sesión desde un dispositivo no reconocido en tu cuenta",
|
|
120
|
-
"auth.notifications.role.assigned.title": "Nuevo rol asignado",
|
|
121
|
-
"auth.notifications.role.assigned.body": "Se te ha asignado un nuevo rol con permisos adicionales",
|
|
122
|
-
"auth.notifications.role.revoked.title": "Rol eliminado",
|
|
123
|
-
"auth.notifications.role.revoked.body": "Se ha eliminado un rol de tu cuenta",
|
|
124
|
-
"auth.actions.contactSupport": "Contactar soporte",
|
|
125
|
-
"auth.actions.viewSessions": "Ver sesiones",
|
|
126
|
-
"auth.actions.viewPermissions": "Ver permisos"
|
|
98
|
+
"auth.email.resetPassword.hint": "Si no solicitaste esto, puedes ignorar este correo de forma segura."
|
|
127
99
|
}
|
|
@@ -80,19 +80,6 @@
|
|
|
80
80
|
"auth.users.form.errors.load": "Nie udało się wczytać danych użytkownika",
|
|
81
81
|
"auth.users.form.errors.aclUpdate": "Nie udało się zaktualizować uprawnień użytkownika",
|
|
82
82
|
"auth.users.form.errors.delete": "Nie udało się usunąć użytkownika",
|
|
83
|
-
"auth.profile.title": "Profil",
|
|
84
|
-
"auth.profile.form.email": "Email",
|
|
85
|
-
"auth.profile.form.password": "Nowe hasło",
|
|
86
|
-
"auth.profile.form.confirmPassword": "Potwierdź nowe hasło",
|
|
87
|
-
"auth.profile.form.save": "Zapisz zmiany",
|
|
88
|
-
"auth.profile.form.loading": "Ładowanie profilu...",
|
|
89
|
-
"auth.profile.form.errors.load": "Nie udało się wczytać profilu.",
|
|
90
|
-
"auth.profile.form.errors.save": "Nie udało się zaktualizować profilu.",
|
|
91
|
-
"auth.profile.form.errors.invalid": "Nieprawidłowa aktualizacja profilu.",
|
|
92
|
-
"auth.profile.form.errors.passwordMismatch": "Hasła nie są zgodne.",
|
|
93
|
-
"auth.profile.form.errors.noChanges": "Brak zmian do zapisania.",
|
|
94
|
-
"auth.profile.form.errors.emailRequired": "Email jest wymagany.",
|
|
95
|
-
"auth.profile.form.success": "Profil zaktualizowany.",
|
|
96
83
|
"auth.users.list.error.load": "Nie udało się wczytać użytkowników",
|
|
97
84
|
"auth.users.list.error.delete": "Nie udało się usunąć użytkownika",
|
|
98
85
|
"auth.users.flash.created": "Użytkownik utworzony",
|
|
@@ -108,20 +95,5 @@
|
|
|
108
95
|
"auth.email.resetPassword.title": "Zresetuj swoje hasło",
|
|
109
96
|
"auth.email.resetPassword.body": "Kliknij poniższy link, aby ustawić nowe hasło. Link wygaśnie za 60 minut.",
|
|
110
97
|
"auth.email.resetPassword.cta": "Ustaw nowe hasło",
|
|
111
|
-
"auth.email.resetPassword.hint": "Jeśli nie prosiłeś o tę wiadomość, możesz ją bezpiecznie zignorować."
|
|
112
|
-
"auth.notifications.passwordReset.requested.title": "Żądanie resetu hasła",
|
|
113
|
-
"auth.notifications.passwordReset.requested.body": "Link do resetu hasła został wysłany na Twój adres e-mail",
|
|
114
|
-
"auth.notifications.passwordReset.completed.title": "Hasło zmienione pomyślnie",
|
|
115
|
-
"auth.notifications.passwordReset.completed.body": "Twoje hasło zostało pomyślnie zaktualizowane",
|
|
116
|
-
"auth.notifications.account.locked.title": "Konto zablokowane",
|
|
117
|
-
"auth.notifications.account.locked.body": "Twoje konto zostało zablokowane z powodów bezpieczeństwa. Skontaktuj się z pomocą techniczną.",
|
|
118
|
-
"auth.notifications.login.newDevice.title": "Wykryto logowanie z nowego urządzenia",
|
|
119
|
-
"auth.notifications.login.newDevice.body": "Wykryto logowanie z nieznanego urządzenia na Twoim koncie",
|
|
120
|
-
"auth.notifications.role.assigned.title": "Przypisano nową rolę",
|
|
121
|
-
"auth.notifications.role.assigned.body": "Przypisano Ci nową rolę z dodatkowymi uprawnieniami",
|
|
122
|
-
"auth.notifications.role.revoked.title": "Rola usunięta",
|
|
123
|
-
"auth.notifications.role.revoked.body": "Z Twojego konta usunięto rolę",
|
|
124
|
-
"auth.actions.contactSupport": "Skontaktuj się z pomocą",
|
|
125
|
-
"auth.actions.viewSessions": "Zobacz sesje",
|
|
126
|
-
"auth.actions.viewPermissions": "Zobacz uprawnienia"
|
|
98
|
+
"auth.email.resetPassword.hint": "Jeśli nie prosiłeś o tę wiadomość, możesz ją bezpiecznie zignorować."
|
|
127
99
|
}
|
|
@@ -75,15 +75,15 @@ export class AuthService {
|
|
|
75
75
|
return { user, token }
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
async confirmPasswordReset(token: string, newPassword: string)
|
|
78
|
+
async confirmPasswordReset(token: string, newPassword: string) {
|
|
79
79
|
const now = new Date()
|
|
80
80
|
const row = await this.em.findOne(PasswordReset, { token })
|
|
81
|
-
if (!row || (row.usedAt && row.usedAt <= now) || row.expiresAt <= now) return
|
|
81
|
+
if (!row || (row.usedAt && row.usedAt <= now) || row.expiresAt <= now) return false
|
|
82
82
|
const user = await this.em.findOne(User, { id: row.user.id })
|
|
83
|
-
if (!user) return
|
|
83
|
+
if (!user) return false
|
|
84
84
|
user.passwordHash = await hash(newPassword, 10)
|
|
85
85
|
row.usedAt = new Date()
|
|
86
86
|
await this.em.flush()
|
|
87
|
-
return
|
|
87
|
+
return true
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -367,7 +367,5 @@
|
|
|
367
367
|
"business_rules.components.conditionRow.field.comparisonPlaceholder": "e.g., user.role",
|
|
368
368
|
"business_rules.components.conditionRow.value.help": "Use JSON for arrays: [\"a\",\"b\"]",
|
|
369
369
|
"business_rules.components.conditionRow.field.comparisonHelp": "Field path to compare with",
|
|
370
|
-
"business_rules.components.conditionRow.deleteCondition": "Delete condition"
|
|
371
|
-
"businessRules.notifications.rule.executionFailed.title": "Business Rule Failed",
|
|
372
|
-
"businessRules.notifications.rule.executionFailed.body": "Rule \"{ruleName}\" failed to execute{entityType, select, other { on {entityType}}}: {errorMessage}"
|
|
370
|
+
"business_rules.components.conditionRow.deleteCondition": "Delete condition"
|
|
373
371
|
}
|
|
@@ -681,7 +681,5 @@
|
|
|
681
681
|
"deleteError": "Failed to delete variant."
|
|
682
682
|
}
|
|
683
683
|
}
|
|
684
|
-
}
|
|
685
|
-
"catalog.notifications.product.lowStock.title": "Low Stock Alert",
|
|
686
|
-
"catalog.notifications.product.lowStock.body": "{productName}{sku, select, other { ({sku})}} is running low on stock ({currentStock} remaining, threshold: {threshold})"
|
|
684
|
+
}
|
|
687
685
|
}
|
|
@@ -2,7 +2,6 @@ import type { ModuleCli } from '@open-mercato/shared/modules/registry'
|
|
|
2
2
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
3
3
|
import type { ModuleConfigService } from './lib/module-config-service'
|
|
4
4
|
import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
|
|
5
|
-
import { DEFAULT_NOTIFICATION_DELIVERY_CONFIG, NOTIFICATIONS_DELIVERY_CONFIG_KEY } from '../notifications/lib/deliveryConfig'
|
|
6
5
|
|
|
7
6
|
function envDisablesAutoIndexing(): boolean {
|
|
8
7
|
const raw = process.env.DISABLE_VECTOR_SEARCH_AUTOINDEXING
|
|
@@ -32,11 +31,6 @@ const restoreDefaults: ModuleCli = {
|
|
|
32
31
|
name: 'auto_index_enabled',
|
|
33
32
|
value: defaultEnabled,
|
|
34
33
|
},
|
|
35
|
-
{
|
|
36
|
-
moduleId: 'notifications',
|
|
37
|
-
name: NOTIFICATIONS_DELIVERY_CONFIG_KEY,
|
|
38
|
-
value: DEFAULT_NOTIFICATION_DELIVERY_CONFIG,
|
|
39
|
-
},
|
|
40
34
|
],
|
|
41
35
|
{ force: true },
|
|
42
36
|
)
|
|
@@ -35,9 +35,6 @@ import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
|
35
35
|
import type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'
|
|
36
36
|
import { E } from '#generated/entities.ids.generated'
|
|
37
37
|
import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
38
|
-
import { resolveNotificationService } from '../../notifications/lib/notificationService'
|
|
39
|
-
import { buildNotificationFromType } from '../../notifications/lib/notificationBuilder'
|
|
40
|
-
import { notificationTypes } from '../notifications'
|
|
41
38
|
|
|
42
39
|
const DEAL_ENTITY_ID = 'customers:customer_deal'
|
|
43
40
|
const dealCrudIndexer: CrudIndexerConfig<CustomerDeal> = {
|
|
@@ -284,8 +281,6 @@ const updateDealCommand: CommandHandler<DealUpdateInput, { dealId: string }> = {
|
|
|
284
281
|
ensureTenantScope(ctx, record.tenantId)
|
|
285
282
|
ensureOrganizationScope(ctx, record.organizationId)
|
|
286
283
|
|
|
287
|
-
const previousStatus = record.status
|
|
288
|
-
|
|
289
284
|
if (parsed.title !== undefined) record.title = parsed.title
|
|
290
285
|
if (parsed.description !== undefined) record.description = parsed.description ?? null
|
|
291
286
|
if (parsed.status !== undefined) record.status = parsed.status ?? record.status
|
|
@@ -324,40 +319,6 @@ const updateDealCommand: CommandHandler<DealUpdateInput, { dealId: string }> = {
|
|
|
324
319
|
indexer: dealCrudIndexer,
|
|
325
320
|
})
|
|
326
321
|
|
|
327
|
-
// Send notifications for deal won/lost status changes
|
|
328
|
-
const newStatus = record.status
|
|
329
|
-
const normalizedStatus = newStatus === 'win' ? 'won' : newStatus === 'loose' ? 'lost' : newStatus
|
|
330
|
-
if (previousStatus !== newStatus && (normalizedStatus === 'won' || normalizedStatus === 'lost') && record.ownerUserId) {
|
|
331
|
-
try {
|
|
332
|
-
const notificationService = resolveNotificationService(ctx.container)
|
|
333
|
-
const notificationType = normalizedStatus === 'won' ? 'customers.deal.won' : 'customers.deal.lost'
|
|
334
|
-
const typeDef = notificationTypes.find((type) => type.type === notificationType)
|
|
335
|
-
if (typeDef) {
|
|
336
|
-
const valueDisplay = record.valueAmount && record.valueCurrency
|
|
337
|
-
? `${record.valueCurrency} ${record.valueAmount}`
|
|
338
|
-
: ''
|
|
339
|
-
|
|
340
|
-
const notificationInput = buildNotificationFromType(typeDef, {
|
|
341
|
-
recipientUserId: record.ownerUserId,
|
|
342
|
-
bodyVariables: {
|
|
343
|
-
dealTitle: record.title,
|
|
344
|
-
dealValue: valueDisplay,
|
|
345
|
-
},
|
|
346
|
-
sourceEntityType: 'customers:customer_deal',
|
|
347
|
-
sourceEntityId: record.id,
|
|
348
|
-
linkHref: `/backend/customers/deals/${record.id}`,
|
|
349
|
-
})
|
|
350
|
-
|
|
351
|
-
await notificationService.create(notificationInput, {
|
|
352
|
-
tenantId: record.tenantId,
|
|
353
|
-
organizationId: record.organizationId,
|
|
354
|
-
})
|
|
355
|
-
}
|
|
356
|
-
} catch {
|
|
357
|
-
// Notification creation is non-critical, don't fail the command
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
322
|
return { dealId: record.id }
|
|
362
323
|
},
|
|
363
324
|
buildLog: async ({ snapshots, ctx }) => {
|
|
@@ -943,9 +943,5 @@
|
|
|
943
943
|
"customers.workPlan.customerTodos.table.state.empty": "No customer tasks yet.",
|
|
944
944
|
"customers.workPlan.customerTodos.table.error.load": "Failed to load customer tasks.",
|
|
945
945
|
"customers.workPlan.customerTodos.table.export.view": "Exports the current list with filters and visible columns.",
|
|
946
|
-
"customers.workPlan.customerTodos.table.export.full": "Exports every linked task field, including hidden attributes."
|
|
947
|
-
"customers.notifications.deal.won.title": "Deal Won",
|
|
948
|
-
"customers.notifications.deal.won.body": "{dealTitle} has been marked as won{dealValue, select, other { ({dealValue})}}",
|
|
949
|
-
"customers.notifications.deal.lost.title": "Deal Lost",
|
|
950
|
-
"customers.notifications.deal.lost.body": "{dealTitle} has been marked as lost"
|
|
946
|
+
"customers.workPlan.customerTodos.table.export.full": "Exports every linked task field, including hidden attributes."
|
|
951
947
|
}
|
|
@@ -11,8 +11,6 @@ import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
|
|
|
11
11
|
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
12
12
|
import { deriveResourceFromCommandId, invalidateCrudCache } from '@open-mercato/shared/lib/crud/cache'
|
|
13
13
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
14
|
-
import { resolveNotificationService } from '../../notifications/lib/notificationService'
|
|
15
|
-
import { buildFeatureNotificationFromType } from '../../notifications/lib/notificationBuilder'
|
|
16
14
|
import { setRecordCustomFields } from '@open-mercato/core/modules/entities/lib/helpers'
|
|
17
15
|
import { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'
|
|
18
16
|
import { normalizeCustomFieldValues } from '@open-mercato/shared/lib/custom-fields/normalize'
|
|
@@ -90,7 +88,6 @@ import { resolveDictionaryEntryValue } from '../lib/dictionaries'
|
|
|
90
88
|
import { resolveStatusEntryIdByValue } from '../lib/statusHelpers'
|
|
91
89
|
import { SalesDocumentNumberGenerator } from '../services/salesDocumentNumberGenerator'
|
|
92
90
|
import { loadSalesSettings } from './settings'
|
|
93
|
-
import { notificationTypes } from '../notifications'
|
|
94
91
|
|
|
95
92
|
type DocumentAddressSnapshot = {
|
|
96
93
|
id: string
|
|
@@ -3114,37 +3111,6 @@ const createQuoteCommand: CommandHandler<QuoteCreateInput, { quoteId: string }>
|
|
|
3114
3111
|
})
|
|
3115
3112
|
await em.flush()
|
|
3116
3113
|
|
|
3117
|
-
// Create notification for users with sales.quotes.manage feature
|
|
3118
|
-
try {
|
|
3119
|
-
const notificationService = resolveNotificationService(ctx.container)
|
|
3120
|
-
const typeDef = notificationTypes.find((type) => type.type === 'sales.quote.created')
|
|
3121
|
-
if (typeDef) {
|
|
3122
|
-
const totalAmount = quote.grandTotalGrossAmount && quote.currencyCode
|
|
3123
|
-
? `${quote.grandTotalGrossAmount} ${quote.currencyCode}`
|
|
3124
|
-
: ''
|
|
3125
|
-
const totalDisplay = totalAmount ? ` (${totalAmount})` : ''
|
|
3126
|
-
const notificationInput = buildFeatureNotificationFromType(typeDef, {
|
|
3127
|
-
requiredFeature: 'sales.quotes.manage',
|
|
3128
|
-
bodyVariables: {
|
|
3129
|
-
quoteNumber: quote.quoteNumber,
|
|
3130
|
-
total: totalDisplay,
|
|
3131
|
-
totalAmount,
|
|
3132
|
-
},
|
|
3133
|
-
sourceEntityType: 'sales:quote',
|
|
3134
|
-
sourceEntityId: quote.id,
|
|
3135
|
-
linkHref: `/backend/sales/quotes/${quote.id}`,
|
|
3136
|
-
})
|
|
3137
|
-
|
|
3138
|
-
await notificationService.createForFeature(notificationInput, {
|
|
3139
|
-
tenantId: quote.tenantId,
|
|
3140
|
-
organizationId: quote.organizationId ?? null,
|
|
3141
|
-
})
|
|
3142
|
-
}
|
|
3143
|
-
} catch (err) {
|
|
3144
|
-
// Notification creation is non-critical, don't fail the command
|
|
3145
|
-
console.error('[sales.quotes.create] Failed to create notification:', err)
|
|
3146
|
-
}
|
|
3147
|
-
|
|
3148
3114
|
return { quoteId: quote.id }
|
|
3149
3115
|
},
|
|
3150
3116
|
captureAfter: async (_input, result, ctx) => {
|
|
@@ -3816,37 +3782,6 @@ const createOrderCommand: CommandHandler<OrderCreateInput, { orderId: string }>
|
|
|
3816
3782
|
})
|
|
3817
3783
|
await em.flush()
|
|
3818
3784
|
|
|
3819
|
-
// Create notification for users with sales.orders.manage feature
|
|
3820
|
-
try {
|
|
3821
|
-
const notificationService = resolveNotificationService(ctx.container)
|
|
3822
|
-
const typeDef = notificationTypes.find((type) => type.type === 'sales.order.created')
|
|
3823
|
-
if (typeDef) {
|
|
3824
|
-
const totalAmount = order.grandTotalGrossAmount && order.currencyCode
|
|
3825
|
-
? `${order.grandTotalGrossAmount} ${order.currencyCode}`
|
|
3826
|
-
: ''
|
|
3827
|
-
const totalDisplay = totalAmount ? ` (${totalAmount})` : ''
|
|
3828
|
-
const notificationInput = buildFeatureNotificationFromType(typeDef, {
|
|
3829
|
-
requiredFeature: 'sales.orders.manage',
|
|
3830
|
-
bodyVariables: {
|
|
3831
|
-
orderNumber: order.orderNumber,
|
|
3832
|
-
total: totalDisplay,
|
|
3833
|
-
totalAmount,
|
|
3834
|
-
},
|
|
3835
|
-
sourceEntityType: 'sales:order',
|
|
3836
|
-
sourceEntityId: order.id,
|
|
3837
|
-
linkHref: `/backend/sales/orders/${order.id}`,
|
|
3838
|
-
})
|
|
3839
|
-
|
|
3840
|
-
await notificationService.createForFeature(notificationInput, {
|
|
3841
|
-
tenantId: order.tenantId,
|
|
3842
|
-
organizationId: order.organizationId ?? null,
|
|
3843
|
-
})
|
|
3844
|
-
}
|
|
3845
|
-
} catch (err) {
|
|
3846
|
-
// Notification creation is non-critical, don't fail the command
|
|
3847
|
-
console.error('[sales.orders.create] Failed to create notification:', err)
|
|
3848
|
-
}
|
|
3849
|
-
|
|
3850
3785
|
return { orderId: order.id }
|
|
3851
3786
|
},
|
|
3852
3787
|
captureAfter: async (_input, result, ctx) => {
|
|
@@ -35,9 +35,6 @@ import { invalidateCrudCache } from '@open-mercato/shared/lib/crud/cache'
|
|
|
35
35
|
import { emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'
|
|
36
36
|
import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
|
|
37
37
|
import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
38
|
-
import { resolveNotificationService } from '../../notifications/lib/notificationService'
|
|
39
|
-
import { buildFeatureNotificationFromType } from '../../notifications/lib/notificationBuilder'
|
|
40
|
-
import { notificationTypes } from '../notifications'
|
|
41
38
|
|
|
42
39
|
export type PaymentAllocationSnapshot = {
|
|
43
40
|
id: string
|
|
@@ -425,36 +422,6 @@ const createPaymentCommand: CommandHandler<
|
|
|
425
422
|
const totals = await recomputeOrderPaymentTotals(em, order)
|
|
426
423
|
await em.flush()
|
|
427
424
|
await invalidateOrderCache(ctx.container, order, ctx.auth?.tenantId ?? null)
|
|
428
|
-
|
|
429
|
-
// Create notification for payment received
|
|
430
|
-
try {
|
|
431
|
-
const notificationService = resolveNotificationService(ctx.container)
|
|
432
|
-
const typeDef = notificationTypes.find((type) => type.type === 'sales.payment.received')
|
|
433
|
-
if (typeDef) {
|
|
434
|
-
const amountDisplay = payment.amount && payment.currencyCode
|
|
435
|
-
? `${payment.currencyCode} ${payment.amount}`
|
|
436
|
-
: ''
|
|
437
|
-
const notificationInput = buildFeatureNotificationFromType(typeDef, {
|
|
438
|
-
requiredFeature: 'sales.orders.manage',
|
|
439
|
-
bodyVariables: {
|
|
440
|
-
orderNumber: order.orderNumber ?? '',
|
|
441
|
-
amount: amountDisplay,
|
|
442
|
-
},
|
|
443
|
-
sourceEntityType: 'sales:order',
|
|
444
|
-
sourceEntityId: order.id,
|
|
445
|
-
linkHref: `/backend/sales/orders/${order.id}`,
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
await notificationService.createForFeature(notificationInput, {
|
|
449
|
-
tenantId: payment.tenantId,
|
|
450
|
-
organizationId: payment.organizationId ?? null,
|
|
451
|
-
})
|
|
452
|
-
}
|
|
453
|
-
} catch (err) {
|
|
454
|
-
// Notification creation is non-critical, don't fail the command
|
|
455
|
-
console.error('[sales.payments.create] Failed to create notification:', err)
|
|
456
|
-
}
|
|
457
|
-
|
|
458
425
|
return { paymentId: payment.id, orderTotals: totals }
|
|
459
426
|
},
|
|
460
427
|
captureAfter: async (_input, result, ctx) => {
|