@revenexx/cover 0.1.0 → 0.1.2
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/app/app.config.ts +1 -1
- package/app/components/account/AccountSidebar.vue +10 -1
- package/app/components/account/address/AccountAddressCard.vue +69 -88
- package/app/components/auth/AuthRegisterPanel.vue +122 -194
- package/app/formkit.config.ts +21 -0
- package/app/interfaces/auth.ts +0 -2
- package/app/interfaces/validation.ts +1 -1
- package/app/validations/formValidationConfig.ts +0 -30
- package/nuxt.config.ts +7 -3
- package/package.json +2 -2
- package/server/api/account/orders.get.ts +6 -5
- package/server/api/account/profile.get.ts +3 -3
- package/server/api/account/profile.put.ts +6 -4
- package/server/api/auth/login.post.ts +2 -5
- package/server/api/auth/recovery.post.ts +5 -13
- package/server/api/auth/recovery.put.ts +5 -14
- package/server/api/auth/register.post.ts +21 -44
- package/server/api/orders/index.post.ts +28 -24
- package/server/api/payment/methods.post.ts +5 -4
- package/server/api/shipping/rates.post.ts +6 -4
- package/server/interfaces/auth.ts +1 -1
- package/server/services/ApiAccountService.ts +44 -0
- package/server/services/ApiAuthService.ts +15 -14
- package/server/services/ApiCartService.ts +45 -27
- package/server/services/ApiMarketService.ts +11 -9
- package/server/utils/accountService.ts +4 -5
- package/server/utils/authService.ts +0 -3
- package/server/utils/liveCatalog.ts +30 -17
- package/server/utils/liveInventories.ts +4 -4
- package/server/utils/liveOrders.ts +5 -3
- package/server/utils/livePrices.ts +10 -9
- package/server/utils/revenexxSdk.ts +213 -0
- package/app/validations/companyName.ts +0 -18
- package/app/validations/maxLength.ts +0 -12
- package/app/validations/optionalCompanyName.ts +0 -23
- package/app/validations/phoneNumber.ts +0 -19
- package/app/validations/termsRequired.ts +0 -4
- package/app/validations/zipCode.ts +0 -15
- package/server/services/SdkAccountService.ts +0 -56
- package/server/services/SdkAuthService.ts +0 -83
- package/server/utils/revenexxApi.ts +0 -136
- package/server/utils/shopSdk.ts +0 -88
|
@@ -34,9 +34,9 @@ function contactName(contact: ApiContact | null, fallback: string): string {
|
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Live authentication via the public revenexx API (customers app):
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
37
|
+
* customersAuthLogin → platform session (incl. secret) + contact
|
|
38
|
+
* auth/me (raw call) → platform user + contact
|
|
39
|
+
* customersAuthLogout → revokes the platform session
|
|
40
40
|
*
|
|
41
41
|
* Session state lives in the same cookie as the other implementations, so
|
|
42
42
|
* the client-side auth flow (store, guards, middleware) is identical. The
|
|
@@ -44,11 +44,10 @@ function contactName(contact: ApiContact | null, fallback: string): string {
|
|
|
44
44
|
*/
|
|
45
45
|
export class ApiAuthService implements IAuthService {
|
|
46
46
|
async login(event: H3Event, email: string, password: string): Promise<AuthLoginResult> {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
);
|
|
47
|
+
const { session, contact } = await useRevenexxSdk().customers.customersAuthLogin({ email, password }) as unknown as {
|
|
48
|
+
session: ApiSession;
|
|
49
|
+
contact: ApiContact | null;
|
|
50
|
+
};
|
|
52
51
|
|
|
53
52
|
const storedSession: StoredSession = {
|
|
54
53
|
id: session.$id,
|
|
@@ -94,11 +93,13 @@ export class ApiAuthService implements IAuthService {
|
|
|
94
93
|
|
|
95
94
|
try {
|
|
96
95
|
// session_id makes the customers app validate the session is still
|
|
97
|
-
// alive — a logged-out/revoked session answers 401 here.
|
|
98
|
-
|
|
96
|
+
// alive — a logged-out/revoked session answers 401 here. Raw call:
|
|
97
|
+
// the AuthMeRequest contract does not declare session_id yet, so
|
|
98
|
+
// the generated customersAuthMe() would drop the session check.
|
|
99
|
+
const { user, contact } = await useRevenexxSdk().call<{
|
|
99
100
|
user: { $id: string; name?: string; email: string };
|
|
100
101
|
contact: ApiContact | null;
|
|
101
|
-
}>("/v1/customers/auth/me", { user_id: parsed.userId, session_id: parsed.id });
|
|
102
|
+
}>("POST", "/v1/customers/auth/me", { body: { user_id: parsed.userId, session_id: parsed.id } });
|
|
102
103
|
|
|
103
104
|
return {
|
|
104
105
|
$id: user.$id,
|
|
@@ -122,9 +123,9 @@ export class ApiAuthService implements IAuthService {
|
|
|
122
123
|
try {
|
|
123
124
|
const parsed = JSON.parse(raw) as StoredSession;
|
|
124
125
|
if (parsed.userId && parsed.id) {
|
|
125
|
-
await
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
await useRevenexxSdk().customers.customersAuthLogout({
|
|
127
|
+
userId: parsed.userId,
|
|
128
|
+
sessionId: parsed.id,
|
|
128
129
|
});
|
|
129
130
|
}
|
|
130
131
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
|
+
import type { Models } from "@revenexx/sdk";
|
|
2
3
|
|
|
3
4
|
import type { CartItem } from "../../app/interfaces/cart-item";
|
|
4
5
|
import type { StoredSession } from "../../app/interfaces/auth";
|
|
@@ -137,11 +138,15 @@ function toSummary(cart: ApiCart): CartSummaryRow {
|
|
|
137
138
|
*/
|
|
138
139
|
export class ApiCartService implements ICartService, ICartManager {
|
|
139
140
|
private async listActive(owner: CartOwner): Promise<ApiCart[]> {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
// Raw call: carts.list does not declare its query parameters in the
|
|
142
|
+
// contract yet, so the generated cartsList() cannot filter by owner.
|
|
143
|
+
const { items } = await useRevenexxSdk().call<{ items: ApiCart[] }>("GET", "/v1/carts", {
|
|
144
|
+
query: {
|
|
145
|
+
...ownerQuery(owner),
|
|
146
|
+
status: "active",
|
|
147
|
+
limit: 100,
|
|
148
|
+
order: "updated_at.desc",
|
|
149
|
+
},
|
|
145
150
|
});
|
|
146
151
|
return items;
|
|
147
152
|
}
|
|
@@ -152,11 +157,11 @@ export class ApiCartService implements ICartService, ICartManager {
|
|
|
152
157
|
if (current) {
|
|
153
158
|
return current;
|
|
154
159
|
}
|
|
155
|
-
return
|
|
160
|
+
return await useRevenexxSdk().carts.cartsCreate({
|
|
156
161
|
name: DEFAULT_CART_NAME,
|
|
157
|
-
...(owner.contactId ? {
|
|
158
|
-
|
|
159
|
-
});
|
|
162
|
+
...(owner.contactId ? { contactId: owner.contactId } : { sessionKey: owner.sessionKey }),
|
|
163
|
+
isCurrent: true,
|
|
164
|
+
}) as unknown as ApiCart;
|
|
160
165
|
}
|
|
161
166
|
|
|
162
167
|
// ---- ICartService (the active cart) --------------------------------
|
|
@@ -168,16 +173,16 @@ export class ApiCartService implements ICartService, ICartManager {
|
|
|
168
173
|
if (!current) {
|
|
169
174
|
return [];
|
|
170
175
|
}
|
|
171
|
-
const { items } = await
|
|
176
|
+
const { items } = await useRevenexxSdk().carts.cartsItemsList({ cartId: current.id }) as unknown as { items: ApiCartLine[] };
|
|
172
177
|
return items.map(toCartItem);
|
|
173
178
|
}
|
|
174
179
|
|
|
175
180
|
async saveItems(event: H3Event, items: CartItem[]): Promise<void> {
|
|
176
181
|
const owner = resolveOwner(event);
|
|
177
182
|
const cart = await this.ensureActiveCart(owner);
|
|
178
|
-
await
|
|
179
|
-
|
|
180
|
-
|
|
183
|
+
await useRevenexxSdk().carts.cartsItemsReplace({
|
|
184
|
+
cartId: cart.id,
|
|
185
|
+
items: items.map((item, index) => toApiLine(item, index)) as unknown as Models.CartItemCreateRequest[],
|
|
181
186
|
});
|
|
182
187
|
}
|
|
183
188
|
|
|
@@ -199,30 +204,43 @@ export class ApiCartService implements ICartService, ICartManager {
|
|
|
199
204
|
|
|
200
205
|
async createCart(event: H3Event, name: string, activate: boolean): Promise<CartSummaryRow> {
|
|
201
206
|
const owner = resolveOwner(event);
|
|
202
|
-
const cart = await
|
|
207
|
+
const cart = await useRevenexxSdk().carts.cartsCreate({
|
|
203
208
|
name: name.trim() || DEFAULT_CART_NAME,
|
|
204
|
-
...(owner.contactId ? {
|
|
205
|
-
|
|
206
|
-
});
|
|
209
|
+
...(owner.contactId ? { contactId: owner.contactId } : { sessionKey: owner.sessionKey }),
|
|
210
|
+
isCurrent: activate,
|
|
211
|
+
}) as unknown as ApiCart;
|
|
207
212
|
return { ...toSummary(cart), active: activate };
|
|
208
213
|
}
|
|
209
214
|
|
|
210
215
|
async renameCart(event: H3Event, id: string, name: string): Promise<void> {
|
|
211
|
-
await
|
|
216
|
+
await useRevenexxSdk().carts.cartsUpdate({ id, name: name.trim() });
|
|
212
217
|
}
|
|
213
218
|
|
|
214
219
|
async deleteCart(event: H3Event, id: string): Promise<void> {
|
|
215
|
-
await
|
|
220
|
+
await useRevenexxSdk().carts.cartsDelete({ id });
|
|
216
221
|
}
|
|
217
222
|
|
|
218
223
|
async activateCart(event: H3Event, id: string): Promise<CartItem[]> {
|
|
219
|
-
await
|
|
220
|
-
const { items } = await
|
|
224
|
+
await useRevenexxSdk().carts.cartsActivate({ id });
|
|
225
|
+
const { items } = await useRevenexxSdk().carts.cartsItemsList({ cartId: id }) as unknown as { items: ApiCartLine[] };
|
|
221
226
|
return items.map(toCartItem);
|
|
222
227
|
}
|
|
223
228
|
|
|
224
229
|
async addItemToCart(event: H3Event, id: string, item: CartItem): Promise<void> {
|
|
225
|
-
|
|
230
|
+
const line = toApiLine(item, 0);
|
|
231
|
+
await useRevenexxSdk().carts.cartsItemsCreate({
|
|
232
|
+
cartId: id,
|
|
233
|
+
...(line.product_id ? { productId: String(line.product_id) } : {}),
|
|
234
|
+
...(line.sku ? { sku: String(line.sku) } : {}),
|
|
235
|
+
name: String(line.name),
|
|
236
|
+
quantity: Number(line.quantity),
|
|
237
|
+
...(line.unit ? { unit: String(line.unit) } : {}),
|
|
238
|
+
unitPrice: Number(line.unit_price),
|
|
239
|
+
taxRate: Number(line.tax_rate),
|
|
240
|
+
position: Number(line.position),
|
|
241
|
+
snapshot: line.snapshot as object,
|
|
242
|
+
metadata: line.metadata as object,
|
|
243
|
+
});
|
|
226
244
|
}
|
|
227
245
|
|
|
228
246
|
async orderActiveCart(event: H3Event, orderRef: string): Promise<string | null> {
|
|
@@ -232,7 +250,7 @@ export class ApiCartService implements ICartService, ICartManager {
|
|
|
232
250
|
if (!current) {
|
|
233
251
|
return null;
|
|
234
252
|
}
|
|
235
|
-
await
|
|
253
|
+
await useRevenexxSdk().carts.cartsOrder({ id: current.id, orderRef });
|
|
236
254
|
return current.id;
|
|
237
255
|
}
|
|
238
256
|
|
|
@@ -245,10 +263,10 @@ export class ApiCartService implements ICartService, ICartManager {
|
|
|
245
263
|
// the session cart is adopted as the customer's cart.
|
|
246
264
|
const customerCarts = await this.listActive({ contactId, sessionKey: "" });
|
|
247
265
|
const target = customerCarts.find(c => c.is_current) ?? customerCarts[0];
|
|
248
|
-
await
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
...(target ? {
|
|
266
|
+
await useRevenexxSdk().carts.cartsClaim({
|
|
267
|
+
sessionKey,
|
|
268
|
+
contactId,
|
|
269
|
+
...(target ? { targetCartId: target.id } : {}),
|
|
252
270
|
});
|
|
253
271
|
}
|
|
254
272
|
}
|
|
@@ -25,7 +25,9 @@ interface ApiListPage<T> {
|
|
|
25
25
|
|
|
26
26
|
const MARKET_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
// Partitioned by tenant — a multi-tenant theme must never serve one
|
|
29
|
+
// tenant's markets to another (see revenexxTenantKey()).
|
|
30
|
+
const cache = new Map<string, { markets: ShopMarket[]; loadedAt: number }>();
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
33
|
* Live markets via the public revenexx API (markets app):
|
|
@@ -34,20 +36,20 @@ let cache: { markets: ShopMarket[]; loadedAt: number } | null = null;
|
|
|
34
36
|
*/
|
|
35
37
|
export class ApiMarketService implements IMarketService {
|
|
36
38
|
async listMarkets(): Promise<ShopMarket[]> {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
const tenantKey = revenexxTenantKey();
|
|
40
|
+
const cached = cache.get(tenantKey);
|
|
41
|
+
if (cached && Date.now() - cached.loadedAt < MARKET_CACHE_TTL_MS) {
|
|
42
|
+
return cached.markets;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
const
|
|
42
|
-
const { items } = await
|
|
45
|
+
const sdk = useRevenexxSdk();
|
|
46
|
+
const { items } = await sdk.markets.marketsList() as unknown as ApiListPage<ApiMarketRow>;
|
|
43
47
|
const active = items
|
|
44
48
|
.filter(m => m.status === "active")
|
|
45
49
|
.sort((a, b) => a.position - b.position || a.code.localeCompare(b.code));
|
|
46
50
|
|
|
47
51
|
const markets = await Promise.all(active.map(async (market): Promise<ShopMarket> => {
|
|
48
|
-
const { items: locales } = await
|
|
49
|
-
`/v1/markets/${encodeURIComponent(market.id)}/locales`,
|
|
50
|
-
);
|
|
52
|
+
const { items: locales } = await sdk.markets.marketsLocalesList({ marketId: market.id }) as unknown as ApiListPage<ApiLocaleRow>;
|
|
51
53
|
return {
|
|
52
54
|
id: market.id,
|
|
53
55
|
code: market.code,
|
|
@@ -65,7 +67,7 @@ export class ApiMarketService implements IMarketService {
|
|
|
65
67
|
};
|
|
66
68
|
}));
|
|
67
69
|
|
|
68
|
-
cache
|
|
70
|
+
cache.set(tenantKey, { markets, loadedAt: Date.now() });
|
|
69
71
|
return markets;
|
|
70
72
|
}
|
|
71
73
|
}
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
2
|
|
|
3
3
|
import type { IAccountService } from "../interfaces/account";
|
|
4
|
+
import { ApiAccountService } from "../services/ApiAccountService";
|
|
4
5
|
import { MockAccountService } from "../services/MockAccountService";
|
|
5
|
-
import { SdkAccountService } from "../services/SdkAccountService";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Registry of available account service implementations.
|
|
9
9
|
* The active implementation is selected by the `accountService` key in app.config.ts:
|
|
10
10
|
* - "mock" — demo identity from the bundled config asset
|
|
11
|
-
* - "
|
|
12
|
-
* runtime config and an authenticated session)
|
|
11
|
+
* - "api" — live identity via the public revenexx API (customers app)
|
|
13
12
|
*/
|
|
14
13
|
const serviceRegistry: Record<string, IAccountService> = {
|
|
15
14
|
mock: new MockAccountService("account/user.json"),
|
|
16
|
-
|
|
15
|
+
api: new ApiAccountService(),
|
|
17
16
|
};
|
|
18
17
|
|
|
19
18
|
/**
|
|
@@ -24,7 +23,7 @@ export function getAccountService(event?: H3Event): IAccountService {
|
|
|
24
23
|
const key = resolveServiceKey(event, {
|
|
25
24
|
domain: "accountService",
|
|
26
25
|
mockKey: "mock",
|
|
27
|
-
liveKey: "
|
|
26
|
+
liveKey: "api",
|
|
28
27
|
});
|
|
29
28
|
return serviceRegistry[key] ?? serviceRegistry["mock"]!;
|
|
30
29
|
}
|
|
@@ -3,20 +3,17 @@ import type { H3Event } from "h3";
|
|
|
3
3
|
import type { IAuthService } from "../interfaces/auth";
|
|
4
4
|
import { ApiAuthService } from "../services/ApiAuthService";
|
|
5
5
|
import { MockAuthService } from "../services/MockAuthService";
|
|
6
|
-
import { SdkAuthService } from "../services/SdkAuthService";
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Registry of available auth implementations:
|
|
10
9
|
* - "mock" — offline login as bundled B2B demo personas (buyer/approver/admin)
|
|
11
10
|
* - "api" — live authentication via the public revenexx API (customers app)
|
|
12
|
-
* - "sdk" — live authentication via the revenexx web SDK (direct platform)
|
|
13
11
|
*/
|
|
14
12
|
const mockAuthService = new MockAuthService("account/personas.json");
|
|
15
13
|
|
|
16
14
|
const serviceRegistry: Record<string, IAuthService> = {
|
|
17
15
|
mock: mockAuthService,
|
|
18
16
|
api: new ApiAuthService(),
|
|
19
|
-
sdk: new SdkAuthService(),
|
|
20
17
|
};
|
|
21
18
|
|
|
22
19
|
export function getAuthService(event?: H3Event): IAuthService {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Collection } from "@revenexx/sdk";
|
|
2
|
+
|
|
1
3
|
import type { ProductCategory, ProductSubcategory } from "../../app/config/navigation";
|
|
2
4
|
import type { Product } from "../../app/composables/useProducts";
|
|
3
5
|
import type { ProductDetail } from "../../app/interfaces/product-detail";
|
|
@@ -58,16 +60,22 @@ const LIVE_DEFAULT_TAX_RATE = 19;
|
|
|
58
60
|
const LIVE_DEFAULT_STOCK = { quantity: 9999, maxOrderQuantity: 999999 };
|
|
59
61
|
|
|
60
62
|
export async function searchLiveProducts(params: LiveSearchParams): Promise<LiveSearchResponse> {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
// Raw call (GET form): the gateway currently 404s the POST search route
|
|
64
|
+
// its spec declares, and the GET contract does not declare facet_by —
|
|
65
|
+
// the generated search methods can't cover this call yet.
|
|
66
|
+
return useRevenexxSdk().call<LiveSearchResponse>(
|
|
67
|
+
"GET",
|
|
68
|
+
`/v1/search/collections/${Collection.Products}/documents/search`,
|
|
63
69
|
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
query: {
|
|
71
|
+
q: params.q,
|
|
72
|
+
query_by: params.queryBy ?? "name,sku,description,manufacturer",
|
|
73
|
+
filter_by: params.filterBy,
|
|
74
|
+
facet_by: params.facetBy,
|
|
75
|
+
sort_by: params.sortBy,
|
|
76
|
+
page: params.page ?? 1,
|
|
77
|
+
per_page: params.perPage ?? 24,
|
|
78
|
+
},
|
|
71
79
|
},
|
|
72
80
|
);
|
|
73
81
|
}
|
|
@@ -167,21 +175,26 @@ interface LiveCategoryPage {
|
|
|
167
175
|
const CATEGORY_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
168
176
|
const CATEGORY_PAGE_LIMIT = 200;
|
|
169
177
|
|
|
170
|
-
|
|
178
|
+
// Partitioned by tenant — a multi-tenant theme must never serve one
|
|
179
|
+
// tenant's category tree to another (see revenexxTenantKey()).
|
|
180
|
+
const categoryCache = new Map<string, { rows: LiveCategoryRow[]; loadedAt: number }>();
|
|
171
181
|
|
|
172
182
|
async function loadLiveCategoryRows(): Promise<LiveCategoryRow[]> {
|
|
173
|
-
|
|
174
|
-
|
|
183
|
+
const tenantKey = revenexxTenantKey();
|
|
184
|
+
const cached = categoryCache.get(tenantKey);
|
|
185
|
+
if (cached && Date.now() - cached.loadedAt < CATEGORY_CACHE_TTL_MS) {
|
|
186
|
+
return cached.rows;
|
|
175
187
|
}
|
|
176
188
|
|
|
177
|
-
const
|
|
189
|
+
const sdk = useRevenexxSdk();
|
|
178
190
|
const rows: LiveCategoryRow[] = [];
|
|
179
191
|
let offset = 0;
|
|
180
192
|
// Paginate defensively — the demo tenant has a few hundred categories.
|
|
193
|
+
// Raw call: the categories list contract does not declare limit/offset
|
|
194
|
+
// yet, so the generated productsCategoriesList() cannot paginate.
|
|
181
195
|
for (let pageIndex = 0; pageIndex < 25; pageIndex++) {
|
|
182
|
-
const page = await
|
|
183
|
-
limit: CATEGORY_PAGE_LIMIT,
|
|
184
|
-
offset,
|
|
196
|
+
const page = await sdk.call<LiveCategoryPage>("GET", "/v1/products/categories", {
|
|
197
|
+
query: { limit: CATEGORY_PAGE_LIMIT, offset },
|
|
185
198
|
});
|
|
186
199
|
rows.push(...page.items);
|
|
187
200
|
if (!page.page.hasMore) {
|
|
@@ -190,7 +203,7 @@ async function loadLiveCategoryRows(): Promise<LiveCategoryRow[]> {
|
|
|
190
203
|
offset += page.page.returned;
|
|
191
204
|
}
|
|
192
205
|
|
|
193
|
-
categoryCache
|
|
206
|
+
categoryCache.set(tenantKey, { rows, loadedAt: Date.now() });
|
|
194
207
|
return rows;
|
|
195
208
|
}
|
|
196
209
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
|
+
import type { Models } from "@revenexx/sdk";
|
|
2
3
|
|
|
3
4
|
import type { Product } from "../../app/composables/useProducts";
|
|
4
5
|
import type { ProductDetail } from "../../app/interfaces/product-detail";
|
|
@@ -30,10 +31,9 @@ function toStock(entry: ItemAvailability): { quantity: number; maxOrderQuantity:
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
async function fetchAvailability(items: Array<{ product_id?: string; sku?: string }>): Promise<Map<string, ItemAvailability>> {
|
|
33
|
-
const { availability } = await
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
);
|
|
34
|
+
const { availability } = await useRevenexxSdk().inventories.inventoriesAvailability({
|
|
35
|
+
items: items as Models.InventoryAvailabilityItem[],
|
|
36
|
+
}) as unknown as { availability: ItemAvailability[] };
|
|
37
37
|
const byKey = new Map<string, ItemAvailability>();
|
|
38
38
|
for (const entry of availability) {
|
|
39
39
|
if (entry.product_id) byKey.set(entry.product_id, entry);
|
|
@@ -129,11 +129,13 @@ export function mapLiveOrderToAccount(order: LiveOrder): AccountOrder {
|
|
|
129
129
|
|
|
130
130
|
/** Fetch one live order by its display number (the account history id). */
|
|
131
131
|
export async function getLiveOrderByNumber(number: string): Promise<LiveOrder | null> {
|
|
132
|
-
const
|
|
133
|
-
|
|
132
|
+
const sdk = useRevenexxSdk();
|
|
133
|
+
// Raw call: orders.list does not declare its query parameters in the
|
|
134
|
+
// contract yet, so the generated ordersList() cannot filter by number.
|
|
135
|
+
const { items } = await sdk.call<{ items: LiveOrder[] }>("GET", "/v1/orders", { query: { number, limit: 1 } });
|
|
134
136
|
const row = items[0];
|
|
135
137
|
if (!row) {
|
|
136
138
|
return null;
|
|
137
139
|
}
|
|
138
|
-
return
|
|
140
|
+
return await sdk.orders.ordersGet({ id: row.id }) as unknown as LiveOrder;
|
|
139
141
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { H3Event } from "h3";
|
|
2
|
+
import type { Models } from "@revenexx/sdk";
|
|
2
3
|
|
|
3
4
|
import type { StoredSession } from "../../app/interfaces/auth";
|
|
4
5
|
import type { Product } from "../../app/composables/useProducts";
|
|
@@ -21,7 +22,7 @@ interface ResolvedPrice {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
/** The buyer's price scope from the session (guests resolve openly). */
|
|
24
|
-
function priceContext(event: H3Event): {
|
|
25
|
+
function priceContext(event: H3Event): { contactId?: string; organizationId?: string } {
|
|
25
26
|
const raw = getCookie(event, SESSION_COOKIE_NAME);
|
|
26
27
|
if (!raw) {
|
|
27
28
|
return {};
|
|
@@ -29,8 +30,8 @@ function priceContext(event: H3Event): { contact_id?: string; organization_id?:
|
|
|
29
30
|
try {
|
|
30
31
|
const parsed = JSON.parse(raw) as StoredSession;
|
|
31
32
|
return {
|
|
32
|
-
...(parsed.contactId ? {
|
|
33
|
-
...(parsed.organizationId ? {
|
|
33
|
+
...(parsed.contactId ? { contactId: parsed.contactId } : {}),
|
|
34
|
+
...(parsed.organizationId ? { organizationId: parsed.organizationId } : {}),
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
37
|
catch {
|
|
@@ -61,10 +62,10 @@ export async function enrichProductsWithLivePrices(event: H3Event, products: Pro
|
|
|
61
62
|
if (products.length === 0) {
|
|
62
63
|
return products;
|
|
63
64
|
}
|
|
64
|
-
const { prices } = await
|
|
65
|
-
items: products.map(p => ({ product_id: p.id, sku: p.sku, quantity: 1 })),
|
|
65
|
+
const { prices } = await useRevenexxSdk().prices.pricesResolve({
|
|
66
|
+
items: products.map(p => ({ product_id: p.id, sku: p.sku, quantity: 1 })) as Models.PriceResolveItem[],
|
|
66
67
|
...priceContext(event),
|
|
67
|
-
});
|
|
68
|
+
}) as unknown as { prices: ResolvedPrice[] };
|
|
68
69
|
|
|
69
70
|
const byKey = new Map<string, ResolvedPrice>();
|
|
70
71
|
for (const price of prices) {
|
|
@@ -84,10 +85,10 @@ export async function enrichDetailWithLivePrices(event: H3Event, detail: Product
|
|
|
84
85
|
if (!product) {
|
|
85
86
|
return detail;
|
|
86
87
|
}
|
|
87
|
-
const { prices } = await
|
|
88
|
-
items: [{ product_id: product.id, sku: product.sku, quantity: 1 }],
|
|
88
|
+
const { prices } = await useRevenexxSdk().prices.pricesResolve({
|
|
89
|
+
items: [{ product_id: product.id, sku: product.sku, quantity: 1 }] as Models.PriceResolveItem[],
|
|
89
90
|
...priceContext(event),
|
|
90
|
-
});
|
|
91
|
+
}) as unknown as { prices: ResolvedPrice[] };
|
|
91
92
|
const resolved = prices[0];
|
|
92
93
|
return { ...detail, prices: resolved ? toPriceMap(resolved) : [] };
|
|
93
94
|
}
|