tango-app-api-trax 3.7.13-qid-halfshutter-8 → 3.7.13-qid-halfshutter-10
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,4 +1,4 @@
|
|
|
1
|
-
import { logger, signedUrl, fileUpload, getOtp, sendEmailWithSES, getUuid, insertOpenSearchData
|
|
1
|
+
import { logger, signedUrl, fileUpload, getOtp, sendEmailWithSES, getUuid, insertOpenSearchData } from 'tango-app-api-middleware';
|
|
2
2
|
import * as processedchecklist from '../services/processedchecklist.services.js';
|
|
3
3
|
import * as processedtask from '../services/processedTaskList.service.js';
|
|
4
4
|
import * as PCLconfig from '../services/processedchecklistconfig.services.js';
|
|
@@ -19,6 +19,7 @@ import timeZone from 'dayjs/plugin/timezone.js';
|
|
|
19
19
|
import { findOTP, updateOneOTP } from '../services/otp.service.js';
|
|
20
20
|
import * as clientService from '../services/clients.services.js';
|
|
21
21
|
import { create } from '../services/authentication.service.js';
|
|
22
|
+
import { readFileSync } from 'fs';
|
|
22
23
|
import { join } from 'path';
|
|
23
24
|
import handlebars from 'handlebars';
|
|
24
25
|
dayjs.extend( customParseFormat );
|
|
@@ -1077,6 +1078,7 @@ export async function sopMobilechecklistMultiSectionFormatterv1( req, res, next
|
|
|
1077
1078
|
|
|
1078
1079
|
if ( requestData?.editSubmit && requestData?.editSubmit == 'true' ) {
|
|
1079
1080
|
let sampleData = reqAnswers[0];
|
|
1081
|
+
console.log( 'Edit submit sampleData :', sampleData );
|
|
1080
1082
|
CLQAnswers.forEach( ( section ) => {
|
|
1081
1083
|
let requestSection = reqAnswers.filter( ( reqSection ) => reqSection.sectionName == section?.sectionOldName || reqSection.sectionName == section?.sectionName );
|
|
1082
1084
|
if ( requestSection.length ) {
|
|
@@ -3771,7 +3773,7 @@ export async function uploadAnswerImage( req, res ) {
|
|
|
3771
3773
|
let imageUrl;
|
|
3772
3774
|
let filePath = `${folder}/${req.user.clientId}/${date}/${input.checklistId}/${input.sectionName.replace( ' ', '' )}/${input.questionNo}/`;
|
|
3773
3775
|
let params = {
|
|
3774
|
-
fileName: `${Date.now()}-${Math.floor( 1000 + Math.random() * 9000 )}
|
|
3776
|
+
fileName: `${Date.now()}-${Math.floor( 1000 + Math.random() * 9000 )}.${req.files.answerImage.name.split( '.' )[1]}`,
|
|
3775
3777
|
Key: filePath,
|
|
3776
3778
|
Bucket: bucket.sop,
|
|
3777
3779
|
body: req.files.answerImage.data,
|
|
@@ -4388,58 +4390,246 @@ export async function questionListV1( req, res ) {
|
|
|
4388
4390
|
}
|
|
4389
4391
|
}
|
|
4390
4392
|
|
|
4393
|
+
export async function sopMobilechecklistQuestionValidatorV6( req, res, next ) {
|
|
4394
|
+
console.log( 'sopMobilechecklistQuestionValidatorV6 called :', req.body );
|
|
4395
|
+
try {
|
|
4396
|
+
let requestData = req.body;
|
|
4397
|
+
// formatter should have normalized questionAnswers into sectionFormat (sections with questions array)
|
|
4398
|
+
requestData.questionAnswers = typeof requestData.questionAnswers == 'string' ? JSON.parse( requestData.questionAnswers ) : requestData.questionAnswers;
|
|
4399
|
+
|
|
4400
|
+
const getChecklistQA = await processedchecklist.findOne( { _id: new ObjectId( requestData.processedcheckListId ) }, { questionAnswers: 1, sourceCheckList_id: 1 } );
|
|
4401
|
+
if ( !getChecklistQA ) {
|
|
4402
|
+
return res.sendError( 'Check List Got Edited Please Fill Again', 422 );
|
|
4403
|
+
}
|
|
4391
4404
|
|
|
4392
|
-
|
|
4405
|
+
// if publish check required
|
|
4406
|
+
if ( getChecklistQA && requestData.submittype == 'submit' ) {
|
|
4407
|
+
let checkChecklistStatus = await checklistService.findOne( { _id: getChecklistQA.sourceCheckList_id }, { publish: 1 } );
|
|
4408
|
+
if ( !checkChecklistStatus.publish ) {
|
|
4409
|
+
return res.sendError( 'Checklist got edited.please contact admin', 400 );
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
|
|
4413
|
+
if ( !requestData.questionAnswers || !requestData.questionAnswers.length ) {
|
|
4414
|
+
return res.sendError( 'Please Fill all Required Fields', 400 );
|
|
4415
|
+
}
|
|
4416
|
+
|
|
4417
|
+
// For draft saves we allow partial payloads
|
|
4418
|
+
if ( requestData.submittype == 'submit' ) {
|
|
4419
|
+
const incomingSections = requestData.questionAnswers; // already normalized by formatter
|
|
4420
|
+
const CLQAnswers = getChecklistQA.questionAnswers || [];
|
|
4421
|
+
|
|
4422
|
+
let validationCount = 0;
|
|
4423
|
+
let errorCount = 0;
|
|
4424
|
+
|
|
4425
|
+
// Build a map of incoming sections by section_id for quick lookup
|
|
4426
|
+
const incomingMap = new Map();
|
|
4427
|
+
incomingSections.forEach( ( s ) => {
|
|
4428
|
+
if ( s && s.section_id ) incomingMap.set( s.section_id.toString(), s );
|
|
4429
|
+
} );
|
|
4430
|
+
|
|
4431
|
+
for ( const section of CLQAnswers ) {
|
|
4432
|
+
const incoming = incomingMap.get( section.section_id?.toString() );
|
|
4433
|
+
if ( !incoming ) {
|
|
4434
|
+
// missing a configured section in final submit
|
|
4435
|
+
errorCount++;
|
|
4436
|
+
continue;
|
|
4437
|
+
}
|
|
4438
|
+
|
|
4439
|
+
// incoming.questions is expected to be the formatted array where each question has userAnswer (array) or similar
|
|
4440
|
+
for ( const question of incoming.questions || [] ) {
|
|
4441
|
+
try {
|
|
4442
|
+
if ( question.answerType == 'multiplechoicemultiple' ) {
|
|
4443
|
+
if ( !question.userAnswer || !question.userAnswer.length ) validationCount++;
|
|
4444
|
+
} else if ( question.answerType == 'multipleImage' ) {
|
|
4445
|
+
// multipleImage expects an array of answers in userAnswer
|
|
4446
|
+
if ( !question.userAnswer || !question.userAnswer.length ) validationCount++;
|
|
4447
|
+
} else {
|
|
4448
|
+
// other types: expect at least one userAnswer entry
|
|
4449
|
+
if ( !question.userAnswer || !question.userAnswer.length ) validationCount++;
|
|
4450
|
+
}
|
|
4451
|
+
} catch ( e ) {
|
|
4452
|
+
validationCount++;
|
|
4453
|
+
}
|
|
4454
|
+
}
|
|
4455
|
+
}
|
|
4456
|
+
|
|
4457
|
+
if ( validationCount ) {
|
|
4458
|
+
return res.sendError( 'Please Fill all Required Fields', 400 );
|
|
4459
|
+
} else if ( errorCount ) {
|
|
4460
|
+
return res.sendError( 'Checklist got edited.please contact admin', 400 );
|
|
4461
|
+
} else {
|
|
4462
|
+
return next();
|
|
4463
|
+
}
|
|
4464
|
+
} else {
|
|
4465
|
+
return next();
|
|
4466
|
+
}
|
|
4467
|
+
} catch ( e ) {
|
|
4468
|
+
logger.error( { function: 'sopMobilechecklistQuestionValidatorV6', error: e, body: req.body } );
|
|
4469
|
+
return res.sendError( e, 500 );
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
|
|
4473
|
+
/**
|
|
4474
|
+
* @param {Object} req - Request object
|
|
4475
|
+
* @param {Object} res - Response object
|
|
4476
|
+
* submitChecklistV6
|
|
4477
|
+
* New behaviour: updating a single section instead of whole checklist
|
|
4478
|
+
* Expected request payload (partial):
|
|
4479
|
+
* {
|
|
4480
|
+
* processedcheckListId,
|
|
4481
|
+
* date,
|
|
4482
|
+
* sectionIndex, // 0-based index of the section being saved (preferred)
|
|
4483
|
+
* sectionId, // optional: _id or identifier of the section (fallback)
|
|
4484
|
+
* questionAnswers, // array with a single section object OR the section object itself
|
|
4485
|
+
* submittype: 'draft' | 'submit' // draft = save current section; submit = if this is last section, finalize checklist
|
|
4486
|
+
* }
|
|
4487
|
+
*/
|
|
4488
|
+
export async function submitChecklistV6( req, res ) {
|
|
4393
4489
|
try {
|
|
4394
|
-
const
|
|
4395
|
-
|
|
4396
|
-
|
|
4490
|
+
const requestData = req.body;
|
|
4491
|
+
console.log( 'submitChecklistV6 requestData =>', requestData );
|
|
4492
|
+
|
|
4493
|
+
const findQuery = [
|
|
4494
|
+
{
|
|
4495
|
+
$match: {
|
|
4496
|
+
$and: [
|
|
4497
|
+
{ _id: new ObjectId( requestData.processedcheckListId ) },
|
|
4498
|
+
{ userId: req.user._id },
|
|
4499
|
+
{ date_string: requestData.date },
|
|
4500
|
+
],
|
|
4501
|
+
},
|
|
4502
|
+
},
|
|
4503
|
+
];
|
|
4504
|
+
|
|
4505
|
+
const getchecklist = await processedchecklist.aggregate( findQuery );
|
|
4506
|
+
if ( !getchecklist.length ) {
|
|
4507
|
+
return res.sendError( 'Checklist Got Edited Please Fill Again', 422 );
|
|
4397
4508
|
}
|
|
4398
|
-
|
|
4399
|
-
const
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
bucket = JSON.parse( process.env.BUCKET )?.sop;
|
|
4405
|
-
} catch {
|
|
4406
|
-
return res.sendError( 'Bucket config error', 500 );
|
|
4509
|
+
console.log( 'getchecklist[0] =>', getchecklist );
|
|
4510
|
+
const doc = getchecklist[0];
|
|
4511
|
+
if ( doc.checklistStatus == 'open' ) {
|
|
4512
|
+
return res.sendError( 'checklist in open status need to start', 400 );
|
|
4513
|
+
} else if ( doc.checklistStatus == 'submit' ) {
|
|
4514
|
+
return res.sendError( 'checklist Already Submitted', 400 );
|
|
4407
4515
|
}
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
} )
|
|
4415
|
-
|
|
4416
|
-
return res.sendSuccess( { message: 'Chunk uploaded to S3', chunkIndex: chunkNum } );
|
|
4516
|
+
|
|
4517
|
+
// Normalize incoming section data
|
|
4518
|
+
let incomingSection = null;
|
|
4519
|
+
if ( Array.isArray( requestData.questionAnswers ) && requestData.questionAnswers.length ) {
|
|
4520
|
+
// if client sends an array with a single section
|
|
4521
|
+
incomingSection = requestData.questionAnswers[0];
|
|
4522
|
+
} else if ( requestData.questionAnswers && typeof requestData.questionAnswers === 'object' ) {
|
|
4523
|
+
incomingSection = requestData.questionAnswers;
|
|
4417
4524
|
}
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
chunkObjs.sort( ( a, b ) => {
|
|
4422
|
-
const ai = Number( a.Key.split( '/' ).pop() );
|
|
4423
|
-
const bi = Number( b.Key.split( '/' ).pop() );
|
|
4424
|
-
return ai - bi;
|
|
4425
|
-
} );
|
|
4426
|
-
const buffers = [];
|
|
4427
|
-
for ( const obj of chunkObjs ) {
|
|
4428
|
-
const s3Obj = await getObject( { Bucket: bucket, Key: obj.Key } );
|
|
4429
|
-
buffers.push( Buffer.from( s3Obj.Body ) );
|
|
4525
|
+
|
|
4526
|
+
if ( !incomingSection && typeof requestData.sectionIndex === 'undefined' ) {
|
|
4527
|
+
return res.sendError( 'sectionIndex or questionAnswers (section) is required', 400 );
|
|
4430
4528
|
}
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
data: finalBuffer,
|
|
4437
|
-
name: chunkFile.name,
|
|
4438
|
-
mimetype: chunkFile.mimetype,
|
|
4529
|
+
|
|
4530
|
+
const updateQuery = {
|
|
4531
|
+
_id: new ObjectId( requestData.processedcheckListId ),
|
|
4532
|
+
userId: req.user._id,
|
|
4533
|
+
date_string: requestData.date,
|
|
4439
4534
|
};
|
|
4440
|
-
|
|
4535
|
+
|
|
4536
|
+
// Merge the existing questionAnswers with incoming section
|
|
4537
|
+
const existingQA = Array.isArray( doc.questionAnswers ) ? doc.questionAnswers : [];
|
|
4538
|
+
const mergedQA = [ ...existingQA ];
|
|
4539
|
+
|
|
4540
|
+
let targetIndex = -1;
|
|
4541
|
+
if ( typeof requestData.sectionIndex !== 'undefined' && Number.isInteger( Number( requestData.sectionIndex ) ) ) {
|
|
4542
|
+
targetIndex = Number( requestData.sectionIndex );
|
|
4543
|
+
} else if ( incomingSection && incomingSection.section_id ) {
|
|
4544
|
+
targetIndex = mergedQA.findIndex( ( s ) => s._id && s._id.toString() == incomingSection.section_id.toString() || s.section_id == incomingSection.section_id );
|
|
4545
|
+
}
|
|
4546
|
+
|
|
4547
|
+
if ( targetIndex === -1 ) {
|
|
4548
|
+
// If sectionIndex not provided or not found, try to find by section name
|
|
4549
|
+
if ( incomingSection && incomingSection.sectionName ) {
|
|
4550
|
+
targetIndex = mergedQA.findIndex( ( s ) => s.sectionName == incomingSection.sectionName );
|
|
4551
|
+
}
|
|
4552
|
+
}
|
|
4553
|
+
|
|
4554
|
+
if ( targetIndex === -1 && incomingSection ) {
|
|
4555
|
+
// If still not found, append as new section
|
|
4556
|
+
mergedQA.push( incomingSection );
|
|
4557
|
+
} else if ( targetIndex >= 0 && incomingSection ) {
|
|
4558
|
+
// Replace the target section with incoming data
|
|
4559
|
+
mergedQA[targetIndex] = Object.assign( {}, mergedQA[targetIndex], incomingSection );
|
|
4560
|
+
}
|
|
4561
|
+
console.log( 'mergedQA =>', mergedQA );
|
|
4562
|
+
// Compute flags based on merged questionAnswers. Reuse QuestionFlag helper by temporarily setting req.body.questionAnswers
|
|
4563
|
+
const prevBodyQA = req.body.questionAnswers;
|
|
4564
|
+
req.body.questionAnswers = mergedQA;
|
|
4565
|
+
let flagCount = await QuestionFlag( req, res );
|
|
4566
|
+
// QuestionFlag may have returned a response (res.sendError) in case of error; ensure numeric
|
|
4567
|
+
if ( typeof flagCount !== 'number' ) flagCount = 0;
|
|
4568
|
+
req.body.questionAnswers = prevBodyQA; // restore
|
|
4569
|
+
|
|
4570
|
+
const currentDateTime = dayjs();
|
|
4571
|
+
|
|
4572
|
+
const updateData = {};
|
|
4573
|
+
updateData.questionAnswers = mergedQA;
|
|
4574
|
+
updateData.questionFlag = flagCount;
|
|
4575
|
+
updateData.updatedAt = dayjs.utc( currentDateTime.format( 'hh:mm:ss A, DD MMM YYYY' ), 'hh:mm:ss A, DD MMM YYYY' ).format();
|
|
4576
|
+
|
|
4577
|
+
// If this is a final submission (submittype === 'submit') and optionally client indicates this is last section
|
|
4578
|
+
if ( requestData.submittype === 'submit' ) {
|
|
4579
|
+
updateData.submitTime = dayjs.utc( currentDateTime.format( 'hh:mm:ss A, DD MMM YYYY' ), 'hh:mm:ss A, DD MMM YYYY' ).format();
|
|
4580
|
+
updateData.checklistStatus = 'submit';
|
|
4581
|
+
updateData.submitMobileTime = requestData?.currentTime;
|
|
4582
|
+
updateData.submitTime_string = currentDateTime.format( 'hh:mm A, DD MMM YYYY' );
|
|
4583
|
+
} else {
|
|
4584
|
+
// draft/partial save
|
|
4585
|
+
updateData.submitTime_string = currentDateTime.format( 'hh:mm A, DD MMM YYYY' );
|
|
4586
|
+
}
|
|
4587
|
+
|
|
4588
|
+
if ( requestData.deviceDetails && requestData.deviceDetails != '' ) {
|
|
4589
|
+
try {
|
|
4590
|
+
updateData.deviceDetails = JSON.parse( requestData.deviceDetails );
|
|
4591
|
+
} catch ( e ) {
|
|
4592
|
+
// ignore parse error and keep raw
|
|
4593
|
+
updateData.deviceDetails = requestData.deviceDetails;
|
|
4594
|
+
}
|
|
4595
|
+
}
|
|
4596
|
+
|
|
4597
|
+
console.log( 'updateData =>', updateData );
|
|
4598
|
+
console.log( 'updateQuery =>', updateQuery );
|
|
4599
|
+
const updateResult = await processedchecklist.updateOne( updateQuery, updateData );
|
|
4600
|
+
|
|
4601
|
+
if ( updateResult?.modifiedCount > 0 ) {
|
|
4602
|
+
// update open search only on final submit
|
|
4603
|
+
if ( requestData.submittype === 'submit' ) {
|
|
4604
|
+
updateOpenSearch( req.user, requestData );
|
|
4605
|
+
const openSearch = JSON.parse( process.env.OPENSEARCH );
|
|
4606
|
+
const inserttraxlogs = {
|
|
4607
|
+
client_id: req.user.clientId,
|
|
4608
|
+
createAt: new Date(),
|
|
4609
|
+
sourceCheckList_id: doc.sourceCheckList_id,
|
|
4610
|
+
checkListName: doc.checkListName,
|
|
4611
|
+
type: 'checkList',
|
|
4612
|
+
action: 'submit',
|
|
4613
|
+
storeName: doc.storeName,
|
|
4614
|
+
store_id: doc.store_id,
|
|
4615
|
+
userName: doc.userName,
|
|
4616
|
+
userEmail: doc.userEmail,
|
|
4617
|
+
createdByEmail: doc.userEmail,
|
|
4618
|
+
createdBy: doc.userName,
|
|
4619
|
+
coverage: doc.coverage,
|
|
4620
|
+
checkListType: doc.checkListType,
|
|
4621
|
+
logDetails: {},
|
|
4622
|
+
userType: req.user.userType,
|
|
4623
|
+
};
|
|
4624
|
+
insertOpenSearchData( openSearch.traxActivityLog, inserttraxlogs );
|
|
4625
|
+
}
|
|
4626
|
+
|
|
4627
|
+
return res.sendSuccess( 'Checklist Updated Successfully' );
|
|
4628
|
+
} else {
|
|
4629
|
+
return res.sendError( 'something went wrong please try again', 500 );
|
|
4630
|
+
}
|
|
4441
4631
|
} catch ( e ) {
|
|
4442
|
-
logger.error( {
|
|
4632
|
+
logger.error( { function: 'submitChecklistV6', error: e, body: req.body } );
|
|
4443
4633
|
return res.sendError( e, 500 );
|
|
4444
4634
|
}
|
|
4445
4635
|
}
|