shuttlepro-shared 1.2.91 → 1.2.93

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.
@@ -0,0 +1,147 @@
1
+ const { getRedisData, setRedisData } = require("../../config/redis");
2
+ const CustomerProfile = require("../../models/CustomerProfile");
3
+
4
+ const CACHE_KEY_ALL = "customer_profiles_all";
5
+
6
+ const getCachedAllCustomerProfiles = async () => {
7
+ let profiles = await getRedisData(CACHE_KEY_ALL);
8
+ if (!profiles) {
9
+ profiles = await CustomerProfile.find({}).lean().exec();
10
+ await setRedisData(CACHE_KEY_ALL, profiles);
11
+ }
12
+ return profiles;
13
+ };
14
+
15
+ const updateCachedAllCustomerProfiles = async () => {
16
+ const profiles = await CustomerProfile.find({}).lean().exec();
17
+ await setRedisData(CACHE_KEY_ALL, profiles);
18
+ };
19
+
20
+ const getCachedCustomerProfiles = async (workspaceId) => {
21
+ const allProfiles = await getCachedAllCustomerProfiles();
22
+ return allProfiles.filter((profile) => profile.workspaceId === workspaceId);
23
+ };
24
+
25
+ const createCustomerProfile = async (data) => {
26
+ const newProfile = new CustomerProfile(data);
27
+ const savedProfile = await newProfile.save();
28
+
29
+ updateCachedAllCustomerProfiles();
30
+ return savedProfile;
31
+ };
32
+
33
+ const findCustomerProfilesByWorkspaceId = async (workspaceId) => {
34
+ return await getCachedCustomerProfiles(workspaceId);
35
+ };
36
+
37
+ const findCustomerProfilesByWorkspace = async (workspaceId, filter = {}) => {
38
+ const profiles = await getCachedCustomerProfiles(workspaceId);
39
+ return profiles.filter((profile) =>
40
+ Object.entries(filter).every(([key, value]) => profile[key] === value)
41
+ );
42
+ };
43
+
44
+ const findCustomerProfileByFilter = async (
45
+ filter = {},
46
+ bodyFilter = {},
47
+ workspaceId = null
48
+ ) => {
49
+ const allProfiles = await getCachedAllCustomerProfiles();
50
+
51
+ const filtered = workspaceId
52
+ ? allProfiles.filter((p) => p.workspaceId === workspaceId)
53
+ : allProfiles;
54
+
55
+ return (
56
+ filtered.find(
57
+ (profile) =>
58
+ Object.entries(filter).every(
59
+ ([key, value]) => profile[key] === value
60
+ ) &&
61
+ Object.entries(bodyFilter).every(
62
+ ([key, value]) => profile.body?.[key] === value
63
+ )
64
+ ) || null
65
+ );
66
+ };
67
+
68
+ const findAllCustomerProfilesByFilter = async (
69
+ filter = {},
70
+ bodyFilter = {},
71
+ workspaceId = null
72
+ ) => {
73
+ const allProfiles = await getCachedAllCustomerProfiles();
74
+
75
+ const filtered = workspaceId
76
+ ? allProfiles.filter((p) => p.workspaceId === workspaceId)
77
+ : allProfiles;
78
+
79
+ return (
80
+ filtered.filter(
81
+ (profile) =>
82
+ Object.entries(filter).every(
83
+ ([key, value]) => profile[key] === value
84
+ ) &&
85
+ Object.entries(bodyFilter).every(
86
+ ([key, value]) => profile.body?.[key] === value
87
+ )
88
+ ) || []
89
+ );
90
+ };
91
+
92
+ const updateCustomerProfile = async (id, data) => {
93
+ const updated = await CustomerProfile.findByIdAndUpdate(id, data, {
94
+ new: true,
95
+ }).exec();
96
+
97
+ if (updated) {
98
+ updateCachedAllCustomerProfiles();
99
+ }
100
+
101
+ return updated;
102
+ };
103
+
104
+ const updateCustomerProfileByFilter = async (
105
+ filter,
106
+ data,
107
+ workspaceId = null
108
+ ) => {
109
+ const query = workspaceId ? { ...filter, workspaceId } : filter;
110
+
111
+ const updated = await CustomerProfile.findOneAndUpdate(query, data, {
112
+ new: true,
113
+ }).exec();
114
+
115
+ if (updated) {
116
+ updateCachedAllCustomerProfiles();
117
+ }
118
+
119
+ return updated;
120
+ };
121
+
122
+ const deleteCustomerProfile = async (id) => {
123
+ const deleted = await CustomerProfile.findByIdAndDelete(id).exec();
124
+
125
+ if (deleted) {
126
+ updateCachedAllCustomerProfiles();
127
+ }
128
+
129
+ return deleted;
130
+ };
131
+
132
+ const deleteAllCustomerProfilesByWorkspace = async (workspaceId) => {
133
+ await CustomerProfile.deleteMany({ workspaceId }).exec();
134
+ await updateCachedAllCustomerProfiles();
135
+ };
136
+
137
+ module.exports = {
138
+ createCustomerProfile,
139
+ findCustomerProfilesByWorkspaceId,
140
+ findCustomerProfilesByWorkspace,
141
+ findCustomerProfileByFilter,
142
+ updateCustomerProfile,
143
+ updateCustomerProfileByFilter,
144
+ deleteCustomerProfile,
145
+ deleteAllCustomerProfilesByWorkspace,
146
+ findAllCustomerProfilesByFilter,
147
+ };
@@ -12,7 +12,7 @@ const roleRepository = require("./role.repository");
12
12
  const userPermissionRepository = require("./userPermission.repository");
13
13
  const userRolePermissionRepository = require("./userRolePermission.repository");
14
14
  const notificationSettingsRepository = require("./notificationSettings.repository");
15
-
15
+ const customerProfileRepository = require("./customerProfile.repository");
16
16
  exports.module = {
17
17
  workspaceRepository,
18
18
  integrationRepository,
@@ -28,4 +28,5 @@ exports.module = {
28
28
  roleRepository,
29
29
  userPermissionRepository,
30
30
  userRolePermissionRepository,
31
+ customerProfileRepository,
31
32
  };
@@ -0,0 +1,22 @@
1
+ const mongoose = require("mongoose");
2
+ const { Schema } = mongoose;
3
+ const CustomerProfileSchema = new Schema(
4
+ {
5
+ name: { type: String, default: "" },
6
+ email: { type: String, default: "" },
7
+ contact: { type: String, default: "" },
8
+ picture: { type: String, default: "" },
9
+ fbId: { type: String, default: "" },
10
+ igId: { type: String, default: "" },
11
+ blackList: { type: Boolean, default: false },
12
+ mergedId: {
13
+ type: Schema.Types.ObjectId,
14
+ ref: "CustomerProfile",
15
+ default: null,
16
+ },
17
+ body: {},
18
+ },
19
+ { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }
20
+ );
21
+
22
+ module.exports = mongoose.model("CustomerProfile", CustomerProfileSchema);
package/models/Order.js CHANGED
@@ -1,5 +1,23 @@
1
1
  const mongoose = require("mongoose");
2
2
  const { Schema } = mongoose;
3
+ const normalizePhoneNumber = (phone) => {
4
+ let normalized = phone.replace(/[^\d]/g, "");
5
+ if (normalized.startsWith("92")) {
6
+ normalized = normalized.replace(/^92/, "");
7
+ }
8
+ if (normalized.startsWith("0")) {
9
+ normalized = normalized.substring(1);
10
+ }
11
+ return normalized;
12
+ };
13
+ const getPhoneRegex = (phone) => {
14
+ const normalizedPhone = normalizePhoneNumber(phone);
15
+ return new RegExp(
16
+ `^0?${normalizedPhone}$|^\\+92${normalizedPhone}$|^${normalizedPhone}$`,
17
+ "i"
18
+ );
19
+ };
20
+ const _ = require("lodash");
3
21
 
4
22
  const ShipperInformation = new Schema({
5
23
  _id: false,
@@ -12,6 +30,7 @@ const ShipperInformation = new Schema({
12
30
  originCityName: { type: String, default: "" }, //M&P, TRAX, INSTA, RIDER, TCS, CC
13
31
  remarks: { type: String, default: "" },
14
32
  customer_note: { type: String, default: "" },
33
+ orderId: { type: Schema.Types.ObjectId, ref: "Order", default: null },
15
34
  return_branch: { type: String, default: "" },
16
35
  cityData: { type: String, default: "" }, //M&P, TRAX, INSTA, RIDER, TCS, CC
17
36
  //TRAX
@@ -33,11 +52,18 @@ const ShipperInformation = new Schema({
33
52
 
34
53
  const OrderSchema = new Schema(
35
54
  {
55
+ webOrderId: { type: String, default: "" },
56
+ webOrderNumber: { type: String, default: "" },
57
+ orderNumber: { type: String, default: "" },
36
58
  customerId: { type: Schema.Types.ObjectId, ref: "Customer", default: null },
37
- swap: { type: String, default: "" },
38
- shipperInformation: ShipperInformation,
39
- trackingId: { type: String, default: "" },
59
+ customerName: { type: String, default: "" },
60
+ customerPhone: { type: String, default: "" },
61
+ customerAddress: { type: String, default: "" },
40
62
  cityName: { type: String, default: "" },
63
+ trackingId: { type: String, default: "" },
64
+ shipperInformation: ShipperInformation,
65
+ isCnGenerated: { type: Boolean, default: false },
66
+ swap: { type: String, default: "" },
41
67
  status: { type: String, default: "" },
42
68
  lastStatusTime: { type: String, default: "" },
43
69
  statusType: { type: String, default: "" },
@@ -60,16 +86,15 @@ const OrderSchema = new Schema(
60
86
  payableAmount: { type: Number, default: 0 },
61
87
  parcelValue: { type: Number, default: 0 },
62
88
  payableAmount: { type: Number, default: 0 },
63
- customerName: { type: String, default: "" },
64
- customerPhone: { type: String, default: "" },
65
- customerAddress: { type: String, default: "" },
66
89
  discountPercentage: { type: Number, default: 0 },
67
90
  discountValue: { type: Number, default: 0 },
68
91
  productDetails: { type: String, default: "" },
69
92
  webOrderId: { type: String, default: "" },
93
+ parentOrderId: { type: Schema.Types.ObjectId, ref: "Order", default: null },
70
94
  webOrderNumber: { type: String, default: "" },
71
95
  fulfillmentStatus: { type: String, default: "" },
72
96
  webFulfillmentId: { type: String, default: "" },
97
+ platformFinancialStatus: { type: String, default: "pending" },
73
98
  pinLocation: { type: String, default: "" },
74
99
  deliveryCharges: { type: Number, default: 0 },
75
100
  tax: { type: Number, default: 0 },
@@ -83,7 +108,6 @@ const OrderSchema = new Schema(
83
108
  quantity: { type: Number, default: 0 },
84
109
  assignTo: { type: Schema.Types.ObjectId, ref: "User", default: null },
85
110
  isLoadSheetGenerated: { type: Boolean, default: false },
86
- orderNumber: { type: String, default: "" },
87
111
  orderDate: { type: String, default: "" },
88
112
  returnDate: { type: String, default: "" },
89
113
  receivedDate: { type: String, default: "" },
@@ -98,8 +122,8 @@ const OrderSchema = new Schema(
98
122
  invalidCredentials: { type: Boolean, default: false },
99
123
  isConfirmed: { type: Boolean, default: false },
100
124
  confirmedBy: { type: Schema.Types.ObjectId, ref: "User", default: null },
101
- isBulk: { type: String, default: "" },
102
125
  priority: { type: String, default: "" },
126
+ isBulk: { type: String, default: "" },
103
127
  conversationId: { type: String, default: "" },
104
128
  createdBy: { type: Schema.Types.ObjectId, ref: "User", default: null },
105
129
  updatedBy: { type: Schema.Types.ObjectId, ref: "User", default: null },
@@ -107,13 +131,13 @@ const OrderSchema = new Schema(
107
131
  isDuplicate: { type: Boolean, default: false },
108
132
  ivrGenerated: { type: Boolean, default: false },
109
133
  isActivityGenerated: { type: Boolean, default: false },
110
- platformFinancialStatus: { type: String, default: "pending" },
111
134
  payment: {
112
135
  isPaid: { type: Boolean, default: false },
113
136
  paymentDate: { type: String, default: "" },
114
137
  paymentDetails: {},
115
138
  },
116
139
  credentials: {},
140
+ ticketId: { type: String, default: "" },
117
141
  sender: { type: String, default: "" },
118
142
  orderName: { type: String, default: "" },
119
143
  totalWeight: { type: String, default: "" },
@@ -133,6 +157,80 @@ OrderSchema.virtual("orderProducts", {
133
157
  foreignField: "orderId",
134
158
  });
135
159
 
160
+ OrderSchema.post("save", async function (doc) {
161
+ try {
162
+ if (doc) {
163
+ markOrUnMarkOrderAsDuplicate(doc);
164
+ }
165
+ return { code: 200 };
166
+ } catch (err) {
167
+ console.log("err", err);
168
+ return { code: 400 };
169
+ }
170
+ });
171
+
172
+ OrderSchema.post("findOneAndUpdate", async function (doc) {
173
+ try {
174
+ if (doc) {
175
+ markOrUnMarkOrderAsDuplicate(doc);
176
+ }
177
+ return { code: 200 };
178
+ } catch (err) {
179
+ console.log("err", err);
180
+ return { code: 400 };
181
+ }
182
+ });
183
+
184
+ const markOrUnMarkOrderAsDuplicate = async (doc) => {
185
+ try {
186
+ if (["pending", "cancelled", "published"].includes(doc?.statusType)) {
187
+ if (["cancelled"].includes(doc?.statusType)) {
188
+ await Order.updateOne(
189
+ { _id: doc?._id },
190
+ { $set: { isDuplicate: false } }
191
+ );
192
+ }
193
+ const phoneRegex = getPhoneRegex(doc?.customerPhone);
194
+ let orders = await Order.find({
195
+ workspaceId: doc?.workspaceId,
196
+ isDeleted: false,
197
+ statusType: "pending",
198
+ customerPhone: phoneRegex,
199
+ })
200
+ .lean()
201
+ .exec();
202
+ if (orders?.length > 1) {
203
+ const ordersByCity = _.groupBy(orders, "cityName");
204
+ const bulkOps = [];
205
+ for (const cityName in ordersByCity) {
206
+ const cityOrders = ordersByCity[cityName];
207
+ const isDuplicate = cityOrders.length > 1;
208
+ const orderIds = cityOrders.map((order) => order?._id);
209
+ bulkOps.push({
210
+ updateMany: {
211
+ filter: { _id: { $in: orderIds } },
212
+ update: { $set: { isDuplicate } },
213
+ },
214
+ });
215
+ }
216
+ if (bulkOps.length > 0) {
217
+ await Order.bulkWrite(bulkOps);
218
+ }
219
+ } else {
220
+ await Order.updateMany(
221
+ { _id: { $in: orders.map((d) => d?._id?.toString()) } },
222
+ { $set: { isDuplicate: false } }
223
+ );
224
+ }
225
+ return { code: 200 };
226
+ }
227
+ return { code: 200 };
228
+ } catch (err) {
229
+ console.log("err", err);
230
+ return { code: 400 };
231
+ }
232
+ };
233
+
136
234
  const Order = mongoose.model("Order", OrderSchema);
137
235
 
138
236
  module.exports = Order;
package/models.js CHANGED
@@ -41,6 +41,8 @@ const Role = require("./models/Role");
41
41
  const NewProduct = require("./models/NewProduct");
42
42
  const NotificationSettings = require("./models/NotificationSettings");
43
43
  const UserWorkflow = require("./models/UserWorkflow");
44
+ const Setting = require("./models/Setting");
45
+ const CustomerProfile = require("./models/CustomerProfile");
44
46
  module.exports = {
45
47
  Website,
46
48
  ProductQueue,
@@ -85,4 +87,6 @@ module.exports = {
85
87
  NewProduct,
86
88
  NotificationSettings,
87
89
  UserWorkflow,
90
+ Setting,
91
+ CustomerProfile,
88
92
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shuttlepro-shared",
3
- "version": "1.2.91",
3
+ "version": "1.2.93",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {