@porulle/core 0.8.0 → 0.10.0
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/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +26 -7
- package/dist/auth/setup.d.ts.map +1 -1
- package/dist/auth/setup.js +7 -5
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +3 -1
- package/dist/config/types.d.ts +13 -3
- package/dist/config/types.d.ts.map +1 -1
- package/dist/generated/plugin-manifest.d.ts +3 -0
- package/dist/generated/plugin-manifest.d.ts.map +1 -1
- package/dist/generated/plugin-manifest.js +1 -0
- package/dist/hooks/checkout-completion.d.ts.map +1 -1
- package/dist/hooks/checkout-completion.js +2 -2
- package/dist/hooks/checkout.d.ts.map +1 -1
- package/dist/hooks/checkout.js +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/interfaces/rest/router.d.ts.map +1 -1
- package/dist/interfaces/rest/router.js +2 -0
- package/dist/interfaces/rest/routes/admin/staff.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/admin/staff.js +37 -3
- package/dist/interfaces/rest/routes/checkout.d.ts +9 -0
- package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/checkout.js +82 -22
- package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/inventory.js +13 -0
- package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/orders.js +13 -0
- package/dist/interfaces/rest/schemas/catalog.d.ts +391 -0
- package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/orders.js +2 -2
- package/dist/interfaces/rest/schemas/responses.d.ts +85 -0
- package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/search.d.ts +17 -0
- package/dist/interfaces/rest/schemas/search.d.ts.map +1 -1
- package/dist/kernel/database/scoped-db.d.ts +2 -1
- package/dist/kernel/database/scoped-db.d.ts.map +1 -1
- package/dist/kernel/database/scoped-db.js +74 -15
- package/dist/kernel/errors.d.ts +12 -0
- package/dist/kernel/errors.d.ts.map +1 -1
- package/dist/kernel/errors.js +16 -0
- package/dist/kernel/jobs/adapter.d.ts +27 -8
- package/dist/kernel/jobs/adapter.d.ts.map +1 -1
- package/dist/kernel/jobs/adapter.js +1 -2
- package/dist/kernel/jobs/drizzle-adapter.d.ts +13 -3
- package/dist/kernel/jobs/drizzle-adapter.d.ts.map +1 -1
- package/dist/kernel/jobs/drizzle-adapter.js +46 -5
- package/dist/kernel/jobs/runner.d.ts +2 -1
- package/dist/kernel/jobs/runner.d.ts.map +1 -1
- package/dist/kernel/jobs/runner.js +100 -15
- package/dist/kernel/jobs/types.d.ts +13 -0
- package/dist/kernel/jobs/types.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.d.ts +5 -0
- package/dist/kernel/plugin/manifest.d.ts.map +1 -1
- package/dist/kernel/plugin/manifest.js +9 -0
- package/dist/modules/analytics/drizzle-adapter.d.ts.map +1 -1
- package/dist/modules/analytics/drizzle-adapter.js +16 -3
- package/dist/modules/cart/service.d.ts.map +1 -1
- package/dist/modules/cart/service.js +3 -2
- package/dist/modules/catalog/entity-service.d.ts +1 -0
- package/dist/modules/catalog/entity-service.d.ts.map +1 -1
- package/dist/modules/catalog/entity-service.js +49 -3
- package/dist/modules/catalog/repository/index.d.ts +2 -2
- package/dist/modules/catalog/repository/index.d.ts.map +1 -1
- package/dist/modules/catalog/repository/index.js +9 -1
- package/dist/modules/catalog/schema.d.ts +51 -0
- package/dist/modules/catalog/schema.d.ts.map +1 -1
- package/dist/modules/catalog/schema.js +12 -1
- package/dist/modules/catalog/schemas.d.ts +3 -1
- package/dist/modules/catalog/schemas.d.ts.map +1 -1
- package/dist/modules/channels/adapter.d.ts +117 -0
- package/dist/modules/channels/adapter.d.ts.map +1 -0
- package/dist/modules/channels/adapter.js +3 -0
- package/dist/modules/customers/service.d.ts.map +1 -1
- package/dist/modules/customers/service.js +6 -0
- package/dist/modules/inventory/repository/index.js +4 -4
- package/dist/modules/inventory/service.d.ts.map +1 -1
- package/dist/modules/inventory/service.js +7 -3
- package/dist/modules/orders/schema.d.ts +17 -0
- package/dist/modules/orders/schema.d.ts.map +1 -1
- package/dist/modules/orders/schema.js +4 -0
- package/dist/modules/orders/service.d.ts +16 -2
- package/dist/modules/orders/service.d.ts.map +1 -1
- package/dist/modules/orders/service.js +226 -26
- package/dist/modules/orders/stale-order-cleanup.d.ts.map +1 -1
- package/dist/modules/orders/stale-order-cleanup.js +37 -16
- package/dist/modules/pricing/service.d.ts.map +1 -1
- package/dist/modules/pricing/service.js +5 -5
- package/dist/modules/promotions/repository/index.d.ts +1 -1
- package/dist/modules/promotions/repository/index.d.ts.map +1 -1
- package/dist/modules/promotions/repository/index.js +6 -3
- package/dist/modules/promotions/service.d.ts +1 -0
- package/dist/modules/promotions/service.d.ts.map +1 -1
- package/dist/modules/promotions/service.js +1 -1
- package/dist/modules/webhooks/repository/index.d.ts +1 -1
- package/dist/modules/webhooks/repository/index.d.ts.map +1 -1
- package/dist/modules/webhooks/repository/index.js +16 -10
- package/dist/runtime/kernel.d.ts.map +1 -1
- package/dist/runtime/kernel.js +14 -2
- package/dist/runtime/server.d.ts +1 -4
- package/dist/runtime/server.d.ts.map +1 -1
- package/dist/runtime/server.js +16 -34
- package/dist/test-utils/create-test-config.d.ts.map +1 -1
- package/dist/test-utils/create-test-config.js +2 -0
- package/dist/test-utils/order-test-helpers.d.ts +12 -0
- package/dist/test-utils/order-test-helpers.d.ts.map +1 -0
- package/dist/test-utils/order-test-helpers.js +18 -0
- package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -1
- package/dist/test-utils/rest-api-test-utils.js +1 -0
- package/dist/test-utils/test-actors.js +1 -1
- package/dist/testing.d.ts +1 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +1 -0
- package/package.json +1 -1
- package/src/auth/middleware.ts +26 -7
- package/src/auth/setup.ts +10 -4
- package/src/config/defaults.ts +3 -1
- package/src/config/types.ts +16 -3
- package/src/generated/plugin-capabilities.d.ts +1 -0
- package/src/generated/plugin-manifest.ts +1 -0
- package/src/generated/plugin-repositories.d.ts +1 -0
- package/src/hooks/checkout-completion.ts +22 -16
- package/src/hooks/checkout.ts +11 -8
- package/src/index.ts +31 -1
- package/src/interfaces/rest/router.ts +2 -0
- package/src/interfaces/rest/routes/admin/staff.ts +45 -3
- package/src/interfaces/rest/routes/checkout.ts +94 -28
- package/src/interfaces/rest/routes/inventory.ts +14 -0
- package/src/interfaces/rest/routes/orders.ts +11 -0
- package/src/interfaces/rest/schemas/orders.ts +2 -2
- package/src/kernel/database/scoped-db.ts +83 -15
- package/src/kernel/errors.ts +20 -0
- package/src/kernel/jobs/adapter.ts +39 -8
- package/src/kernel/jobs/drizzle-adapter.ts +72 -9
- package/src/kernel/jobs/runner.ts +194 -72
- package/src/kernel/jobs/types.ts +25 -0
- package/src/kernel/plugin/manifest.ts +15 -0
- package/src/modules/analytics/drizzle-adapter.ts +19 -3
- package/src/modules/cart/service.ts +3 -2
- package/src/modules/catalog/entity-service.ts +37 -2
- package/src/modules/catalog/repository/index.ts +13 -3
- package/src/modules/catalog/schema.ts +33 -18
- package/src/modules/catalog/schemas.ts +3 -1
- package/src/modules/channels/adapter.ts +139 -0
- package/src/modules/customers/service.ts +6 -0
- package/src/modules/inventory/repository/index.ts +4 -4
- package/src/modules/inventory/service.ts +12 -2
- package/src/modules/orders/schema.ts +4 -0
- package/src/modules/orders/service.ts +404 -64
- package/src/modules/orders/stale-order-cleanup.ts +52 -27
- package/src/modules/pricing/service.ts +5 -6
- package/src/modules/promotions/repository/index.ts +6 -2
- package/src/modules/promotions/service.ts +2 -0
- package/src/modules/webhooks/repository/index.ts +16 -14
- package/src/runtime/kernel.ts +16 -2
- package/src/runtime/server.ts +22 -42
- package/src/test-utils/create-test-config.ts +2 -0
- package/src/test-utils/order-test-helpers.ts +26 -0
- package/src/test-utils/rest-api-test-utils.ts +1 -0
- package/src/test-utils/test-actors.ts +1 -1
- package/src/testing.ts +1 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AnyPgColumn } from "drizzle-orm/pg-core";
|
|
2
|
+
import { sql } from "drizzle-orm";
|
|
2
3
|
import {
|
|
3
4
|
boolean,
|
|
4
5
|
index,
|
|
@@ -17,6 +18,8 @@ export const sellableEntities = pgTable(
|
|
|
17
18
|
{
|
|
18
19
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
19
20
|
organizationId: text("organization_id").notNull().references(() => organization.id, { onDelete: "cascade" }),
|
|
21
|
+
// Connected-store id when externally sourced; null means platform-native.
|
|
22
|
+
sourceStoreId: text("source_store_id"),
|
|
20
23
|
type: text("type").notNull(),
|
|
21
24
|
slug: text("slug").notNull(),
|
|
22
25
|
status: text("status", {
|
|
@@ -179,24 +182,36 @@ export const optionValues = pgTable("option_values", {
|
|
|
179
182
|
metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
|
|
180
183
|
});
|
|
181
184
|
|
|
182
|
-
export const variants = pgTable(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
})
|
|
185
|
+
export const variants = pgTable(
|
|
186
|
+
"variants",
|
|
187
|
+
{
|
|
188
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
189
|
+
entityId: uuid("entity_id")
|
|
190
|
+
.references(() => sellableEntities.id, { onDelete: "cascade" })
|
|
191
|
+
.notNull(),
|
|
192
|
+
organizationId: text("organization_id").notNull().references(() => organization.id, { onDelete: "cascade" }),
|
|
193
|
+
sourceStoreId: text("source_store_id"),
|
|
194
|
+
sku: text("sku"),
|
|
195
|
+
barcode: text("barcode"),
|
|
196
|
+
// Overrides the entity's taxClass when set (issue #57).
|
|
197
|
+
taxClass: text("tax_class"),
|
|
198
|
+
status: text("status", { enum: ["active", "discontinued"] })
|
|
199
|
+
.notNull()
|
|
200
|
+
.default("active"),
|
|
201
|
+
sortOrder: integer("sort_order").notNull().default(0),
|
|
202
|
+
metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
|
|
203
|
+
},
|
|
204
|
+
(table) => ({
|
|
205
|
+
barcodeIdx: index("idx_variants_barcode").on(table.barcode),
|
|
206
|
+
skuIdx: index("idx_variants_sku").on(table.sku),
|
|
207
|
+
nativeSkuUnique: uniqueIndex("variants_native_org_sku_unique")
|
|
208
|
+
.on(table.organizationId, table.sku)
|
|
209
|
+
.where(sql`${table.sourceStoreId} IS NULL AND ${table.sku} IS NOT NULL`),
|
|
210
|
+
sourceSkuUnique: uniqueIndex("variants_source_store_sku_unique")
|
|
211
|
+
.on(table.sourceStoreId, table.sku)
|
|
212
|
+
.where(sql`${table.sourceStoreId} IS NOT NULL AND ${table.sku} IS NOT NULL`),
|
|
213
|
+
}),
|
|
214
|
+
);
|
|
200
215
|
|
|
201
216
|
export const variantOptionValues = pgTable("variant_option_values", {
|
|
202
217
|
variantId: uuid("variant_id")
|
|
@@ -92,7 +92,9 @@ export const GenerateVariantsBodySchema = z
|
|
|
92
92
|
|
|
93
93
|
// ─── Derived Input Types ─────────────────────────────────────────────────────
|
|
94
94
|
|
|
95
|
-
export type CreateEntityInput = z.infer<typeof CreateEntityBodySchema
|
|
95
|
+
export type CreateEntityInput = z.infer<typeof CreateEntityBodySchema> & {
|
|
96
|
+
sourceStoreId?: string;
|
|
97
|
+
};
|
|
96
98
|
|
|
97
99
|
export type UpdateEntityInput = z.infer<typeof UpdateEntityBodySchema>;
|
|
98
100
|
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { Result } from "../../kernel/result.js";
|
|
2
|
+
|
|
3
|
+
export interface ChannelConnectorError {
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
retriable?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ChannelConnectorCapabilities {
|
|
10
|
+
readonly importCatalog: boolean;
|
|
11
|
+
readonly importInventory: boolean;
|
|
12
|
+
readonly pushOrder: boolean;
|
|
13
|
+
readonly receiveWebhooks: boolean;
|
|
14
|
+
readonly reserve?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ChannelStore {
|
|
18
|
+
id: string;
|
|
19
|
+
organizationId: string;
|
|
20
|
+
provider: string;
|
|
21
|
+
credentials: Record<string, unknown>;
|
|
22
|
+
storeDomain: string;
|
|
23
|
+
status: "connected" | "disconnected" | "error";
|
|
24
|
+
webhookSecret: string | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ChannelCatalogVariant {
|
|
28
|
+
externalId: string;
|
|
29
|
+
sku?: string;
|
|
30
|
+
barcode?: string;
|
|
31
|
+
metadata?: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ChannelCatalogItem {
|
|
35
|
+
externalId: string;
|
|
36
|
+
slug: string;
|
|
37
|
+
title: string;
|
|
38
|
+
description?: string;
|
|
39
|
+
variants: ChannelCatalogVariant[];
|
|
40
|
+
metadata?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ChannelCatalogPage {
|
|
44
|
+
items: ChannelCatalogItem[];
|
|
45
|
+
nextCursor?: string | null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ChannelInventoryLevel {
|
|
49
|
+
externalId: string;
|
|
50
|
+
available: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ChannelOrderLine {
|
|
54
|
+
externalVariantId: string;
|
|
55
|
+
sku?: string;
|
|
56
|
+
title: string;
|
|
57
|
+
quantity: number;
|
|
58
|
+
unitPrice: number;
|
|
59
|
+
totalPrice: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ChannelOrderSlice {
|
|
63
|
+
orderId: string;
|
|
64
|
+
currency: string;
|
|
65
|
+
grandTotal: number;
|
|
66
|
+
lines: ChannelOrderLine[];
|
|
67
|
+
customer: {
|
|
68
|
+
name: string;
|
|
69
|
+
email: string;
|
|
70
|
+
shippingAddress: Record<string, unknown>;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface ChannelPushOrderResult {
|
|
75
|
+
remoteOrderId: string;
|
|
76
|
+
remoteUrl?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface ChannelOrderStatus {
|
|
80
|
+
status: "pending" | "confirmed" | "failed" | "cancelled" | "fulfilled";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface ChannelWebhookEvent {
|
|
84
|
+
id: string;
|
|
85
|
+
type: string;
|
|
86
|
+
data: unknown;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface ChannelReservation {
|
|
90
|
+
id: string;
|
|
91
|
+
expiresAt?: Date;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface ChannelRefundResult {
|
|
95
|
+
remoteRefundId: string;
|
|
96
|
+
status: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface ChannelConnector {
|
|
100
|
+
readonly providerId: string;
|
|
101
|
+
readonly capabilities: ChannelConnectorCapabilities;
|
|
102
|
+
buildAuthUrl?(params: {
|
|
103
|
+
storeDomain: string;
|
|
104
|
+
state: string;
|
|
105
|
+
redirectUri: string;
|
|
106
|
+
callbackUri: string;
|
|
107
|
+
scopes: string[];
|
|
108
|
+
}): Result<string, ChannelConnectorError>;
|
|
109
|
+
completeAuth?(
|
|
110
|
+
request: Request,
|
|
111
|
+
ctx: { storeDomain: string },
|
|
112
|
+
): Promise<Result<{ credentials: Record<string, unknown>; storeDomain: string }, ChannelConnectorError>>;
|
|
113
|
+
importCatalog(store: ChannelStore, cursor?: string): Promise<Result<ChannelCatalogPage>>;
|
|
114
|
+
fetchInventory(store: ChannelStore, ids?: string[]): Promise<Result<ChannelInventoryLevel[]>>;
|
|
115
|
+
pushOrder(store: ChannelStore, slice: ChannelOrderSlice): Promise<Result<ChannelPushOrderResult, ChannelConnectorError>>;
|
|
116
|
+
fetchOrderStatus(store: ChannelStore, remoteId: string): Promise<Result<ChannelOrderStatus, ChannelConnectorError>>;
|
|
117
|
+
verifyWebhook(store: ChannelStore, request: Request): Promise<Result<ChannelWebhookEvent>>;
|
|
118
|
+
verifyAppWebhook?(
|
|
119
|
+
request: Request,
|
|
120
|
+
): Promise<Result<{ topic: string; shopDomain: string; data: unknown }, ChannelConnectorError>>;
|
|
121
|
+
registerWebhooks?(
|
|
122
|
+
store: ChannelStore,
|
|
123
|
+
topics: string[],
|
|
124
|
+
callbackUrl: string,
|
|
125
|
+
): Promise<Result<{ registered: number }, ChannelConnectorError>>;
|
|
126
|
+
reserve?(
|
|
127
|
+
store: ChannelStore,
|
|
128
|
+
lines: ChannelOrderLine[],
|
|
129
|
+
): Promise<Result<ChannelReservation>>;
|
|
130
|
+
refundExecute(
|
|
131
|
+
store: ChannelStore,
|
|
132
|
+
slice: ChannelOrderSlice,
|
|
133
|
+
amount: number,
|
|
134
|
+
): Promise<Result<ChannelRefundResult>>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function defineChannelConnector<T extends ChannelConnector>(connector: T): T {
|
|
138
|
+
return connector;
|
|
139
|
+
}
|
|
@@ -241,6 +241,12 @@ export class CustomerService {
|
|
|
241
241
|
ctx?: TxContext,
|
|
242
242
|
options?: { replaceMetadata?: boolean },
|
|
243
243
|
): Promise<Result<Customer>> {
|
|
244
|
+
try {
|
|
245
|
+
assertPermission(actor ?? null, "customers:update");
|
|
246
|
+
} catch (error) {
|
|
247
|
+
return Err(toCommerceError(error));
|
|
248
|
+
}
|
|
249
|
+
|
|
244
250
|
const orgId = resolveOrgId(actor ?? ctx?.actor ?? null);
|
|
245
251
|
const existing = await this.repo.findById(orgId, id, ctx);
|
|
246
252
|
if (!existing) return Err(new CommerceNotFoundError("Customer not found."));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { eq, and, inArray, isNull, sql } from "drizzle-orm";
|
|
2
|
-
import { CommerceValidationError } from "../../../kernel/errors.js";
|
|
2
|
+
import { CommerceValidationError, INVENTORY_RECORD_NOT_FOUND_MESSAGE } from "../../../kernel/errors.js";
|
|
3
3
|
import type { TxContext } from "../../../kernel/database/tx-context.js";
|
|
4
4
|
import type {
|
|
5
5
|
DrizzleDatabase,
|
|
@@ -495,7 +495,7 @@ export class InventoryRepository {
|
|
|
495
495
|
if (!level) {
|
|
496
496
|
return {
|
|
497
497
|
ok: false,
|
|
498
|
-
reason:
|
|
498
|
+
reason: INVENTORY_RECORD_NOT_FOUND_MESSAGE,
|
|
499
499
|
};
|
|
500
500
|
}
|
|
501
501
|
|
|
@@ -550,7 +550,7 @@ export class InventoryRepository {
|
|
|
550
550
|
if (!level) {
|
|
551
551
|
return {
|
|
552
552
|
ok: false,
|
|
553
|
-
reason:
|
|
553
|
+
reason: INVENTORY_RECORD_NOT_FOUND_MESSAGE,
|
|
554
554
|
};
|
|
555
555
|
}
|
|
556
556
|
|
|
@@ -600,7 +600,7 @@ export class InventoryRepository {
|
|
|
600
600
|
if (!level) {
|
|
601
601
|
return {
|
|
602
602
|
ok: false,
|
|
603
|
-
reason:
|
|
603
|
+
reason: INVENTORY_RECORD_NOT_FOUND_MESSAGE,
|
|
604
604
|
};
|
|
605
605
|
}
|
|
606
606
|
|
|
@@ -3,8 +3,10 @@ import { assertPermission } from "../../auth/permissions.js";
|
|
|
3
3
|
import type { Actor } from "../../auth/types.js";
|
|
4
4
|
import type { CommerceConfig } from "../../config/types.js";
|
|
5
5
|
import {
|
|
6
|
+
CommerceInventoryRecordNotFoundError,
|
|
6
7
|
CommerceNotFoundError,
|
|
7
8
|
CommerceValidationError,
|
|
9
|
+
INVENTORY_RECORD_NOT_FOUND_MESSAGE,
|
|
8
10
|
toCommerceError,
|
|
9
11
|
} from "../../kernel/errors.js";
|
|
10
12
|
import { runAfterHooks } from "../../kernel/hooks/executor.js";
|
|
@@ -232,7 +234,11 @@ export class InventoryService {
|
|
|
232
234
|
);
|
|
233
235
|
|
|
234
236
|
if (!reserveResult.ok) {
|
|
235
|
-
return Err(
|
|
237
|
+
return Err(
|
|
238
|
+
reserveResult.reason === INVENTORY_RECORD_NOT_FOUND_MESSAGE
|
|
239
|
+
? new CommerceInventoryRecordNotFoundError()
|
|
240
|
+
: new CommerceValidationError(reserveResult.reason),
|
|
241
|
+
);
|
|
236
242
|
}
|
|
237
243
|
|
|
238
244
|
await this.repo.createMovement(
|
|
@@ -281,7 +287,11 @@ export class InventoryService {
|
|
|
281
287
|
);
|
|
282
288
|
|
|
283
289
|
if (!releaseResult.ok) {
|
|
284
|
-
return Err(
|
|
290
|
+
return Err(
|
|
291
|
+
releaseResult.reason === INVENTORY_RECORD_NOT_FOUND_MESSAGE
|
|
292
|
+
? new CommerceInventoryRecordNotFoundError()
|
|
293
|
+
: new CommerceValidationError(releaseResult.reason),
|
|
294
|
+
);
|
|
285
295
|
}
|
|
286
296
|
|
|
287
297
|
await this.repo.createMovement(
|
|
@@ -50,6 +50,10 @@ export const orderLineItems = pgTable("order_line_items", {
|
|
|
50
50
|
quantity: integer("quantity").notNull(),
|
|
51
51
|
unitPrice: integer("unit_price").notNull(),
|
|
52
52
|
totalPrice: integer("total_price").notNull(),
|
|
53
|
+
// Price provenance (matches Medusa's `is_custom_price`): true when a staff
|
|
54
|
+
// actor with `orders:manage` supplied an explicit override price rather than
|
|
55
|
+
// the server-derived catalog price. Audit + reporting signal.
|
|
56
|
+
isCustomPrice: boolean("is_custom_price").notNull().default(false),
|
|
53
57
|
taxAmount: integer("tax_amount").notNull().default(0),
|
|
54
58
|
discountAmount: integer("discount_amount").notNull().default(0),
|
|
55
59
|
// Units already refunded on this line (issue #52) — line-level refund REST
|