tango-app-api-store-builder 1.1.14 → 1.1.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-store-builder",
3
- "version": "1.1.14",
3
+ "version": "1.1.15",
4
4
  "description": "storeBuilder",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -33,7 +33,7 @@
33
33
  "path": "^0.12.7",
34
34
  "selenium-webdriver": "^4.31.0",
35
35
  "sharp": "^0.34.1",
36
- "tango-api-schema": "^2.5.45",
36
+ "tango-api-schema": "^2.5.49",
37
37
  "tango-app-api-middleware": "3.1.48",
38
38
  "url": "^0.11.4",
39
39
  "winston": "^3.17.0",
@@ -10,6 +10,7 @@ import * as storeBuilderService from '../service/storeBuilder.service.js';
10
10
  import * as planoRevisionService from '../service/planoRevision.service.js';
11
11
  import * as storeBuilderController from './storeBuilder.controller.js';
12
12
  import * as taskController from './task.controller.js';
13
+ import * as processedService from '../service/processedTaskservice.js';
13
14
  import mongoose from 'mongoose';
14
15
  import fetch from 'node-fetch';
15
16
  import fs from 'fs';
@@ -1673,6 +1674,9 @@ export async function approveReplace( req, res ) {
1673
1674
  skipped: [],
1674
1675
  };
1675
1676
 
1677
+ // Map to track task IDs for each store
1678
+ const storeTaskIds = new Map();
1679
+
1676
1680
  for ( const store of storesToProcess ) {
1677
1681
  try {
1678
1682
  const replaceResponseData = store.replaceResponseData;
@@ -1833,11 +1837,27 @@ export async function approveReplace( req, res ) {
1833
1837
  body: taskPayload,
1834
1838
  user: req.user || {},
1835
1839
  };
1840
+ let taskId = null;
1836
1841
  const mockTaskRes = {
1837
- sendSuccess: () => {},
1842
+ sendSuccess: ( data ) => {
1843
+ taskId = data?.taskId || null;
1844
+ },
1838
1845
  sendError: () => {},
1839
1846
  };
1840
1847
  await taskController.createTask( mockTaskReq, mockTaskRes );
1848
+
1849
+ // Store the task ID based on the checklist name
1850
+ if ( taskId ) {
1851
+ if ( !storeTaskIds.has( store.storeName ) ) {
1852
+ storeTaskIds.set( store.storeName, { merchRolloutId: null, vmRolloutId: null } );
1853
+ }
1854
+ const taskIds = storeTaskIds.get( store.storeName );
1855
+ if ( taskPayload.checkListName === 'Merchandise Rollout' ) {
1856
+ taskIds.merchRolloutId = taskId;
1857
+ } else if ( taskPayload.checkListName === 'Visual Merch Rollout' ) {
1858
+ taskIds.vmRolloutId = taskId;
1859
+ }
1860
+ }
1841
1861
  }
1842
1862
  } else {
1843
1863
  logger.info( {
@@ -1873,6 +1893,25 @@ export async function approveReplace( req, res ) {
1873
1893
  }
1874
1894
  }
1875
1895
 
1896
+ // Update stores in findReplaceDoc with task IDs
1897
+ const updatedStores = findReplaceDoc.stores.map( ( store ) => {
1898
+ const taskIds = storeTaskIds.get( store.storeName );
1899
+ if ( taskIds ) {
1900
+ return {
1901
+ ...store,
1902
+ merchRolloutId: taskIds.merchRolloutId || store.merchRolloutId,
1903
+ vmRolloutId: taskIds.vmRolloutId || store.vmRolloutId,
1904
+ };
1905
+ }
1906
+ return store;
1907
+ } );
1908
+
1909
+ // Update findReplaceAiModel with task IDs
1910
+ await findReplaceAiService.updateOne(
1911
+ { _id: findReplaceId },
1912
+ { stores: updatedStores },
1913
+ );
1914
+
1876
1915
  const allStoresProcessed = storesToProcess.length === results.success.length + results.failed.length + results.skipped.length;
1877
1916
  if ( allStoresProcessed ) {
1878
1917
  await findReplaceAiService.updateOne(
@@ -1897,6 +1936,119 @@ export async function approveReplace( req, res ) {
1897
1936
  }
1898
1937
  }
1899
1938
 
1939
+ export async function processScheduledFindReplace( req, res ) {
1940
+ try {
1941
+ const today = dayjs();
1942
+ const startOfDay = today.startOf( 'day' ).toDate();
1943
+ const endOfDay = today.endOf( 'day' ).toDate();
1944
+
1945
+ // Find all scheduled entries for today
1946
+ const scheduledEntries = await findReplaceAiService.find( {
1947
+ schedule: true,
1948
+ scheduleDate: {
1949
+ $gte: startOfDay,
1950
+ $lte: endOfDay,
1951
+ },
1952
+ status: { $in: [ 'scheduled', 'yet-to-approve' ] },
1953
+ } );
1954
+
1955
+ if ( !scheduledEntries || scheduledEntries.length === 0 ) {
1956
+ return res.sendSuccess( {
1957
+ message: 'No scheduled entries found for today',
1958
+ processed: 0,
1959
+ results: [],
1960
+ } );
1961
+ }
1962
+
1963
+ const results = {
1964
+ processed: [],
1965
+ failed: [],
1966
+ };
1967
+
1968
+ // Process each scheduled entry
1969
+ for ( const entry of scheduledEntries ) {
1970
+ try {
1971
+ const findReplaceId = entry._id.toString();
1972
+ const entryDoc = entry.toObject ? entry.toObject() : entry;
1973
+
1974
+ // Verify the schedule date matches today (double check)
1975
+ const scheduleDate = dayjs( entryDoc.scheduleDate );
1976
+ if ( !scheduleDate.isSame( today, 'day' ) ) {
1977
+ logger.warn( {
1978
+ functionName: 'processScheduledFindReplace',
1979
+ findReplaceId: findReplaceId,
1980
+ message: 'Schedule date does not match today, skipping',
1981
+ scheduleDate: scheduleDate.format( 'YYYY-MM-DD' ),
1982
+ today: today.format( 'YYYY-MM-DD' ),
1983
+ } );
1984
+ continue;
1985
+ }
1986
+
1987
+ // Call approveReplace internally
1988
+ const mockApproveReq = {
1989
+ body: {
1990
+ findReplaceId: findReplaceId,
1991
+ sendRollout: true,
1992
+ },
1993
+ user: req.user || {},
1994
+ };
1995
+
1996
+ let approveResult = null;
1997
+ let approveError = null;
1998
+
1999
+ const mockApproveRes = {
2000
+ sendSuccess: ( data ) => {
2001
+ approveResult = data;
2002
+ },
2003
+ sendError: ( error, statusCode ) => {
2004
+ approveError = { error, statusCode };
2005
+ },
2006
+ };
2007
+
2008
+ await approveReplace( mockApproveReq, mockApproveRes );
2009
+
2010
+ if ( approveError ) {
2011
+ results.failed.push( {
2012
+ findReplaceId: findReplaceId,
2013
+ name: entryDoc.name || 'Unknown',
2014
+ error: approveError.error || approveError,
2015
+ } );
2016
+ } else {
2017
+ results.processed.push( {
2018
+ findReplaceId: findReplaceId,
2019
+ name: entryDoc.name || 'Unknown',
2020
+ result: approveResult,
2021
+ } );
2022
+ }
2023
+ } catch ( entryError ) {
2024
+ logger.error( {
2025
+ functionName: 'processScheduledFindReplace',
2026
+ findReplaceId: entry._id.toString(),
2027
+ error: entryError,
2028
+ } );
2029
+ results.failed.push( {
2030
+ findReplaceId: entry._id.toString(),
2031
+ name: ( entry.toObject ? entry.toObject() : entry ).name || 'Unknown',
2032
+ error: entryError.message || 'Unknown error',
2033
+ } );
2034
+ }
2035
+ }
2036
+
2037
+ return res.sendSuccess( {
2038
+ message: 'Scheduled entries processed',
2039
+ summary: {
2040
+ total: scheduledEntries.length,
2041
+ processed: results.processed.length,
2042
+ failed: results.failed.length,
2043
+ },
2044
+ results: results,
2045
+ } );
2046
+ } catch ( e ) {
2047
+ logger.error( { functionName: 'processScheduledFindReplace', error: e } );
2048
+ return res.sendError( e, 500 );
2049
+ }
2050
+ }
2051
+
1900
2052
  export async function cancelFindReplace( req, res ) {
1901
2053
  try {
1902
2054
  const { findReplaceId } = req.body;
@@ -1996,6 +2148,168 @@ export async function cancelFindReplace( req, res ) {
1996
2148
  }
1997
2149
  }
1998
2150
 
2151
+ export async function revokeFindReplace( req, res ) {
2152
+ try {
2153
+ const { findReplaceId } = req.body;
2154
+
2155
+ if ( !findReplaceId || !mongoose.isValidObjectId( findReplaceId ) ) {
2156
+ return res.sendError( 'Valid findReplaceId is required', 400 );
2157
+ }
2158
+
2159
+ const findReplaceData = await findReplaceAiService.findOne( { _id: findReplaceId } );
2160
+ if ( !findReplaceData ) {
2161
+ return res.sendError( 'FindReplace document not found', 404 );
2162
+ }
2163
+
2164
+ const findReplaceDoc = findReplaceData.toObject ? findReplaceData.toObject() : findReplaceData;
2165
+
2166
+ if ( !Array.isArray( findReplaceDoc.stores ) || findReplaceDoc.stores.length === 0 ) {
2167
+ return res.sendError( 'No stores found in the document', 400 );
2168
+ }
2169
+
2170
+ const results = {
2171
+ success: [],
2172
+ failed: [],
2173
+ };
2174
+
2175
+ // Map to track which task IDs were successfully revoked
2176
+ const revokedTaskIds = new Set();
2177
+
2178
+ // Process each store to revoke tasks
2179
+ for ( const store of findReplaceDoc.stores ) {
2180
+ try {
2181
+ const tasksToRevoke = [];
2182
+
2183
+ // Check for merchRolloutId
2184
+ if ( store.merchRolloutId ) {
2185
+ tasksToRevoke.push( {
2186
+ taskId: store.merchRolloutId,
2187
+ type: 'merchRollout',
2188
+ fieldName: 'merchRolloutId',
2189
+ } );
2190
+ }
2191
+
2192
+ // Check for vmRolloutId
2193
+ if ( store.vmRolloutId ) {
2194
+ tasksToRevoke.push( {
2195
+ taskId: store.vmRolloutId,
2196
+ type: 'vmRollout',
2197
+ fieldName: 'vmRolloutId',
2198
+ } );
2199
+ }
2200
+
2201
+ let revokedCount = 0;
2202
+
2203
+ // Revoke each task
2204
+ for ( const task of tasksToRevoke ) {
2205
+ try {
2206
+ // Get task details to retrieve floorId and planoId
2207
+ const taskDetails = await processedService.findOne( { _id: new mongoose.Types.ObjectId( task.taskId ) } );
2208
+
2209
+ if ( !taskDetails ) {
2210
+ logger.warn( {
2211
+ functionName: 'revokeFindReplace',
2212
+ storeName: store.storeName,
2213
+ taskId: task.taskId,
2214
+ message: 'Task not found, skipping revocation'
2215
+ } );
2216
+ continue;
2217
+ }
2218
+
2219
+ const taskData = taskDetails.toObject ? taskDetails.toObject() : taskDetails;
2220
+ const floorId = taskData.floorId ? taskData.floorId.toString() : null;
2221
+ const planoId = taskData.planoId ? taskData.planoId.toString() : store.planoId || null;
2222
+
2223
+ if ( !floorId || !planoId ) {
2224
+ logger.warn( {
2225
+ functionName: 'revokeFindReplace',
2226
+ storeName: store.storeName,
2227
+ taskId: task.taskId,
2228
+ message: 'Missing floorId or planoId, skipping revocation'
2229
+ } );
2230
+ continue;
2231
+ }
2232
+
2233
+ // Call revokeTask internally
2234
+ const mockRevokeReq = {
2235
+ body: {
2236
+ taskId: task.taskId,
2237
+ floorId: floorId,
2238
+ planoId: planoId,
2239
+ type: task.type,
2240
+ },
2241
+ };
2242
+ const mockRevokeRes = {
2243
+ sendSuccess: () => {},
2244
+ sendError: ( error ) => {
2245
+ throw new Error( error );
2246
+ },
2247
+ };
2248
+
2249
+ await taskController.revokeTask( mockRevokeReq, mockRevokeRes );
2250
+
2251
+ // Mark task as successfully revoked
2252
+ revokedTaskIds.add( task.taskId );
2253
+ revokedCount++;
2254
+ } catch ( taskError ) {
2255
+ logger.error( {
2256
+ functionName: 'revokeFindReplace',
2257
+ storeName: store.storeName,
2258
+ taskId: task.taskId,
2259
+ taskError: taskError
2260
+ } );
2261
+ // Continue with other tasks even if one fails
2262
+ }
2263
+ }
2264
+
2265
+ if ( revokedCount > 0 ) {
2266
+ results.success.push( {
2267
+ storeName: store.storeName,
2268
+ revokedTasks: revokedCount,
2269
+ } );
2270
+ }
2271
+ } catch ( storeError ) {
2272
+ logger.error( { functionName: 'revokeFindReplace', storeName: store.storeName, error: storeError } );
2273
+ results.failed.push( {
2274
+ storeName: store.storeName,
2275
+ reason: storeError.message || 'Unknown error',
2276
+ } );
2277
+ }
2278
+ }
2279
+
2280
+ // Update stores to remove only successfully revoked task IDs
2281
+ const updatedStores = findReplaceDoc.stores.map( ( store ) => {
2282
+ const updatedStore = { ...store };
2283
+ if ( store.merchRolloutId && revokedTaskIds.has( store.merchRolloutId ) ) {
2284
+ delete updatedStore.merchRolloutId;
2285
+ }
2286
+ if ( store.vmRolloutId && revokedTaskIds.has( store.vmRolloutId ) ) {
2287
+ delete updatedStore.vmRolloutId;
2288
+ }
2289
+ return updatedStore;
2290
+ } );
2291
+
2292
+ // Update findReplaceAiModel to remove task IDs
2293
+ await findReplaceAiService.updateOne(
2294
+ { _id: findReplaceId },
2295
+ { stores: updatedStores },
2296
+ );
2297
+
2298
+ return res.sendSuccess( {
2299
+ message: 'Tasks revoked successfully',
2300
+ results: results,
2301
+ summary: {
2302
+ total: findReplaceDoc.stores.length,
2303
+ success: results.success.length,
2304
+ failed: results.failed.length,
2305
+ },
2306
+ } );
2307
+ } catch ( e ) {
2308
+ logger.error( { functionName: 'revokeFindReplace', error: e } );
2309
+ return res.sendError( e, 500 );
2310
+ }
2311
+ }
2312
+
1999
2313
  export async function deleteFindReplaceAi( req, res ) {
2000
2314
  try {
2001
2315
  const { id } = req.body;
@@ -1,5 +1,5 @@
1
1
  import express from 'express';
2
- import { isAllowedSessionHandler } from 'tango-app-api-middleware';
2
+ import { isAllowedSessionHandler, isAllowedInternalAPIHandler } from 'tango-app-api-middleware';
3
3
  import * as planoAIController from '../controllers/planoAI.controller.js';
4
4
 
5
5
 
@@ -18,5 +18,7 @@ planoAIRouter
18
18
  .post( '/batchFindReplace', isAllowedSessionHandler, planoAIController.batchFindReplace )
19
19
  .post( '/updateBatchProgress', isAllowedSessionHandler, planoAIController.updateBatchProgress )
20
20
  .post( '/approveReplace', isAllowedSessionHandler, planoAIController.approveReplace )
21
+ .post( '/processScheduledFindReplace', planoAIController.processScheduledFindReplace )
21
22
  .post( '/cancelFindReplace', isAllowedSessionHandler, planoAIController.cancelFindReplace )
23
+ .post( '/revokeFindReplace', isAllowedSessionHandler, planoAIController.revokeFindReplace )
22
24
  .post( '/deleteFindReplaceAi', isAllowedSessionHandler, planoAIController.deleteFindReplaceAi );