@websolutespa/bom-mixer-models 0.4.0 → 0.4.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/CHANGELOG.md +12 -0
- package/dist/index.js +10 -1
- package/dist/index.mjs +10 -1
- package/package.json +7 -5
- package/src/cart/cart.ts +30 -0
- package/src/category/category.service.ts +50 -0
- package/src/checkout/checkout.service.ts +350 -0
- package/src/checkout/checkout.ts +64 -0
- package/src/country/country.service.ts +9 -0
- package/src/feature_type/feature_type.service.ts +10 -0
- package/src/feature_type/feature_type.ts +13 -0
- package/src/index.ts +27 -0
- package/src/label/label.service.ts +23 -0
- package/src/layout/layout.service.ts +41 -0
- package/src/lazy/lazy.ts +14 -0
- package/src/link/link.ts +7 -0
- package/src/list/list.service.ts +19 -0
- package/src/list/list.ts +11 -0
- package/src/locale/locale.service.ts +10 -0
- package/src/market/market.service.ts +9 -0
- package/src/menu/menu.service.ts +15 -0
- package/src/order/order.service.ts +29 -0
- package/src/order/order.ts +31 -0
- package/src/page/page.service.ts +145 -0
- package/src/province/province.service.ts +9 -0
- package/src/region/region.service.ts +9 -0
- package/src/route/route-revalidate.handler.ts +37 -0
- package/src/route/route.interceptor.ts +36 -0
- package/src/route/route.service.ts +171 -0
- package/src/store/store.ts +20 -0
- package/src/user/user.ts +57 -0
package/CHANGELOG.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -79,6 +79,7 @@ async function getSegments(item, params = {}) {
|
|
|
79
79
|
return getRouteSegments(item, categories);
|
|
80
80
|
}
|
|
81
81
|
function getRouteSegments(item, categories) {
|
|
82
|
+
const routeCategories = [];
|
|
82
83
|
const segments = [];
|
|
83
84
|
let parentId = item.category || null;
|
|
84
85
|
while (parentId != null) {
|
|
@@ -88,7 +89,15 @@ function getRouteSegments(item, categories) {
|
|
|
88
89
|
const segment = { ...parentCategory };
|
|
89
90
|
segments.unshift(segment);
|
|
90
91
|
}
|
|
91
|
-
parentId = parentCategory.category
|
|
92
|
+
parentId = parentCategory.category ? String(parentCategory.category) : null;
|
|
93
|
+
if (parentId) {
|
|
94
|
+
if (routeCategories.includes(parentId)) {
|
|
95
|
+
console.log("CategoryService.getRouteSegments", "circular reference found", parentId);
|
|
96
|
+
parentId = null;
|
|
97
|
+
} else {
|
|
98
|
+
routeCategories.push(parentId);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
92
101
|
} else {
|
|
93
102
|
parentId = null;
|
|
94
103
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -10,6 +10,7 @@ async function getSegments(item, params = {}) {
|
|
|
10
10
|
return getRouteSegments(item, categories);
|
|
11
11
|
}
|
|
12
12
|
function getRouteSegments(item, categories) {
|
|
13
|
+
const routeCategories = [];
|
|
13
14
|
const segments = [];
|
|
14
15
|
let parentId = item.category || null;
|
|
15
16
|
while (parentId != null) {
|
|
@@ -19,7 +20,15 @@ function getRouteSegments(item, categories) {
|
|
|
19
20
|
const segment = { ...parentCategory };
|
|
20
21
|
segments.unshift(segment);
|
|
21
22
|
}
|
|
22
|
-
parentId = parentCategory.category
|
|
23
|
+
parentId = parentCategory.category ? String(parentCategory.category) : null;
|
|
24
|
+
if (parentId) {
|
|
25
|
+
if (routeCategories.includes(parentId)) {
|
|
26
|
+
console.log("CategoryService.getRouteSegments", "circular reference found", parentId);
|
|
27
|
+
parentId = null;
|
|
28
|
+
} else {
|
|
29
|
+
routeCategories.push(parentId);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
23
32
|
} else {
|
|
24
33
|
parentId = null;
|
|
25
34
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@websolutespa/bom-mixer-models",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Mixer Models module of the BOM Repository",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bom",
|
|
@@ -44,14 +44,16 @@
|
|
|
44
44
|
"sideEffects": false,
|
|
45
45
|
"prepack": {
|
|
46
46
|
"types": "./dist/index.d.ts",
|
|
47
|
-
"main": "./dist/index.
|
|
48
|
-
"module": "./dist/index.mjs"
|
|
47
|
+
"main": "./dist/index.mjs",
|
|
48
|
+
"module": "./dist/index.mjs",
|
|
49
|
+
"source": "./src/index.ts"
|
|
49
50
|
},
|
|
50
51
|
"postpack": {
|
|
51
52
|
"types": "./src/index.ts",
|
|
52
53
|
"main": "./src/index.ts"
|
|
53
54
|
},
|
|
54
55
|
"types": "./dist/index.d.ts",
|
|
55
|
-
"main": "./dist/index.
|
|
56
|
-
"module": "./dist/index.mjs"
|
|
56
|
+
"main": "./dist/index.mjs",
|
|
57
|
+
"module": "./dist/index.mjs",
|
|
58
|
+
"source": "./src/index.ts"
|
|
57
59
|
}
|
package/src/cart/cart.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { IEquatable, IMedia, ISchema } from '@websolutespa/bom-core';
|
|
2
|
+
|
|
3
|
+
export type ICartItem = ISchema & {
|
|
4
|
+
id: IEquatable;
|
|
5
|
+
schema: string;
|
|
6
|
+
media: IMedia;
|
|
7
|
+
title: string;
|
|
8
|
+
abstract?: string;
|
|
9
|
+
href: string;
|
|
10
|
+
price: number;
|
|
11
|
+
qty: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type ICartAddItem = Omit<ICartItem, 'qty'>;
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
export type ICartAddItem = ISchema & {
|
|
18
|
+
id: IEquatable;
|
|
19
|
+
schema: string;
|
|
20
|
+
media: IMedia;
|
|
21
|
+
title: string;
|
|
22
|
+
abstract?: string;
|
|
23
|
+
href: string;
|
|
24
|
+
price: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type ICartItem = ICartAddItem & {
|
|
28
|
+
qty: number;
|
|
29
|
+
}
|
|
30
|
+
*/
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ICategorized, ICategory, QueryParams } from '@websolutespa/bom-core';
|
|
2
|
+
import { getStore } from '@websolutespa/bom-mixer-store';
|
|
3
|
+
import { IModelStore } from '../store/store';
|
|
4
|
+
|
|
5
|
+
export async function getCategories(params: QueryParams = {}): Promise<ICategory[]> {
|
|
6
|
+
const store = await getStore<IModelStore>();
|
|
7
|
+
const categories = await store.category.findMany(params);
|
|
8
|
+
return categories;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function getSegments(item: ICategorized, params: QueryParams = {}): Promise<ICategory[]> {
|
|
12
|
+
const categories: ICategory[] = await getCategories(params);
|
|
13
|
+
return getRouteSegments(item, categories);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getRouteSegments(item: ICategorized, categories: ICategory[]): ICategory[] {
|
|
17
|
+
const routeCategories: string[] = [];
|
|
18
|
+
const segments: ICategory[] = [];
|
|
19
|
+
let parentId = item.category || null;
|
|
20
|
+
while (parentId != null) { // !!! loose
|
|
21
|
+
const parentCategory = categories.find(c => c.id === parentId);
|
|
22
|
+
if (parentCategory) {
|
|
23
|
+
if (parentCategory.slug) {
|
|
24
|
+
const segment = { ...parentCategory };
|
|
25
|
+
segments.unshift(segment);
|
|
26
|
+
}
|
|
27
|
+
parentId = parentCategory.category ? String(parentCategory.category) : null;
|
|
28
|
+
if (parentId) {
|
|
29
|
+
if (routeCategories.includes(parentId)) {
|
|
30
|
+
console.log('CategoryService.getRouteSegments', 'circular reference found', parentId);
|
|
31
|
+
parentId = null;
|
|
32
|
+
} else {
|
|
33
|
+
routeCategories.push(parentId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
parentId = null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (item.isDefault !== true) {
|
|
41
|
+
const segment: ICategory = {
|
|
42
|
+
id: item.id,
|
|
43
|
+
title: item.title,
|
|
44
|
+
slug: item.slug,
|
|
45
|
+
media: item.media,
|
|
46
|
+
};
|
|
47
|
+
segments.push(segment);
|
|
48
|
+
}
|
|
49
|
+
return segments;
|
|
50
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { IOption } from '@websolutespa/bom-core';
|
|
2
|
+
import { ICartItem } from '../cart/cart';
|
|
3
|
+
import { getCountries } from '../country/country.service';
|
|
4
|
+
import { getProvinces } from '../province/province.service';
|
|
5
|
+
import { getRegions } from '../region/region.service';
|
|
6
|
+
import { getRoutesForSchemas } from '../route/route.service';
|
|
7
|
+
import { IAddressOptions, IUser } from '../user/user';
|
|
8
|
+
import { ICheckoutDelivery, ICheckoutItem, ICheckoutPartial, ICheckoutPayment, ICheckoutStore } from './checkout';
|
|
9
|
+
|
|
10
|
+
// step 1.
|
|
11
|
+
export async function getItems(items: ICartItem[], market: string, locale: string, user?: IUser): Promise<ICheckoutItem[]> {
|
|
12
|
+
// !!! todo calculate user discount price
|
|
13
|
+
return items.map(x => ({ ...x, fullPrice: x.price, price: x.price * 0.9 }));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// step 2.
|
|
17
|
+
export async function getInfo(checkout: ICheckoutPartial, market: string, locale: string): Promise<IAddressOptions> {
|
|
18
|
+
const countries = await getCountries(locale) as IOption[];
|
|
19
|
+
const provinces = await getProvinces(locale) as IOption[];
|
|
20
|
+
const regions = await getRegions(locale) as IOption[];
|
|
21
|
+
const data = { countries, regions, provinces };
|
|
22
|
+
return data;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// step 3.
|
|
26
|
+
export async function getDeliveries(checkout: ICheckoutPartial, market: string, locale: string): Promise<ICheckoutDelivery[]> {
|
|
27
|
+
return [
|
|
28
|
+
{
|
|
29
|
+
'id': 1,
|
|
30
|
+
'name': 'In-store pick up',
|
|
31
|
+
'abstract': 'Conveniently collect from your nearest shop at no extra charge',
|
|
32
|
+
'price': 0,
|
|
33
|
+
'fullPrice': 0
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
'id': 2,
|
|
37
|
+
'name': 'Courier delivery',
|
|
38
|
+
'abstract': 'Packaged goods are delivered by our trusted forwarder.',
|
|
39
|
+
'price': 81,
|
|
40
|
+
'fullPrice': 81
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
'id': 3,
|
|
44
|
+
'name': 'In-home courier delivery',
|
|
45
|
+
'abstract': 'Packaged goods are delivered inside at front door. <br>White glove delivery, unpacking, assembly, or removal of debris is optional on request.',
|
|
46
|
+
'price': 156,
|
|
47
|
+
'fullPrice': 156
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// step 4.
|
|
53
|
+
export async function getStores(checkout: ICheckoutPartial, market: string, locale: string): Promise<ICheckoutStore[]> {
|
|
54
|
+
return [
|
|
55
|
+
{
|
|
56
|
+
'id': 10366,
|
|
57
|
+
'category': { 'id': 'store_category_distributor', 'name': 'Mixer Distributor' },
|
|
58
|
+
'name': 'Di Paolo Arredamenti',
|
|
59
|
+
'address': 'Viale Cesare Battisti',
|
|
60
|
+
'streetNumber': '80',
|
|
61
|
+
'zipCode': '64020',
|
|
62
|
+
'city': 'Bellante',
|
|
63
|
+
'country': { 'id': 'it', 'name': 'Italy' },
|
|
64
|
+
'phoneNumber': '+39 0861 616436',
|
|
65
|
+
'timetable': [],
|
|
66
|
+
'position': {
|
|
67
|
+
'latitude': 0.0,
|
|
68
|
+
'longitude': 0.0
|
|
69
|
+
},
|
|
70
|
+
'distance': 151.84174300013095,
|
|
71
|
+
'rank': 1
|
|
72
|
+
}, {
|
|
73
|
+
'id': 7430,
|
|
74
|
+
'category': { 'id': 'store_category_distributor', 'name': 'Mixer Distributor' },
|
|
75
|
+
'name': 'Mancini Italian Design & Art S.r.l.',
|
|
76
|
+
'address': 'Via Ascari',
|
|
77
|
+
'streetNumber': '2',
|
|
78
|
+
'zipCode': '41053',
|
|
79
|
+
'city': 'Maranello',
|
|
80
|
+
'country': { 'id': 'it', 'name': 'Italy' },
|
|
81
|
+
'email': 'info@mancinimida.com',
|
|
82
|
+
'timetable': [],
|
|
83
|
+
'position': {
|
|
84
|
+
'latitude': 0.0,
|
|
85
|
+
'longitude': 0.0
|
|
86
|
+
},
|
|
87
|
+
'distance': 170.6551472799284,
|
|
88
|
+
'rank': 2
|
|
89
|
+
}, {
|
|
90
|
+
'id': 325,
|
|
91
|
+
'category': { 'id': 'store_category_distributor', 'name': 'Mixer Distributor' },
|
|
92
|
+
'name': 'AD dal Pozzo',
|
|
93
|
+
'address': 'Via Mazzini',
|
|
94
|
+
'streetNumber': '24',
|
|
95
|
+
'zipCode': '36040',
|
|
96
|
+
'city': 'Grisignano Di Zocco',
|
|
97
|
+
'country': { 'id': 'it', 'name': 'Italy' },
|
|
98
|
+
'phoneNumber': '+39 0444 614521',
|
|
99
|
+
'email': 'mfort@websolute.it',
|
|
100
|
+
'timetable': [],
|
|
101
|
+
'position': {
|
|
102
|
+
'latitude': 0.0,
|
|
103
|
+
'longitude': 0.0
|
|
104
|
+
},
|
|
105
|
+
'distance': 196.0600048738732,
|
|
106
|
+
'rank': 3
|
|
107
|
+
}];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// step 5.
|
|
111
|
+
export async function getPayments(checkout: ICheckoutPartial, market: string, locale: string): Promise<ICheckoutPayment[]> {
|
|
112
|
+
return [
|
|
113
|
+
{
|
|
114
|
+
'id': 'alipay',
|
|
115
|
+
'name': 'Alipay',
|
|
116
|
+
'media': {
|
|
117
|
+
'type': 'image',
|
|
118
|
+
'src': '/assets/payment/alipay.svg'
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
'id': 'amex',
|
|
123
|
+
'name': 'American Express',
|
|
124
|
+
'media': {
|
|
125
|
+
'type': 'image',
|
|
126
|
+
'src': '/assets/payment/american-express.svg'
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
'id': 'apple-pay',
|
|
131
|
+
'name': 'Apple Pay',
|
|
132
|
+
'media': {
|
|
133
|
+
'type': 'image',
|
|
134
|
+
'src': '/assets/payment/apple-pay.svg'
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
'id': 'bank-transfer',
|
|
139
|
+
'name': 'Bank Transfer',
|
|
140
|
+
'media': {
|
|
141
|
+
'type': 'image',
|
|
142
|
+
'src': '/assets/payment/bank-transfer.svg'
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
'id': 'cbc',
|
|
147
|
+
'name': 'CBC',
|
|
148
|
+
'media': {
|
|
149
|
+
'type': 'image',
|
|
150
|
+
'src': '/assets/payment/cbc.svg'
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
'id': 'direct-debit',
|
|
155
|
+
'name': 'Direct Debit',
|
|
156
|
+
'media': {
|
|
157
|
+
'type': 'image',
|
|
158
|
+
'src': '/assets/payment/direct-debit.svg'
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
'id': 'googlepay',
|
|
163
|
+
'name': 'Google Pay',
|
|
164
|
+
'media': {
|
|
165
|
+
'type': 'image',
|
|
166
|
+
'src': '/assets/payment/googlepay.svg'
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
'id': 'ideal',
|
|
171
|
+
'name': 'iDEAL',
|
|
172
|
+
'media': {
|
|
173
|
+
'type': 'image',
|
|
174
|
+
'src': '/assets/payment/ideal.svg'
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
'id': 'maestro',
|
|
179
|
+
'name': 'Maestro',
|
|
180
|
+
'media': {
|
|
181
|
+
'type': 'image',
|
|
182
|
+
'src': '/assets/payment/maestro.svg'
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
'id': 'mastercard',
|
|
187
|
+
'name': 'MasterCard',
|
|
188
|
+
'abstract': '<p>You can use your Mastercard credit card. The 3DS authentication procedure will be used when the order is concluded, and you will be redirected to the bank\'s web page. To complete the order, follow the required steps.</p>',
|
|
189
|
+
'media': {
|
|
190
|
+
'type': 'image',
|
|
191
|
+
'src': '/assets/payment/mastercard.svg'
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
'id': 'mybank',
|
|
196
|
+
'name': 'MyBank',
|
|
197
|
+
'media': {
|
|
198
|
+
'type': 'image',
|
|
199
|
+
'src': '/assets/payment/mybank.svg'
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
'id': 'paypal',
|
|
204
|
+
'name': 'PayPal',
|
|
205
|
+
'media': {
|
|
206
|
+
'type': 'image',
|
|
207
|
+
'src': '/assets/payment/paypal.svg'
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
'id': 'trustly',
|
|
212
|
+
'name': 'Trustly',
|
|
213
|
+
'media': {
|
|
214
|
+
'type': 'image',
|
|
215
|
+
'src': '/assets/payment/trustly.svg'
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
'id': 'visa',
|
|
220
|
+
'name': 'Visa',
|
|
221
|
+
'abstract': '<p>You can use your Visa credit card. The 3DS authentication procedure will be used when the order is concluded, and you will be redirected to the bank\'s web page. To complete the order, follow the required steps.</p>',
|
|
222
|
+
'media': {
|
|
223
|
+
'type': 'image',
|
|
224
|
+
'src': '/assets/payment/visa.svg'
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
'id': 'wechatpay',
|
|
229
|
+
'name': 'WeChat Pay',
|
|
230
|
+
'media': {
|
|
231
|
+
'type': 'image',
|
|
232
|
+
'src': '/assets/payment/wechatpay.svg'
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
'id': 'wire-transfer',
|
|
237
|
+
'name': 'Wire Transfer',
|
|
238
|
+
'abstract': '<p>The order confirmation will contain a summary of the total amount to be paid and the details of the bank to which the payment is to be made..</p><p> Once the payment has been made, the order will be activated and the delivery terms indicated for the various items ordered will apply. The successful completion of the payment will be communicated by email to the email address indicated during registration.</p>',
|
|
239
|
+
'media': {
|
|
240
|
+
'type': 'image',
|
|
241
|
+
'src': '/assets/payment/wire-transfer.svg'
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// update
|
|
248
|
+
export async function updateCheckout(checkout: ICheckoutPartial, action: string, market: string, locale: string): Promise<ICheckoutPartial> {
|
|
249
|
+
// generic checkout update
|
|
250
|
+
/*
|
|
251
|
+
switch (action) {
|
|
252
|
+
case 'basket':
|
|
253
|
+
break;
|
|
254
|
+
case 'info':
|
|
255
|
+
break;
|
|
256
|
+
case 'delivery':
|
|
257
|
+
break;
|
|
258
|
+
case 'store':
|
|
259
|
+
break;
|
|
260
|
+
case 'discount':
|
|
261
|
+
break;
|
|
262
|
+
case 'payment':
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
*/
|
|
266
|
+
// recalculate price
|
|
267
|
+
const subTotal = checkout.items ? checkout.items.reduce((p, c) => {
|
|
268
|
+
return p + c.price * c.qty;
|
|
269
|
+
}, 0) : 0;
|
|
270
|
+
const subTotalFull = checkout.items ? checkout.items.reduce((p, c) => {
|
|
271
|
+
return p + c.fullPrice * c.qty;
|
|
272
|
+
}, 0) : 0;
|
|
273
|
+
const subTotalDiscountPrice = checkout.discounts ? checkout.discounts.reduce((p, c) => {
|
|
274
|
+
let value = 0;
|
|
275
|
+
switch (c.id) {
|
|
276
|
+
case 'coupon10':
|
|
277
|
+
value = subTotal * 0.1 * -1;
|
|
278
|
+
c.price = value;
|
|
279
|
+
break;
|
|
280
|
+
case 'coupon50':
|
|
281
|
+
value = subTotal * 0.5 * -1;
|
|
282
|
+
c.price = value;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
return p + value;
|
|
286
|
+
}, 0) : 0;
|
|
287
|
+
const deliveryPrice = checkout.delivery?.price || 0;
|
|
288
|
+
const afterTaxesDiscountPrice = checkout.discounts ? checkout.discounts.reduce((p, c) => {
|
|
289
|
+
let value = 0;
|
|
290
|
+
switch (c.id) {
|
|
291
|
+
case 'shipping':
|
|
292
|
+
value = deliveryPrice * -1;
|
|
293
|
+
c.price = value;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
return p + value;
|
|
297
|
+
}, 0) : 0;
|
|
298
|
+
// const totalDiscountPrice = subTotalDiscountPrice + afterTaxesDiscountPrice;
|
|
299
|
+
const taxes = (subTotal + subTotalDiscountPrice) * 0.2;
|
|
300
|
+
const total = subTotal + subTotalDiscountPrice + taxes + deliveryPrice + afterTaxesDiscountPrice;
|
|
301
|
+
checkout.subTotal = subTotal;
|
|
302
|
+
checkout.subTotalFull = subTotalFull;
|
|
303
|
+
checkout.taxes = taxes;
|
|
304
|
+
checkout.total = total;
|
|
305
|
+
return checkout;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// discounts
|
|
309
|
+
export async function setDiscountCode(discountCode: string, checkout: ICheckoutPartial, market: string, locale: string): Promise<ICheckoutPartial> {
|
|
310
|
+
const discounts = []; // checkout.discounts || [];
|
|
311
|
+
if (discountCode === 'shipping') {
|
|
312
|
+
discounts.push({
|
|
313
|
+
'id': 'shipping',
|
|
314
|
+
'name': 'shipping',
|
|
315
|
+
'abstract': 'Free shipping',
|
|
316
|
+
'price': (checkout.delivery?.price || 0) * -1,
|
|
317
|
+
'validFrom': new Date(),
|
|
318
|
+
'validTo': new Date(),
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
if (discountCode === 'coupon10') {
|
|
322
|
+
discounts.push({
|
|
323
|
+
'id': 'coupon10',
|
|
324
|
+
'name': 'coupon10',
|
|
325
|
+
'abstract': '10%',
|
|
326
|
+
'price': (checkout.items?.reduce((p, c) => p + c.price * c.qty * 0.1, 0) || 0) * -1,
|
|
327
|
+
'validFrom': new Date(),
|
|
328
|
+
'validTo': new Date(),
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
if (discountCode === 'coupon50') {
|
|
332
|
+
discounts.push({
|
|
333
|
+
'id': 'coupon50',
|
|
334
|
+
'name': 'coupon50',
|
|
335
|
+
'abstract': '50%',
|
|
336
|
+
'price': (checkout.items?.reduce((p, c) => p + c.price * c.qty * 0.5, 0) || 0) * -1,
|
|
337
|
+
'validFrom': new Date(),
|
|
338
|
+
'validTo': new Date(),
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return await updateCheckout({ ...checkout, discounts }, 'discount', market, locale);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// payment
|
|
345
|
+
export async function getPayment(checkout: ICheckoutPartial, market: string, locale: string): Promise<{ redirectUrl: string }> {
|
|
346
|
+
const knownRoutes = await getRoutesForSchemas(['checkout_result'], market, locale);
|
|
347
|
+
// !!! todo return payment redirectUrl
|
|
348
|
+
const redirectUrl = `${knownRoutes.checkout_result}?orderId=1&status=OK`;
|
|
349
|
+
return { redirectUrl };
|
|
350
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { IMedia, IOption } from '@websolutespa/bom-core';
|
|
2
|
+
import { ICartItem } from '../cart/cart';
|
|
3
|
+
import { ICompanyAddress, IUser, IUserAddress } from '../user/user';
|
|
4
|
+
|
|
5
|
+
export type ICheckout = {
|
|
6
|
+
items: ICheckoutItem[];
|
|
7
|
+
user?: Partial<IUser>;
|
|
8
|
+
shippingAddress: IUserAddress;
|
|
9
|
+
hasInvoice?: boolean;
|
|
10
|
+
hasBilling?: boolean;
|
|
11
|
+
billingAddress?: IUserAddress;
|
|
12
|
+
delivery: ICheckoutDelivery;
|
|
13
|
+
store: ICheckoutStore;
|
|
14
|
+
discounts: ICheckoutDiscount[];
|
|
15
|
+
payment: ICheckoutPayment;
|
|
16
|
+
subTotal: number;
|
|
17
|
+
subTotalFull: number;
|
|
18
|
+
taxes: number;
|
|
19
|
+
total: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type ICheckoutPartial = Partial<ICheckout>;
|
|
23
|
+
|
|
24
|
+
export type ICheckoutItem = ICartItem & {
|
|
25
|
+
fullPrice: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type ICheckoutInfo = {
|
|
29
|
+
user?: Partial<IUser>;
|
|
30
|
+
shippingAddress: IUserAddress;
|
|
31
|
+
hasInvoice?: boolean;
|
|
32
|
+
hasBilling?: boolean;
|
|
33
|
+
billingAddress?: IUserAddress;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type ICheckoutDelivery = IOption & {
|
|
37
|
+
abstract: string;
|
|
38
|
+
price: number;
|
|
39
|
+
fullPrice: number;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type ICheckoutStore = Partial<ICompanyAddress> & IOption & {
|
|
43
|
+
category?: IOption;
|
|
44
|
+
timetable?: string[];
|
|
45
|
+
position?: {
|
|
46
|
+
latitude: number;
|
|
47
|
+
longitude: number;
|
|
48
|
+
};
|
|
49
|
+
distance?: number;
|
|
50
|
+
rank?: number;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type ICheckoutDiscount = IOption & {
|
|
54
|
+
abstract?: string;
|
|
55
|
+
price: number;
|
|
56
|
+
validFrom: Date | string;
|
|
57
|
+
validTo: Date | string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type ICheckoutPayment = IOption & {
|
|
61
|
+
abstract?: string;
|
|
62
|
+
media?: IMedia;
|
|
63
|
+
};
|
|
64
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { INamedEntity } from '@websolutespa/bom-core';
|
|
2
|
+
import { getStore } from '@websolutespa/bom-mixer-store';
|
|
3
|
+
import { IModelStore } from '../store/store';
|
|
4
|
+
|
|
5
|
+
export async function getCountries(locale?: string): Promise<INamedEntity[]> {
|
|
6
|
+
const store = await getStore<IModelStore>();
|
|
7
|
+
const items = await store.i18n_country.findMany({ locale });
|
|
8
|
+
return items;
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { QueryParams } from '@websolutespa/bom-core';
|
|
2
|
+
import { getStore } from '@websolutespa/bom-mixer-store';
|
|
3
|
+
import { IModelStore } from '../store/store';
|
|
4
|
+
import { IFeatureType } from './feature_type';
|
|
5
|
+
|
|
6
|
+
export async function getFeatureTypes(params: QueryParams = {}): Promise<IFeatureType[]> {
|
|
7
|
+
const store = await getStore<IModelStore>();
|
|
8
|
+
const items = await store.feature_type.findMany(params);
|
|
9
|
+
return items;
|
|
10
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export * from './cart/cart';
|
|
2
|
+
export * from './category/category.service';
|
|
3
|
+
export * from './checkout/checkout';
|
|
4
|
+
export * from './checkout/checkout.service';
|
|
5
|
+
export * from './country/country.service';
|
|
6
|
+
export * from './feature_type/feature_type';
|
|
7
|
+
export * from './feature_type/feature_type.service';
|
|
8
|
+
export * from './label/label.service';
|
|
9
|
+
export * from './layout/layout.service';
|
|
10
|
+
export * from './lazy/lazy';
|
|
11
|
+
export * from './link/link';
|
|
12
|
+
export * from './list/list';
|
|
13
|
+
export * from './list/list.service';
|
|
14
|
+
export * from './locale/locale.service';
|
|
15
|
+
export * from './market/market.service';
|
|
16
|
+
export * from './menu/menu.service';
|
|
17
|
+
export * from './order/order';
|
|
18
|
+
export * from './order/order.service';
|
|
19
|
+
export * from './page/page.service';
|
|
20
|
+
export * from './province/province.service';
|
|
21
|
+
export * from './region/region.service';
|
|
22
|
+
export * from './route/route-revalidate.handler';
|
|
23
|
+
export * from './route/route.interceptor';
|
|
24
|
+
export * from './route/route.service';
|
|
25
|
+
export * from './store/store';
|
|
26
|
+
export * from './user/user';
|
|
27
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ILabel, QueryParams } from '@websolutespa/bom-core';
|
|
2
|
+
import { getStore } from '@websolutespa/bom-mixer-store';
|
|
3
|
+
import { IModelStore } from '../store/store';
|
|
4
|
+
|
|
5
|
+
export async function getLabels(params: QueryParams = {}): Promise<ILabel[]> {
|
|
6
|
+
const store = await getStore<IModelStore>();
|
|
7
|
+
const items = await store.label.findMany(params);
|
|
8
|
+
return items;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function resolveLabel(labels: ILabel[], id: string): string {
|
|
12
|
+
const label = labels.find(x => x.id === id);
|
|
13
|
+
return label && label.text ? label.text.toString() : id;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
export function localizedLabel(labels: Label[], id: string, locale: string = 'en', defaultLocale: string = 'en'): Promise<Label> {
|
|
18
|
+
const label = labels.find(x => x.id === id);
|
|
19
|
+
if (label) {
|
|
20
|
+
return store.localizeValue(label, locale, defaultLocale);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
*/
|