@open-mercato/core 0.6.7-develop.6552.1.5208a19369 → 0.6.7-develop.6565.1.1026d85fff

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/attachments/lib/reconcileOrganization.js +9 -7
  3. package/dist/modules/attachments/lib/reconcileOrganization.js.map +2 -2
  4. package/dist/modules/customers/api/companies/[id]/people/route.js +2 -2
  5. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  6. package/dist/modules/customers/api/deals/[id]/companies/route.js +2 -2
  7. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  8. package/dist/modules/customers/api/deals/[id]/people/route.js +2 -2
  9. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  10. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +4 -4
  11. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
  12. package/dist/modules/customers/api/entity-roles-factory.js +3 -3
  13. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  14. package/dist/modules/customers/api/labels/assign/route.js +2 -2
  15. package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
  16. package/dist/modules/customers/api/labels/unassign/route.js +2 -2
  17. package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
  18. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +3 -7
  19. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
  20. package/dist/modules/customers/api/people/[id]/companies/context.js +3 -3
  21. package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
  22. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +2 -2
  23. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  24. package/dist/modules/customers/api/todos/route.js +2 -2
  25. package/dist/modules/customers/api/todos/route.js.map +2 -2
  26. package/dist/modules/customers/commands/addresses.js +3 -3
  27. package/dist/modules/customers/commands/addresses.js.map +2 -2
  28. package/dist/modules/customers/commands/comments.js +3 -3
  29. package/dist/modules/customers/commands/comments.js.map +2 -2
  30. package/dist/modules/customers/commands/companies.js +2 -2
  31. package/dist/modules/customers/commands/companies.js.map +2 -2
  32. package/dist/modules/customers/commands/deals.js +3 -3
  33. package/dist/modules/customers/commands/deals.js.map +2 -2
  34. package/dist/modules/customers/commands/dictionaries.js +3 -3
  35. package/dist/modules/customers/commands/dictionaries.js.map +2 -2
  36. package/dist/modules/customers/commands/entity-roles.js +3 -3
  37. package/dist/modules/customers/commands/entity-roles.js.map +2 -2
  38. package/dist/modules/customers/commands/interactions.js +6 -6
  39. package/dist/modules/customers/commands/interactions.js.map +2 -2
  40. package/dist/modules/customers/commands/labels.js +7 -7
  41. package/dist/modules/customers/commands/labels.js.map +2 -2
  42. package/dist/modules/customers/commands/people.js +3 -3
  43. package/dist/modules/customers/commands/people.js.map +2 -2
  44. package/dist/modules/customers/commands/personCompanyLinks.js +6 -6
  45. package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
  46. package/dist/modules/customers/commands/pipeline-stages.js +3 -3
  47. package/dist/modules/customers/commands/pipeline-stages.js.map +2 -2
  48. package/dist/modules/customers/commands/pipelines.js +3 -3
  49. package/dist/modules/customers/commands/pipelines.js.map +2 -2
  50. package/dist/modules/customers/commands/shared.js +3 -3
  51. package/dist/modules/customers/commands/shared.js.map +2 -2
  52. package/dist/modules/customers/commands/tags.js +7 -7
  53. package/dist/modules/customers/commands/tags.js.map +2 -2
  54. package/dist/modules/customers/commands/todos.js +3 -3
  55. package/dist/modules/customers/commands/todos.js.map +2 -2
  56. package/dist/modules/customers/lib/personCompanies.js +4 -4
  57. package/dist/modules/customers/lib/personCompanies.js.map +2 -2
  58. package/dist/modules/perspectives/data/validators.js +4 -0
  59. package/dist/modules/perspectives/data/validators.js.map +2 -2
  60. package/dist/modules/sales/api/notes/route.js +2 -4
  61. package/dist/modules/sales/api/notes/route.js.map +2 -2
  62. package/dist/modules/sales/api/quotes/accept/route.js +2 -2
  63. package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
  64. package/dist/modules/sales/api/quotes/public/[token]/route.js +3 -7
  65. package/dist/modules/sales/api/quotes/public/[token]/route.js.map +2 -2
  66. package/dist/modules/sales/api/quotes/send/route.js +2 -2
  67. package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
  68. package/dist/modules/sales/api/returns/[id]/route.js +2 -2
  69. package/dist/modules/sales/api/returns/[id]/route.js.map +2 -2
  70. package/dist/modules/sales/commands/configuration.js +11 -11
  71. package/dist/modules/sales/commands/configuration.js.map +2 -2
  72. package/dist/modules/sales/commands/documentAddresses.js +3 -3
  73. package/dist/modules/sales/commands/documentAddresses.js.map +2 -2
  74. package/dist/modules/sales/commands/documents.js +31 -39
  75. package/dist/modules/sales/commands/documents.js.map +2 -2
  76. package/dist/modules/sales/commands/notes.js +7 -7
  77. package/dist/modules/sales/commands/notes.js.map +2 -2
  78. package/dist/modules/sales/commands/payments.js +2 -2
  79. package/dist/modules/sales/commands/payments.js.map +2 -2
  80. package/dist/modules/sales/commands/returns.js +8 -8
  81. package/dist/modules/sales/commands/returns.js.map +2 -2
  82. package/dist/modules/sales/commands/shared.js +2 -2
  83. package/dist/modules/sales/commands/shared.js.map +2 -2
  84. package/dist/modules/sales/commands/shipments.js +5 -5
  85. package/dist/modules/sales/commands/shipments.js.map +2 -2
  86. package/dist/modules/sales/commands/statuses.js +2 -2
  87. package/dist/modules/sales/commands/statuses.js.map +2 -2
  88. package/dist/modules/sales/commands/tags.js +3 -3
  89. package/dist/modules/sales/commands/tags.js.map +2 -2
  90. package/dist/modules/sales/lib/calculations.js +6 -0
  91. package/dist/modules/sales/lib/calculations.js.map +2 -2
  92. package/package.json +7 -7
  93. package/src/modules/attachments/lib/reconcileOrganization.ts +16 -7
  94. package/src/modules/customer_accounts/i18n/de.json +12 -0
  95. package/src/modules/customer_accounts/i18n/en.json +12 -0
  96. package/src/modules/customer_accounts/i18n/es.json +34 -22
  97. package/src/modules/customer_accounts/i18n/pl.json +32 -20
  98. package/src/modules/customers/api/companies/[id]/people/route.ts +2 -2
  99. package/src/modules/customers/api/deals/[id]/companies/route.ts +2 -2
  100. package/src/modules/customers/api/deals/[id]/people/route.ts +2 -2
  101. package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +4 -4
  102. package/src/modules/customers/api/entity-roles-factory.ts +3 -3
  103. package/src/modules/customers/api/labels/assign/route.ts +2 -2
  104. package/src/modules/customers/api/labels/unassign/route.ts +2 -2
  105. package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +3 -7
  106. package/src/modules/customers/api/people/[id]/companies/context.ts +3 -3
  107. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +2 -2
  108. package/src/modules/customers/api/todos/route.ts +2 -2
  109. package/src/modules/customers/commands/addresses.ts +3 -3
  110. package/src/modules/customers/commands/comments.ts +3 -3
  111. package/src/modules/customers/commands/companies.ts +2 -2
  112. package/src/modules/customers/commands/deals.ts +3 -3
  113. package/src/modules/customers/commands/dictionaries.ts +3 -3
  114. package/src/modules/customers/commands/entity-roles.ts +3 -3
  115. package/src/modules/customers/commands/interactions.ts +6 -6
  116. package/src/modules/customers/commands/labels.ts +7 -7
  117. package/src/modules/customers/commands/people.ts +3 -3
  118. package/src/modules/customers/commands/personCompanyLinks.ts +6 -6
  119. package/src/modules/customers/commands/pipeline-stages.ts +3 -3
  120. package/src/modules/customers/commands/pipelines.ts +3 -3
  121. package/src/modules/customers/commands/shared.ts +3 -3
  122. package/src/modules/customers/commands/tags.ts +7 -7
  123. package/src/modules/customers/commands/todos.ts +3 -3
  124. package/src/modules/customers/lib/personCompanies.ts +4 -4
  125. package/src/modules/perspectives/data/validators.ts +4 -0
  126. package/src/modules/sales/api/notes/route.ts +2 -4
  127. package/src/modules/sales/api/quotes/accept/route.ts +2 -2
  128. package/src/modules/sales/api/quotes/public/[token]/route.ts +3 -7
  129. package/src/modules/sales/api/quotes/send/route.ts +2 -2
  130. package/src/modules/sales/api/returns/[id]/route.ts +2 -2
  131. package/src/modules/sales/commands/configuration.ts +11 -11
  132. package/src/modules/sales/commands/documentAddresses.ts +3 -3
  133. package/src/modules/sales/commands/documents.ts +23 -31
  134. package/src/modules/sales/commands/notes.ts +7 -7
  135. package/src/modules/sales/commands/payments.ts +2 -2
  136. package/src/modules/sales/commands/returns.ts +8 -8
  137. package/src/modules/sales/commands/shared.ts +2 -2
  138. package/src/modules/sales/commands/shipments.ts +5 -5
  139. package/src/modules/sales/commands/statuses.ts +2 -2
  140. package/src/modules/sales/commands/tags.ts +3 -3
  141. package/src/modules/sales/lib/calculations.ts +15 -0
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import type { EntityManager } from '@mikro-orm/postgresql'
3
3
  import { makeCrudRoute, type CrudCtx } from '@open-mercato/shared/lib/crud/factory'
4
- import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
4
+ import { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
5
5
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
6
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
7
7
  import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
@@ -97,9 +97,7 @@ async function loadNoteForPermission(
97
97
  })
98
98
 
99
99
  if (!note) {
100
- throw new CrudHttpError(404, {
101
- error: translate('sales.documents.detail.error', 'Document not found or inaccessible.'),
102
- })
100
+ throw notFound(translate('sales.documents.detail.error', 'Document not found or inaccessible.'))
103
101
  }
104
102
 
105
103
  return note
@@ -3,7 +3,7 @@ import { z } from 'zod'
3
3
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
4
4
  import type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
5
5
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
6
- import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
6
+ import { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
7
7
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
8
8
  import type { EntityManager } from '@mikro-orm/postgresql'
9
9
  import { LockMode } from '@mikro-orm/core'
@@ -93,7 +93,7 @@ export async function POST(req: Request) {
93
93
  )
94
94
  const quote = (await findQuoteByToken(hashedToken)) ?? (await findQuoteByToken(token))
95
95
  if (!quote) {
96
- throw new CrudHttpError(404, { error: translate('sales.quotes.accept.notFound', 'Quote not found.') })
96
+ throw notFound(translate('sales.quotes.accept.notFound', 'Quote not found.'))
97
97
  }
98
98
 
99
99
  const now = new Date()
@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
5
- import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
5
+ import { isCrudHttpError, notFound } from "@open-mercato/shared/lib/crud/errors";
6
6
  import type { OpenApiRouteDoc } from "@open-mercato/shared/lib/openapi";
7
7
  import type { EntityManager } from "@mikro-orm/postgresql";
8
8
  import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
@@ -43,16 +43,12 @@ export async function GET(req: Request, ctx: { params: { token: string } }) {
43
43
  }));
44
44
  const { translate } = await resolveTranslations();
45
45
  if (!quote) {
46
- throw new CrudHttpError(404, {
47
- error: translate("sales.quotes.public.notFound", "Quote not found."),
48
- });
46
+ throw notFound(translate("sales.quotes.public.notFound", "Quote not found."));
49
47
  }
50
48
 
51
49
  const auth = await getAuthFromRequest(req);
52
50
  if (auth?.tenantId && quote.tenantId !== auth.tenantId) {
53
- throw new CrudHttpError(404, {
54
- error: translate("sales.quotes.public.notFound", "Quote not found."),
55
- });
51
+ throw notFound(translate("sales.quotes.public.notFound", "Quote not found."));
56
52
  }
57
53
 
58
54
  const now = new Date();
@@ -5,7 +5,7 @@ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
5
  import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
6
6
  import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
7
7
  import { resolveTranslations, detectLocale } from '@open-mercato/shared/lib/i18n/server'
8
- import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
8
+ import { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
9
9
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
10
10
  import {
11
11
  bridgeLegacyGuard,
@@ -151,7 +151,7 @@ export async function POST(req: Request) {
151
151
  const tenantScope = ctx.auth?.tenantId ? { tenantId: ctx.auth.tenantId } : undefined
152
152
  const quote = await findOneWithDecryption(em, SalesQuote, { id: input.quoteId, deletedAt: null }, {}, tenantScope)
153
153
  if (!quote) {
154
- throw new CrudHttpError(404, { error: translate('sales.documents.detail.error', 'Document not found or inaccessible.') })
154
+ throw notFound(translate('sales.documents.detail.error', 'Document not found or inaccessible.'))
155
155
  }
156
156
  if (quote.tenantId !== ctx.auth?.tenantId || quote.organizationId !== ctx.selectedOrganizationId) {
157
157
  throw new CrudHttpError(403, { error: translate('sales.documents.errors.forbidden', 'Forbidden') })
@@ -6,7 +6,7 @@ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
6
6
  import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
7
7
  import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
8
8
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
9
- import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
9
+ import { CrudHttpError, isCrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
10
10
  import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
11
11
  import { SalesReturn, SalesReturnLine } from '../../../data/entities'
12
12
  import { createLogger } from '@open-mercato/shared/lib/logger'
@@ -56,7 +56,7 @@ export async function GET(req: Request, ctx: { params: { id: string } }) {
56
56
  { tenantId: auth.tenantId, organizationId },
57
57
  )
58
58
  if (!header || !header.order) {
59
- throw new CrudHttpError(404, { error: translate('sales.returns.notFound', 'Return not found.') })
59
+ throw notFound(translate('sales.returns.notFound', 'Return not found.'))
60
60
  }
61
61
 
62
62
  const lines = await findWithDecryption(
@@ -9,7 +9,7 @@ import {
9
9
  emitCrudUndoSideEffects,
10
10
  } from '@open-mercato/shared/lib/commands/helpers'
11
11
  import type { EntityManager } from '@mikro-orm/postgresql'
12
- import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
12
+ import { notFound } from '@open-mercato/shared/lib/crud/errors'
13
13
  import { loadCustomFieldSnapshot, buildCustomFieldResetMap } from '@open-mercato/shared/lib/commands/customFieldSnapshots'
14
14
  import { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'
15
15
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
@@ -751,7 +751,7 @@ const updateChannelCommand: CommandHandler<ChannelUpdateInput, { channelId: stri
751
751
  const { parsed, custom } = parseWithCustomFields(channelUpdateSchema, rawInput)
752
752
  const em = (ctx.container.resolve('em') as EntityManager).fork()
753
753
  const record = await findOneWithDecryption(em, SalesChannel, { id: parsed.id, deletedAt: null }, {}, { tenantId: parsed.tenantId, organizationId: parsed.organizationId })
754
- if (!record) throw new CrudHttpError(404, { error: 'Channel not found' })
754
+ if (!record) throw notFound('Channel not found')
755
755
  const dataEngine = ctx.container.resolve('dataEngine') as DataEngine
756
756
  const scope = resolveScopeFromUpdate(record, parsed, ctx)
757
757
 
@@ -899,7 +899,7 @@ const deleteChannelCommand: CommandHandler<
899
899
  const id = requireId(input, 'Channel id is required')
900
900
  const em = (ctx.container.resolve('em') as EntityManager).fork()
901
901
  const record = await findOneWithDecryption(em, SalesChannel, { id }, {})
902
- if (!record) throw new CrudHttpError(404, { error: 'Channel not found' })
902
+ if (!record) throw notFound('Channel not found')
903
903
  ensureTenantScope(ctx, record.tenantId)
904
904
  ensureOrganizationScope(ctx, record.organizationId)
905
905
  em.remove(record)
@@ -1097,7 +1097,7 @@ const updateDeliveryWindowCommand: CommandHandler<
1097
1097
  const { parsed, custom } = parseWithCustomFields(deliveryWindowUpdateSchema, rawInput)
1098
1098
  const em = (ctx.container.resolve('em') as EntityManager).fork()
1099
1099
  const record = await em.findOne(SalesDeliveryWindow, { id: parsed.id, deletedAt: null })
1100
- if (!record) throw new CrudHttpError(404, { error: 'Delivery window not found' })
1100
+ if (!record) throw notFound('Delivery window not found')
1101
1101
  const scope = resolveScopeFromUpdate(record, parsed, ctx)
1102
1102
  record.organizationId = scope.organizationId
1103
1103
  record.tenantId = scope.tenantId
@@ -1201,7 +1201,7 @@ const deleteDeliveryWindowCommand: CommandHandler<
1201
1201
  const id = requireId(input, 'Delivery window id is required')
1202
1202
  const em = (ctx.container.resolve('em') as EntityManager).fork()
1203
1203
  const record = await em.findOne(SalesDeliveryWindow, { id })
1204
- if (!record) throw new CrudHttpError(404, { error: 'Delivery window not found' })
1204
+ if (!record) throw notFound('Delivery window not found')
1205
1205
  ensureTenantScope(ctx, record.tenantId)
1206
1206
  ensureOrganizationScope(ctx, record.organizationId)
1207
1207
  em.remove(record)
@@ -1377,7 +1377,7 @@ const updateShippingMethodCommand: CommandHandler<
1377
1377
  const { parsed, custom } = parseWithCustomFields(shippingMethodUpdateSchema, rawInput)
1378
1378
  const em = (ctx.container.resolve('em') as EntityManager).fork()
1379
1379
  const record = await em.findOne(SalesShippingMethod, { id: parsed.id, deletedAt: null })
1380
- if (!record) throw new CrudHttpError(404, { error: 'Shipping method not found' })
1380
+ if (!record) throw notFound('Shipping method not found')
1381
1381
  const scope = resolveScopeFromUpdate(record, parsed, ctx)
1382
1382
  record.organizationId = scope.organizationId
1383
1383
  record.tenantId = scope.tenantId
@@ -1501,7 +1501,7 @@ const deleteShippingMethodCommand: CommandHandler<
1501
1501
  const id = requireId(input, 'Shipping method id is required')
1502
1502
  const em = (ctx.container.resolve('em') as EntityManager).fork()
1503
1503
  const record = await em.findOne(SalesShippingMethod, { id })
1504
- if (!record) throw new CrudHttpError(404, { error: 'Shipping method not found' })
1504
+ if (!record) throw notFound('Shipping method not found')
1505
1505
  ensureTenantScope(ctx, record.tenantId)
1506
1506
  ensureOrganizationScope(ctx, record.organizationId)
1507
1507
  em.remove(record)
@@ -1672,7 +1672,7 @@ const updatePaymentMethodCommand: CommandHandler<
1672
1672
  const { parsed, custom } = parseWithCustomFields(paymentMethodUpdateSchema, rawInput)
1673
1673
  const em = (ctx.container.resolve('em') as EntityManager).fork()
1674
1674
  const record = await em.findOne(SalesPaymentMethod, { id: parsed.id, deletedAt: null })
1675
- if (!record) throw new CrudHttpError(404, { error: 'Payment method not found' })
1675
+ if (!record) throw notFound('Payment method not found')
1676
1676
  const scope = resolveScopeFromUpdate(record, parsed, ctx)
1677
1677
  record.organizationId = scope.organizationId
1678
1678
  record.tenantId = scope.tenantId
@@ -1785,7 +1785,7 @@ const deletePaymentMethodCommand: CommandHandler<
1785
1785
  const id = requireId(input, 'Payment method id is required')
1786
1786
  const em = (ctx.container.resolve('em') as EntityManager).fork()
1787
1787
  const record = await em.findOne(SalesPaymentMethod, { id })
1788
- if (!record) throw new CrudHttpError(404, { error: 'Payment method not found' })
1788
+ if (!record) throw notFound('Payment method not found')
1789
1789
  ensureTenantScope(ctx, record.tenantId)
1790
1790
  ensureOrganizationScope(ctx, record.organizationId)
1791
1791
  em.remove(record)
@@ -1971,7 +1971,7 @@ const updateTaxRateCommand: CommandHandler<TaxRateUpdateInput, { taxRateId: stri
1971
1971
  const { parsed, custom } = parseWithCustomFields(taxRateUpdateSchema, rawInput)
1972
1972
  const em = (ctx.container.resolve('em') as EntityManager).fork()
1973
1973
  const record = await em.findOne(SalesTaxRate, { id: parsed.id, deletedAt: null })
1974
- if (!record) throw new CrudHttpError(404, { error: 'Tax rate not found' })
1974
+ if (!record) throw notFound('Tax rate not found')
1975
1975
  const scope = resolveScopeFromUpdate(record, parsed, ctx)
1976
1976
  record.organizationId = scope.organizationId
1977
1977
  record.tenantId = scope.tenantId
@@ -2102,7 +2102,7 @@ const deleteTaxRateCommand: CommandHandler<
2102
2102
  const id = requireId(input, 'Tax rate id is required')
2103
2103
  const em = (ctx.container.resolve('em') as EntityManager).fork()
2104
2104
  const record = await em.findOne(SalesTaxRate, { id })
2105
- if (!record) throw new CrudHttpError(404, { error: 'Tax rate not found' })
2105
+ if (!record) throw notFound('Tax rate not found')
2106
2106
  ensureTenantScope(ctx, record.tenantId)
2107
2107
  ensureOrganizationScope(ctx, record.organizationId)
2108
2108
  const snapshot = await loadTaxRateSnapshot((ctx.container.resolve('em') as EntityManager), id)
@@ -4,7 +4,7 @@ import { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/c
4
4
  import { emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'
5
5
  import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
6
6
  import type { EntityManager } from '@mikro-orm/postgresql'
7
- import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
7
+ import { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
8
8
  import {
9
9
  documentAddressCreateSchema,
10
10
  documentAddressDeleteSchema,
@@ -106,7 +106,7 @@ function applyDocumentAddressSnapshot(em: EntityManager, entity: SalesDocumentAd
106
106
 
107
107
  function assertDocumentAddressParent(entity: SalesDocumentAddress, input: { documentId: string; documentKind: 'order' | 'quote' }): void {
108
108
  if (entity.documentId !== input.documentId || entity.documentKind !== input.documentKind) {
109
- throw new CrudHttpError(404, { error: 'sales.document.address.not_found' })
109
+ throw notFound('sales.document.address.not_found')
110
110
  }
111
111
  }
112
112
 
@@ -145,7 +145,7 @@ async function requireDocument(
145
145
  const repo = kind === 'order' ? SalesOrder : SalesQuote
146
146
  const doc = await findOneWithDecryption(em, repo, { id, organizationId, tenantId }, {}, { tenantId, organizationId })
147
147
  if (!doc) {
148
- throw new CrudHttpError(404, { error: 'sales.document.not_found' })
148
+ throw notFound('sales.document.not_found')
149
149
  }
150
150
  return doc
151
151
  }
@@ -15,7 +15,7 @@ import { LockMode } from "@mikro-orm/core";
15
15
  import type { EntityManager } from "@mikro-orm/postgresql";
16
16
  import type { EventBus } from "@open-mercato/events";
17
17
  import type { DataEngine } from "@open-mercato/shared/lib/data/engine";
18
- import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
18
+ import { CrudHttpError, notFound } from "@open-mercato/shared/lib/crud/errors";
19
19
  import {
20
20
  deriveResourceFromCommandId,
21
21
  invalidateCrudCache,
@@ -4854,7 +4854,7 @@ const deleteQuoteCommand: CommandHandler<
4854
4854
  const em = (ctx.container.resolve("em") as EntityManager).fork();
4855
4855
  const quote = await findOneWithDecryption(em, SalesQuote, { id });
4856
4856
  if (!quote)
4857
- throw new CrudHttpError(404, { error: "Sales quote not found" });
4857
+ throw notFound("Sales quote not found");
4858
4858
  ensureQuoteScope(ctx, quote.organizationId, quote.tenantId);
4859
4859
  const [addresses, notes, tags, adjustments, lines] = await Promise.all([
4860
4860
  findWithDecryption(
@@ -4984,7 +4984,7 @@ const updateQuoteCommand: CommandHandler<
4984
4984
  deletedAt: null,
4985
4985
  });
4986
4986
  if (!quote)
4987
- throw new CrudHttpError(404, { error: "Sales quote not found" });
4987
+ throw notFound("Sales quote not found");
4988
4988
  ensureQuoteScope(ctx, quote.organizationId, quote.tenantId);
4989
4989
  await enforceSalesDocumentOptimisticLock(ctx, quote, SALES_RESOURCE_KIND_QUOTE);
4990
4990
  const shouldInvalidateSentToken = (quote.status ?? null) === "sent";
@@ -5216,7 +5216,7 @@ const updateOrderCommand: CommandHandler<
5216
5216
  deletedAt: null,
5217
5217
  });
5218
5218
  if (!order)
5219
- throw new CrudHttpError(404, { error: "Sales order not found" });
5219
+ throw notFound("Sales order not found");
5220
5220
  ensureOrderScope(ctx, order.organizationId, order.tenantId);
5221
5221
  await enforceSalesDocumentOptimisticLock(ctx, order, SALES_RESOURCE_KIND_ORDER);
5222
5222
  const previousStatus = normalizeStatusValue(order.status);
@@ -5861,7 +5861,7 @@ const deleteOrderCommand: CommandHandler<
5861
5861
  const em = (ctx.container.resolve("em") as EntityManager).fork();
5862
5862
  const order = await findOneWithDecryption(em, SalesOrder, { id });
5863
5863
  if (!order)
5864
- throw new CrudHttpError(404, { error: "Sales order not found" });
5864
+ throw notFound("Sales order not found");
5865
5865
  ensureOrderScope(ctx, order.organizationId, order.tenantId);
5866
5866
  const shipments = await em.find(SalesShipment, { order: order.id });
5867
5867
  const shipmentIds = shipments.map((entry) => entry.id);
@@ -6055,22 +6055,18 @@ const convertQuoteToOrderCommand: CommandHandler<
6055
6055
  );
6056
6056
  const { translate } = await resolveTranslations();
6057
6057
  if (!quote)
6058
- throw new CrudHttpError(404, {
6059
- error: translate(
6058
+ throw notFound(translate(
6060
6059
  "sales.documents.detail.error",
6061
6060
  "Document not found or inaccessible.",
6062
- ),
6063
- });
6061
+ ));
6064
6062
  ensureQuoteScope(ctx, quote.organizationId, quote.tenantId);
6065
6063
  await enforceSalesDocumentOptimisticLock(ctx, quote, SALES_RESOURCE_KIND_QUOTE);
6066
6064
  const snapshot = await loadQuoteSnapshot(em, payload.quoteId);
6067
6065
  if (!snapshot)
6068
- throw new CrudHttpError(404, {
6069
- error: translate(
6066
+ throw notFound(translate(
6070
6067
  "sales.documents.detail.error",
6071
6068
  "Document not found or inaccessible.",
6072
- ),
6073
- });
6069
+ ));
6074
6070
  const orderId = payload.orderId ?? quote.id;
6075
6071
  const existingOrder = await findOneWithDecryption(em, SalesOrder, {
6076
6072
  id: orderId,
@@ -6613,7 +6609,7 @@ const orderLineUpsertCommand: CommandHandler<
6613
6609
  deletedAt: null,
6614
6610
  });
6615
6611
  if (!order)
6616
- throw new CrudHttpError(404, { error: "Sales order not found" });
6612
+ throw notFound("Sales order not found");
6617
6613
  ensureOrderScope(ctx, order.organizationId, order.tenantId);
6618
6614
  await enforceSalesDocumentOptimisticLock(ctx, order, SALES_RESOURCE_KIND_ORDER);
6619
6615
 
@@ -6930,12 +6926,10 @@ const orderLineDeleteCommand: CommandHandler<
6930
6926
  deletedAt: null,
6931
6927
  });
6932
6928
  if (!order)
6933
- throw new CrudHttpError(404, {
6934
- error: translate(
6929
+ throw notFound(translate(
6935
6930
  "sales.documents.detail.error",
6936
6931
  "Document not found or inaccessible.",
6937
- ),
6938
- });
6932
+ ));
6939
6933
  ensureOrderScope(ctx, order.organizationId, order.tenantId);
6940
6934
  await enforceSalesDocumentOptimisticLock(ctx, order, SALES_RESOURCE_KIND_ORDER);
6941
6935
  const shipmentCount = await em.count(SalesShipmentItem, {
@@ -6962,12 +6956,10 @@ const orderLineDeleteCommand: CommandHandler<
6962
6956
  );
6963
6957
  const filtered = existingLines.filter((line) => line.id !== parsed.id);
6964
6958
  if (filtered.length === existingLines.length) {
6965
- throw new CrudHttpError(404, {
6966
- error: translate(
6959
+ throw notFound(translate(
6967
6960
  "sales.documents.detail.error",
6968
6961
  "Document not found or inaccessible.",
6969
- ),
6970
- });
6962
+ ));
6971
6963
  }
6972
6964
  const sourceInputs = filtered.map((line, index) => ({
6973
6965
  ...mapOrderLineEntityToSnapshot(line),
@@ -7106,7 +7098,7 @@ const quoteLineUpsertCommand: CommandHandler<
7106
7098
  deletedAt: null,
7107
7099
  });
7108
7100
  if (!quote)
7109
- throw new CrudHttpError(404, { error: "Sales quote not found" });
7101
+ throw notFound("Sales quote not found");
7110
7102
  ensureQuoteScope(ctx, quote.organizationId, quote.tenantId);
7111
7103
  await enforceSalesDocumentOptimisticLock(ctx, quote, SALES_RESOURCE_KIND_QUOTE);
7112
7104
  const [existingLines, adjustments] = await Promise.all([
@@ -7418,7 +7410,7 @@ const quoteLineDeleteCommand: CommandHandler<
7418
7410
  deletedAt: null,
7419
7411
  });
7420
7412
  if (!quote)
7421
- throw new CrudHttpError(404, { error: "Sales quote not found" });
7413
+ throw notFound("Sales quote not found");
7422
7414
  ensureQuoteScope(ctx, quote.organizationId, quote.tenantId);
7423
7415
  await enforceSalesDocumentOptimisticLock(ctx, quote, SALES_RESOURCE_KIND_QUOTE);
7424
7416
  const existingLines = await em.find(
@@ -7433,7 +7425,7 @@ const quoteLineDeleteCommand: CommandHandler<
7433
7425
  );
7434
7426
  const filtered = existingLines.filter((line) => line.id !== parsed.id);
7435
7427
  if (filtered.length === existingLines.length) {
7436
- throw new CrudHttpError(404, { error: "Quote line not found" });
7428
+ throw notFound("Quote line not found");
7437
7429
  }
7438
7430
  const sourceInputs = filtered.map((line, index) => ({
7439
7431
  ...mapQuoteLineEntityToSnapshot(line),
@@ -7572,7 +7564,7 @@ const orderAdjustmentUpsertCommand: CommandHandler<
7572
7564
  deletedAt: null,
7573
7565
  });
7574
7566
  if (!order)
7575
- throw new CrudHttpError(404, { error: "Sales order not found" });
7567
+ throw notFound("Sales order not found");
7576
7568
  ensureOrderScope(ctx, order.organizationId, order.tenantId);
7577
7569
  await enforceSalesDocumentOptimisticLock(ctx, order, SALES_RESOURCE_KIND_ORDER);
7578
7570
  if (parsed.scope === "line") {
@@ -7866,7 +7858,7 @@ const orderAdjustmentDeleteCommand: CommandHandler<
7866
7858
  deletedAt: null,
7867
7859
  });
7868
7860
  if (!order)
7869
- throw new CrudHttpError(404, { error: "Sales order not found" });
7861
+ throw notFound("Sales order not found");
7870
7862
  ensureOrderScope(ctx, order.organizationId, order.tenantId);
7871
7863
  await enforceSalesDocumentOptimisticLock(ctx, order, SALES_RESOURCE_KIND_ORDER);
7872
7864
 
@@ -7880,7 +7872,7 @@ const orderAdjustmentDeleteCommand: CommandHandler<
7880
7872
  ]);
7881
7873
  const filtered = adjustments.filter((adj) => adj.id !== parsed.id);
7882
7874
  if (filtered.length === adjustments.length) {
7883
- throw new CrudHttpError(404, { error: "Adjustment not found" });
7875
+ throw notFound("Adjustment not found");
7884
7876
  }
7885
7877
  const lineSnapshots = existingLines.map(mapOrderLineEntityToSnapshot);
7886
7878
  const calcLines = lineSnapshots.map((line, index) =>
@@ -8031,7 +8023,7 @@ const quoteAdjustmentUpsertCommand: CommandHandler<
8031
8023
  deletedAt: null,
8032
8024
  });
8033
8025
  if (!quote)
8034
- throw new CrudHttpError(404, { error: "Sales quote not found" });
8026
+ throw notFound("Sales quote not found");
8035
8027
  ensureQuoteScope(ctx, quote.organizationId, quote.tenantId);
8036
8028
  await enforceSalesDocumentOptimisticLock(ctx, quote, SALES_RESOURCE_KIND_QUOTE);
8037
8029
  if (parsed.scope === "line") {
@@ -8323,7 +8315,7 @@ const quoteAdjustmentDeleteCommand: CommandHandler<
8323
8315
  deletedAt: null,
8324
8316
  });
8325
8317
  if (!quote)
8326
- throw new CrudHttpError(404, { error: "Sales quote not found" });
8318
+ throw notFound("Sales quote not found");
8327
8319
  ensureQuoteScope(ctx, quote.organizationId, quote.tenantId);
8328
8320
  await enforceSalesDocumentOptimisticLock(ctx, quote, SALES_RESOURCE_KIND_QUOTE);
8329
8321
 
@@ -8337,7 +8329,7 @@ const quoteAdjustmentDeleteCommand: CommandHandler<
8337
8329
  ]);
8338
8330
  const filtered = adjustments.filter((adj) => adj.id !== parsed.id);
8339
8331
  if (filtered.length === adjustments.length) {
8340
- throw new CrudHttpError(404, { error: "Adjustment not found" });
8332
+ throw notFound("Adjustment not found");
8341
8333
  }
8342
8334
  const lineSnapshots = existingLines.map(mapQuoteLineEntityToSnapshot);
8343
8335
  const calcLines = lineSnapshots.map((line, index) =>
@@ -7,7 +7,7 @@ import {
7
7
  } from '@open-mercato/shared/lib/commands/helpers'
8
8
  import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
9
9
  import type { EntityManager } from '@mikro-orm/postgresql'
10
- import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
10
+ import { notFound } from '@open-mercato/shared/lib/crud/errors'
11
11
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
12
12
  import type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'
13
13
  import { E } from '#generated/entities.ids.generated'
@@ -96,7 +96,7 @@ async function requireContext(
96
96
  if (contextType === 'order') {
97
97
  const order = await findOneWithDecryption(em, SalesOrder, { id: contextId }, {}, { tenantId, organizationId })
98
98
  if (!order) {
99
- throw new CrudHttpError(404, { error: 'sales.notes.context_not_found' })
99
+ throw notFound('sales.notes.context_not_found')
100
100
  }
101
101
  if (organizationId && tenantId) {
102
102
  ensureSameScope(order, organizationId, tenantId)
@@ -111,7 +111,7 @@ async function requireContext(
111
111
  if (contextType === 'quote') {
112
112
  const quote = await findOneWithDecryption(em, SalesQuote, { id: contextId }, {}, { tenantId, organizationId })
113
113
  if (!quote) {
114
- throw new CrudHttpError(404, { error: 'sales.notes.context_not_found' })
114
+ throw notFound('sales.notes.context_not_found')
115
115
  }
116
116
  if (organizationId && tenantId) {
117
117
  ensureSameScope(quote, organizationId, tenantId)
@@ -132,7 +132,7 @@ async function requireContext(
132
132
  { tenantId, organizationId },
133
133
  ) as (SalesInvoice | SalesCreditMemo) | null
134
134
  if (!entity) {
135
- throw new CrudHttpError(404, { error: 'sales.notes.context_not_found' })
135
+ throw notFound('sales.notes.context_not_found')
136
136
  }
137
137
  if (organizationId && tenantId) {
138
138
  ensureSameScope(entity, organizationId, tenantId)
@@ -256,7 +256,7 @@ const createNoteCommand: CommandHandler<NoteCreateInput, { noteId: string; autho
256
256
  beforeRestore: async ({ em, snapshot }) => {
257
257
  const context = await requireContext(em, snapshot.contextType, snapshot.contextId).catch(() => null)
258
258
  if (!context) {
259
- throw new CrudHttpError(404, { error: 'sales.notes.context_not_found' })
259
+ throw notFound('sales.notes.context_not_found')
260
260
  }
261
261
  return {
262
262
  order: snapshot.orderId ? context.order ?? null : null,
@@ -279,7 +279,7 @@ const updateNoteCommand: CommandHandler<NoteUpdateInput, { noteId: string }> = {
279
279
  const parsed = noteUpdateSchema.parse(rawInput ?? {})
280
280
  const em = (ctx.container.resolve('em') as EntityManager).fork()
281
281
  const note = await findOneWithDecryption(em, SalesNote, { id: parsed.id }, {})
282
- if (!note) throw new CrudHttpError(404, { error: 'sales.notes.not_found' })
282
+ if (!note) throw notFound('sales.notes.not_found')
283
283
  ensureTenantScope(ctx, note.tenantId)
284
284
  ensureOrganizationScope(ctx, note.organizationId)
285
285
 
@@ -413,7 +413,7 @@ const deleteNoteCommand: CommandHandler<{ body?: Record<string, unknown>; query?
413
413
  const id = requireId(input, 'Note id required')
414
414
  const em = (ctx.container.resolve('em') as EntityManager).fork()
415
415
  const note = await findOneWithDecryption(em, SalesNote, { id }, {})
416
- if (!note) throw new CrudHttpError(404, { error: 'sales.notes.not_found' })
416
+ if (!note) throw notFound('sales.notes.not_found')
417
417
  ensureTenantScope(ctx, note.tenantId)
418
418
  ensureOrganizationScope(ctx, note.organizationId)
419
419
  em.remove(note)
@@ -3,7 +3,7 @@
3
3
  import { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'
4
4
  import { LockMode } from '@mikro-orm/core'
5
5
  import type { EntityManager } from '@mikro-orm/postgresql'
6
- import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
6
+ import { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
7
7
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
8
8
  import { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'
9
9
  import { setRecordCustomFields } from '@open-mercato/core/modules/entities/lib/helpers'
@@ -346,7 +346,7 @@ const createPaymentCommand: CommandHandler<
346
346
  )
347
347
  ensureSameScope(order, input.organizationId, input.tenantId)
348
348
  if (order.deletedAt) {
349
- throw new CrudHttpError(404, { error: 'sales.payments.order_not_found' })
349
+ throw notFound('sales.payments.order_not_found')
350
350
  }
351
351
  // Guard the parent order's aggregate version (Gap A): a payment mutation
352
352
  // recalculates the order totals, so a stale parent must 409 before we touch it.
@@ -3,7 +3,7 @@ import { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/c
3
3
  import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
4
4
  import { LockMode } from '@mikro-orm/core'
5
5
  import type { EntityManager } from '@mikro-orm/postgresql'
6
- import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
6
+ import { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
7
7
  import { invalidateCrudCache } from '@open-mercato/shared/lib/crud/cache'
8
8
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
9
9
  import { emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'
@@ -456,7 +456,7 @@ async function restoreReturnEffects(
456
456
  { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },
457
457
  )
458
458
  if (!order) {
459
- throw new CrudHttpError(404, { error: 'sales.returns.orderMissing' })
459
+ throw notFound('sales.returns.orderMissing')
460
460
  }
461
461
  ensureSameScope(order, snapshot.organizationId, snapshot.tenantId)
462
462
 
@@ -620,7 +620,7 @@ const createReturnCommand: CommandHandler<ReturnCreateInput, { returnId: string
620
620
  { tenantId: input.tenantId, organizationId: input.organizationId },
621
621
  )
622
622
  if (!order) {
623
- throw new CrudHttpError(404, { error: translate('sales.returns.orderMissing', 'Order not found.') })
623
+ throw notFound(translate('sales.returns.orderMissing', 'Order not found.'))
624
624
  }
625
625
  ensureSameScope(order, input.organizationId, input.tenantId)
626
626
  await enforceSalesDocumentOptimisticLock(ctx, order, SALES_RESOURCE_KIND_ORDER)
@@ -642,7 +642,7 @@ const createReturnCommand: CommandHandler<ReturnCreateInput, { returnId: string
642
642
  requested.forEach(({ orderLineId, quantity }) => {
643
643
  const line = lineMap.get(orderLineId)
644
644
  if (!line) {
645
- throw new CrudHttpError(404, { error: translate('sales.returns.lineMissing', 'Order line not found.') })
645
+ throw notFound(translate('sales.returns.lineMissing', 'Order line not found.'))
646
646
  }
647
647
  const available = computeAvailableReturnQuantity({
648
648
  quantity: toNumeric(line.quantity),
@@ -846,7 +846,7 @@ const createReturnCommand: CommandHandler<ReturnCreateInput, { returnId: string
846
846
  { tenantId: after.tenantId, organizationId: after.organizationId },
847
847
  )
848
848
  if (!header) {
849
- throw new CrudHttpError(404, { error: 'sales.returns.orderMissing' })
849
+ throw notFound('sales.returns.orderMissing')
850
850
  }
851
851
 
852
852
  await invalidateOrderCache(ctx.container, {
@@ -911,7 +911,7 @@ const updateReturnCommand: CommandHandler<ReturnUpdateInput, { returnId: string
911
911
  { tenantId: input.tenantId, organizationId: input.organizationId },
912
912
  )
913
913
  if (!entity || !entity.order) {
914
- throw new CrudHttpError(404, { error: translate('sales.returns.notFound', 'Return not found.') })
914
+ throw notFound(translate('sales.returns.notFound', 'Return not found.'))
915
915
  }
916
916
  ensureSameScope(entity, input.organizationId, input.tenantId)
917
917
  const orderId = typeof entity.order === 'string' ? entity.order : entity.order.id
@@ -1031,7 +1031,7 @@ const deleteReturnCommand: CommandHandler<ReturnDeleteInput, { returnId: string
1031
1031
 
1032
1032
  const snapshot = await loadReturnSnapshot(em, input.id)
1033
1033
  if (!snapshot) {
1034
- throw new CrudHttpError(404, { error: translate('sales.returns.notFound', 'Return not found.') })
1034
+ throw notFound(translate('sales.returns.notFound', 'Return not found.'))
1035
1035
  }
1036
1036
  ensureSameScope(snapshot, input.organizationId, input.tenantId)
1037
1037
  if (input.orderId !== snapshot.orderId) {
@@ -1046,7 +1046,7 @@ const deleteReturnCommand: CommandHandler<ReturnDeleteInput, { returnId: string
1046
1046
  { tenantId: input.tenantId, organizationId: input.organizationId },
1047
1047
  )
1048
1048
  if (!header) {
1049
- throw new CrudHttpError(404, { error: translate('sales.returns.notFound', 'Return not found.') })
1049
+ throw notFound(translate('sales.returns.notFound', 'Return not found.'))
1050
1050
  }
1051
1051
  ensureSameScope(header, input.organizationId, input.tenantId)
1052
1052
  // Lock on the return's own version, captured before any mutation.
@@ -1,5 +1,5 @@
1
1
  import type { EntityManager } from '@mikro-orm/postgresql'
2
- import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
2
+ import { notFound } from '@open-mercato/shared/lib/crud/errors'
3
3
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
4
  import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
5
5
  import { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
@@ -138,6 +138,6 @@ export async function requireScopedEntity<T extends { id: string; deletedAt?: Da
138
138
  if (scope.organizationId) where.organizationId = scope.organizationId
139
139
  if (scope.tenantId) where.tenantId = scope.tenantId
140
140
  const entity = await findOneWithDecryption(em, entityClass, where, {}, scope)
141
- if (!entity) throw new CrudHttpError(404, { error: message })
141
+ if (!entity) throw notFound(message)
142
142
  return entity
143
143
  }
@@ -4,7 +4,7 @@ import { randomUUID } from 'crypto'
4
4
  import { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'
5
5
  import { LockMode } from '@mikro-orm/core'
6
6
  import type { EntityManager } from '@mikro-orm/postgresql'
7
- import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
7
+ import { CrudHttpError, notFound } from '@open-mercato/shared/lib/crud/errors'
8
8
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
9
9
  import { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'
10
10
  import { setRecordCustomFields } from '@open-mercato/core/modules/entities/lib/helpers'
@@ -340,7 +340,7 @@ async function loadOrder(
340
340
  scope?: { tenantId: string; organizationId: string }
341
341
  ): Promise<SalesOrder> {
342
342
  const order = await findOneWithDecryption(em, SalesOrder, { id, deletedAt: null }, {}, scope)
343
- if (!order) throw new CrudHttpError(404, { error: 'sales.shipments.not_found' })
343
+ if (!order) throw notFound('sales.shipments.not_found')
344
344
  return order
345
345
  }
346
346
 
@@ -403,7 +403,7 @@ async function validateShipmentItems(params: {
403
403
  }
404
404
  const line = lineMap.get(lineId)
405
405
  if (!line) {
406
- throw new CrudHttpError(404, { error: translate('sales.shipments.line_missing', 'Order line not found.') })
406
+ throw notFound(translate('sales.shipments.line_missing', 'Order line not found.'))
407
407
  }
408
408
  const lineTotal = toNumber(line.quantity)
409
409
  const alreadyShipped = shippedTotals.get(lineId) ?? 0
@@ -675,7 +675,7 @@ const updateShipmentCommand: CommandHandler<ShipmentUpdateInput, { shipmentId: s
675
675
  { tenantId: input.tenantId, organizationId: input.organizationId },
676
676
  )
677
677
  if (!shipmentEntity || !shipmentEntity.order) {
678
- throw new CrudHttpError(404, { error: 'sales.shipments.not_found' })
678
+ throw notFound('sales.shipments.not_found')
679
679
  }
680
680
  ensureSameScope(shipmentEntity, input.organizationId, input.tenantId)
681
681
  const order = shipmentEntity.order as SalesOrder
@@ -970,7 +970,7 @@ const deleteShipmentCommand: CommandHandler<
970
970
  { tenantId: payload.tenantId, organizationId: payload.organizationId },
971
971
  )
972
972
  if (!shipmentEntity || !shipmentEntity.order) {
973
- throw new CrudHttpError(404, { error: translate('sales.shipments.not_found', 'Shipment not found') })
973
+ throw notFound(translate('sales.shipments.not_found', 'Shipment not found'))
974
974
  }
975
975
  try {
976
976
  ensureSameScope(shipmentEntity, payload.organizationId, payload.tenantId)