@open-mercato/core 0.6.6-develop.6304.1.4cf2b975cb → 0.6.6-develop.6309.1.983aeec27a
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/customers/data/guards.js +10 -1
- package/dist/modules/customers/data/guards.js.map +2 -2
- package/dist/modules/sales/commands/configuration.js +8 -8
- package/dist/modules/sales/commands/configuration.js.map +2 -2
- package/dist/modules/sales/commands/documentAddresses.js +8 -7
- package/dist/modules/sales/commands/documentAddresses.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +120 -42
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/notes.js +8 -8
- package/dist/modules/sales/commands/notes.js.map +2 -2
- package/dist/modules/sales/commands/payments.js +16 -10
- package/dist/modules/sales/commands/payments.js.map +2 -2
- package/dist/modules/sales/commands/shipments.js +7 -7
- package/dist/modules/sales/commands/shipments.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/customers/data/guards.ts +29 -0
- package/src/modules/sales/commands/configuration.ts +8 -8
- package/src/modules/sales/commands/documentAddresses.ts +8 -7
- package/src/modules/sales/commands/documents.ts +120 -42
- package/src/modules/sales/commands/notes.ts +8 -8
- package/src/modules/sales/commands/payments.ts +13 -18
- package/src/modules/sales/commands/shipments.ts +11 -7
|
@@ -1058,12 +1058,18 @@ async function applyDocumentUpdate({
|
|
|
1058
1058
|
if (input.channelId === null) {
|
|
1059
1059
|
entity.channelId = null;
|
|
1060
1060
|
} else {
|
|
1061
|
-
const channel = await
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1061
|
+
const channel = await findOneWithDecryption(
|
|
1062
|
+
em,
|
|
1063
|
+
SalesChannel,
|
|
1064
|
+
{
|
|
1065
|
+
id: input.channelId,
|
|
1066
|
+
organizationId,
|
|
1067
|
+
tenantId,
|
|
1068
|
+
deletedAt: null,
|
|
1069
|
+
},
|
|
1070
|
+
{},
|
|
1071
|
+
{ tenantId, organizationId },
|
|
1072
|
+
);
|
|
1067
1073
|
if (!channel) {
|
|
1068
1074
|
throw new CrudHttpError(400, {
|
|
1069
1075
|
error: translate(
|
|
@@ -3793,7 +3799,16 @@ async function restoreQuoteGraph(
|
|
|
3793
3799
|
em: EntityManager,
|
|
3794
3800
|
snapshot: QuoteGraphSnapshot,
|
|
3795
3801
|
): Promise<SalesQuote> {
|
|
3796
|
-
let quote = await
|
|
3802
|
+
let quote = await findOneWithDecryption(
|
|
3803
|
+
em,
|
|
3804
|
+
SalesQuote,
|
|
3805
|
+
{ id: snapshot.quote.id },
|
|
3806
|
+
{},
|
|
3807
|
+
{
|
|
3808
|
+
tenantId: snapshot.quote.tenantId,
|
|
3809
|
+
organizationId: snapshot.quote.organizationId,
|
|
3810
|
+
},
|
|
3811
|
+
);
|
|
3797
3812
|
if (!quote) {
|
|
3798
3813
|
quote = em.create(SalesQuote, {
|
|
3799
3814
|
id: snapshot.quote.id,
|
|
@@ -4085,7 +4100,16 @@ async function restoreOrderGraph(
|
|
|
4085
4100
|
em: EntityManager,
|
|
4086
4101
|
snapshot: OrderGraphSnapshot,
|
|
4087
4102
|
): Promise<SalesOrder> {
|
|
4088
|
-
let order = await
|
|
4103
|
+
let order = await findOneWithDecryption(
|
|
4104
|
+
em,
|
|
4105
|
+
SalesOrder,
|
|
4106
|
+
{ id: snapshot.order.id },
|
|
4107
|
+
{},
|
|
4108
|
+
{
|
|
4109
|
+
tenantId: snapshot.order.tenantId,
|
|
4110
|
+
organizationId: snapshot.order.organizationId,
|
|
4111
|
+
},
|
|
4112
|
+
);
|
|
4089
4113
|
if (!order) {
|
|
4090
4114
|
order = em.create(SalesOrder, {
|
|
4091
4115
|
id: snapshot.order.id,
|
|
@@ -4783,7 +4807,16 @@ const createQuoteCommand: CommandHandler<
|
|
|
4783
4807
|
const after = payload?.after;
|
|
4784
4808
|
if (!after) return;
|
|
4785
4809
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
4786
|
-
const quote = await
|
|
4810
|
+
const quote = await findOneWithDecryption(
|
|
4811
|
+
em,
|
|
4812
|
+
SalesQuote,
|
|
4813
|
+
{ id: after.quote.id },
|
|
4814
|
+
{},
|
|
4815
|
+
{
|
|
4816
|
+
tenantId: after.quote.tenantId,
|
|
4817
|
+
organizationId: after.quote.organizationId,
|
|
4818
|
+
},
|
|
4819
|
+
);
|
|
4787
4820
|
if (!quote) return;
|
|
4788
4821
|
ensureQuoteScope(ctx, quote.organizationId, quote.tenantId);
|
|
4789
4822
|
await em.nativeDelete(SalesQuoteAdjustment, { quote: quote.id });
|
|
@@ -4814,7 +4847,7 @@ const deleteQuoteCommand: CommandHandler<
|
|
|
4814
4847
|
async execute(input, ctx) {
|
|
4815
4848
|
const id = requireId(input, "Quote id is required");
|
|
4816
4849
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
4817
|
-
const quote = await em
|
|
4850
|
+
const quote = await findOneWithDecryption(em, SalesQuote, { id });
|
|
4818
4851
|
if (!quote)
|
|
4819
4852
|
throw new CrudHttpError(404, { error: "Sales quote not found" });
|
|
4820
4853
|
ensureQuoteScope(ctx, quote.organizationId, quote.tenantId);
|
|
@@ -4941,7 +4974,7 @@ const updateQuoteCommand: CommandHandler<
|
|
|
4941
4974
|
async execute(rawInput, ctx) {
|
|
4942
4975
|
const parsed = documentUpdateSchema.parse(rawInput ?? {});
|
|
4943
4976
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
4944
|
-
const quote = await em
|
|
4977
|
+
const quote = await findOneWithDecryption(em, SalesQuote, {
|
|
4945
4978
|
id: parsed.id,
|
|
4946
4979
|
deletedAt: null,
|
|
4947
4980
|
});
|
|
@@ -5173,7 +5206,7 @@ const updateOrderCommand: CommandHandler<
|
|
|
5173
5206
|
async execute(rawInput, ctx) {
|
|
5174
5207
|
const parsed = documentUpdateSchema.parse(rawInput ?? {});
|
|
5175
5208
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
5176
|
-
const order = await em
|
|
5209
|
+
const order = await findOneWithDecryption(em, SalesOrder, {
|
|
5177
5210
|
id: parsed.id,
|
|
5178
5211
|
deletedAt: null,
|
|
5179
5212
|
});
|
|
@@ -5779,7 +5812,16 @@ const createOrderCommand: CommandHandler<
|
|
|
5779
5812
|
const after = payload?.after;
|
|
5780
5813
|
if (!after) return;
|
|
5781
5814
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
5782
|
-
const order = await
|
|
5815
|
+
const order = await findOneWithDecryption(
|
|
5816
|
+
em,
|
|
5817
|
+
SalesOrder,
|
|
5818
|
+
{ id: after.order.id },
|
|
5819
|
+
{},
|
|
5820
|
+
{
|
|
5821
|
+
tenantId: after.order.tenantId,
|
|
5822
|
+
organizationId: after.order.organizationId,
|
|
5823
|
+
},
|
|
5824
|
+
);
|
|
5783
5825
|
if (!order) return;
|
|
5784
5826
|
ensureOrderScope(ctx, order.organizationId, order.tenantId);
|
|
5785
5827
|
await em.nativeDelete(SalesOrderAdjustment, { order: order.id });
|
|
@@ -5810,7 +5852,7 @@ const deleteOrderCommand: CommandHandler<
|
|
|
5810
5852
|
async execute(input, ctx) {
|
|
5811
5853
|
const id = requireId(input, "Order id is required");
|
|
5812
5854
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
5813
|
-
const order = await em
|
|
5855
|
+
const order = await findOneWithDecryption(em, SalesOrder, { id });
|
|
5814
5856
|
if (!order)
|
|
5815
5857
|
throw new CrudHttpError(404, { error: "Sales order not found" });
|
|
5816
5858
|
ensureOrderScope(ctx, order.organizationId, order.tenantId);
|
|
@@ -6246,14 +6288,32 @@ const convertQuoteToOrderCommand: CommandHandler<
|
|
|
6246
6288
|
});
|
|
6247
6289
|
|
|
6248
6290
|
const [addresses, notes, tags] = await Promise.all([
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6291
|
+
findWithDecryption(
|
|
6292
|
+
em,
|
|
6293
|
+
SalesDocumentAddress,
|
|
6294
|
+
{
|
|
6295
|
+
documentId: snapshot.quote.id,
|
|
6296
|
+
documentKind: "quote",
|
|
6297
|
+
},
|
|
6298
|
+
{},
|
|
6299
|
+
{
|
|
6300
|
+
tenantId: snapshot.quote.tenantId,
|
|
6301
|
+
organizationId: snapshot.quote.organizationId,
|
|
6302
|
+
},
|
|
6303
|
+
),
|
|
6304
|
+
findWithDecryption(
|
|
6305
|
+
em,
|
|
6306
|
+
SalesNote,
|
|
6307
|
+
{
|
|
6308
|
+
contextType: "quote",
|
|
6309
|
+
contextId: snapshot.quote.id,
|
|
6310
|
+
},
|
|
6311
|
+
{},
|
|
6312
|
+
{
|
|
6313
|
+
tenantId: snapshot.quote.tenantId,
|
|
6314
|
+
organizationId: snapshot.quote.organizationId,
|
|
6315
|
+
},
|
|
6316
|
+
),
|
|
6257
6317
|
em.find(SalesDocumentTagAssignment, {
|
|
6258
6318
|
documentId: snapshot.quote.id,
|
|
6259
6319
|
documentKind: "quote",
|
|
@@ -6478,7 +6538,7 @@ const orderLineUpsertCommand: CommandHandler<
|
|
|
6478
6538
|
const rawBody = (input?.body as Record<string, unknown> | undefined) ?? {};
|
|
6479
6539
|
const parsed = orderLineUpsertSchema.parse(rawBody);
|
|
6480
6540
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
6481
|
-
const order = await em
|
|
6541
|
+
const order = await findOneWithDecryption(em, SalesOrder, {
|
|
6482
6542
|
id: parsed.orderId,
|
|
6483
6543
|
deletedAt: null,
|
|
6484
6544
|
});
|
|
@@ -6794,7 +6854,7 @@ const orderLineDeleteCommand: CommandHandler<
|
|
|
6794
6854
|
(input?.body as Record<string, unknown> | undefined) ?? {},
|
|
6795
6855
|
);
|
|
6796
6856
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
6797
|
-
const order = await em
|
|
6857
|
+
const order = await findOneWithDecryption(em, SalesOrder, {
|
|
6798
6858
|
id: parsed.orderId,
|
|
6799
6859
|
deletedAt: null,
|
|
6800
6860
|
});
|
|
@@ -6970,7 +7030,7 @@ const quoteLineUpsertCommand: CommandHandler<
|
|
|
6970
7030
|
const rawBody = (input?.body as Record<string, unknown> | undefined) ?? {};
|
|
6971
7031
|
const parsed = quoteLineUpsertSchema.parse(rawBody);
|
|
6972
7032
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
6973
|
-
const quote = await em
|
|
7033
|
+
const quote = await findOneWithDecryption(em, SalesQuote, {
|
|
6974
7034
|
id: parsed.quoteId,
|
|
6975
7035
|
deletedAt: null,
|
|
6976
7036
|
});
|
|
@@ -7282,7 +7342,7 @@ const quoteLineDeleteCommand: CommandHandler<
|
|
|
7282
7342
|
(input?.body as Record<string, unknown> | undefined) ?? {},
|
|
7283
7343
|
);
|
|
7284
7344
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
7285
|
-
const quote = await em
|
|
7345
|
+
const quote = await findOneWithDecryption(em, SalesQuote, {
|
|
7286
7346
|
id: parsed.quoteId,
|
|
7287
7347
|
deletedAt: null,
|
|
7288
7348
|
});
|
|
@@ -7436,7 +7496,7 @@ const orderAdjustmentUpsertCommand: CommandHandler<
|
|
|
7436
7496
|
(input?.body as Record<string, unknown> | undefined) ?? {},
|
|
7437
7497
|
);
|
|
7438
7498
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
7439
|
-
const order = await em
|
|
7499
|
+
const order = await findOneWithDecryption(em, SalesOrder, {
|
|
7440
7500
|
id: parsed.orderId,
|
|
7441
7501
|
deletedAt: null,
|
|
7442
7502
|
});
|
|
@@ -7730,7 +7790,7 @@ const orderAdjustmentDeleteCommand: CommandHandler<
|
|
|
7730
7790
|
(input?.body as Record<string, unknown> | undefined) ?? {},
|
|
7731
7791
|
);
|
|
7732
7792
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
7733
|
-
const order = await em
|
|
7793
|
+
const order = await findOneWithDecryption(em, SalesOrder, {
|
|
7734
7794
|
id: parsed.orderId,
|
|
7735
7795
|
deletedAt: null,
|
|
7736
7796
|
});
|
|
@@ -7895,7 +7955,7 @@ const quoteAdjustmentUpsertCommand: CommandHandler<
|
|
|
7895
7955
|
(input?.body as Record<string, unknown> | undefined) ?? {},
|
|
7896
7956
|
);
|
|
7897
7957
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
7898
|
-
const quote = await em
|
|
7958
|
+
const quote = await findOneWithDecryption(em, SalesQuote, {
|
|
7899
7959
|
id: parsed.quoteId,
|
|
7900
7960
|
deletedAt: null,
|
|
7901
7961
|
});
|
|
@@ -8187,7 +8247,7 @@ const quoteAdjustmentDeleteCommand: CommandHandler<
|
|
|
8187
8247
|
(input?.body as Record<string, unknown> | undefined) ?? {},
|
|
8188
8248
|
);
|
|
8189
8249
|
const em = (ctx.container.resolve("em") as EntityManager).fork();
|
|
8190
|
-
const quote = await em
|
|
8250
|
+
const quote = await findOneWithDecryption(em, SalesQuote, {
|
|
8191
8251
|
id: parsed.quoteId,
|
|
8192
8252
|
deletedAt: null,
|
|
8193
8253
|
});
|
|
@@ -8379,12 +8439,21 @@ const createInvoiceCommand: CommandHandler<
|
|
|
8379
8439
|
|
|
8380
8440
|
// Validate orderId belongs to same org/tenant
|
|
8381
8441
|
if (parsed.orderId) {
|
|
8382
|
-
const orderExists = await
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8442
|
+
const orderExists = await findOneWithDecryption(
|
|
8443
|
+
em,
|
|
8444
|
+
SalesOrder,
|
|
8445
|
+
{
|
|
8446
|
+
id: parsed.orderId,
|
|
8447
|
+
organizationId: parsed.organizationId,
|
|
8448
|
+
tenantId: parsed.tenantId,
|
|
8449
|
+
deletedAt: null,
|
|
8450
|
+
},
|
|
8451
|
+
{},
|
|
8452
|
+
{
|
|
8453
|
+
tenantId: parsed.tenantId,
|
|
8454
|
+
organizationId: parsed.organizationId,
|
|
8455
|
+
},
|
|
8456
|
+
);
|
|
8388
8457
|
if (!orderExists) {
|
|
8389
8458
|
throw new CrudHttpError(400, { error: "Referenced order not found in current scope." });
|
|
8390
8459
|
}
|
|
@@ -8869,12 +8938,21 @@ const createCreditMemoCommand: CommandHandler<
|
|
|
8869
8938
|
|
|
8870
8939
|
// Validate orderId belongs to same org/tenant
|
|
8871
8940
|
if (parsed.orderId) {
|
|
8872
|
-
const orderExists = await
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
|
|
8941
|
+
const orderExists = await findOneWithDecryption(
|
|
8942
|
+
em,
|
|
8943
|
+
SalesOrder,
|
|
8944
|
+
{
|
|
8945
|
+
id: parsed.orderId,
|
|
8946
|
+
organizationId: parsed.organizationId,
|
|
8947
|
+
tenantId: parsed.tenantId,
|
|
8948
|
+
deletedAt: null,
|
|
8949
|
+
},
|
|
8950
|
+
{},
|
|
8951
|
+
{
|
|
8952
|
+
tenantId: parsed.tenantId,
|
|
8953
|
+
organizationId: parsed.organizationId,
|
|
8954
|
+
},
|
|
8955
|
+
);
|
|
8878
8956
|
if (!orderExists) {
|
|
8879
8957
|
throw new CrudHttpError(400, { error: "Referenced order not found in current scope." });
|
|
8880
8958
|
}
|
|
@@ -64,7 +64,7 @@ const noteCrudEvents: CrudEventsConfig = {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
async function loadNoteSnapshot(em: EntityManager, id: string): Promise<NoteSnapshot | null> {
|
|
67
|
-
const note = await em
|
|
67
|
+
const note = await findOneWithDecryption(em, SalesNote, { id }, {})
|
|
68
68
|
if (!note) return null
|
|
69
69
|
return {
|
|
70
70
|
id: note.id,
|
|
@@ -94,7 +94,7 @@ async function requireContext(
|
|
|
94
94
|
quote?: SalesQuote | null
|
|
95
95
|
}> {
|
|
96
96
|
if (contextType === 'order') {
|
|
97
|
-
const order = await em
|
|
97
|
+
const order = await findOneWithDecryption(em, SalesOrder, { id: contextId }, {}, { tenantId, organizationId })
|
|
98
98
|
if (!order) {
|
|
99
99
|
throw new CrudHttpError(404, { error: 'sales.notes.context_not_found' })
|
|
100
100
|
}
|
|
@@ -109,7 +109,7 @@ async function requireContext(
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
if (contextType === 'quote') {
|
|
112
|
-
const quote = await em
|
|
112
|
+
const quote = await findOneWithDecryption(em, SalesQuote, { id: contextId }, {}, { tenantId, organizationId })
|
|
113
113
|
if (!quote) {
|
|
114
114
|
throw new CrudHttpError(404, { error: 'sales.notes.context_not_found' })
|
|
115
115
|
}
|
|
@@ -216,7 +216,7 @@ const createNoteCommand: CommandHandler<NoteCreateInput, { noteId: string; autho
|
|
|
216
216
|
const noteId = logEntry?.resourceId ?? null
|
|
217
217
|
if (!noteId) return
|
|
218
218
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
219
|
-
const existing = await em
|
|
219
|
+
const existing = await findOneWithDecryption(em, SalesNote, { id: noteId }, {})
|
|
220
220
|
if (existing) {
|
|
221
221
|
em.remove(existing)
|
|
222
222
|
await em.flush()
|
|
@@ -272,7 +272,7 @@ const updateNoteCommand: CommandHandler<NoteUpdateInput, { noteId: string }> = {
|
|
|
272
272
|
async execute(rawInput, ctx) {
|
|
273
273
|
const parsed = noteUpdateSchema.parse(rawInput ?? {})
|
|
274
274
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
275
|
-
const note = await em
|
|
275
|
+
const note = await findOneWithDecryption(em, SalesNote, { id: parsed.id }, {})
|
|
276
276
|
if (!note) throw new CrudHttpError(404, { error: 'sales.notes.not_found' })
|
|
277
277
|
ensureTenantScope(ctx, note.tenantId)
|
|
278
278
|
ensureOrganizationScope(ctx, note.organizationId)
|
|
@@ -344,7 +344,7 @@ const updateNoteCommand: CommandHandler<NoteUpdateInput, { noteId: string }> = {
|
|
|
344
344
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
345
345
|
const context = await requireContext(em, before.contextType, before.contextId).catch(() => null)
|
|
346
346
|
if (!context) return
|
|
347
|
-
let note = await em
|
|
347
|
+
let note = await findOneWithDecryption(em, SalesNote, { id: before.id }, {}, { tenantId: before.tenantId, organizationId: before.organizationId })
|
|
348
348
|
if (!note) {
|
|
349
349
|
note = em.create(SalesNote, {
|
|
350
350
|
id: before.id,
|
|
@@ -404,7 +404,7 @@ const deleteNoteCommand: CommandHandler<{ body?: Record<string, unknown>; query?
|
|
|
404
404
|
async execute(input, ctx) {
|
|
405
405
|
const id = requireId(input, 'Note id required')
|
|
406
406
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
407
|
-
const note = await em
|
|
407
|
+
const note = await findOneWithDecryption(em, SalesNote, { id }, {})
|
|
408
408
|
if (!note) throw new CrudHttpError(404, { error: 'sales.notes.not_found' })
|
|
409
409
|
ensureTenantScope(ctx, note.tenantId)
|
|
410
410
|
ensureOrganizationScope(ctx, note.organizationId)
|
|
@@ -453,7 +453,7 @@ const deleteNoteCommand: CommandHandler<{ body?: Record<string, unknown>; query?
|
|
|
453
453
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
454
454
|
const context = await requireContext(em, before.contextType, before.contextId).catch(() => null)
|
|
455
455
|
if (!context) return
|
|
456
|
-
let note = await em
|
|
456
|
+
let note = await findOneWithDecryption(em, SalesNote, { id: before.id }, {}, { tenantId: before.tenantId, organizationId: before.organizationId })
|
|
457
457
|
if (!note) {
|
|
458
458
|
note = em.create(SalesNote, {
|
|
459
459
|
id: before.id,
|
|
@@ -253,12 +253,7 @@ async function recomputeOrderPaymentTotals(
|
|
|
253
253
|
const scope = { organizationId: order.organizationId, tenantId: order.tenantId }
|
|
254
254
|
|
|
255
255
|
if (options?.lock) {
|
|
256
|
-
|
|
257
|
-
// SalesOrder field is read, so decryption (findOneWithDecryption) is unnecessary.
|
|
258
|
-
// Scope filter is defence-in-depth (#2111): the caller is expected to pass a
|
|
259
|
-
// correctly scoped order, but filtering here ensures we never lock a foreign
|
|
260
|
-
// tenant's row even if a caller messed up.
|
|
261
|
-
await em.findOne(SalesOrder, { id: orderId, ...scope }, { lockMode: LockMode.PESSIMISTIC_WRITE })
|
|
256
|
+
await findOneWithDecryption(em, SalesOrder, { id: orderId, ...scope }, { lockMode: LockMode.PESSIMISTIC_WRITE }, scope)
|
|
262
257
|
}
|
|
263
258
|
|
|
264
259
|
const allocations = await findWithDecryption(
|
|
@@ -666,11 +661,9 @@ const createPaymentCommand: CommandHandler<
|
|
|
666
661
|
if (recomputed && (!totals || orderId === after.orderId)) {
|
|
667
662
|
totals = recomputed
|
|
668
663
|
}
|
|
669
|
-
// Raw findOne is intentional: the order is fetched only to read its id/scope
|
|
670
|
-
// for cache invalidation — no encrypted field is read, so decryption is unnecessary.
|
|
671
664
|
// Scope filter (#2111): never cache-invalidate a foreign tenant's order even
|
|
672
665
|
// if a snapshot's orderId was somehow tampered with.
|
|
673
|
-
const target = await em
|
|
666
|
+
const target = await findOneWithDecryption(em, SalesOrder, { id: orderId, organizationId: after.organizationId, tenantId: after.tenantId }, {}, { tenantId: after.tenantId, organizationId: after.organizationId })
|
|
674
667
|
if (target) {
|
|
675
668
|
ensureSameScope(target, after.organizationId, after.tenantId)
|
|
676
669
|
await invalidateOrderCache(ctx.container, target, ctx.auth?.tenantId ?? null)
|
|
@@ -939,10 +932,12 @@ const updatePaymentCommand: CommandHandler<
|
|
|
939
932
|
totals = await em.transactional(async (tx) => {
|
|
940
933
|
// Scope filter (#2111): never lock or recompute totals on a foreign
|
|
941
934
|
// tenant's order, even if payment.order somehow points there.
|
|
942
|
-
const lockedOrder = await
|
|
935
|
+
const lockedOrder = await findOneWithDecryption(
|
|
936
|
+
tx,
|
|
943
937
|
SalesOrder,
|
|
944
938
|
{ id: nextOrderId, organizationId: payment.organizationId, tenantId: payment.tenantId },
|
|
945
939
|
{ lockMode: LockMode.PESSIMISTIC_WRITE },
|
|
940
|
+
{ tenantId: payment.tenantId, organizationId: payment.organizationId },
|
|
946
941
|
)
|
|
947
942
|
if (!lockedOrder) return undefined
|
|
948
943
|
ensureSameScope(lockedOrder, payment.organizationId, payment.tenantId)
|
|
@@ -951,10 +946,8 @@ const updatePaymentCommand: CommandHandler<
|
|
|
951
946
|
return result
|
|
952
947
|
})
|
|
953
948
|
if (totals) {
|
|
954
|
-
// Raw findOne is intentional: the order is fetched only to read its id/scope
|
|
955
|
-
// for cache invalidation — no encrypted field is read, so decryption is unnecessary.
|
|
956
949
|
// Scope filter (#2111): same rationale as the lock above.
|
|
957
|
-
const nextOrder = await em
|
|
950
|
+
const nextOrder = await findOneWithDecryption(em, SalesOrder, { id: nextOrderId, organizationId: payment.organizationId, tenantId: payment.tenantId }, {}, { tenantId: payment.tenantId, organizationId: payment.organizationId })
|
|
958
951
|
if (nextOrder) {
|
|
959
952
|
ensureSameScope(nextOrder, payment.organizationId, payment.tenantId)
|
|
960
953
|
await invalidateOrderCache(ctx.container, nextOrder, ctx.auth?.tenantId ?? null)
|
|
@@ -966,10 +959,12 @@ const updatePaymentCommand: CommandHandler<
|
|
|
966
959
|
// Scope filter (#2111): previousOrder was already loaded via the
|
|
967
960
|
// payment's scope, so its tenant/org match the payment's. Filter
|
|
968
961
|
// the lock query the same way as defence-in-depth.
|
|
969
|
-
const lockedOrder = await
|
|
962
|
+
const lockedOrder = await findOneWithDecryption(
|
|
963
|
+
tx,
|
|
970
964
|
SalesOrder,
|
|
971
965
|
{ id: previousOrder.id, organizationId: payment.organizationId, tenantId: payment.tenantId },
|
|
972
966
|
{ lockMode: LockMode.PESSIMISTIC_WRITE },
|
|
967
|
+
{ tenantId: payment.tenantId, organizationId: payment.organizationId },
|
|
973
968
|
)
|
|
974
969
|
if (!lockedOrder) return
|
|
975
970
|
ensureSameScope(lockedOrder, payment.organizationId, payment.tenantId)
|
|
@@ -1098,10 +1093,12 @@ const deletePaymentCommand: CommandHandler<
|
|
|
1098
1093
|
const recomputed = await em.transactional(async (tx) => {
|
|
1099
1094
|
// Scope filter (#2111): never lock or recompute totals on a foreign
|
|
1100
1095
|
// tenant's order, even if a payment allocation somehow points there.
|
|
1101
|
-
const lockedOrder = await
|
|
1096
|
+
const lockedOrder = await findOneWithDecryption(
|
|
1097
|
+
tx,
|
|
1102
1098
|
SalesOrder,
|
|
1103
1099
|
{ id: orderId, organizationId: payment.organizationId, tenantId: payment.tenantId },
|
|
1104
1100
|
{ lockMode: LockMode.PESSIMISTIC_WRITE },
|
|
1101
|
+
{ tenantId: payment.tenantId, organizationId: payment.organizationId },
|
|
1105
1102
|
)
|
|
1106
1103
|
if (!lockedOrder) return undefined
|
|
1107
1104
|
ensureSameScope(lockedOrder, payment.organizationId, payment.tenantId)
|
|
@@ -1112,10 +1109,8 @@ const deletePaymentCommand: CommandHandler<
|
|
|
1112
1109
|
if (recomputed && (!totals || (primaryOrderId && orderId === primaryOrderId))) {
|
|
1113
1110
|
totals = recomputed
|
|
1114
1111
|
}
|
|
1115
|
-
// Raw findOne is intentional: the order is fetched only to read its id/scope
|
|
1116
|
-
// for cache invalidation — no encrypted field is read, so decryption is unnecessary.
|
|
1117
1112
|
// Scope filter (#2111): same rationale as the lock above.
|
|
1118
|
-
const target = await em
|
|
1113
|
+
const target = await findOneWithDecryption(em, SalesOrder, { id: orderId, organizationId: payment.organizationId, tenantId: payment.tenantId }, {}, { tenantId: payment.tenantId, organizationId: payment.organizationId })
|
|
1119
1114
|
if (target) {
|
|
1120
1115
|
ensureSameScope(target, payment.organizationId, payment.tenantId)
|
|
1121
1116
|
await invalidateOrderCache(ctx.container, target, ctx.auth?.tenantId ?? null)
|
|
@@ -219,7 +219,7 @@ export async function loadShipmentSnapshot(em: EntityManager, id: string): Promi
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
export async function restoreShipmentSnapshot(em: EntityManager, snapshot: ShipmentSnapshot): Promise<void> {
|
|
222
|
-
const order = await em
|
|
222
|
+
const order = await findOneWithDecryption(em, SalesOrder, { id: snapshot.orderId }, {}, { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId })
|
|
223
223
|
if (!order) return
|
|
224
224
|
const existing = await em.findOne(SalesShipment, { id: snapshot.id })
|
|
225
225
|
const entity =
|
|
@@ -330,8 +330,12 @@ async function recomputeFulfilledQuantities(em: EntityManager, order: SalesOrder
|
|
|
330
330
|
})
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
-
async function loadOrder(
|
|
334
|
-
|
|
333
|
+
async function loadOrder(
|
|
334
|
+
em: EntityManager,
|
|
335
|
+
id: string,
|
|
336
|
+
scope?: { tenantId: string; organizationId: string }
|
|
337
|
+
): Promise<SalesOrder> {
|
|
338
|
+
const order = await findOneWithDecryption(em, SalesOrder, { id, deletedAt: null }, {}, scope)
|
|
335
339
|
if (!order) throw new CrudHttpError(404, { error: 'sales.shipments.not_found' })
|
|
336
340
|
return order
|
|
337
341
|
}
|
|
@@ -436,7 +440,7 @@ const createShipmentCommand: CommandHandler<ShipmentCreateInput, { shipmentId: s
|
|
|
436
440
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
437
441
|
const { translate } = await resolveTranslations()
|
|
438
442
|
const shipment = await em.transactional(async (tx) => {
|
|
439
|
-
const order = await loadOrder(tx, input.orderId)
|
|
443
|
+
const order = await loadOrder(tx, input.orderId, { tenantId: input.tenantId, organizationId: input.organizationId })
|
|
440
444
|
ensureSameScope(order, input.organizationId, input.tenantId)
|
|
441
445
|
const { items: normalizedItems, lineMap } = await validateShipmentItems({
|
|
442
446
|
em: tx,
|
|
@@ -601,7 +605,7 @@ const createShipmentCommand: CommandHandler<ShipmentCreateInput, { shipmentId: s
|
|
|
601
605
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
602
606
|
await em.transactional(async (tx) => {
|
|
603
607
|
await restoreShipmentSnapshot(tx, after)
|
|
604
|
-
const order = await tx
|
|
608
|
+
const order = await findOneWithDecryption(tx, SalesOrder, { id: after.orderId }, {}, { tenantId: after.tenantId, organizationId: after.organizationId })
|
|
605
609
|
await tx.flush()
|
|
606
610
|
if (order) {
|
|
607
611
|
await recomputeFulfilledQuantities(tx, order)
|
|
@@ -872,7 +876,7 @@ const updateShipmentCommand: CommandHandler<ShipmentUpdateInput, { shipmentId: s
|
|
|
872
876
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
873
877
|
await em.transactional(async (tx) => {
|
|
874
878
|
await restoreShipmentSnapshot(tx, before)
|
|
875
|
-
const order = await tx
|
|
879
|
+
const order = await findOneWithDecryption(tx, SalesOrder, { id: before.orderId }, {}, { tenantId: before.tenantId, organizationId: before.organizationId })
|
|
876
880
|
await tx.flush()
|
|
877
881
|
if (order) {
|
|
878
882
|
await recomputeFulfilledQuantities(tx, order)
|
|
@@ -1021,7 +1025,7 @@ const deleteShipmentCommand: CommandHandler<
|
|
|
1021
1025
|
const em = (ctx.container.resolve('em') as EntityManager).fork()
|
|
1022
1026
|
await em.transactional(async (tx) => {
|
|
1023
1027
|
await restoreShipmentSnapshot(tx, snapshot)
|
|
1024
|
-
const order = await tx
|
|
1028
|
+
const order = await findOneWithDecryption(tx, SalesOrder, { id: snapshot.orderId }, {}, { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId })
|
|
1025
1029
|
await tx.flush()
|
|
1026
1030
|
if (order) {
|
|
1027
1031
|
await recomputeFulfilledQuantities(tx, order)
|