@open-mercato/core 0.6.8-develop.6878.1.ab817e58ac → 0.6.8-develop.6881.1.8ea75912f2
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/modules/directory/commands/tenants.js +4 -7
- package/dist/modules/directory/commands/tenants.js.map +2 -2
- package/dist/modules/sales/lib/makeSalesLineRoute.js +8 -0
- package/dist/modules/sales/lib/makeSalesLineRoute.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/directory/commands/tenants.ts +3 -6
- package/src/modules/sales/lib/makeSalesLineRoute.ts +8 -0
|
@@ -71,7 +71,7 @@ const createTenantCommand = {
|
|
|
71
71
|
const identifiers = {
|
|
72
72
|
id: String(tenant.id),
|
|
73
73
|
organizationId: null,
|
|
74
|
-
tenantId:
|
|
74
|
+
tenantId: null
|
|
75
75
|
};
|
|
76
76
|
await emitCrudSideEffects({
|
|
77
77
|
dataEngine: de,
|
|
@@ -100,10 +100,7 @@ const createTenantCommand = {
|
|
|
100
100
|
entityClass: Tenant,
|
|
101
101
|
buildResult: (entity) => entity,
|
|
102
102
|
events: tenantCrudEvents,
|
|
103
|
-
indexer: tenantCrudIndexer
|
|
104
|
-
afterRestore: ({ entity }) => {
|
|
105
|
-
Reflect.set(entity, "tenantId", String(entity.id));
|
|
106
|
-
}
|
|
103
|
+
indexer: tenantCrudIndexer
|
|
107
104
|
})
|
|
108
105
|
};
|
|
109
106
|
const updateTenantCommand = {
|
|
@@ -141,7 +138,7 @@ const updateTenantCommand = {
|
|
|
141
138
|
const identifiers = {
|
|
142
139
|
id: String(tenant.id),
|
|
143
140
|
organizationId: null,
|
|
144
|
-
tenantId:
|
|
141
|
+
tenantId: null
|
|
145
142
|
};
|
|
146
143
|
await emitCrudSideEffects({
|
|
147
144
|
dataEngine: de,
|
|
@@ -191,7 +188,7 @@ const deleteTenantCommand = {
|
|
|
191
188
|
const identifiers = {
|
|
192
189
|
id: String(id),
|
|
193
190
|
organizationId: null,
|
|
194
|
-
tenantId:
|
|
191
|
+
tenantId: null
|
|
195
192
|
};
|
|
196
193
|
await emitCrudSideEffects({
|
|
197
194
|
dataEngine: de,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/directory/commands/tenants.ts"],
|
|
4
|
-
"sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { tenantCreateSchema, tenantUpdateSchema } from '@open-mercato/core/modules/directory/data/validators'\nimport { Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { requireSuperAdmin } from '@open-mercato/core/modules/auth/lib/tenantAccess'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n buildChanges,\n requireId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('directory').child({ component: 'tenants' })\n\nexport const tenantCrudEvents: CrudEventsConfig = {\n module: 'directory',\n entity: 'tenant',\n persistent: true,\n}\n\nexport const tenantCrudIndexer: CrudIndexerConfig = {\n entityType: E.directory.tenant,\n}\n\nfunction serializeTenant(entity: Tenant) {\n return {\n id: String(entity.id),\n name: entity.name,\n isActive: !!entity.isActive,\n createdAt: entity.createdAt ? entity.createdAt.toISOString() : null,\n updatedAt: entity.updatedAt ? entity.updatedAt.toISOString() : null,\n deletedAt: entity.deletedAt ? entity.deletedAt.toISOString() : null,\n }\n}\n\ntype TenantPayload = Record<string, unknown>\ntype SerializedTenant = ReturnType<typeof serializeTenant>\n\nconst createTenantCommand: CommandHandler<TenantPayload, Tenant> = {\n id: 'directory.tenants.create',\n async execute(rawInput, ctx) {\n await requireSuperAdmin(ctx)\n const { parsed, custom } = parseWithCustomFields(tenantCreateSchema, rawInput)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n\n const tenant = await de.createOrmEntity({\n entity: Tenant,\n data: {\n name: parsed.name,\n isActive: parsed.isActive ?? true,\n },\n })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.directory.tenant,\n recordId: String(tenant.id),\n organizationId: null,\n tenantId: ctx.auth?.tenantId ?? null,\n values: custom,\n })\n\n if (isTenantDataEncryptionEnabled()) {\n try {\n const kms = ctx.container.resolve('kmsService') as { createTenantDek?: (id: string) => Promise<unknown>; isHealthy?: () => boolean }\n if (kms?.isHealthy?.()) {\n await kms?.createTenantDek?.(String(tenant.id))\n logger.info('Created tenant DEK', { tenantId: String(tenant.id) })\n } else {\n logger.warn('KMS not healthy, skipping tenant DEK provisioning', { tenantId: String(tenant.id) })\n }\n } catch (err) {\n logger.warn('Failed to provision tenant key', { err })\n }\n }\n\n const identifiers = {\n id: String(tenant.id),\n organizationId: null,\n tenantId:
|
|
5
|
-
"mappings": "AACA,SAAS,uBAAuB;AAChC,SAAS,oBAAoB,0BAA0B;AACvD,SAAS,cAAc;AAGvB,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,SAAS;AAClB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AAEpC,SAAS,qCAAqC;AAC9C,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,WAAW,EAAE,MAAM,EAAE,WAAW,UAAU,CAAC;AAEhE,MAAM,mBAAqC;AAAA,EAChD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AACd;AAEO,MAAM,oBAAuC;AAAA,EAClD,YAAY,EAAE,UAAU;AAC1B;AAEA,SAAS,gBAAgB,QAAgB;AACvC,SAAO;AAAA,IACL,IAAI,OAAO,OAAO,EAAE;AAAA,IACpB,MAAM,OAAO;AAAA,IACb,UAAU,CAAC,CAAC,OAAO;AAAA,IACnB,WAAW,OAAO,YAAY,OAAO,UAAU,YAAY,IAAI;AAAA,IAC/D,WAAW,OAAO,YAAY,OAAO,UAAU,YAAY,IAAI;AAAA,IAC/D,WAAW,OAAO,YAAY,OAAO,UAAU,YAAY,IAAI;AAAA,EACjE;AACF;AAKA,MAAM,sBAA6D;AAAA,EACjE,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,kBAAkB,GAAG;AAC3B,UAAM,EAAE,QAAQ,OAAO,IAAI,sBAAsB,oBAAoB,QAAQ;AAC7E,UAAM,KAAM,IAAI,UAAU,QAAQ,YAAY;AAE9C,UAAM,SAAS,MAAM,GAAG,gBAAgB;AAAA,MACtC,QAAQ;AAAA,MACR,MAAM;AAAA,QACJ,MAAM,OAAO;AAAA,QACb,UAAU,OAAO,YAAY;AAAA,MAC/B;AAAA,IACF,CAAC;AAED,UAAM,qBAAqB;AAAA,MACzB,YAAY;AAAA,MACZ,UAAU,EAAE,UAAU;AAAA,MACtB,UAAU,OAAO,OAAO,EAAE;AAAA,MAC1B,gBAAgB;AAAA,MAChB,UAAU,IAAI,MAAM,YAAY;AAAA,MAChC,QAAQ;AAAA,IACV,CAAC;AAED,QAAI,8BAA8B,GAAG;AACnC,UAAI;AACF,cAAM,MAAM,IAAI,UAAU,QAAQ,YAAY;AAC9C,YAAI,KAAK,YAAY,GAAG;AACtB,gBAAM,KAAK,kBAAkB,OAAO,OAAO,EAAE,CAAC;AAC9C,iBAAO,KAAK,sBAAsB,EAAE,UAAU,OAAO,OAAO,EAAE,EAAE,CAAC;AAAA,QACnE,OAAO;AACL,iBAAO,KAAK,qDAAqD,EAAE,UAAU,OAAO,OAAO,EAAE,EAAE,CAAC;AAAA,QAClG;AAAA,MACF,SAAS,KAAK;AACZ,eAAO,KAAK,kCAAkC,EAAE,IAAI,CAAC;AAAA,MACvD;AAAA,IACF;AAEA,UAAM,cAAc;AAAA,MAClB,IAAI,OAAO,OAAO,EAAE;AAAA,MACpB,gBAAgB;AAAA,MAChB,UAAU
|
|
4
|
+
"sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { tenantCreateSchema, tenantUpdateSchema } from '@open-mercato/core/modules/directory/data/validators'\nimport { Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { requireSuperAdmin } from '@open-mercato/core/modules/auth/lib/tenantAccess'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n buildChanges,\n requireId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('directory').child({ component: 'tenants' })\n\nexport const tenantCrudEvents: CrudEventsConfig = {\n module: 'directory',\n entity: 'tenant',\n persistent: true,\n}\n\nexport const tenantCrudIndexer: CrudIndexerConfig = {\n entityType: E.directory.tenant,\n}\n\nfunction serializeTenant(entity: Tenant) {\n return {\n id: String(entity.id),\n name: entity.name,\n isActive: !!entity.isActive,\n createdAt: entity.createdAt ? entity.createdAt.toISOString() : null,\n updatedAt: entity.updatedAt ? entity.updatedAt.toISOString() : null,\n deletedAt: entity.deletedAt ? entity.deletedAt.toISOString() : null,\n }\n}\n\ntype TenantPayload = Record<string, unknown>\ntype SerializedTenant = ReturnType<typeof serializeTenant>\n\nconst createTenantCommand: CommandHandler<TenantPayload, Tenant> = {\n id: 'directory.tenants.create',\n async execute(rawInput, ctx) {\n await requireSuperAdmin(ctx)\n const { parsed, custom } = parseWithCustomFields(tenantCreateSchema, rawInput)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n\n const tenant = await de.createOrmEntity({\n entity: Tenant,\n data: {\n name: parsed.name,\n isActive: parsed.isActive ?? true,\n },\n })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.directory.tenant,\n recordId: String(tenant.id),\n organizationId: null,\n tenantId: ctx.auth?.tenantId ?? null,\n values: custom,\n })\n\n if (isTenantDataEncryptionEnabled()) {\n try {\n const kms = ctx.container.resolve('kmsService') as { createTenantDek?: (id: string) => Promise<unknown>; isHealthy?: () => boolean }\n if (kms?.isHealthy?.()) {\n await kms?.createTenantDek?.(String(tenant.id))\n logger.info('Created tenant DEK', { tenantId: String(tenant.id) })\n } else {\n logger.warn('KMS not healthy, skipping tenant DEK provisioning', { tenantId: String(tenant.id) })\n }\n } catch (err) {\n logger.warn('Failed to provision tenant key', { err })\n }\n }\n\n const identifiers = {\n id: String(tenant.id),\n organizationId: null,\n tenantId: null,\n }\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: tenant,\n identifiers,\n events: tenantCrudEvents,\n indexer: tenantCrudIndexer,\n })\n\n return tenant\n },\n captureAfter: (_input, result) => serializeTenant(result),\n buildLog: async ({ snapshots, result, ctx }) => {\n const { translate } = await resolveTranslations()\n const after = (snapshots.after as SerializedTenant | undefined) ?? serializeTenant(result)\n return {\n actionLabel: translate('directory.audit.tenants.create', 'Create tenant'),\n resourceKind: 'directory.tenant',\n resourceId: String(result.id),\n tenantId: ctx.auth?.tenantId ?? null,\n snapshotAfter: after,\n payload: { undo: { after } },\n }\n },\n redo: makeCreateRedo<Tenant, SerializedTenant, TenantPayload, Tenant>({\n entityClass: Tenant,\n buildResult: (entity) => entity,\n events: tenantCrudEvents,\n indexer: tenantCrudIndexer,\n }),\n}\n\nconst updateTenantCommand: CommandHandler<TenantPayload, Tenant> = {\n id: 'directory.tenants.update',\n async prepare(rawInput, ctx) {\n await requireSuperAdmin(ctx)\n const { parsed } = parseWithCustomFields(tenantUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const current = await em.findOne(Tenant, { id: parsed.id, deletedAt: null })\n if (!current) throw new CrudHttpError(404, { error: 'Tenant not found' })\n return { before: serializeTenant(current) }\n },\n async execute(rawInput, ctx) {\n await requireSuperAdmin(ctx)\n const { parsed, custom } = parseWithCustomFields(tenantUpdateSchema, rawInput)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const tenant = await de.updateOrmEntity({\n entity: Tenant,\n where: { id: parsed.id, deletedAt: null } as FilterQuery<Tenant>,\n apply: (entity) => {\n if (parsed.name !== undefined) entity.name = parsed.name\n if (parsed.isActive !== undefined) entity.isActive = parsed.isActive\n entity.updatedAt = new Date()\n },\n })\n if (!tenant) throw new CrudHttpError(404, { error: 'Tenant not found' })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.directory.tenant,\n recordId: String(tenant.id),\n organizationId: null,\n tenantId: ctx.auth?.tenantId ?? null,\n values: custom,\n })\n\n const identifiers = {\n id: String(tenant.id),\n organizationId: null,\n tenantId: null,\n }\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: tenant,\n identifiers,\n events: tenantCrudEvents,\n indexer: tenantCrudIndexer,\n })\n\n return tenant\n },\n captureAfter: (_input, result) => serializeTenant(result),\n buildLog: async ({ result, snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const beforeRecord = (snapshots.before ?? null) as Record<string, unknown> | null\n const after = serializeTenant(result)\n const changes = buildChanges(beforeRecord, after as Record<string, unknown>, ['name', 'isActive'])\n return {\n actionLabel: translate('directory.audit.tenants.update', 'Update tenant'),\n resourceKind: 'directory.tenant',\n resourceId: String(result.id),\n changes,\n tenantId: ctx.auth?.tenantId ?? null,\n }\n },\n}\n\nconst deleteTenantCommand: CommandHandler<{ body: any; query: Record<string, string> }, Tenant> = {\n id: 'directory.tenants.delete',\n async prepare(input, ctx) {\n await requireSuperAdmin(ctx)\n const id = requireId(input, 'Tenant id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const existing = await em.findOne(Tenant, { id, deletedAt: null })\n return existing ? { before: serializeTenant(existing) } : {}\n },\n async execute(rawInput, ctx) {\n await requireSuperAdmin(ctx)\n const id = requireId(rawInput, 'Tenant id required')\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const tenant = await de.deleteOrmEntity({\n entity: Tenant,\n where: { id, deletedAt: null } as FilterQuery<Tenant>,\n soft: true,\n softDeleteField: 'deletedAt',\n })\n if (!tenant) throw new CrudHttpError(404, { error: 'Tenant not found' })\n\n const identifiers = {\n id: String(id),\n organizationId: null,\n tenantId: null,\n }\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: tenant,\n identifiers,\n events: tenantCrudEvents,\n indexer: tenantCrudIndexer,\n })\n\n return tenant\n },\n buildLog: async ({ snapshots, input, ctx }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshot = (snapshots.before ?? null) as SerializedTenant | null\n const id = String(input?.body?.id ?? input?.query?.id ?? '')\n const fallbackId = beforeSnapshot?.id ?? null\n return {\n actionLabel: translate('directory.audit.tenants.delete', 'Delete tenant'),\n resourceKind: 'directory.tenant',\n resourceId: id || fallbackId || null,\n snapshotBefore: beforeSnapshot ?? null,\n tenantId: ctx.auth?.tenantId ?? null,\n }\n },\n}\n\nregisterCommand(createTenantCommand)\nregisterCommand(updateTenantCommand)\nregisterCommand(deleteTenantCommand)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -165,10 +165,18 @@ function makeSalesLineRoute(config) {
|
|
|
165
165
|
return fields;
|
|
166
166
|
})(),
|
|
167
167
|
sortFieldMap: {
|
|
168
|
+
id: F.id,
|
|
168
169
|
createdAt: F.created_at,
|
|
169
170
|
updatedAt: F.updated_at,
|
|
170
171
|
lineNumber: F.line_number
|
|
171
172
|
},
|
|
173
|
+
// Line ids are random v4 UUIDs, so the factory's `id` default orders a
|
|
174
|
+
// document's lines arbitrarily and reshuffles them whenever an integration
|
|
175
|
+
// rewrites the lines (delete-and-reinsert is the normal upsert shape).
|
|
176
|
+
// `line_number` is the document's real reading order; `id` only breaks ties
|
|
177
|
+
// between legacy rows that all still sit at the column default of 0.
|
|
178
|
+
defaultSort: { field: "lineNumber", dir: "asc" },
|
|
179
|
+
tiebreakSortField: "id",
|
|
172
180
|
buildFilters: async (query, ctx) => {
|
|
173
181
|
const filters = {};
|
|
174
182
|
if (query.id) filters.id = { $eq: query.id };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/sales/lib/makeSalesLineRoute.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/core'\nimport { makeCrudRoute, type CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n buildCustomFieldFiltersFromQuery,\n extractAllCustomFieldEntries,\n} from '@open-mercato/shared/lib/crud/custom-fields'\nimport {\n canonicalizeUnitCode,\n REFERENCE_UNIT_CODES,\n} from '@open-mercato/shared/lib/units/unitCodes'\nimport {\n createPagedListResponseSchema,\n createSalesCrudOpenApi,\n defaultOkResponseSchema,\n} from '../api/openapi'\nimport { withScopedPayload } from '../api/utils'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- MikroORM entity class constructor\ntype EntityClass = new (...args: any[]) => unknown\n\ninterface SalesLineRouteConfig {\n entity: EntityClass\n entityId: string\n fieldConstants: Record<string, string>\n parentFkColumn: string\n parentFkParam: string\n createSchema: z.ZodObject<z.ZodRawShape>\n features: { view: string; manage: string }\n commandPrefix: string\n openApi: {\n resourceName: string\n description: string\n }\n}\n\nconst rawBodySchema = z.object({}).passthrough()\n\nfunction resolveRawBody(raw: unknown): Record<string, unknown> {\n if (!raw || typeof raw !== 'object') return {}\n if ('body' in raw) {\n const payload = raw as { body?: unknown }\n if (payload.body && typeof payload.body === 'object') {\n return payload.body as Record<string, unknown>\n }\n }\n return raw as Record<string, unknown>\n}\n\nfunction transformItem(item: Record<string, unknown> | null | undefined) {\n if (!item) return item\n const normalized = { ...item }\n const cfEntries = extractAllCustomFieldEntries(item)\n for (const key of Object.keys(normalized)) {\n if (key.startsWith('cf:')) delete normalized[key]\n }\n const quantityUnit = canonicalizeUnitCode(\n normalized['quantity_unit'] ?? normalized['quantityUnit'],\n )\n const normalizedUnit =\n canonicalizeUnitCode(\n normalized['normalized_unit'] ?? normalized['normalizedUnit'],\n ) ?? quantityUnit\n return {\n ...normalized,\n quantity_unit: quantityUnit,\n normalized_unit: normalizedUnit,\n ...cfEntries,\n }\n}\n\nconst uomSnapshotOpenApiSchema = z\n .object({\n version: z.literal(1),\n productId: z.string().nullable(),\n productVariantId: z.string().nullable(),\n baseUnitCode: z.string().nullable(),\n enteredUnitCode: z.string().nullable(),\n enteredQuantity: z.string(),\n toBaseFactor: z.string(),\n normalizedQuantity: z.string(),\n rounding: z.object({\n mode: z.enum(['half_up', 'down', 'up']),\n scale: z.number().int(),\n }),\n source: z.object({\n conversionId: z.string().nullable(),\n resolvedAt: z.string(),\n }),\n unitPriceReference: z\n .object({\n enabled: z.boolean(),\n referenceUnitCode: z.enum(REFERENCE_UNIT_CODES).nullable(),\n baseQuantity: z.string().nullable(),\n grossPerReference: z.string().nullable().optional(),\n netPerReference: z.string().nullable().optional(),\n })\n .optional(),\n })\n .nullable()\n .optional()\n\nexport function makeSalesLineRoute(config: SalesLineRouteConfig) {\n const {\n entity,\n entityId,\n fieldConstants: F,\n parentFkColumn,\n parentFkParam,\n createSchema,\n features,\n commandPrefix,\n } = config\n\n const listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n id: z.string().uuid().optional(),\n [parentFkParam]: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\n const upsertSchema = createSchema.extend({\n id: z.string().uuid().optional(),\n })\n\n const deleteSchema = z.object({\n id: z.string().uuid(),\n [parentFkParam]: z.string().uuid(),\n })\n\n const routeMetadata = {\n GET: { requireAuth: true, requireFeatures: [features.view] },\n POST: { requireAuth: true, requireFeatures: [features.manage] },\n PUT: { requireAuth: true, requireFeatures: [features.manage] },\n DELETE: { requireAuth: true, requireFeatures: [features.manage] },\n }\n\n const crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: {\n entityType: entityId,\n },\n enrichers: {\n entityId,\n },\n list: {\n schema: listSchema,\n entityId,\n fields: (() => {\n const fields: string[] = [\n F.id,\n parentFkColumn,\n F.line_number,\n F.kind,\n F.status_entry_id,\n F.status,\n F.product_id,\n F.product_variant_id,\n F.catalog_snapshot,\n F.name,\n F.description,\n F.comment,\n F.organization_id,\n F.tenant_id,\n F.quantity,\n F.quantity_unit,\n F.normalized_quantity,\n F.normalized_unit,\n F.uom_snapshot,\n F.currency_code,\n F.unit_price_net,\n F.unit_price_gross,\n F.discount_amount,\n F.discount_percent,\n F.tax_rate,\n F.tax_amount,\n F.total_net_amount,\n F.total_gross_amount,\n F.configuration,\n F.promotion_code,\n F.promotion_snapshot,\n F.metadata,\n F.custom_field_set_id,\n F.created_at,\n F.updated_at,\n ]\n const returnedQuantity = F['returned_quantity']\n if (typeof returnedQuantity === 'string') fields.push(returnedQuantity)\n return fields\n })(),\n sortFieldMap: {\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n lineNumber: F.line_number,\n },\n buildFilters: async (query: Record<string, unknown>, ctx: CrudCtx) => {\n const filters: Record<string, unknown> = {}\n if (query.id) filters.id = { $eq: query.id }\n if (query[parentFkParam]) filters[parentFkColumn] = { $eq: query[parentFkParam] }\n try {\n const em = ctx.container.resolve<EntityManager>('em')\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityId,\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n })\n Object.assign(filters, cfFilters)\n } catch {\n // ignore\n }\n return filters\n },\n transformItem,\n },\n actions: {\n create: {\n commandId: `${commandPrefix}.upsert`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n return { body: payload }\n },\n response: ({ result }: { result: Record<string, unknown> | null }) => ({\n id: result?.lineId ?? null,\n [parentFkParam]: result?.[parentFkParam] ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: `${commandPrefix}.upsert`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n return { body: payload }\n },\n response: ({ result }: { result: Record<string, unknown> | null }) => ({\n id: result?.lineId ?? null,\n [parentFkParam]: result?.[parentFkParam] ?? null,\n }),\n },\n delete: {\n commandId: `${commandPrefix}.delete`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = deleteSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n if (!payload.id || !payload[parentFkParam]) {\n throw new CrudHttpError(400, {\n error: translate(\n 'sales.documents.detail.error',\n 'Document not found or inaccessible.',\n ),\n })\n }\n return { body: payload }\n },\n response: () => ({ ok: true }),\n },\n },\n })\n\n const lineItemSchema = z.object({\n id: z.string().uuid(),\n [parentFkColumn]: z.string().uuid(),\n line_number: z.number(),\n kind: z.string(),\n status_entry_id: z.string().uuid().nullable().optional(),\n status: z.string().nullable().optional(),\n product_id: z.string().uuid().nullable().optional(),\n product_variant_id: z.string().uuid().nullable().optional(),\n catalog_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n name: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n comment: z.string().nullable().optional(),\n quantity: z.number(),\n quantity_unit: z.string().nullable().optional(),\n normalized_quantity: z.number(),\n normalized_unit: z.string().nullable().optional(),\n uom_snapshot: uomSnapshotOpenApiSchema,\n currency_code: z.string(),\n unit_price_net: z.number(),\n unit_price_gross: z.number(),\n discount_amount: z.number(),\n discount_percent: z.number(),\n tax_rate: z.number(),\n tax_amount: z.number(),\n total_net_amount: z.number(),\n total_gross_amount: z.number(),\n configuration: z.record(z.string(), z.unknown()).nullable().optional(),\n promotion_code: z.string().nullable().optional(),\n promotion_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_field_set_id: z.string().uuid().nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n })\n\n const upsertResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n [parentFkParam]: z.string().uuid().nullable(),\n })\n\n const openApi = createSalesCrudOpenApi({\n resourceName: config.openApi.resourceName,\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(lineItemSchema),\n create: {\n schema: upsertSchema,\n responseSchema: upsertResponseSchema,\n description: `Creates ${config.openApi.description}.`,\n },\n update: {\n schema: upsertSchema,\n responseSchema: upsertResponseSchema,\n description: `Updates ${config.openApi.description}.`,\n },\n del: {\n schema: deleteSchema,\n responseSchema: defaultOkResponseSchema,\n description: `Deletes ${config.openApi.description}.`,\n },\n })\n\n return {\n metadata: routeMetadata,\n openApi,\n GET: crud.GET,\n POST: crud.POST,\n PUT: crud.PUT,\n DELETE: crud.DELETE,\n }\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/core'\nimport { makeCrudRoute, type CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n buildCustomFieldFiltersFromQuery,\n extractAllCustomFieldEntries,\n} from '@open-mercato/shared/lib/crud/custom-fields'\nimport {\n canonicalizeUnitCode,\n REFERENCE_UNIT_CODES,\n} from '@open-mercato/shared/lib/units/unitCodes'\nimport {\n createPagedListResponseSchema,\n createSalesCrudOpenApi,\n defaultOkResponseSchema,\n} from '../api/openapi'\nimport { withScopedPayload } from '../api/utils'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- MikroORM entity class constructor\ntype EntityClass = new (...args: any[]) => unknown\n\ninterface SalesLineRouteConfig {\n entity: EntityClass\n entityId: string\n fieldConstants: Record<string, string>\n parentFkColumn: string\n parentFkParam: string\n createSchema: z.ZodObject<z.ZodRawShape>\n features: { view: string; manage: string }\n commandPrefix: string\n openApi: {\n resourceName: string\n description: string\n }\n}\n\nconst rawBodySchema = z.object({}).passthrough()\n\nfunction resolveRawBody(raw: unknown): Record<string, unknown> {\n if (!raw || typeof raw !== 'object') return {}\n if ('body' in raw) {\n const payload = raw as { body?: unknown }\n if (payload.body && typeof payload.body === 'object') {\n return payload.body as Record<string, unknown>\n }\n }\n return raw as Record<string, unknown>\n}\n\nfunction transformItem(item: Record<string, unknown> | null | undefined) {\n if (!item) return item\n const normalized = { ...item }\n const cfEntries = extractAllCustomFieldEntries(item)\n for (const key of Object.keys(normalized)) {\n if (key.startsWith('cf:')) delete normalized[key]\n }\n const quantityUnit = canonicalizeUnitCode(\n normalized['quantity_unit'] ?? normalized['quantityUnit'],\n )\n const normalizedUnit =\n canonicalizeUnitCode(\n normalized['normalized_unit'] ?? normalized['normalizedUnit'],\n ) ?? quantityUnit\n return {\n ...normalized,\n quantity_unit: quantityUnit,\n normalized_unit: normalizedUnit,\n ...cfEntries,\n }\n}\n\nconst uomSnapshotOpenApiSchema = z\n .object({\n version: z.literal(1),\n productId: z.string().nullable(),\n productVariantId: z.string().nullable(),\n baseUnitCode: z.string().nullable(),\n enteredUnitCode: z.string().nullable(),\n enteredQuantity: z.string(),\n toBaseFactor: z.string(),\n normalizedQuantity: z.string(),\n rounding: z.object({\n mode: z.enum(['half_up', 'down', 'up']),\n scale: z.number().int(),\n }),\n source: z.object({\n conversionId: z.string().nullable(),\n resolvedAt: z.string(),\n }),\n unitPriceReference: z\n .object({\n enabled: z.boolean(),\n referenceUnitCode: z.enum(REFERENCE_UNIT_CODES).nullable(),\n baseQuantity: z.string().nullable(),\n grossPerReference: z.string().nullable().optional(),\n netPerReference: z.string().nullable().optional(),\n })\n .optional(),\n })\n .nullable()\n .optional()\n\nexport function makeSalesLineRoute(config: SalesLineRouteConfig) {\n const {\n entity,\n entityId,\n fieldConstants: F,\n parentFkColumn,\n parentFkParam,\n createSchema,\n features,\n commandPrefix,\n } = config\n\n const listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n id: z.string().uuid().optional(),\n [parentFkParam]: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\n const upsertSchema = createSchema.extend({\n id: z.string().uuid().optional(),\n })\n\n const deleteSchema = z.object({\n id: z.string().uuid(),\n [parentFkParam]: z.string().uuid(),\n })\n\n const routeMetadata = {\n GET: { requireAuth: true, requireFeatures: [features.view] },\n POST: { requireAuth: true, requireFeatures: [features.manage] },\n PUT: { requireAuth: true, requireFeatures: [features.manage] },\n DELETE: { requireAuth: true, requireFeatures: [features.manage] },\n }\n\n const crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: {\n entityType: entityId,\n },\n enrichers: {\n entityId,\n },\n list: {\n schema: listSchema,\n entityId,\n fields: (() => {\n const fields: string[] = [\n F.id,\n parentFkColumn,\n F.line_number,\n F.kind,\n F.status_entry_id,\n F.status,\n F.product_id,\n F.product_variant_id,\n F.catalog_snapshot,\n F.name,\n F.description,\n F.comment,\n F.organization_id,\n F.tenant_id,\n F.quantity,\n F.quantity_unit,\n F.normalized_quantity,\n F.normalized_unit,\n F.uom_snapshot,\n F.currency_code,\n F.unit_price_net,\n F.unit_price_gross,\n F.discount_amount,\n F.discount_percent,\n F.tax_rate,\n F.tax_amount,\n F.total_net_amount,\n F.total_gross_amount,\n F.configuration,\n F.promotion_code,\n F.promotion_snapshot,\n F.metadata,\n F.custom_field_set_id,\n F.created_at,\n F.updated_at,\n ]\n const returnedQuantity = F['returned_quantity']\n if (typeof returnedQuantity === 'string') fields.push(returnedQuantity)\n return fields\n })(),\n sortFieldMap: {\n id: F.id,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n lineNumber: F.line_number,\n },\n // Line ids are random v4 UUIDs, so the factory's `id` default orders a\n // document's lines arbitrarily and reshuffles them whenever an integration\n // rewrites the lines (delete-and-reinsert is the normal upsert shape).\n // `line_number` is the document's real reading order; `id` only breaks ties\n // between legacy rows that all still sit at the column default of 0.\n defaultSort: { field: 'lineNumber', dir: 'asc' },\n tiebreakSortField: 'id',\n buildFilters: async (query: Record<string, unknown>, ctx: CrudCtx) => {\n const filters: Record<string, unknown> = {}\n if (query.id) filters.id = { $eq: query.id }\n if (query[parentFkParam]) filters[parentFkColumn] = { $eq: query[parentFkParam] }\n try {\n const em = ctx.container.resolve<EntityManager>('em')\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityId,\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n })\n Object.assign(filters, cfFilters)\n } catch {\n // ignore\n }\n return filters\n },\n transformItem,\n },\n actions: {\n create: {\n commandId: `${commandPrefix}.upsert`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n return { body: payload }\n },\n response: ({ result }: { result: Record<string, unknown> | null }) => ({\n id: result?.lineId ?? null,\n [parentFkParam]: result?.[parentFkParam] ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: `${commandPrefix}.upsert`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n return { body: payload }\n },\n response: ({ result }: { result: Record<string, unknown> | null }) => ({\n id: result?.lineId ?? null,\n [parentFkParam]: result?.[parentFkParam] ?? null,\n }),\n },\n delete: {\n commandId: `${commandPrefix}.delete`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = deleteSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n if (!payload.id || !payload[parentFkParam]) {\n throw new CrudHttpError(400, {\n error: translate(\n 'sales.documents.detail.error',\n 'Document not found or inaccessible.',\n ),\n })\n }\n return { body: payload }\n },\n response: () => ({ ok: true }),\n },\n },\n })\n\n const lineItemSchema = z.object({\n id: z.string().uuid(),\n [parentFkColumn]: z.string().uuid(),\n line_number: z.number(),\n kind: z.string(),\n status_entry_id: z.string().uuid().nullable().optional(),\n status: z.string().nullable().optional(),\n product_id: z.string().uuid().nullable().optional(),\n product_variant_id: z.string().uuid().nullable().optional(),\n catalog_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n name: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n comment: z.string().nullable().optional(),\n quantity: z.number(),\n quantity_unit: z.string().nullable().optional(),\n normalized_quantity: z.number(),\n normalized_unit: z.string().nullable().optional(),\n uom_snapshot: uomSnapshotOpenApiSchema,\n currency_code: z.string(),\n unit_price_net: z.number(),\n unit_price_gross: z.number(),\n discount_amount: z.number(),\n discount_percent: z.number(),\n tax_rate: z.number(),\n tax_amount: z.number(),\n total_net_amount: z.number(),\n total_gross_amount: z.number(),\n configuration: z.record(z.string(), z.unknown()).nullable().optional(),\n promotion_code: z.string().nullable().optional(),\n promotion_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_field_set_id: z.string().uuid().nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n })\n\n const upsertResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n [parentFkParam]: z.string().uuid().nullable(),\n })\n\n const openApi = createSalesCrudOpenApi({\n resourceName: config.openApi.resourceName,\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(lineItemSchema),\n create: {\n schema: upsertSchema,\n responseSchema: upsertResponseSchema,\n description: `Creates ${config.openApi.description}.`,\n },\n update: {\n schema: upsertSchema,\n responseSchema: upsertResponseSchema,\n description: `Updates ${config.openApi.description}.`,\n },\n del: {\n schema: deleteSchema,\n responseSchema: defaultOkResponseSchema,\n description: `Deletes ${config.openApi.description}.`,\n },\n })\n\n return {\n metadata: routeMetadata,\n openApi,\n GET: crud.GET,\n POST: crud.POST,\n PUT: crud.PUT,\n DELETE: crud.DELETE,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.8-develop.
|
|
3
|
+
"version": "0.6.8-develop.6881.1.8ea75912f2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -254,16 +254,16 @@
|
|
|
254
254
|
"zod": "^4.4.3"
|
|
255
255
|
},
|
|
256
256
|
"peerDependencies": {
|
|
257
|
-
"@open-mercato/ai-assistant": "0.6.8-develop.
|
|
258
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
259
|
-
"@open-mercato/ui": "0.6.8-develop.
|
|
257
|
+
"@open-mercato/ai-assistant": "0.6.8-develop.6881.1.8ea75912f2",
|
|
258
|
+
"@open-mercato/shared": "0.6.8-develop.6881.1.8ea75912f2",
|
|
259
|
+
"@open-mercato/ui": "0.6.8-develop.6881.1.8ea75912f2",
|
|
260
260
|
"react": "^19.0.0",
|
|
261
261
|
"react-dom": "^19.0.0"
|
|
262
262
|
},
|
|
263
263
|
"devDependencies": {
|
|
264
|
-
"@open-mercato/ai-assistant": "0.6.8-develop.
|
|
265
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
266
|
-
"@open-mercato/ui": "0.6.8-develop.
|
|
264
|
+
"@open-mercato/ai-assistant": "0.6.8-develop.6881.1.8ea75912f2",
|
|
265
|
+
"@open-mercato/shared": "0.6.8-develop.6881.1.8ea75912f2",
|
|
266
|
+
"@open-mercato/ui": "0.6.8-develop.6881.1.8ea75912f2",
|
|
267
267
|
"@testing-library/dom": "^10.4.1",
|
|
268
268
|
"@testing-library/jest-dom": "^7.0.0",
|
|
269
269
|
"@testing-library/react": "^16.3.1",
|
|
@@ -87,7 +87,7 @@ const createTenantCommand: CommandHandler<TenantPayload, Tenant> = {
|
|
|
87
87
|
const identifiers = {
|
|
88
88
|
id: String(tenant.id),
|
|
89
89
|
organizationId: null,
|
|
90
|
-
tenantId:
|
|
90
|
+
tenantId: null,
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
await emitCrudSideEffects({
|
|
@@ -119,9 +119,6 @@ const createTenantCommand: CommandHandler<TenantPayload, Tenant> = {
|
|
|
119
119
|
buildResult: (entity) => entity,
|
|
120
120
|
events: tenantCrudEvents,
|
|
121
121
|
indexer: tenantCrudIndexer,
|
|
122
|
-
afterRestore: ({ entity }) => {
|
|
123
|
-
Reflect.set(entity, 'tenantId', String(entity.id))
|
|
124
|
-
},
|
|
125
122
|
}),
|
|
126
123
|
}
|
|
127
124
|
|
|
@@ -162,7 +159,7 @@ const updateTenantCommand: CommandHandler<TenantPayload, Tenant> = {
|
|
|
162
159
|
const identifiers = {
|
|
163
160
|
id: String(tenant.id),
|
|
164
161
|
organizationId: null,
|
|
165
|
-
tenantId:
|
|
162
|
+
tenantId: null,
|
|
166
163
|
}
|
|
167
164
|
|
|
168
165
|
await emitCrudSideEffects({
|
|
@@ -216,7 +213,7 @@ const deleteTenantCommand: CommandHandler<{ body: any; query: Record<string, str
|
|
|
216
213
|
const identifiers = {
|
|
217
214
|
id: String(id),
|
|
218
215
|
organizationId: null,
|
|
219
|
-
tenantId:
|
|
216
|
+
tenantId: null,
|
|
220
217
|
}
|
|
221
218
|
|
|
222
219
|
await emitCrudSideEffects({
|
|
@@ -202,10 +202,18 @@ export function makeSalesLineRoute(config: SalesLineRouteConfig) {
|
|
|
202
202
|
return fields
|
|
203
203
|
})(),
|
|
204
204
|
sortFieldMap: {
|
|
205
|
+
id: F.id,
|
|
205
206
|
createdAt: F.created_at,
|
|
206
207
|
updatedAt: F.updated_at,
|
|
207
208
|
lineNumber: F.line_number,
|
|
208
209
|
},
|
|
210
|
+
// Line ids are random v4 UUIDs, so the factory's `id` default orders a
|
|
211
|
+
// document's lines arbitrarily and reshuffles them whenever an integration
|
|
212
|
+
// rewrites the lines (delete-and-reinsert is the normal upsert shape).
|
|
213
|
+
// `line_number` is the document's real reading order; `id` only breaks ties
|
|
214
|
+
// between legacy rows that all still sit at the column default of 0.
|
|
215
|
+
defaultSort: { field: 'lineNumber', dir: 'asc' },
|
|
216
|
+
tiebreakSortField: 'id',
|
|
209
217
|
buildFilters: async (query: Record<string, unknown>, ctx: CrudCtx) => {
|
|
210
218
|
const filters: Record<string, unknown> = {}
|
|
211
219
|
if (query.id) filters.id = { $eq: query.id }
|