@open-mercato/core 0.4.7-develop-78d7541539 → 0.4.7-develop-74069040de
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/AGENTS.md +1 -0
- package/dist/modules/catalog/api/bulk-delete/route.js +86 -0
- package/dist/modules/catalog/api/bulk-delete/route.js.map +7 -0
- package/dist/modules/catalog/api/prices/route.js +39 -6
- package/dist/modules/catalog/api/prices/route.js.map +2 -2
- package/dist/modules/catalog/api/products/route.js +6 -11
- package/dist/modules/catalog/api/products/route.js.map +2 -2
- package/dist/modules/catalog/commands/products.js +2 -0
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +9 -1
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/lib/bulkDelete.js +70 -0
- package/dist/modules/catalog/lib/bulkDelete.js.map +7 -0
- package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js +185 -0
- package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js.map +7 -0
- package/dist/modules/catalog/widgets/injection-table.js +9 -1
- package/dist/modules/catalog/widgets/injection-table.js.map +2 -2
- package/dist/modules/catalog/workers/catalog-product-bulk-delete.js +40 -0
- package/dist/modules/catalog/workers/catalog-product-bulk-delete.js.map +7 -0
- package/dist/modules/data_sync/api/options.js +52 -0
- package/dist/modules/data_sync/api/options.js.map +7 -0
- package/dist/modules/data_sync/api/run.js +30 -35
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +15 -30
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +109 -0
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +7 -0
- package/dist/modules/data_sync/api/schedules/route.js +72 -0
- package/dist/modules/data_sync/api/schedules/route.js.map +7 -0
- package/dist/modules/data_sync/api/schedules/serialize.js +21 -0
- package/dist/modules/data_sync/api/schedules/serialize.js.map +7 -0
- package/dist/modules/data_sync/backend/data-sync/page.js +656 -47
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +116 -34
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +394 -0
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +7 -0
- package/dist/modules/data_sync/data/validators.js +32 -0
- package/dist/modules/data_sync/data/validators.js.map +2 -2
- package/dist/modules/data_sync/di.js +2 -0
- package/dist/modules/data_sync/di.js.map +2 -2
- package/dist/modules/data_sync/lib/id-mapping.js +24 -2
- package/dist/modules/data_sync/lib/id-mapping.js.map +2 -2
- package/dist/modules/data_sync/lib/start-run.js +57 -0
- package/dist/modules/data_sync/lib/start-run.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-engine.js +93 -4
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +5 -1
- package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-schedule-service.js +138 -0
- package/dist/modules/data_sync/lib/sync-schedule-service.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-export.js +28 -2
- package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-import.js +28 -2
- package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-scheduled.js +5 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js.map +2 -2
- package/dist/modules/entities/api/definitions.js +5 -2
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/lib/field-definitions.js +3 -1
- package/dist/modules/entities/lib/field-definitions.js.map +2 -2
- package/dist/modules/integrations/api/[id]/route.js +14 -15
- package/dist/modules/integrations/api/[id]/route.js.map +2 -2
- package/dist/modules/integrations/api/route.js +3 -3
- package/dist/modules/integrations/api/route.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +148 -33
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/lib/state-service.js +15 -1
- package/dist/modules/integrations/lib/state-service.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +24 -22
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/progress/api/active/route.js +3 -1
- package/dist/modules/progress/api/active/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/[id]/route.js +1 -1
- package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/route.js +1 -1
- package/dist/modules/progress/api/jobs/route.js.map +2 -2
- package/dist/modules/progress/lib/events.js.map +1 -1
- package/dist/modules/progress/lib/progressService.js.map +2 -2
- package/dist/modules/progress/lib/progressServiceImpl.js +42 -1
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/query_index/lib/document.js +35 -1
- package/dist/modules/query_index/lib/document.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +91 -4
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/indexer.js +2 -0
- package/dist/modules/query_index/lib/indexer.js.map +2 -2
- package/dist/modules/sales/api/adjustment-kinds/route.js +3 -9
- package/dist/modules/sales/api/adjustment-kinds/route.js.map +2 -2
- package/dist/modules/sales/api/channels/route.js +3 -10
- package/dist/modules/sales/api/channels/route.js.map +2 -2
- package/dist/modules/sales/api/delivery-windows/route.js +3 -10
- package/dist/modules/sales/api/delivery-windows/route.js.map +2 -2
- package/dist/modules/sales/api/payment-methods/route.js +3 -11
- package/dist/modules/sales/api/payment-methods/route.js.map +2 -2
- package/dist/modules/sales/api/price-kinds/route.js +3 -5
- package/dist/modules/sales/api/price-kinds/route.js.map +2 -2
- package/dist/modules/sales/api/shipping-methods/route.js +3 -11
- package/dist/modules/sales/api/shipping-methods/route.js.map +2 -2
- package/dist/modules/sales/api/tags/route.js +3 -9
- package/dist/modules/sales/api/tags/route.js.map +2 -2
- package/dist/modules/sales/api/tax-rates/route.js +3 -13
- package/dist/modules/sales/api/tax-rates/route.js.map +2 -2
- package/dist/modules/sales/api/utils.js +9 -0
- package/dist/modules/sales/api/utils.js.map +2 -2
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js +3 -9
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js +3 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/route.js +4 -3
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/serialize.js +25 -0
- package/dist/modules/workflows/api/definitions/serialize.js.map +7 -0
- package/package.json +3 -3
- package/src/modules/catalog/api/bulk-delete/route.ts +93 -0
- package/src/modules/catalog/api/prices/route.ts +53 -6
- package/src/modules/catalog/api/products/route.ts +6 -11
- package/src/modules/catalog/commands/products.ts +2 -0
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +8 -0
- package/src/modules/catalog/i18n/de.json +10 -0
- package/src/modules/catalog/i18n/en.json +10 -0
- package/src/modules/catalog/i18n/es.json +10 -0
- package/src/modules/catalog/i18n/pl.json +10 -0
- package/src/modules/catalog/lib/bulkDelete.ts +106 -0
- package/src/modules/catalog/widgets/injection/product-bulk-delete/widget.ts +242 -0
- package/src/modules/catalog/widgets/injection-table.ts +8 -0
- package/src/modules/catalog/workers/catalog-product-bulk-delete.ts +48 -0
- package/src/modules/data_sync/AGENTS.md +11 -3
- package/src/modules/data_sync/api/options.ts +58 -0
- package/src/modules/data_sync/api/run.ts +34 -36
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +2 -2
- package/src/modules/data_sync/api/runs/[id]/retry.ts +14 -31
- package/src/modules/data_sync/api/schedules/[id]/route.ts +130 -0
- package/src/modules/data_sync/api/schedules/route.ts +77 -0
- package/src/modules/data_sync/api/schedules/serialize.ts +31 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +756 -2
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +179 -53
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +512 -0
- package/src/modules/data_sync/data/validators.ts +35 -0
- package/src/modules/data_sync/di.ts +6 -0
- package/src/modules/data_sync/i18n/de.json +72 -0
- package/src/modules/data_sync/i18n/en.json +72 -0
- package/src/modules/data_sync/i18n/es.json +72 -0
- package/src/modules/data_sync/i18n/pl.json +72 -0
- package/src/modules/data_sync/lib/adapter.ts +4 -1
- package/src/modules/data_sync/lib/id-mapping.ts +32 -2
- package/src/modules/data_sync/lib/start-run.ts +90 -0
- package/src/modules/data_sync/lib/sync-engine.ts +111 -4
- package/src/modules/data_sync/lib/sync-run-service.ts +5 -1
- package/src/modules/data_sync/lib/sync-schedule-service.ts +207 -0
- package/src/modules/data_sync/workers/sync-export.ts +33 -2
- package/src/modules/data_sync/workers/sync-import.ts +33 -2
- package/src/modules/data_sync/workers/sync-scheduled.ts +7 -0
- package/src/modules/entities/api/definitions.ts +12 -2
- package/src/modules/entities/lib/field-definitions.ts +2 -0
- package/src/modules/integrations/AGENTS.md +16 -3
- package/src/modules/integrations/api/[id]/route.ts +14 -15
- package/src/modules/integrations/api/route.ts +3 -3
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +176 -54
- package/src/modules/integrations/lib/state-service.ts +25 -1
- package/src/modules/messages/api/[id]/route.ts +25 -22
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +3 -3
- package/src/modules/progress/api/active/route.ts +4 -1
- package/src/modules/progress/api/jobs/[id]/route.ts +1 -1
- package/src/modules/progress/api/jobs/route.ts +1 -1
- package/src/modules/progress/lib/events.ts +6 -0
- package/src/modules/progress/lib/progressService.ts +1 -0
- package/src/modules/progress/lib/progressServiceImpl.ts +47 -1
- package/src/modules/query_index/lib/document.ts +52 -1
- package/src/modules/query_index/lib/engine.ts +104 -4
- package/src/modules/query_index/lib/indexer.ts +2 -0
- package/src/modules/sales/api/adjustment-kinds/route.ts +3 -9
- package/src/modules/sales/api/channels/route.ts +3 -10
- package/src/modules/sales/api/delivery-windows/route.ts +3 -10
- package/src/modules/sales/api/payment-methods/route.ts +3 -11
- package/src/modules/sales/api/price-kinds/route.ts +3 -5
- package/src/modules/sales/api/shipping-methods/route.ts +3 -11
- package/src/modules/sales/api/tags/route.ts +3 -9
- package/src/modules/sales/api/tax-rates/route.ts +3 -13
- package/src/modules/sales/api/utils.ts +9 -0
- package/src/modules/sales/lib/makeStatusDictionaryRoute.ts +3 -9
- package/src/modules/workflows/api/definitions/[id]/route.ts +3 -2
- package/src/modules/workflows/api/definitions/route.ts +4 -3
- package/src/modules/workflows/api/definitions/serialize.ts +23 -0
|
@@ -16,6 +16,7 @@ function buildJobPayload(job: ProgressJob): Record<string, unknown> {
|
|
|
16
16
|
totalCount: job.totalCount ?? null,
|
|
17
17
|
etaSeconds: job.etaSeconds ?? null,
|
|
18
18
|
cancellable: job.cancellable,
|
|
19
|
+
meta: job.meta ?? null,
|
|
19
20
|
startedAt: job.startedAt?.toISOString() ?? null,
|
|
20
21
|
finishedAt: job.finishedAt?.toISOString() ?? null,
|
|
21
22
|
}
|
|
@@ -53,6 +54,9 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
53
54
|
|
|
54
55
|
async startJob(jobId, ctx) {
|
|
55
56
|
const job = await em.findOneOrFail(ProgressJob, { id: jobId, tenantId: ctx.tenantId })
|
|
57
|
+
if (job.status === 'cancelled') {
|
|
58
|
+
return job
|
|
59
|
+
}
|
|
56
60
|
|
|
57
61
|
job.status = 'running'
|
|
58
62
|
job.startedAt = new Date()
|
|
@@ -71,6 +75,9 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
71
75
|
|
|
72
76
|
async updateProgress(jobId, input, ctx) {
|
|
73
77
|
const job = await em.findOneOrFail(ProgressJob, { id: jobId, tenantId: ctx.tenantId })
|
|
78
|
+
if (job.status === 'completed' || job.status === 'failed' || job.status === 'cancelled') {
|
|
79
|
+
return job
|
|
80
|
+
}
|
|
74
81
|
|
|
75
82
|
if (input.processedCount !== undefined) {
|
|
76
83
|
job.processedCount = input.processedCount
|
|
@@ -109,6 +116,9 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
109
116
|
|
|
110
117
|
async incrementProgress(jobId, delta, ctx) {
|
|
111
118
|
const job = await em.findOneOrFail(ProgressJob, { id: jobId, tenantId: ctx.tenantId })
|
|
119
|
+
if (job.status === 'completed' || job.status === 'failed' || job.status === 'cancelled') {
|
|
120
|
+
return job
|
|
121
|
+
}
|
|
112
122
|
|
|
113
123
|
job.processedCount += delta
|
|
114
124
|
job.heartbeatAt = new Date()
|
|
@@ -134,6 +144,9 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
134
144
|
async completeJob(jobId, input, ctx) {
|
|
135
145
|
const job = await em.findOne(ProgressJob, { id: jobId, tenantId: ctx.tenantId })
|
|
136
146
|
if (!job) throw new Error(`Job ${jobId} not found`)
|
|
147
|
+
if (job.status === 'cancelled') {
|
|
148
|
+
return job
|
|
149
|
+
}
|
|
137
150
|
|
|
138
151
|
job.status = 'completed'
|
|
139
152
|
job.finishedAt = new Date()
|
|
@@ -158,6 +171,9 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
158
171
|
async failJob(jobId, input, ctx) {
|
|
159
172
|
const job = await em.findOne(ProgressJob, { id: jobId, tenantId: ctx.tenantId })
|
|
160
173
|
if (!job) throw new Error(`Job ${jobId} not found`)
|
|
174
|
+
if (job.status === 'cancelled') {
|
|
175
|
+
return job
|
|
176
|
+
}
|
|
161
177
|
|
|
162
178
|
job.status = 'failed'
|
|
163
179
|
job.finishedAt = new Date()
|
|
@@ -203,6 +219,30 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
203
219
|
return job
|
|
204
220
|
},
|
|
205
221
|
|
|
222
|
+
async markCancelled(jobId, ctx) {
|
|
223
|
+
const job = await em.findOne(ProgressJob, { id: jobId, tenantId: ctx.tenantId })
|
|
224
|
+
if (!job) throw new Error(`Job ${jobId} not found`)
|
|
225
|
+
if (job.status === 'cancelled') {
|
|
226
|
+
return job
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
job.cancelRequestedAt = job.cancelRequestedAt ?? new Date()
|
|
230
|
+
job.cancelledByUserId = ctx.userId
|
|
231
|
+
job.status = 'cancelled'
|
|
232
|
+
job.finishedAt = job.finishedAt ?? new Date()
|
|
233
|
+
job.etaSeconds = 0
|
|
234
|
+
|
|
235
|
+
await em.flush()
|
|
236
|
+
|
|
237
|
+
await eventBus.emit(PROGRESS_EVENTS.JOB_CANCELLED, {
|
|
238
|
+
...buildJobPayload(job),
|
|
239
|
+
tenantId: ctx.tenantId,
|
|
240
|
+
organizationId: job.organizationId ?? null,
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
return job
|
|
244
|
+
},
|
|
245
|
+
|
|
206
246
|
async isCancellationRequested(jobId) {
|
|
207
247
|
const job = await em.findOne(ProgressJob, { id: jobId })
|
|
208
248
|
return job?.cancelRequestedAt != null
|
|
@@ -247,7 +287,13 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
247
287
|
const staleJobs = await em.find(ProgressJob, {
|
|
248
288
|
tenantId,
|
|
249
289
|
status: 'running',
|
|
250
|
-
|
|
290
|
+
$or: [
|
|
291
|
+
{ heartbeatAt: { $lt: cutoff } },
|
|
292
|
+
{
|
|
293
|
+
heartbeatAt: null,
|
|
294
|
+
startedAt: { $lt: cutoff },
|
|
295
|
+
},
|
|
296
|
+
],
|
|
251
297
|
})
|
|
252
298
|
|
|
253
299
|
for (const job of staleJobs) {
|
|
@@ -10,6 +10,8 @@ export type IndexCustomFieldValue = {
|
|
|
10
10
|
tenantId?: string | null
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export const AGGREGATE_SEARCH_FIELD = 'search_text'
|
|
14
|
+
|
|
13
15
|
function normalizeScopeValue(value: string | null | undefined): string | null {
|
|
14
16
|
if (value === undefined || value === null || value === '') return null
|
|
15
17
|
return value
|
|
@@ -28,6 +30,55 @@ function normalizeValue(value: unknown): unknown {
|
|
|
28
30
|
return value
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
function collectAggregateSearchValues(field: string, value: unknown): string[] {
|
|
34
|
+
const lower = field.toLowerCase()
|
|
35
|
+
if (
|
|
36
|
+
lower === AGGREGATE_SEARCH_FIELD
|
|
37
|
+
|| lower === 'id'
|
|
38
|
+
|| lower.endsWith('_id')
|
|
39
|
+
|| lower.endsWith('.id')
|
|
40
|
+
|| lower.endsWith('_at')
|
|
41
|
+
|| ['created_at', 'updated_at', 'deleted_at', 'tenant_id', 'organization_id'].includes(lower)
|
|
42
|
+
) {
|
|
43
|
+
return []
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (typeof value === 'string') {
|
|
47
|
+
const trimmed = value.trim()
|
|
48
|
+
return trimmed.length > 0 ? [trimmed] : []
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (Array.isArray(value)) {
|
|
52
|
+
return value
|
|
53
|
+
.filter((entry): entry is string => typeof entry === 'string')
|
|
54
|
+
.map((entry) => entry.trim())
|
|
55
|
+
.filter((entry) => entry.length > 0)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return []
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function attachAggregateSearchField(doc: Record<string, unknown>): Record<string, unknown> {
|
|
62
|
+
const parts: string[] = []
|
|
63
|
+
const seen = new Set<string>()
|
|
64
|
+
|
|
65
|
+
for (const [field, value] of Object.entries(doc)) {
|
|
66
|
+
const values = collectAggregateSearchValues(field, value)
|
|
67
|
+
for (const entry of values) {
|
|
68
|
+
const key = entry.toLowerCase()
|
|
69
|
+
if (seen.has(key)) continue
|
|
70
|
+
seen.add(key)
|
|
71
|
+
parts.push(entry)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (parts.length > 0) {
|
|
76
|
+
doc[AGGREGATE_SEARCH_FIELD] = parts.join('\n')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return doc
|
|
80
|
+
}
|
|
81
|
+
|
|
31
82
|
export function buildIndexDocument(
|
|
32
83
|
baseRow: Record<string, unknown>,
|
|
33
84
|
customFieldValues: Iterable<IndexCustomFieldValue> = [],
|
|
@@ -72,5 +123,5 @@ export function buildIndexDocument(
|
|
|
72
123
|
}
|
|
73
124
|
}
|
|
74
125
|
|
|
75
|
-
return doc
|
|
126
|
+
return attachAggregateSearchField(doc)
|
|
76
127
|
}
|
|
@@ -561,14 +561,41 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
561
561
|
}
|
|
562
562
|
|
|
563
563
|
for (const filter of baseFilters) {
|
|
564
|
-
const
|
|
565
|
-
|
|
564
|
+
const fieldName = String(filter.field)
|
|
565
|
+
const baseField = resolveBaseColumn(fieldName)
|
|
566
|
+
if (!baseField) {
|
|
567
|
+
builder = this.applyIndexDocFilterFromAlias(
|
|
568
|
+
knex,
|
|
569
|
+
builder,
|
|
570
|
+
'ei',
|
|
571
|
+
entity,
|
|
572
|
+
fieldName,
|
|
573
|
+
filter.op,
|
|
574
|
+
filter.value,
|
|
575
|
+
'b.id',
|
|
576
|
+
searchRuntime,
|
|
577
|
+
)
|
|
578
|
+
if (optimizedCountBuilder) {
|
|
579
|
+
optimizedCountBuilder = this.applyIndexDocFilterFromAlias(
|
|
580
|
+
knex,
|
|
581
|
+
optimizedCountBuilder,
|
|
582
|
+
'ei',
|
|
583
|
+
entity,
|
|
584
|
+
fieldName,
|
|
585
|
+
filter.op,
|
|
586
|
+
filter.value,
|
|
587
|
+
'b.id',
|
|
588
|
+
searchRuntime,
|
|
589
|
+
)
|
|
590
|
+
}
|
|
591
|
+
continue
|
|
592
|
+
}
|
|
566
593
|
const column = qualify(baseField)
|
|
567
594
|
builder = this.applyColumnFilter(builder, column, filter, {
|
|
568
595
|
...searchRuntime,
|
|
569
596
|
knex,
|
|
570
597
|
entity,
|
|
571
|
-
field:
|
|
598
|
+
field: fieldName,
|
|
572
599
|
recordIdColumn: 'b.id',
|
|
573
600
|
})
|
|
574
601
|
if (optimizedCountBuilder) {
|
|
@@ -576,7 +603,7 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
576
603
|
...searchRuntime,
|
|
577
604
|
knex,
|
|
578
605
|
entity,
|
|
579
|
-
field:
|
|
606
|
+
field: fieldName,
|
|
580
607
|
recordIdColumn: 'b.id',
|
|
581
608
|
})
|
|
582
609
|
}
|
|
@@ -1141,6 +1168,79 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1141
1168
|
}
|
|
1142
1169
|
}
|
|
1143
1170
|
|
|
1171
|
+
private applyIndexDocFilterFromAlias(
|
|
1172
|
+
knex: Knex,
|
|
1173
|
+
q: ResultBuilder,
|
|
1174
|
+
alias: string,
|
|
1175
|
+
entityType: string,
|
|
1176
|
+
key: string,
|
|
1177
|
+
op: FilterOp,
|
|
1178
|
+
value: unknown,
|
|
1179
|
+
recordIdColumn: string,
|
|
1180
|
+
search?: SearchRuntime,
|
|
1181
|
+
): ResultBuilder {
|
|
1182
|
+
const text = knex.raw(`(${alias}.doc ->> ?)`, [key])
|
|
1183
|
+
if ((op === 'like' || op === 'ilike') && search?.enabled && typeof value === 'string') {
|
|
1184
|
+
const tokens = tokenizeText(String(value), search.config)
|
|
1185
|
+
const hashes = tokens.hashes
|
|
1186
|
+
if (hashes.length) {
|
|
1187
|
+
const applied = this.applySearchTokens(q, {
|
|
1188
|
+
knex,
|
|
1189
|
+
entity: entityType,
|
|
1190
|
+
field: key,
|
|
1191
|
+
hashes,
|
|
1192
|
+
recordIdColumn,
|
|
1193
|
+
tenantId: search.tenantId ?? null,
|
|
1194
|
+
organizationScope: search.organizationScope ?? null,
|
|
1195
|
+
})
|
|
1196
|
+
this.logSearchDebug('search:index-doc-filter', {
|
|
1197
|
+
entity: entityType,
|
|
1198
|
+
field: key,
|
|
1199
|
+
tokens: tokens.tokens,
|
|
1200
|
+
hashes,
|
|
1201
|
+
applied,
|
|
1202
|
+
tenantId: search.tenantId ?? null,
|
|
1203
|
+
organizationScope: search.organizationScope,
|
|
1204
|
+
})
|
|
1205
|
+
if (applied) return q
|
|
1206
|
+
} else {
|
|
1207
|
+
this.logSearchDebug('search:index-doc-skip-empty-hashes', {
|
|
1208
|
+
entity: entityType,
|
|
1209
|
+
field: key,
|
|
1210
|
+
value,
|
|
1211
|
+
})
|
|
1212
|
+
}
|
|
1213
|
+
return q
|
|
1214
|
+
}
|
|
1215
|
+
switch (op) {
|
|
1216
|
+
case 'eq':
|
|
1217
|
+
return q.where(text, '=', value as Knex.Value)
|
|
1218
|
+
case 'ne':
|
|
1219
|
+
return q.where(text, '!=', value as Knex.Value)
|
|
1220
|
+
case 'in':
|
|
1221
|
+
return q.whereIn(text as any, this.toArray(value) as readonly Knex.Value[])
|
|
1222
|
+
case 'nin':
|
|
1223
|
+
return q.whereNotIn(text as any, this.toArray(value) as readonly Knex.Value[])
|
|
1224
|
+
case 'like':
|
|
1225
|
+
return q.where(text, 'like', value as Knex.Value)
|
|
1226
|
+
case 'ilike':
|
|
1227
|
+
return q.where(text, 'ilike', value as Knex.Value)
|
|
1228
|
+
case 'exists':
|
|
1229
|
+
return value
|
|
1230
|
+
? q.whereRaw(`${text.toString()} is not null`)
|
|
1231
|
+
: q.whereRaw(`${text.toString()} is null`)
|
|
1232
|
+
case 'gt':
|
|
1233
|
+
case 'gte':
|
|
1234
|
+
case 'lt':
|
|
1235
|
+
case 'lte': {
|
|
1236
|
+
const operator = op === 'gt' ? '>' : op === 'gte' ? '>=' : op === 'lt' ? '<' : '<='
|
|
1237
|
+
return q.where(text, operator, value as Knex.Value)
|
|
1238
|
+
}
|
|
1239
|
+
default:
|
|
1240
|
+
return q
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1144
1244
|
private async queryCustomEntity<T = unknown>(entity: string, opts: QueryOptions = {}): Promise<QueryResult<T>> {
|
|
1145
1245
|
const knex = this.getKnex()
|
|
1146
1246
|
const alias = 'ce'
|
|
@@ -4,6 +4,7 @@ import { resolveTenantEncryptionService } from '@open-mercato/shared/lib/encrypt
|
|
|
4
4
|
import { decryptIndexDocForSearch, encryptIndexDocForStorage } from '@open-mercato/shared/lib/encryption/indexDoc'
|
|
5
5
|
import type { Knex } from 'knex'
|
|
6
6
|
import { replaceSearchTokensForRecord, deleteSearchTokensForRecord } from './search-tokens'
|
|
7
|
+
import { attachAggregateSearchField } from './document'
|
|
7
8
|
|
|
8
9
|
type BuildDocParams = {
|
|
9
10
|
entityType: string // '<module>:<entity>'
|
|
@@ -91,6 +92,7 @@ export async function buildIndexDoc(em: EntityManager, params: BuildDocParams):
|
|
|
91
92
|
} catch {}
|
|
92
93
|
|
|
93
94
|
try {
|
|
95
|
+
doc = attachAggregateSearchField(doc)
|
|
94
96
|
const encryption = resolveTenantEncryptionService(em as any)
|
|
95
97
|
doc = await encryptIndexDocForStorage(
|
|
96
98
|
params.entityType,
|
|
@@ -8,13 +8,12 @@ import { E } from '#generated/entities.ids.generated'
|
|
|
8
8
|
import * as F from '#generated/entities/dictionary_entry'
|
|
9
9
|
import { statusDictionaryCreateSchema, statusDictionaryUpdateSchema } from '../../data/validators'
|
|
10
10
|
import { getSalesDictionaryDefinition, ensureSalesDictionary, type SalesDictionaryKind } from '../../lib/dictionaries'
|
|
11
|
-
import { parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
11
|
+
import { buildAggregateSearchFilter, parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
12
12
|
import {
|
|
13
13
|
createPagedListResponseSchema,
|
|
14
14
|
createSalesCrudOpenApi,
|
|
15
15
|
defaultDeleteRequestSchema,
|
|
16
16
|
} from '../openapi'
|
|
17
|
-
import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
|
|
18
17
|
|
|
19
18
|
const rawBodySchema = z.object({}).passthrough()
|
|
20
19
|
|
|
@@ -143,13 +142,8 @@ const crud = makeCrudRoute({
|
|
|
143
142
|
const filters: Record<string, unknown> = {
|
|
144
143
|
dictionary_id: dictionaryId,
|
|
145
144
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
filters.$or = [
|
|
149
|
-
{ [F.value]: { $ilike: term } },
|
|
150
|
-
{ [F.label]: { $ilike: term } },
|
|
151
|
-
]
|
|
152
|
-
}
|
|
145
|
+
const searchFilter = buildAggregateSearchFilter(query.search)
|
|
146
|
+
if (searchFilter) Object.assign(filters, searchFilter)
|
|
153
147
|
return filters
|
|
154
148
|
},
|
|
155
149
|
transformItem: (item: any) => ({
|
|
@@ -5,7 +5,7 @@ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
|
5
5
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
6
6
|
import { SalesChannel } from '../../data/entities'
|
|
7
7
|
import { channelCreateSchema, channelUpdateSchema } from '../../data/validators'
|
|
8
|
-
import { parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
8
|
+
import { buildAggregateSearchFilter, parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
9
9
|
import { E } from '#generated/entities.ids.generated'
|
|
10
10
|
import * as F from '#generated/entities/sales_channel'
|
|
11
11
|
import {
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
defaultDeleteRequestSchema,
|
|
15
15
|
} from '../openapi'
|
|
16
16
|
import { CatalogOffer } from '@open-mercato/core/modules/catalog/data/entities'
|
|
17
|
-
import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
|
|
18
17
|
import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
|
|
19
18
|
|
|
20
19
|
const rawBodySchema = z.object({}).passthrough()
|
|
@@ -76,14 +75,8 @@ export function buildSearchFilters(query: z.infer<typeof listSchema>): Record<st
|
|
|
76
75
|
const ids = parseIdList(query.ids)
|
|
77
76
|
if (ids.length) filters.id = { $in: ids }
|
|
78
77
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
filters.$or = [
|
|
82
|
-
{ name: { $ilike: term } },
|
|
83
|
-
{ code: { $ilike: term } },
|
|
84
|
-
{ description: { $ilike: term } },
|
|
85
|
-
]
|
|
86
|
-
}
|
|
78
|
+
const searchFilter = buildAggregateSearchFilter(query.search)
|
|
79
|
+
if (searchFilter) Object.assign(filters, searchFilter)
|
|
87
80
|
const isActive = parseBooleanToken(query.isActive)
|
|
88
81
|
if (isActive !== null) filters.is_active = isActive
|
|
89
82
|
return filters
|
|
@@ -4,7 +4,7 @@ import { splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fi
|
|
|
4
4
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
5
5
|
import { SalesDeliveryWindow } from '../../data/entities'
|
|
6
6
|
import { deliveryWindowCreateSchema, deliveryWindowUpdateSchema } from '../../data/validators'
|
|
7
|
-
import { parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
7
|
+
import { buildAggregateSearchFilter, parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
8
8
|
import { E } from '#generated/entities.ids.generated'
|
|
9
9
|
import * as F from '#generated/entities/sales_delivery_window'
|
|
10
10
|
import {
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
createSalesCrudOpenApi,
|
|
13
13
|
defaultDeleteRequestSchema,
|
|
14
14
|
} from '../openapi'
|
|
15
|
-
import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
|
|
16
15
|
import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
|
|
17
16
|
|
|
18
17
|
const rawBodySchema = z.object({}).passthrough()
|
|
@@ -59,14 +58,8 @@ const deliveryWindowListResponseSchema = createPagedListResponseSchema(deliveryW
|
|
|
59
58
|
|
|
60
59
|
function buildFilters(query: z.infer<typeof listSchema>): Record<string, unknown> {
|
|
61
60
|
const filters: Record<string, unknown> = {}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
filters.$or = [
|
|
65
|
-
{ name: { $ilike: term } },
|
|
66
|
-
{ code: { $ilike: term } },
|
|
67
|
-
{ description: { $ilike: term } },
|
|
68
|
-
]
|
|
69
|
-
}
|
|
61
|
+
const searchFilter = buildAggregateSearchFilter(query.search)
|
|
62
|
+
if (searchFilter) Object.assign(filters, searchFilter)
|
|
70
63
|
const isActive = parseBooleanToken(query.isActive)
|
|
71
64
|
if (isActive !== null) filters.is_active = isActive
|
|
72
65
|
return filters
|
|
@@ -4,7 +4,7 @@ import { splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fi
|
|
|
4
4
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
5
5
|
import { SalesPaymentMethod } from '../../data/entities'
|
|
6
6
|
import { paymentMethodCreateSchema, paymentMethodUpdateSchema } from '../../data/validators'
|
|
7
|
-
import { parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
7
|
+
import { buildAggregateSearchFilter, parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
8
8
|
import { E } from '#generated/entities.ids.generated'
|
|
9
9
|
import * as F from '#generated/entities/sales_payment_method'
|
|
10
10
|
import {
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
createSalesCrudOpenApi,
|
|
13
13
|
defaultDeleteRequestSchema,
|
|
14
14
|
} from '../openapi'
|
|
15
|
-
import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
|
|
16
15
|
import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
|
|
17
16
|
|
|
18
17
|
const rawBodySchema = z.object({}).passthrough()
|
|
@@ -59,15 +58,8 @@ const paymentMethodListResponseSchema = createPagedListResponseSchema(paymentMet
|
|
|
59
58
|
|
|
60
59
|
function buildFilters(query: z.infer<typeof listSchema>): Record<string, unknown> {
|
|
61
60
|
const filters: Record<string, unknown> = {}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
filters.$or = [
|
|
65
|
-
{ name: { $ilike: term } },
|
|
66
|
-
{ code: { $ilike: term } },
|
|
67
|
-
{ provider_key: { $ilike: term } },
|
|
68
|
-
{ description: { $ilike: term } },
|
|
69
|
-
]
|
|
70
|
-
}
|
|
61
|
+
const searchFilter = buildAggregateSearchFilter(query.search)
|
|
62
|
+
if (searchFilter) Object.assign(filters, searchFilter)
|
|
71
63
|
const isActive = parseBooleanToken(query.isActive)
|
|
72
64
|
if (isActive !== null) filters.is_active = isActive
|
|
73
65
|
return filters
|
|
@@ -5,7 +5,7 @@ import { sanitizeSearchTerm, parseBooleanFlag } from '@open-mercato/core/modules
|
|
|
5
5
|
import { E } from '#generated/entities.ids.generated'
|
|
6
6
|
import * as F from '#generated/entities/catalog_price_kind'
|
|
7
7
|
import { createPagedListResponseSchema, createSalesCrudOpenApi } from '../openapi'
|
|
8
|
-
import {
|
|
8
|
+
import { buildAggregateSearchFilter } from '../utils'
|
|
9
9
|
|
|
10
10
|
const routeMetadata = {
|
|
11
11
|
GET: { requireAuth: true, requireFeatures: ['sales.channels.manage'] },
|
|
@@ -45,10 +45,8 @@ const crud = makeCrudRoute({
|
|
|
45
45
|
buildFilters: async (query) => {
|
|
46
46
|
const filters: Record<string, unknown> = {}
|
|
47
47
|
const term = sanitizeSearchTerm(query.search)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
filters.$or = [{ [F.code]: { $ilike: like } }, { [F.title]: { $ilike: like } }]
|
|
51
|
-
}
|
|
48
|
+
const searchFilter = buildAggregateSearchFilter(term)
|
|
49
|
+
if (searchFilter) Object.assign(filters, searchFilter)
|
|
52
50
|
const isActive = parseBooleanFlag(query.isActive)
|
|
53
51
|
if (isActive !== undefined) {
|
|
54
52
|
filters[F.is_active] = isActive
|
|
@@ -4,7 +4,7 @@ import { splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fi
|
|
|
4
4
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
5
5
|
import { SalesShippingMethod } from '../../data/entities'
|
|
6
6
|
import { shippingMethodCreateSchema, shippingMethodUpdateSchema } from '../../data/validators'
|
|
7
|
-
import { parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
7
|
+
import { buildAggregateSearchFilter, parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
8
8
|
import { E } from '#generated/entities.ids.generated'
|
|
9
9
|
import * as F from '#generated/entities/sales_shipping_method'
|
|
10
10
|
import {
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
createSalesCrudOpenApi,
|
|
13
13
|
defaultDeleteRequestSchema,
|
|
14
14
|
} from '../openapi'
|
|
15
|
-
import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
|
|
16
15
|
import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
|
|
17
16
|
|
|
18
17
|
const rawBodySchema = z.object({}).passthrough()
|
|
@@ -65,15 +64,8 @@ const shippingMethodListResponseSchema = createPagedListResponseSchema(shippingM
|
|
|
65
64
|
|
|
66
65
|
function buildFilters(query: z.infer<typeof listSchema>): Record<string, unknown> {
|
|
67
66
|
const filters: Record<string, unknown> = {}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
filters.$or = [
|
|
71
|
-
{ name: { $ilike: term } },
|
|
72
|
-
{ code: { $ilike: term } },
|
|
73
|
-
{ carrier_code: { $ilike: term } },
|
|
74
|
-
{ service_level: { $ilike: term } },
|
|
75
|
-
]
|
|
76
|
-
}
|
|
67
|
+
const searchFilter = buildAggregateSearchFilter(query.search)
|
|
68
|
+
if (searchFilter) Object.assign(filters, searchFilter)
|
|
77
69
|
if (query.currency && query.currency.trim().length > 0) {
|
|
78
70
|
filters.currency_code = query.currency.trim().toUpperCase()
|
|
79
71
|
}
|
|
@@ -4,10 +4,9 @@ import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
|
4
4
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
5
5
|
import { SalesDocumentTag } from '../../data/entities'
|
|
6
6
|
import { salesTagCreateSchema, salesTagUpdateSchema } from '../../data/validators'
|
|
7
|
-
import { withScopedPayload } from '../utils'
|
|
7
|
+
import { buildAggregateSearchFilter, withScopedPayload } from '../utils'
|
|
8
8
|
import { createPagedListResponseSchema, createSalesCrudOpenApi, defaultOkResponseSchema } from '../openapi'
|
|
9
9
|
import { slugifyTagLabel } from '@open-mercato/shared/lib/utils'
|
|
10
|
-
import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
|
|
11
10
|
|
|
12
11
|
const rawBodySchema = z.object({}).passthrough()
|
|
13
12
|
|
|
@@ -44,13 +43,8 @@ const crud = makeCrudRoute({
|
|
|
44
43
|
fields: ['id', 'slug', 'label', 'color', 'description', 'organization_id', 'tenant_id'],
|
|
45
44
|
buildFilters: async (query: any) => {
|
|
46
45
|
const filters: Record<string, any> = {}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
filters.$or = [
|
|
50
|
-
{ label: { $ilike: pattern } },
|
|
51
|
-
{ slug: { $ilike: pattern } },
|
|
52
|
-
]
|
|
53
|
-
}
|
|
46
|
+
const searchFilter = buildAggregateSearchFilter(query.search)
|
|
47
|
+
if (searchFilter) Object.assign(filters, searchFilter)
|
|
54
48
|
return filters
|
|
55
49
|
},
|
|
56
50
|
},
|
|
@@ -5,10 +5,9 @@ import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
|
5
5
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
6
6
|
import { SalesTaxRate } from '../../data/entities'
|
|
7
7
|
import { taxRateCreateSchema, taxRateUpdateSchema } from '../../data/validators'
|
|
8
|
-
import { parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
8
|
+
import { buildAggregateSearchFilter, parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
9
9
|
import { E } from '#generated/entities.ids.generated'
|
|
10
10
|
import * as F from '#generated/entities/sales_tax_rate'
|
|
11
|
-
import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
|
|
12
11
|
import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
|
|
13
12
|
|
|
14
13
|
const rawBodySchema = z.object({}).passthrough()
|
|
@@ -76,17 +75,8 @@ const taxRateDeleteSchema = z.object({
|
|
|
76
75
|
|
|
77
76
|
function buildFilters(query: z.infer<typeof listSchema>): Record<string, unknown> {
|
|
78
77
|
const filters: Record<string, unknown> = {}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
filters.$or = [
|
|
82
|
-
{ name: { $ilike: term } },
|
|
83
|
-
{ code: { $ilike: term } },
|
|
84
|
-
{ country_code: { $ilike: term } },
|
|
85
|
-
{ region_code: { $ilike: term } },
|
|
86
|
-
{ postal_code: { $ilike: term } },
|
|
87
|
-
{ city: { $ilike: term } },
|
|
88
|
-
]
|
|
89
|
-
}
|
|
78
|
+
const searchFilter = buildAggregateSearchFilter(query.search)
|
|
79
|
+
if (searchFilter) Object.assign(filters, searchFilter)
|
|
90
80
|
if (query.country && query.country.trim().length > 0) {
|
|
91
81
|
filters.country_code = query.country.trim().toUpperCase()
|
|
92
82
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createScopedApiHelpers } from '@open-mercato/shared/lib/api/scoped'
|
|
2
|
+
import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
|
|
2
3
|
|
|
3
4
|
const {
|
|
4
5
|
withScopedPayload,
|
|
@@ -14,3 +15,11 @@ const {
|
|
|
14
15
|
})
|
|
15
16
|
|
|
16
17
|
export { withScopedPayload, parseScopedCommandInput, requireRecordId, resolveCrudRecordId }
|
|
18
|
+
|
|
19
|
+
export function buildAggregateSearchFilter(search?: string | null): Record<string, unknown> | null {
|
|
20
|
+
const term = typeof search === 'string' ? search.trim() : ''
|
|
21
|
+
if (!term) return null
|
|
22
|
+
return {
|
|
23
|
+
search_text: { $ilike: `%${escapeLikePattern(term)}%` },
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -6,13 +6,12 @@ import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
|
6
6
|
import { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'
|
|
7
7
|
import { statusDictionaryCreateSchema, statusDictionaryUpdateSchema } from '../data/validators'
|
|
8
8
|
import { getSalesDictionaryDefinition, ensureSalesDictionary, type SalesDictionaryKind } from './dictionaries'
|
|
9
|
-
import { parseScopedCommandInput, resolveCrudRecordId } from '../api/utils'
|
|
9
|
+
import { buildAggregateSearchFilter, parseScopedCommandInput, resolveCrudRecordId } from '../api/utils'
|
|
10
10
|
import {
|
|
11
11
|
createPagedListResponseSchema,
|
|
12
12
|
createSalesCrudOpenApi,
|
|
13
13
|
defaultDeleteRequestSchema,
|
|
14
14
|
} from '../api/openapi'
|
|
15
|
-
import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
|
|
16
15
|
|
|
17
16
|
interface StatusDictionaryRouteConfig {
|
|
18
17
|
kind: SalesDictionaryKind
|
|
@@ -153,13 +152,8 @@ export function makeStatusDictionaryRoute(config: StatusDictionaryRouteConfig) {
|
|
|
153
152
|
const filters: Record<string, unknown> = {
|
|
154
153
|
dictionary_id: dictionaryId,
|
|
155
154
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
filters.$or = [
|
|
159
|
-
{ [F.value]: { $ilike: term } },
|
|
160
|
-
{ [F.label]: { $ilike: term } },
|
|
161
|
-
]
|
|
162
|
-
}
|
|
155
|
+
const searchFilter = buildAggregateSearchFilter(query.search)
|
|
156
|
+
if (searchFilter) Object.assign(filters, searchFilter)
|
|
163
157
|
return filters
|
|
164
158
|
},
|
|
165
159
|
transformItem: (item: Record<string, unknown>) => ({
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
updateWorkflowDefinitionInputSchema,
|
|
18
18
|
type UpdateWorkflowDefinitionApiInput,
|
|
19
19
|
} from '../../../data/validators'
|
|
20
|
+
import { serializeWorkflowDefinition } from '../serialize'
|
|
20
21
|
|
|
21
22
|
export const metadata = {
|
|
22
23
|
requireAuth: true,
|
|
@@ -66,7 +67,7 @@ export async function GET(
|
|
|
66
67
|
)
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
return NextResponse.json({ data: definition })
|
|
70
|
+
return NextResponse.json({ data: serializeWorkflowDefinition(definition) })
|
|
70
71
|
} catch (error) {
|
|
71
72
|
console.error('Error getting workflow definition:', error)
|
|
72
73
|
return NextResponse.json(
|
|
@@ -162,7 +163,7 @@ export async function PUT(
|
|
|
162
163
|
await em.flush()
|
|
163
164
|
|
|
164
165
|
return NextResponse.json({
|
|
165
|
-
data: definition,
|
|
166
|
+
data: serializeWorkflowDefinition(definition),
|
|
166
167
|
message: 'Workflow definition updated successfully',
|
|
167
168
|
})
|
|
168
169
|
} catch (error) {
|