@jsdev_ninja/core 0.12.11 → 0.13.14

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.
Files changed (36) hide show
  1. package/dist/core.cjs.js +1 -1
  2. package/dist/core.cjs.js.map +1 -1
  3. package/dist/core.es.js +283 -235
  4. package/dist/core.es.js.map +1 -1
  5. package/dist/core.umd.js +1 -1
  6. package/dist/core.umd.js.map +1 -1
  7. package/dist/entities/DeliveryNote.d.ts +134 -0
  8. package/dist/entities/DeliveryNote.d.ts.map +1 -0
  9. package/dist/entities/DeliveryNote.js +34 -0
  10. package/dist/entities/Discount/engine.d.ts.map +1 -1
  11. package/dist/entities/Discount/engine.js +15 -10
  12. package/dist/entities/Order.d.ts +10 -0
  13. package/dist/entities/Order.d.ts.map +1 -1
  14. package/dist/entities/Order.js +1 -0
  15. package/dist/entities/Organization.d.ts +34 -0
  16. package/dist/entities/Organization.d.ts.map +1 -0
  17. package/dist/entities/Organization.js +9 -0
  18. package/dist/entities/Profile.d.ts +3 -0
  19. package/dist/entities/Profile.d.ts.map +1 -1
  20. package/dist/entities/Profile.js +1 -0
  21. package/dist/entities/Store.d.ts +2 -2
  22. package/dist/entities/index.d.ts +2 -0
  23. package/dist/entities/index.d.ts.map +1 -1
  24. package/dist/entities/index.js +2 -0
  25. package/dist/firebase-api/index.d.ts +3 -0
  26. package/dist/firebase-api/index.d.ts.map +1 -1
  27. package/dist/firebase-api/index.js +1 -0
  28. package/dist/tsconfig.app.tsbuildinfo +1 -1
  29. package/lib/entities/DeliveryNote.ts +42 -0
  30. package/lib/entities/Discount/engine.ts +134 -125
  31. package/lib/entities/Order.ts +1 -0
  32. package/lib/entities/Organization.ts +14 -0
  33. package/lib/entities/Profile.ts +1 -0
  34. package/lib/entities/index.ts +2 -0
  35. package/lib/firebase-api/index.ts +1 -0
  36. package/package.json +1 -1
@@ -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
- static calculateDiscounts(
6
- cart: Array<{
7
- amount: number;
8
- product: {
9
- id: string;
10
- price: number;
11
- };
12
- }>,
13
- discounts: TDiscount[],
14
- user?: Record<string, unknown>
15
- ): {
16
- items: Array<{
17
- amount: number;
18
- product: {
19
- id: string;
20
- price: number;
21
- };
22
- originalPrice: number;
23
- finalPrice: number;
24
- finalDiscount: number;
25
- appliedDiscounts: string[];
26
- }>;
27
- totalDiscount: number;
28
- appliedDiscounts: AppliedDiscount[];
29
- } {
30
- const context: DiscountContext = {
31
- cart,
32
- user,
33
- appliedDiscounts: [],
34
- };
35
-
36
- // Filter active discounts
37
- const activeDiscounts = this.filterActiveDiscounts(discounts);
38
-
39
- // Apply discounts
40
- const appliedDiscounts: AppliedDiscount[] = [];
41
-
42
- for (const discount of activeDiscounts) {
43
- const strategy = DiscountStrategyFactory.getStrategy(discount);
44
- if (!strategy) continue;
45
-
46
- // Check if discount can be applied
47
- if (!strategy.canApply(discount, context)) continue;
48
-
49
- // Check if discount is stackable
50
- if (!discount.conditions?.stackable && appliedDiscounts.length > 0) continue;
51
-
52
- // Calculate discount
53
- const result = strategy.calculate(discount, context);
54
- if (!result.applicable) continue;
55
-
56
- // Apply discount
57
- appliedDiscounts.push({
58
- discountId: discount.id,
59
- discountName: discount.name[0]?.value || "Discount",
60
- discountAmount: Number(result.discountAmount.toFixed(2)),
61
- affectedItems: result.affectedItems,
62
- });
63
-
64
- // Update context for next iteration
65
- context.appliedDiscounts = appliedDiscounts;
66
- }
67
-
68
- // Calculate final prices
69
- const items = this.calculateFinalPrices(cart, appliedDiscounts);
70
-
71
- const totalDiscount = appliedDiscounts.reduce((sum, discount) =>
72
- sum + discount.discountAmount, 0);
73
-
74
- return {
75
- items,
76
- totalDiscount: Number(totalDiscount.toFixed(2)),
77
- appliedDiscounts,
78
- };
79
- }
80
-
81
- private static filterActiveDiscounts(discounts: TDiscount[]): TDiscount[] {
82
- const now = Date.now();
83
- return discounts.filter(discount =>
84
- discount.active &&
85
- discount.startDate <= now &&
86
- discount.endDate >= now
87
- );
88
- }
89
-
90
- private static calculateFinalPrices(
91
- cart: DiscountContext["cart"],
92
- appliedDiscounts: AppliedDiscount[]
93
- ) {
94
- return cart.map(item => {
95
- const itemDiscounts = appliedDiscounts.filter(discount =>
96
- discount.affectedItems.some(affected => affected.productId === item.product.id)
97
- );
98
-
99
- const totalItemDiscount = itemDiscounts.reduce((sum, discount) => {
100
- const affectedItem = discount.affectedItems.find(affected =>
101
- affected.productId === item.product.id);
102
- return sum + (affectedItem?.discountAmount || 0);
103
- }, 0);
104
-
105
- const discountPerUnit = totalItemDiscount / item.amount;
106
- const finalPrice = item.product.price - discountPerUnit;
107
-
108
- return {
109
- amount: item.amount,
110
- product: item.product,
111
- originalPrice: Number(item.product.price.toFixed(2)),
112
- finalPrice: Number(Math.max(0, finalPrice).toFixed(2)),
113
- finalDiscount: Number(totalItemDiscount.toFixed(2)),
114
- appliedDiscounts: itemDiscounts.map(d => d.discountId),
115
- };
116
- });
117
- }
118
-
119
- static isDiscountActive(discount: TDiscount): boolean {
120
- const now = Date.now();
121
- return discount.active &&
122
- discount.startDate <= now &&
123
- discount.endDate >= now;
124
- }
125
-
126
- static getActiveDiscounts(discounts: TDiscount[]): TDiscount[] {
127
- return this.filterActiveDiscounts(discounts);
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
+ }
@@ -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>;
@@ -23,6 +23,7 @@ export const ProfileSchema = z.object({
23
23
  createdDate: z.number(),
24
24
  lastActivityDate: z.number(),
25
25
  paymentType: ProfilePaymentTypeSchema,
26
+ organizationId: z.string().optional(),
26
27
  });
27
28
 
28
29
  export type TProfile = z.infer<typeof ProfileSchema>;
@@ -10,3 +10,5 @@ export * from "./Product";
10
10
  export * from "./Profile";
11
11
  export * from "./Store";
12
12
  export * from "./Discount";
13
+ export * from "./Organization";
14
+ export * from "./DeliveryNote";
@@ -14,6 +14,7 @@ export const storeCollections = {
14
14
  payments: "payments",
15
15
  settings: "settings",
16
16
  discounts: "discounts",
17
+ organizations: "organizations",
17
18
  } as const;
18
19
 
19
20
  export const FirestoreApi = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsdev_ninja/core",
3
- "version": "0.12.11",
3
+ "version": "0.13.14",
4
4
  "main": "dist/core.cjs.js",
5
5
  "module": "dist/core.es.js",
6
6
  "types": "dist/index.d.ts",