@b2y/ecommerce-common 1.2.1 → 1.2.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.
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="WEB_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <excludeFolder url="file://$MODULE_DIR$/.tmp" />
6
+ <excludeFolder url="file://$MODULE_DIR$/temp" />
7
+ <excludeFolder url="file://$MODULE_DIR$/tmp" />
8
+ </content>
9
+ <orderEntry type="inheritedJdk" />
10
+ <orderEntry type="sourceFolder" forTests="false" />
11
+ </component>
12
+ </module>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/ecommerce-common.iml" filepath="$PROJECT_DIR$/.idea/ecommerce-common.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
package/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
@@ -10,6 +10,7 @@ const AppConstants = {
10
10
  CUSTOMER_PORTAL_DOMAIN: 'CustomerPortalDomain',
11
11
  ADMIN_PORTAL_DOMAIN: 'AdminPortalDomain',
12
12
  CREATED_BY:'0000d6ab-8064-4786-a906-f458567224b8',
13
- BOOLEAN:'boolean'
13
+ BOOLEAN:'boolean',
14
+ OBJECT: 'object'
14
15
  }
15
16
  module.exports = AppConstants;
@@ -3,6 +3,9 @@ const StatusMessageConstants = {
3
3
  FAILURE: 'FAILURE',
4
4
  INVALID_REQUEST: 'Invalid request parameters',
5
5
  SERVER_ERROR: 'Internal server error',
6
+ INVALID_JSON_FORMAT: 'Invalid JSON format in request body',
7
+ INVALID_SORT_ORDER: 'SortOrder must be integer',
8
+ FILE_NOT_FOUND: 'File not found',
6
9
  // Tenant
7
10
  TENANT_NOT_FOUND: 'Tenant not found',
8
11
  TENANT_ID_REQUIRED_FIELD: 'TenantID is required for updating the tenant',
@@ -22,10 +25,15 @@ const StatusMessageConstants = {
22
25
  INVALID_START_END_DATE: 'End date can not be before start date',
23
26
  SUBSCRIPTION_PLAN_NOT_FOUND: "Subscription plan not found",
24
27
  INVALID_BOOLEAN_TYPE: "{field} must be boolean value",
25
-
26
28
  //TenantSettings
27
- TENANT_SETTINGS_NOT_FOUND: "Tenant settings not foundd",
28
-
29
+ TENANT_SETTINGS_NOT_FOUND: 'Tenant settings not found',
30
+ TENANT_SETTINGS_IMAGE_UPLOAD_FAILED: 'Failed to upload company logo',
31
+ TENANT_SETTINGS_REQUIRED_FIELD: 'TenantID is required for tenant settings updation',
32
+ TENANT_SETTINGS_UPDATED_SUCCESS: 'Tenant settings updated successfully',
33
+ TENANT_SETTINGS_ALREADY_EXISTS: 'Tenant settings already exist for this tenant',
34
+ INVALID_REQUEST: 'Invalid request',
35
+ TENANT_SETTINGS_UPDATE_ERROR: 'Failed to update tenant settings',
36
+ TENANT_SETTINGS_SINGLE_LOGO_ALLOWED: "Only one logo is allowed per tenant.",
29
37
  //Location
30
38
  STATE_CODE_COUNTRY_CODE_REQUIRE: "State code and country code are required to fetch cities",
31
39
  CITIES_NOT_FOUND: "No cities found for given state code",
@@ -1,7 +1,7 @@
1
1
  const StatusMessage = require('../constants/StatusMessageConstants');
2
2
  const AppUtil = require('../utility/AppUtil');
3
3
  const LocationUtility = require('../utility/LocationUtility');
4
- const EntityTypeEnum = require('../enum/EntityTypeEnum');
4
+
5
5
  // getAllSubscriptionPlans
6
6
  const getAllSubscriptionPlans = (
7
7
  SubscriptionPlan,
@@ -56,129 +56,5 @@ const getAllSubscriptionPlans = (
56
56
  };
57
57
  };
58
58
 
59
- const getTenantSubscription = (
60
- Tenant,
61
- TenantSubscription,
62
- SubscriptionPlan,
63
- TenantSettings,
64
- Document,
65
- getPublicUrl,
66
- logger,
67
- ) => {
68
- return async (req, res) => {
69
- try {
70
- const { id } = req.params;
71
-
72
- const tenant = await Tenant.findOne({
73
- where: { TenantID: id },
74
- include: [
75
- {
76
- model: TenantSettings,
77
- as: 'TenantSettings',
78
- attributes: ['TenantSettingID']
79
- },
80
- {
81
- model: TenantSubscription,
82
- as: "CurrentSubscription",
83
- attributes: [
84
- "TenantSubscriptionID",
85
- "Status",
86
- "StartDate",
87
- "EndDate",
88
- "GraceEndDate",
89
- "TrialEndDate",
90
- "IsTrial",
91
- "AutoRenew",
92
- ],
93
- include: [
94
- {
95
- model: SubscriptionPlan,
96
- as: "SubscriptionPlan",
97
- attributes: ["SubscriptionPlanID", "PlanName", "PlanCode"],
98
- },
99
- ],
100
- },
101
- ],
102
- });
103
-
104
- if (!tenant) {
105
- return AppUtil.generateResponse(
106
- 404,
107
- StatusMessage.FAILURE,
108
- StatusMessage.TENANT_NOT_FOUND,
109
- null,
110
- res,
111
- );
112
- }
113
- const documents = await Document.findAll({
114
- where: {
115
- EntityType: EntityTypeEnum.TENANT_SETTINGS,
116
- EntityID: tenant.TenantSettings.TenantSettingID
117
- },
118
- order: [["SortOrder", "ASC"]]
119
- });
120
-
121
- const logo = await Promise.all(
122
- documents.map(async (doc) => ({
123
- documentId: doc.DocumentID,
124
- sortOrder: doc.SortOrder,
125
- documentUrl: await getPublicUrl(doc, logger)
126
- }))
127
- );
128
- const { countryName, stateName } =
129
- await LocationUtility.resolveCountryAndState(
130
- tenant.CountryCode,
131
- tenant.StateCode,
132
- logger,
133
- );
134
-
135
- const formattedTenant = {
136
- TenantID: tenant.TenantID,
137
- CompanyName: tenant.CompanyName,
138
- CompanyCode: tenant.CompanyCode,
139
- Email: tenant.Email,
140
- CountryCallingCode: tenant.CountryCallingCode,
141
- PhoneNumber: tenant.PhoneNumber,
142
- GSTNo: tenant.GSTNo,
143
- CustomerPortalDomain: tenant.CustomerPortalDomain,
144
- AdminPortalDomain: tenant.AdminPortalDomain,
145
- AddressLine: tenant.AddressLine,
146
- CityName: tenant.CityName,
147
- StateCode: tenant.StateCode,
148
- StateName: stateName,
149
- CountryCode: tenant.CountryCode,
150
- CountryName: countryName,
151
- Zipcode: tenant.Zipcode,
152
- TenantLogo: logo,
153
- SubscriptionPlanID: tenant.CurrentSubscription?.SubscriptionPlan?.SubscriptionPlanID,
154
- PlanName: tenant.CurrentSubscription?.SubscriptionPlan?.PlanName,
155
- TenantSubscriptionStatus: tenant.CurrentSubscription?.Status,
156
- StartDate: tenant.CurrentSubscription?.StartDate,
157
- EndDate: tenant.CurrentSubscription?.EndDate,
158
- GraceEndDate: tenant.CurrentSubscription?.GraceEndDate,
159
- TrialEndDate: tenant.CurrentSubscription?.TrialEndDate,
160
- IsTrial: tenant.CurrentSubscription?.IsTrial,
161
- AutoRenew: tenant.CurrentSubscription?.AutoRenew,
162
- };
163
-
164
- return AppUtil.generateResponse(
165
- 200,
166
- StatusMessage.SUCCESS,
167
- null,
168
- formattedTenant,
169
- res,
170
- );
171
- } catch (error) {
172
- logger.error("Error fetching tenant:", error);
173
- return AppUtil.generateResponse(
174
- 500,
175
- StatusMessage.FAILURE,
176
- StatusMessage.SERVER_ERROR,
177
- null,
178
- res,
179
- );
180
- }
181
- };
182
- };
183
59
 
184
- module.exports = {getAllSubscriptionPlans, getTenantSubscription}
60
+ module.exports = {getAllSubscriptionPlans}
@@ -3,7 +3,7 @@ const StatusMessage = require('../constants/StatusMessageConstants');
3
3
  const AppUtil = require('../utility/AppUtil');
4
4
  const SubscriptionStatusEnum = require('../enum/SubscriptionStatusEnum');
5
5
  const AppConstants = require('../constants/AppConstants');
6
-
6
+ const EntityTypeEnum = require('../enum/EntityTypeEnum');
7
7
  const updateTenant = (
8
8
  Tenant,
9
9
  TenantSubscription,
@@ -348,5 +348,149 @@ const updateTenant = (
348
348
  };
349
349
  };
350
350
 
351
- module.exports = { updateTenant };
351
+ const getTenantById = (
352
+ Tenant,
353
+ TenantSubscription,
354
+ SubscriptionPlan,
355
+ TenantSettings,
356
+ Document,
357
+ getPublicUrl,
358
+ logger,
359
+ ) => {
360
+ return async (req, res) => {
361
+ try {
362
+ const { id } = req.params;
363
+
364
+ const tenant = await Tenant.findOne({
365
+ where: { TenantID: id },
366
+ attributes: [
367
+ 'TenantID',
368
+ 'CompanyName',
369
+ 'CompanyCode',
370
+ 'Email',
371
+ 'CountryCallingCode',
372
+ 'PhoneNumber',
373
+ 'GSTNo',
374
+ 'CustomerPortalDomain',
375
+ 'AdminPortalDomain',
376
+ 'AddressLine',
377
+ 'CityName',
378
+ 'StateCode',
379
+ 'CountryCode',
380
+ 'Zipcode',
381
+ ],
382
+ include: [
383
+ {
384
+ model: TenantSettings,
385
+ as: 'TenantSettings',
386
+ attributes: ['TenantSettingID']
387
+ },
388
+ {
389
+ model: TenantSubscription,
390
+ as: "CurrentSubscription",
391
+ attributes: [
392
+ "TenantSubscriptionID",
393
+ "Status",
394
+ "StartDate",
395
+ "EndDate",
396
+ "GraceEndDate",
397
+ "TrialEndDate",
398
+ "IsTrial",
399
+ "AutoRenew",
400
+ ],
401
+ include: [
402
+ {
403
+ model: SubscriptionPlan,
404
+ as: "SubscriptionPlan",
405
+ attributes: ["SubscriptionPlanID", "PlanName", "PlanCode"],
406
+ },
407
+ ],
408
+ },
409
+ ],
410
+ });
411
+
412
+ if (!tenant) {
413
+ return AppUtil.generateResponse(
414
+ 404,
415
+ StatusMessage.FAILURE,
416
+ StatusMessage.TENANT_NOT_FOUND,
417
+ null,
418
+ res,
419
+ );
420
+ }
421
+ let logo = [];
422
+ if(tenant.TenantSettings) {
423
+ const documents = await Document.findAll({
424
+ where: {
425
+ EntityType: EntityTypeEnum.TENANT_SETTINGS,
426
+ EntityID: tenant.TenantSettings.TenantSettingID
427
+ },
428
+ order: [["SortOrder", "ASC"]]
429
+ });
430
+
431
+ logo = await Promise.all(
432
+ documents.map(async (doc) => ({
433
+ documentId: doc.DocumentID,
434
+ sortOrder: doc.SortOrder,
435
+ documentUrl: await getPublicUrl(doc, logger)
436
+ }))
437
+ );
438
+ }
439
+ const { countryName, stateName } =
440
+ await LocationUtility.resolveCountryAndState(
441
+ tenant.CountryCode,
442
+ tenant.StateCode,
443
+ logger,
444
+ );
445
+
446
+ const formattedTenant = {
447
+ TenantID: tenant.TenantID,
448
+ CompanyName: tenant.CompanyName,
449
+ CompanyCode: tenant.CompanyCode,
450
+ Email: tenant.Email,
451
+ CountryCallingCode: tenant.CountryCallingCode,
452
+ PhoneNumber: tenant.PhoneNumber,
453
+ GSTNo: tenant.GSTNo,
454
+ CustomerPortalDomain: tenant.CustomerPortalDomain,
455
+ AdminPortalDomain: tenant.AdminPortalDomain,
456
+ AddressLine: tenant.AddressLine,
457
+ CityName: tenant.CityName,
458
+ StateCode: tenant.StateCode,
459
+ StateName: stateName,
460
+ CountryCode: tenant.CountryCode,
461
+ CountryName: countryName,
462
+ Zipcode: tenant.Zipcode,
463
+ TenantLogo: logo,
464
+ SubscriptionPlanID: tenant.CurrentSubscription?.SubscriptionPlan?.SubscriptionPlanID,
465
+ PlanName: tenant.CurrentSubscription?.SubscriptionPlan?.PlanName,
466
+ TenantSubscriptionStatus: tenant.CurrentSubscription?.Status,
467
+ StartDate: tenant.CurrentSubscription?.StartDate,
468
+ EndDate: tenant.CurrentSubscription?.EndDate,
469
+ GraceEndDate: tenant.CurrentSubscription?.GraceEndDate,
470
+ TrialEndDate: tenant.CurrentSubscription?.TrialEndDate,
471
+ IsTrial: tenant.CurrentSubscription?.IsTrial,
472
+ AutoRenew: tenant.CurrentSubscription?.AutoRenew,
473
+ };
474
+
475
+ return AppUtil.generateResponse(
476
+ 200,
477
+ StatusMessage.SUCCESS,
478
+ null,
479
+ formattedTenant,
480
+ res,
481
+ );
482
+ } catch (error) {
483
+ logger.error("Error fetching tenant:", error);
484
+ return AppUtil.generateResponse(
485
+ 500,
486
+ StatusMessage.FAILURE,
487
+ StatusMessage.SERVER_ERROR,
488
+ null,
489
+ res,
490
+ );
491
+ }
492
+ };
493
+ };
494
+
495
+ module.exports = { updateTenant, getTenantById };
352
496
 
@@ -1,6 +1,7 @@
1
1
  const EntityTypeEnum = require('../enum/EntityTypeEnum');
2
2
  const StatusMessage = require('../constants/StatusMessageConstants');
3
3
  const AppUtil = require('../utility/AppUtil');
4
+ const AppConstants = require('../constants/AppConstants')
4
5
 
5
6
  const getTenantSettingsById = (TenantSettings, Document, getPublicUrl, logger) => {
6
7
  return async (req, res) => {
@@ -66,5 +67,166 @@ return async (req, res) => {
66
67
  }
67
68
  }}
68
69
 
69
- module.exports = {getTenantSettingsById};
70
+ const updateTenantSettings = (TenantSettings, multer, upload, FileUploadUtil, FILE_PATHS, UpdatedBy, sequelize, logger) => {
71
+ return async (req, res) => {
72
+ upload(req, res, async (err) => {
73
+ if (err instanceof multer.MulterError || err) {
74
+ logger.error('Upload Error:', err);
75
+ return AppUtil.generateResponse(
76
+ 400,
77
+ StatusMessage.FAILURE,
78
+ StatusMessage.TENANT_SETTINGS_IMAGE_UPLOAD_FAILED,
79
+ null,
80
+ res
81
+ );
82
+ }
83
+
84
+ let transaction;
85
+ let uploadedFilePath = [];
86
+ try {
87
+ let data;
88
+ try {
89
+ data = req.body.data ? JSON.parse(req.body.data) : req.body;
90
+ } catch (error) {
91
+ logger.error('Invalid JSON in request body', error);
92
+ return AppUtil.generateResponse(
93
+ 400,
94
+ StatusMessage.FAILURE,
95
+ StatusMessage.INVALID_JSON_FORMAT,
96
+ null,
97
+ res
98
+ );
99
+ }
100
+
101
+ const {
102
+ TenantSettingID,
103
+ TenantID,
104
+ ThemeColour,
105
+ documentMetadata
106
+ } = data;
107
+
108
+ // Validation
109
+ if (!TenantSettingID && !TenantID) {
110
+ logger.warn('TenantSettingID or TenantID is required for update.');
111
+ return AppUtil.generateResponse(
112
+ 400,
113
+ StatusMessage.FAILURE,
114
+ StatusMessage.TENANT_SETTINGS_REQUIRED_FIELD,
115
+ null,
116
+ res
117
+ );
118
+ }
119
+
120
+ // Find existing settings
121
+ const whereClause = TenantSettingID ? { TenantSettingID } : { TenantID };
122
+ const existingSettings = await TenantSettings.findOne({ where: whereClause });
123
+
124
+ if (!existingSettings) {
125
+ logger.warn('Tenant settings not found for update', whereClause);
126
+ return AppUtil.generateResponse(
127
+ 404,
128
+ StatusMessage.FAILURE,
129
+ StatusMessage.TENANT_SETTINGS_NOT_FOUND,
130
+ null,
131
+ res
132
+ );
133
+ }
134
+
135
+ // Upload new file to storage before starting transaction
136
+ let uploadResult = [];
137
+ if (documentMetadata !== undefined) {
138
+ if (
139
+ typeof documentMetadata !== AppConstants.OBJECT ||
140
+ Array.isArray(documentMetadata)
141
+ ) {
142
+ logger.warn(
143
+ "Validation Failed: documentMetadata must be a single object for this operation.",
144
+ );
145
+ return AppUtil.generateResponse(
146
+ 400,
147
+ StatusMessage.FAILURE,
148
+ StatusMessage.TENANT_SETTINGS_SINGLE_LOGO_ALLOWED,
149
+ null,
150
+ res,
151
+ );
152
+ }
153
+ logger.info('Uploading new files to storage before transaction');
154
+ uploadResult = await FileUploadUtil.uploadFilesToStorage(
155
+ documentMetadata,
156
+ req.files,
157
+ FILE_PATHS.IMAGE_DIR
158
+ );
159
+ uploadedFilePath = uploadResult.map(r => r.path);
160
+ }
161
+
162
+ // Start database transaction
163
+ transaction = await sequelize.transaction();
164
+
165
+ // Update tenant settings
166
+ const updatePayload = {};
167
+ if (ThemeColour !== undefined) updatePayload.ThemeColour = ThemeColour;
168
+ updatePayload.UpdatedBy = UpdatedBy;
169
+ updatePayload.UpdatedAt = new Date();
170
+
171
+ await existingSettings.update(updatePayload, { transaction });
172
+
173
+ // Save/Update documents (handles both create and replace logic)
174
+ if (uploadResult.length > 0) {
175
+ logger.info('Saving document records for TenantSettingID:', existingSettings.TenantSettingID);
176
+
177
+ await FileUploadUtil.saveDocumentRecords(
178
+ uploadResult,
179
+ EntityTypeEnum.TENANT_SETTINGS,
180
+ existingSettings.TenantSettingID,
181
+ existingSettings.TenantID,
182
+ UpdatedBy,
183
+ transaction
184
+ );
185
+ }
186
+
187
+ await transaction.commit();
188
+ logger.info('Tenant settings updated successfully');
189
+
190
+ return AppUtil.generateResponse(
191
+ 200,
192
+ StatusMessage.SUCCESS,
193
+ StatusMessage.TENANT_SETTINGS_UPDATED_SUCCESS,
194
+ null,
195
+ res
196
+ );
197
+
198
+ } catch (error) {
199
+ if (transaction) await transaction.rollback();
200
+
201
+ // Cleanup newly uploaded files if transaction failed
202
+ if (uploadedFilePath.length > 0) {
203
+ logger.info('Cleaning up uploaded files due to error');
204
+ await FileUploadUtil.cleanupFiles(uploadedFilePath);
205
+ }
206
+
207
+ // Error handling
208
+ if (error.message === StatusMessage.INVALID_SORT_ORDER) {
209
+ return AppUtil.generateResponse(400, StatusMessage.FAILURE, StatusMessage.INVALID_SORT_ORDER, null, res);
210
+ }
211
+ if (error.message === StatusMessage.FILE_NOT_FOUND) {
212
+ return AppUtil.generateResponse(400, StatusMessage.FAILURE, StatusMessage.FILE_NOT_FOUND, null, res);
213
+ }
214
+ if(error.message === StatusMessage.INVALID_FILE_UPLOAD_INPUT) {
215
+ return AppUtil.generateResponse(400, StatusMessage.FAILURE, StatusMessage.INVALID_FILE_UPLOAD_INPUT, null, res);
216
+ }
217
+ if (error.name === 'SequelizeUniqueConstraintError') {
218
+ return AppUtil.generateResponse(400, StatusMessage.FAILURE, StatusMessage.TENANT_SETTINGS_ALREADY_EXISTS, null, res);
219
+ } else if (error.name === 'SequelizeValidationError') {
220
+ const details = error.errors.map((e) => e.message);
221
+ logger.warn(`Validation Error detected: ${details.join('; ')}`);
222
+ return AppUtil.generateResponse(400, StatusMessage.FAILURE, StatusMessage.INVALID_REQUEST, null, res);
223
+ } else {
224
+ logger.error("Error in updateTenantSettings:", error);
225
+ return AppUtil.generateResponse(500, StatusMessage.FAILURE, StatusMessage.TENANT_SETTINGS_UPDATE_ERROR, null, res);
226
+ }
227
+ }
228
+ });
229
+ };
230
+ }
231
+ module.exports = {getTenantSettingsById, updateTenantSettings};
70
232
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b2y/ecommerce-common",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "E-commerce common library",
5
5
  "main": "index.js",
6
6
  "scripts": {