@jsdev_ninja/core 0.12.10 → 0.13.13
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/core.cjs.js +1 -1
- package/dist/core.cjs.js.map +1 -1
- package/dist/core.es.js +93 -78
- package/dist/core.es.js.map +1 -1
- package/dist/core.umd.js +1 -1
- package/dist/core.umd.js.map +1 -1
- package/dist/entities/Cart.d.ts +90 -90
- package/dist/entities/Discount/engine.d.ts.map +1 -1
- package/dist/entities/Discount/engine.js +15 -10
- package/dist/entities/Order.d.ts +82 -72
- package/dist/entities/Order.d.ts.map +1 -1
- package/dist/entities/Order.js +2 -1
- package/dist/entities/Organization.d.ts +34 -0
- package/dist/entities/Organization.d.ts.map +1 -0
- package/dist/entities/Organization.js +9 -0
- package/dist/entities/Product.d.ts +42 -42
- package/dist/entities/Product.js +3 -3
- package/dist/entities/Profile.d.ts +3 -0
- package/dist/entities/Profile.d.ts.map +1 -1
- package/dist/entities/Profile.js +4 -1
- package/dist/entities/Store.d.ts +2 -2
- package/dist/entities/index.d.ts +1 -0
- package/dist/entities/index.d.ts.map +1 -1
- package/dist/entities/index.js +1 -0
- package/dist/firebase-api/index.d.ts +3 -0
- package/dist/firebase-api/index.d.ts.map +1 -1
- package/dist/firebase-api/index.js +1 -0
- package/dist/tsconfig.app.tsbuildinfo +1 -1
- package/dist/utils/index.d.ts +8 -8
- package/dist/utils/index.js +3 -3
- package/lib/entities/Discount/engine.ts +134 -125
- package/lib/entities/Order.ts +1 -0
- package/lib/entities/Organization.ts +14 -0
- package/lib/entities/Product.ts +3 -3
- package/lib/entities/Profile.ts +1 -0
- package/lib/entities/index.ts +1 -0
- package/lib/firebase-api/index.ts +1 -0
- package/package.json +1 -1
package/dist/entities/Product.js
CHANGED
|
@@ -44,7 +44,7 @@ export const ProductSchema = z.object({
|
|
|
44
44
|
updated_at: z.number(),
|
|
45
45
|
categoryIds: z.array(z.string().nonempty()),
|
|
46
46
|
// @deprecated
|
|
47
|
-
categoryList: z.array(CategorySchema),
|
|
47
|
+
categoryList: z.array(CategorySchema).optional(),
|
|
48
48
|
// @deprecated
|
|
49
49
|
categories: z.object({
|
|
50
50
|
lvl0: z.array(z.string()),
|
|
@@ -52,9 +52,9 @@ export const ProductSchema = z.object({
|
|
|
52
52
|
lvl2: z.array(z.string()),
|
|
53
53
|
lvl3: z.array(z.string()),
|
|
54
54
|
lvl4: z.array(z.string()),
|
|
55
|
-
}),
|
|
55
|
+
}).optional(),
|
|
56
56
|
// @deprecated
|
|
57
|
-
categoryNames: z.array(z.string()),
|
|
57
|
+
categoryNames: z.array(z.string()).optional(),
|
|
58
58
|
});
|
|
59
59
|
export const NewProductSchema = ProductSchema.extend({
|
|
60
60
|
image: z.instanceof(File).optional(),
|
|
@@ -41,6 +41,7 @@ export declare const ProfileSchema: z.ZodObject<{
|
|
|
41
41
|
createdDate: z.ZodNumber;
|
|
42
42
|
lastActivityDate: z.ZodNumber;
|
|
43
43
|
paymentType: z.ZodEnum<["default", "delayed"]>;
|
|
44
|
+
organizationId: z.ZodOptional<z.ZodString>;
|
|
44
45
|
}, "strip", z.ZodTypeAny, {
|
|
45
46
|
type: "Profile";
|
|
46
47
|
id: string;
|
|
@@ -65,6 +66,7 @@ export declare const ProfileSchema: z.ZodObject<{
|
|
|
65
66
|
apartmentEnterNumber: string;
|
|
66
67
|
apartmentNumber: string;
|
|
67
68
|
} | undefined;
|
|
69
|
+
organizationId?: string | undefined;
|
|
68
70
|
}, {
|
|
69
71
|
type: "Profile";
|
|
70
72
|
id: string;
|
|
@@ -89,6 +91,7 @@ export declare const ProfileSchema: z.ZodObject<{
|
|
|
89
91
|
apartmentEnterNumber: string;
|
|
90
92
|
apartmentNumber: string;
|
|
91
93
|
} | undefined;
|
|
94
|
+
organizationId?: string | undefined;
|
|
92
95
|
}>;
|
|
93
96
|
export type TProfile = z.infer<typeof ProfileSchema>;
|
|
94
97
|
export declare function createEmptyProfile(): TProfile;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Profile.d.ts","sourceRoot":"","sources":["../../lib/entities/Profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,wBAAwB,
|
|
1
|
+
{"version":3,"file":"Profile.d.ts","sourceRoot":"","sources":["../../lib/entities/Profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,wBAAwB,mCAEnC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE3E,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBxB,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAErD,wBAAgB,kBAAkB,IAAI,QAAQ,CAyB7C"}
|
package/dist/entities/Profile.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AddressSchema } from "./Address";
|
|
3
3
|
import { notEmptyTextSchema } from "./Atoms";
|
|
4
|
-
export const ProfilePaymentTypeSchema = z.enum(["default", "delayed"]
|
|
4
|
+
export const ProfilePaymentTypeSchema = z.enum(["default", "delayed"], {
|
|
5
|
+
description: "delayed is J5 transaction",
|
|
6
|
+
});
|
|
5
7
|
export const ProfileSchema = z.object({
|
|
6
8
|
type: z.literal("Profile"),
|
|
7
9
|
id: notEmptyTextSchema,
|
|
@@ -18,6 +20,7 @@ export const ProfileSchema = z.object({
|
|
|
18
20
|
createdDate: z.number(),
|
|
19
21
|
lastActivityDate: z.number(),
|
|
20
22
|
paymentType: ProfilePaymentTypeSchema,
|
|
23
|
+
organizationId: z.string().optional(),
|
|
21
24
|
});
|
|
22
25
|
export function createEmptyProfile() {
|
|
23
26
|
return {
|
package/dist/entities/Store.d.ts
CHANGED
|
@@ -25,9 +25,9 @@ export declare const StoreSchema: z.ZodObject<{
|
|
|
25
25
|
allowAnonymousClients: boolean;
|
|
26
26
|
isVatIncludedInPrice: boolean;
|
|
27
27
|
clientTypes: ("company" | "individual")[];
|
|
28
|
+
deliveryPrice?: number | undefined;
|
|
28
29
|
minimumOrder?: number | undefined;
|
|
29
30
|
freeDeliveryPrice?: number | undefined;
|
|
30
|
-
deliveryPrice?: number | undefined;
|
|
31
31
|
}, {
|
|
32
32
|
id: string;
|
|
33
33
|
companyId: string;
|
|
@@ -39,9 +39,9 @@ export declare const StoreSchema: z.ZodObject<{
|
|
|
39
39
|
allowAnonymousClients: boolean;
|
|
40
40
|
isVatIncludedInPrice: boolean;
|
|
41
41
|
clientTypes: ("company" | "individual")[];
|
|
42
|
+
deliveryPrice?: number | undefined;
|
|
42
43
|
minimumOrder?: number | undefined;
|
|
43
44
|
freeDeliveryPrice?: number | undefined;
|
|
44
|
-
deliveryPrice?: number | undefined;
|
|
45
45
|
}>;
|
|
46
46
|
export type TStore = z.infer<typeof StoreSchema>;
|
|
47
47
|
//# sourceMappingURL=Store.d.ts.map
|
package/dist/entities/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/entities/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/entities/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC"}
|
package/dist/entities/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export declare const storeCollections: {
|
|
|
13
13
|
readonly payments: "payments";
|
|
14
14
|
readonly settings: "settings";
|
|
15
15
|
readonly discounts: "discounts";
|
|
16
|
+
readonly organizations: "organizations";
|
|
16
17
|
};
|
|
17
18
|
export declare const FirestoreApi: {
|
|
18
19
|
systemCollections: {
|
|
@@ -30,6 +31,7 @@ export declare const FirestoreApi: {
|
|
|
30
31
|
readonly payments: "payments";
|
|
31
32
|
readonly settings: "settings";
|
|
32
33
|
readonly discounts: "discounts";
|
|
34
|
+
readonly organizations: "organizations";
|
|
33
35
|
};
|
|
34
36
|
getPath: ({ companyId, storeId, collectionName, id, }: {
|
|
35
37
|
companyId: string;
|
|
@@ -56,6 +58,7 @@ export declare const FirebaseAPI: {
|
|
|
56
58
|
readonly payments: "payments";
|
|
57
59
|
readonly settings: "settings";
|
|
58
60
|
readonly discounts: "discounts";
|
|
61
|
+
readonly organizations: "organizations";
|
|
59
62
|
};
|
|
60
63
|
getPath: ({ companyId, storeId, collectionName, id, }: {
|
|
61
64
|
companyId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/firebase-api/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB;;;CAGpB,CAAC;AAEX,eAAO,MAAM,gBAAgB
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/firebase-api/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB;;;CAGpB,CAAC;AAEX,eAAO,MAAM,gBAAgB;;;;;;;;;;;;CAYnB,CAAC;AAEX,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;2DASrB;QACF,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,cAAc,EAAE,MAAM,OAAO,gBAAgB,CAAC;QAC9C,EAAE,CAAC,EAAE,MAAM,CAAC;KACZ;iCAI4B,MAAM,OAAO,gBAAgB;CAG1D,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;+DAdpB;YACF,SAAS,EAAE,MAAM,CAAC;YAClB,OAAO,EAAE,MAAM,CAAC;YAChB,cAAc,EAAE,MAAM,OAAO,gBAAgB,CAAC;YAC9C,EAAE,CAAC,EAAE,MAAM,CAAC;SACZ;qCAI4B,MAAM,OAAO,gBAAgB;;CAO1D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../lib/index.tsx","../lib/entities/address.ts","../lib/entities/atoms.ts","../lib/entities/cart.ts","../lib/entities/category.ts","../lib/entities/company.ts","../lib/entities/favoriteproduct.ts","../lib/entities/locale.ts","../lib/entities/order.ts","../lib/entities/product.ts","../lib/entities/profile.ts","../lib/entities/store.ts","../lib/entities/index.ts","../lib/entities/discount/engine.ts","../lib/entities/discount/factory.ts","../lib/entities/discount/index.ts","../lib/entities/discount/strategy.ts","../lib/entities/discount/types.ts","../lib/entities/discount/utils.ts","../lib/entities/discount/__tests__/engine.test.ts","../lib/entities/discount/__tests__/factory.test.ts","../lib/entities/discount/__tests__/integration.test.ts","../lib/entities/discount/__tests__/simple.test.ts","../lib/entities/discount/__tests__/utils.test.ts","../lib/entities/discount/strategies/bundlestrategy.ts","../lib/entities/discount/strategies/__tests__/bundlestrategy.test.ts","../lib/firebase-api/app.ts","../lib/firebase-api/index.ts","../lib/utils/index.ts"],"version":"5.7.3"}
|
|
1
|
+
{"root":["../lib/index.tsx","../lib/entities/address.ts","../lib/entities/atoms.ts","../lib/entities/cart.ts","../lib/entities/category.ts","../lib/entities/company.ts","../lib/entities/favoriteproduct.ts","../lib/entities/locale.ts","../lib/entities/order.ts","../lib/entities/organization.ts","../lib/entities/product.ts","../lib/entities/profile.ts","../lib/entities/store.ts","../lib/entities/index.ts","../lib/entities/discount/engine.ts","../lib/entities/discount/factory.ts","../lib/entities/discount/index.ts","../lib/entities/discount/strategy.ts","../lib/entities/discount/types.ts","../lib/entities/discount/utils.ts","../lib/entities/discount/__tests__/engine.test.ts","../lib/entities/discount/__tests__/factory.test.ts","../lib/entities/discount/__tests__/integration.test.ts","../lib/entities/discount/__tests__/simple.test.ts","../lib/entities/discount/__tests__/utils.test.ts","../lib/entities/discount/strategies/bundlestrategy.ts","../lib/entities/discount/strategies/__tests__/bundlestrategy.test.ts","../lib/firebase-api/app.ts","../lib/firebase-api/index.ts","../lib/utils/index.ts"],"version":"5.7.3"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -62,7 +62,10 @@ export declare function getCartCost({ cart, discounts, store, }: {
|
|
|
62
62
|
created_at: number;
|
|
63
63
|
updated_at: number;
|
|
64
64
|
categoryIds: string[];
|
|
65
|
-
|
|
65
|
+
purchasePrice?: number | undefined;
|
|
66
|
+
profitPercentage?: number | undefined;
|
|
67
|
+
isDiscountable?: boolean | undefined;
|
|
68
|
+
categoryList?: ({
|
|
66
69
|
id: string;
|
|
67
70
|
companyId: string;
|
|
68
71
|
storeId: string;
|
|
@@ -86,18 +89,15 @@ export declare function getCartCost({ cart, discounts, store, }: {
|
|
|
86
89
|
parentId?: string | null | undefined;
|
|
87
90
|
tag?: string | undefined;
|
|
88
91
|
} & /*elided*/ any)[];
|
|
89
|
-
})[];
|
|
90
|
-
categories
|
|
92
|
+
})[] | undefined;
|
|
93
|
+
categories?: {
|
|
91
94
|
lvl0: string[];
|
|
92
95
|
lvl1: string[];
|
|
93
96
|
lvl2: string[];
|
|
94
97
|
lvl3: string[];
|
|
95
98
|
lvl4: string[];
|
|
96
|
-
};
|
|
97
|
-
categoryNames
|
|
98
|
-
purchasePrice?: number | undefined;
|
|
99
|
-
profitPercentage?: number | undefined;
|
|
100
|
-
isDiscountable?: boolean | undefined;
|
|
99
|
+
} | undefined;
|
|
100
|
+
categoryNames?: string[] | undefined;
|
|
101
101
|
};
|
|
102
102
|
originalPrice: number;
|
|
103
103
|
finalPrice: number;
|
package/dist/utils/index.js
CHANGED
|
@@ -26,7 +26,7 @@ function getPriceAfterDiscount(product) {
|
|
|
26
26
|
export function getCartCost({ cart, discounts, store, }) {
|
|
27
27
|
const { isVatIncludedInPrice } = store;
|
|
28
28
|
// Convert cart items to the format expected by the discount engine
|
|
29
|
-
const cartForEngine = cart.map(item => ({
|
|
29
|
+
const cartForEngine = cart.map((item) => ({
|
|
30
30
|
amount: item.amount,
|
|
31
31
|
product: {
|
|
32
32
|
id: item.product.id,
|
|
@@ -35,7 +35,6 @@ export function getCartCost({ cart, discounts, store, }) {
|
|
|
35
35
|
}));
|
|
36
36
|
// Apply discounts using the new discount engine
|
|
37
37
|
const discountResult = DiscountEngine.calculateDiscounts(cartForEngine, discounts);
|
|
38
|
-
console.log("discountResult", discountResult);
|
|
39
38
|
// Map the results back to the original format with additional product info
|
|
40
39
|
const result = cart.map((item, index) => {
|
|
41
40
|
const engineItem = discountResult.items[index];
|
|
@@ -70,7 +69,8 @@ export function getCartCost({ cart, discounts, store, }) {
|
|
|
70
69
|
acc.cost += amount * roundedFinalPrice;
|
|
71
70
|
acc.discount += finalDiscount ? amount * finalDiscount : finalDiscount;
|
|
72
71
|
acc.finalCost += amount * roundedFinalPrice + (isVatIncludedInPrice ? 0 : productVatValue);
|
|
73
|
-
acc.productsCost +=
|
|
72
|
+
acc.productsCost +=
|
|
73
|
+
amount * roundedFinalPrice + (isVatIncludedInPrice ? 0 : productVatValue);
|
|
74
74
|
// Round all accumulated values to prevent floating point errors
|
|
75
75
|
acc.cost = Number(acc.cost.toFixed(2));
|
|
76
76
|
acc.discount = Number(acc.discount.toFixed(2));
|
|
@@ -1,129 +1,138 @@
|
|
|
1
1
|
import { TDiscount, AppliedDiscount, DiscountContext } from "./types";
|
|
2
2
|
import { DiscountStrategyFactory } from "./factory";
|
|
3
3
|
|
|
4
|
+
// discount engine - calculate discounts and return final prices
|
|
5
|
+
// should register all discount types
|
|
6
|
+
// should receive all store discounts
|
|
7
|
+
// should check active discount that should be applied on cart
|
|
8
|
+
// should find best discount per product
|
|
9
|
+
// should apply discount to if stackable or best one
|
|
10
|
+
// any applyd discount should mark products that effected and price before and after and average price per product in cart
|
|
11
|
+
// should return items list with all calculations and final prices and discounts
|
|
12
|
+
|
|
4
13
|
export class DiscountEngine {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
14
|
+
static calculateDiscounts(
|
|
15
|
+
cart: Array<{
|
|
16
|
+
amount: number;
|
|
17
|
+
product: {
|
|
18
|
+
id: string;
|
|
19
|
+
price: number;
|
|
20
|
+
};
|
|
21
|
+
}>,
|
|
22
|
+
discounts: TDiscount[],
|
|
23
|
+
user?: Record<string, unknown>
|
|
24
|
+
): {
|
|
25
|
+
items: Array<{
|
|
26
|
+
amount: number;
|
|
27
|
+
product: {
|
|
28
|
+
id: string;
|
|
29
|
+
price: number;
|
|
30
|
+
};
|
|
31
|
+
originalPrice: number;
|
|
32
|
+
finalPrice: number;
|
|
33
|
+
finalDiscount: number;
|
|
34
|
+
appliedDiscounts: string[];
|
|
35
|
+
}>;
|
|
36
|
+
totalDiscount: number;
|
|
37
|
+
appliedDiscounts: AppliedDiscount[];
|
|
38
|
+
} {
|
|
39
|
+
const context: DiscountContext = {
|
|
40
|
+
cart,
|
|
41
|
+
user,
|
|
42
|
+
appliedDiscounts: [],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Filter active discounts
|
|
46
|
+
const activeDiscounts = this.filterActiveDiscounts(discounts);
|
|
47
|
+
|
|
48
|
+
// Apply discounts
|
|
49
|
+
const appliedDiscounts: AppliedDiscount[] = [];
|
|
50
|
+
|
|
51
|
+
for (const discount of activeDiscounts) {
|
|
52
|
+
const strategy = DiscountStrategyFactory.getStrategy(discount);
|
|
53
|
+
if (!strategy) continue;
|
|
54
|
+
|
|
55
|
+
// Check if discount can be applied
|
|
56
|
+
if (!strategy.canApply(discount, context)) continue;
|
|
57
|
+
|
|
58
|
+
// Check if discount is stackable
|
|
59
|
+
// todo
|
|
60
|
+
if (!discount.conditions?.stackable && appliedDiscounts.length > 0) continue;
|
|
61
|
+
|
|
62
|
+
// Calculate discount
|
|
63
|
+
const result = strategy.calculate(discount, context);
|
|
64
|
+
if (!result.applicable) continue;
|
|
65
|
+
|
|
66
|
+
// Apply discount
|
|
67
|
+
appliedDiscounts.push({
|
|
68
|
+
discountId: discount.id,
|
|
69
|
+
discountName: discount.name[0]?.value || "Discount",
|
|
70
|
+
discountAmount: Number(result.discountAmount.toFixed(2)),
|
|
71
|
+
affectedItems: result.affectedItems,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Update context for next iteration
|
|
75
|
+
context.appliedDiscounts = appliedDiscounts;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Calculate final prices
|
|
79
|
+
const items = this.calculateFinalPrices(cart, appliedDiscounts);
|
|
80
|
+
|
|
81
|
+
const totalDiscount = appliedDiscounts.reduce(
|
|
82
|
+
(sum, discount) => sum + discount.discountAmount,
|
|
83
|
+
0
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
items,
|
|
88
|
+
totalDiscount: Number(totalDiscount.toFixed(2)),
|
|
89
|
+
appliedDiscounts,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private static filterActiveDiscounts(discounts: TDiscount[]): TDiscount[] {
|
|
94
|
+
const now = Date.now();
|
|
95
|
+
return discounts.filter(
|
|
96
|
+
(discount) => discount.active && discount.startDate <= now && discount.endDate >= now
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private static calculateFinalPrices(
|
|
101
|
+
cart: DiscountContext["cart"],
|
|
102
|
+
appliedDiscounts: AppliedDiscount[]
|
|
103
|
+
) {
|
|
104
|
+
return cart.map((item) => {
|
|
105
|
+
const itemDiscounts = appliedDiscounts.filter((discount) =>
|
|
106
|
+
discount.affectedItems.some((affected) => affected.productId === item.product.id)
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const totalItemDiscount = itemDiscounts.reduce((sum, discount) => {
|
|
110
|
+
const affectedItem = discount.affectedItems.find(
|
|
111
|
+
(affected) => affected.productId === item.product.id
|
|
112
|
+
);
|
|
113
|
+
return sum + (affectedItem?.discountAmount || 0);
|
|
114
|
+
}, 0);
|
|
115
|
+
|
|
116
|
+
const discountPerUnit = totalItemDiscount / item.amount;
|
|
117
|
+
const finalPrice = item.product.price - discountPerUnit;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
amount: item.amount,
|
|
121
|
+
product: item.product,
|
|
122
|
+
originalPrice: Number(item.product.price.toFixed(2)),
|
|
123
|
+
finalPrice: Number(Math.max(0, finalPrice).toFixed(2)),
|
|
124
|
+
finalDiscount: Number(totalItemDiscount.toFixed(2)),
|
|
125
|
+
appliedDiscounts: itemDiscounts.map((d) => d.discountId),
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static isDiscountActive(discount: TDiscount): boolean {
|
|
131
|
+
const now = Date.now();
|
|
132
|
+
return discount.active && discount.startDate <= now && discount.endDate >= now;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
static getActiveDiscounts(discounts: TDiscount[]): TDiscount[] {
|
|
136
|
+
return this.filterActiveDiscounts(discounts);
|
|
137
|
+
}
|
|
138
|
+
}
|
package/lib/entities/Order.ts
CHANGED
|
@@ -34,6 +34,7 @@ export const OrderSchema = z.object({
|
|
|
34
34
|
cartDiscount: z.number(),
|
|
35
35
|
cartTotal: z.number(),
|
|
36
36
|
cartVat: z.number(),
|
|
37
|
+
deliveryPrice: z.number().optional(),
|
|
37
38
|
}),
|
|
38
39
|
originalAmount: z.number().positive().optional(), // what client pay
|
|
39
40
|
actualAmount: z.number().positive().optional(), // what store charge
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// client organization for clients
|
|
4
|
+
export const OrganizationSchema = z.object({
|
|
5
|
+
id: z.string(),
|
|
6
|
+
name: z.string(),
|
|
7
|
+
discountPercentage: z.number().positive().min(0).max(100).optional(),
|
|
8
|
+
nameOnInvoice: z.string().optional(),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const NewOrganizationSchema = OrganizationSchema.omit({ id: true });
|
|
12
|
+
|
|
13
|
+
export type TNewOrganization = z.infer<typeof NewOrganizationSchema>;
|
|
14
|
+
export type TOrganization = z.infer<typeof OrganizationSchema>;
|
package/lib/entities/Product.ts
CHANGED
|
@@ -47,7 +47,7 @@ export const ProductSchema = z.object({
|
|
|
47
47
|
categoryIds: z.array(z.string().nonempty()),
|
|
48
48
|
|
|
49
49
|
// @deprecated
|
|
50
|
-
categoryList: z.array(CategorySchema),
|
|
50
|
+
categoryList: z.array(CategorySchema).optional(),
|
|
51
51
|
// @deprecated
|
|
52
52
|
categories: z.object({
|
|
53
53
|
lvl0: z.array(z.string()),
|
|
@@ -55,9 +55,9 @@ export const ProductSchema = z.object({
|
|
|
55
55
|
lvl2: z.array(z.string()),
|
|
56
56
|
lvl3: z.array(z.string()),
|
|
57
57
|
lvl4: z.array(z.string()),
|
|
58
|
-
}),
|
|
58
|
+
}).optional(),
|
|
59
59
|
// @deprecated
|
|
60
|
-
categoryNames: z.array(z.string()),
|
|
60
|
+
categoryNames: z.array(z.string()).optional(),
|
|
61
61
|
});
|
|
62
62
|
export type TProduct = z.infer<typeof ProductSchema>;
|
|
63
63
|
|
package/lib/entities/Profile.ts
CHANGED
package/lib/entities/index.ts
CHANGED