portal/modules/requests/server/methods.js

import {Meteor} from 'meteor/meteor';
import {check} from 'meteor/check';
import VerifiedFields from '/common/db/verifiedField';
import addSecs  from '/common/utils/date/addSecs';
import {Roles} from 'meteor/alanning:roles';

Meteor.methods({
	/**
	 * Finds all requests for a given listing id and updates the unit info, for any change in listing id that is triggered by changes in url or refreshUrl fields in the listings section(find updateListings method)
	 * @param {Number} listingId listing Id to query for in requests, this can also be used to query for units, if unit object is not passed i.e calling from frontend
	 * @param {Object} unitDetail Unit details object including id,identifier,location,saleInfo,spaces
	 * @returns {void}
	 */
	'request.updateUnitInfo'(listingId, unitDetail) {
		const {default: Units} = require('/common/db/unit');
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.unitUpdate');
		if (!listingId) throw new Meteor.Error(404, 'listingId not found!');
		check(listingId, String);
		if (!unitDetail) {
			const {default: Listings} = require('/common/db/listing');
			const unitId = Listings.findOne({listingId})?.doc?.id;
			const {_id: id, identifier} = Units.findOne({_id: unitId}, {fields: {_id: 1, identifier: 1}}) || {};
			unitDetail = {id, identifier};
		}
		if (!unitDetail.id) throw new Meteor.Error('notFatal', 'Failed to acquire info regarding unit - request.unitUpdate');
		const {default: IncomingRequest} = require('/common/db/request/server');
		IncomingRequest.update({'info.propertyId': listingId}, {$set: {'info.unit': unitDetail}}, {multi: true});
	},
	'request.updateGroup'(_id, {group: {id, identifier, name} = {}} = {}) {
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.unitUpdate');
		const {default: IncomingRequest} = require('/common/db/request/server');
		const ir = IncomingRequest.findOne({_id}, {defaults: false});
		ir && id && ir.updateGroup({id, identifier, name});
	},
	'request.updateStatus'(id, data) {
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.updateStatus');
		const {default: IncomingRequest} = require('/common/db/request/server');
		var ir = IncomingRequest.findOne({_id: id});
		const isAdminOrManager = Roles.userIsInRole(Meteor.userId(), ['admin'], 'portal-group') || Roles.userIsInRole(Meteor.userId(), ['manager'], 'portal-group') || Roles.userIsInRole(Meteor.userId(), 'manager', 'settlin-hr-ops');

		//checking if call is placed after any incoming request
		let mode;
		switch (ir.mode) {
			case 'whatsapp':
			case 'online':
			case 'email':
			case 'sms':
				mode = ir.mode;
				break;
			default: break;
		}
		const {default: Conversations} = require('/common/db/conversation');
		const conversation = Conversations.findOne(
			{
				$or: [
					{mode: 'call', 'talked.flag': true},
					...(mode ? [{mode}] : []),
				],
				'group.id': ir.group.id,
				createdAt: {$gte: ir.createdAt},
			},
			{fields: {_id: 1}},
		);
		if (!conversation && !isAdminOrManager && !(ir.createdAt < new Date(new Date().setDate(new Date().getDate() - require('/common/info/receptionistCanCloseXDaysOldRequest').default)) && data.reason === 'invalidContact')) throw new Meteor.Error('notFatal', 'Call the customer before closing');

		if (ir.info.unit?.id && ir.type === 'verification') {
			let verifiedCount = VerifiedFields.find({unitId: ir.info.unit.id, 'data.resolvedValue': null, 'data.origCorrect': false, removed: false}).count();
			if (verifiedCount) throw new Meteor.Error('notFatal', 'Resolve all wrong entries in unit, to proceed');
		}
		if (!ir) throw new Meteor.Error(404, 'No request found!');
		ir.updateStatus(data);
	},
	'requests.countByTeam'({groupId}) {
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.updateStatus');
		const {default: IncomingRequests} = require('/common/db/request');
		if (!groupId) return {};
		var query = {removed: false, 'status.open': true, 'group.id': groupId};
		let count = IncomingRequests.aggregate([
			{$match: query},
			{$group: {_id: '$team', count: {$sum: 1}}},
		]);
		return count;
	},
	'request.assignedToUserId'({id, assignedId = '', changeBuyerAssignee}) {
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.assignedToUserId');
		const isAdminOrManager = Roles.userIsInRole(this.userId, ['manager', 'admin'], 'portal-group') || Roles.userIsInRole(this.userId, 'manager', 'settlin-seller-ops') || Roles.userIsInRole(this.userId, 'manager', 'settlin-primary-ops') || Roles.userIsInRole(this.userId, 'manager', 'settlin-analysis-ops') || Roles.userIsInRole(this.userId, 'manager', 'settlin-buyer-ops');
		const {default: IncomingRequest} = require('/common/db/request/server');
		let doc = IncomingRequest.findOne({_id: id});
		const $or = [
			...(doc.info.contact.phone.value ? [{'info.contact.phone.value': doc.info.contact.phone.value}] : []),
			...(doc.individual.id ? [{'individual.id': doc.individual.id}] : []),
		];
		if ($or.length) {
			IncomingRequest.find({
				_id: {$ne: doc._id},
				relationship: doc.relationship,
				'status.open': true,
				team: doc.team,
				// admin/manager can reassignee all request to same assignee
				// current user can reassignee his request only
				...(!isAdminOrManager ? {'assignedTo.userId': this.userId} : {}),
				$or,
			}).fetch().forEach(ir => {
				require('/common/server').Temp.insert({requestId: ir._id, docId: doc._id, reason: 'request.assignedToUserId'});
				ir.updateAssignedTo({userId: assignedId, forceReAssign: true});
			});
		}
		doc.updateAssignedTo({userId: assignedId, forceReAssign: true, changeBuyerAssignee});
		return assignedId;
	},
	'request.open'({id, statusOpen, reason = '', remarks = ''}) {
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.open');
		const {default: IncomingRequest} = require('/common/db/request/server');
		const ir = IncomingRequest.findOne({_id: id});
		ir.set({'status.open': statusOpen, 'status.comment': remarks, 'status.reason': reason});
		ir.save();
		return ir.status;
	},
	'request.save'({currentGroupId, currentIndividualId, relationship, source, remarks = '', phone, requestIds, city = 'unknown'} = {}) {
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.save');
		check([currentGroupId, currentIndividualId, relationship, source, remarks], [String]);
		let newReq = false;
		const {default: Groups} = require('/common/db/group');
		const group = Groups.findOne({_id: currentGroupId}) || {};
		const {default: Users} = require('/common/db/user');
		const individual = Users.findOne({_id: currentIndividualId}) || {};
		const {default: IncomingRequest} = require('/common/db/request/server');
		const getUsersInRole  = require('/common/utils/server/getUsersInRole').default;
		const {Random} = require('meteor/random');
		let requestStatus = {};
		requestIds.map(requestId => {
			const ir = IncomingRequest.findOne({_id: requestId}) || new IncomingRequest({_id: Random.id()}, {defaults: true});
			let setAlert = false;
			if (relationship !== 'unknown' && relationship !== ir.relationship) setAlert = true;
			if (!ir) throw new Meteor.Error('notFatal', 'No request found!');
			ir.info = ir.info || {};
			if (IncomingRequest.isNew(ir)) {
				newReq = true;
				ir.info.status = 'interested';
			}
			if (newReq) ir.info.contact = {phone: {value: phone}};
			const date = new Date();
			let existing;
			if (date.getHours() <= 18) existing = group.createdAt <= new Date(date).setHours(-6, 0, 0, 0);  // group created before 6pm yesterday
			else existing = group.createdAt <= new Date(date).setHours(18, 0, 0, 0); // group created before 6pm today
			const groupData = {id: group._id, identifier: group.identifier, name: group.name, existing};
			const individualData = {id: individual._id, identifier: individual.identifier, name: (individual.profile || {}).name};
			// save request
			const {requests} = require('/common/info/collectionBasedRolesAndScopes').default;
			// current user
			const currUser = Meteor.userId();
			switch (relationship) {
				case 'seller': {
					let isCurrAssigneeValid = requests?.isAssigneeValid({doc: ir});
					if (!isCurrAssigneeValid) isCurrAssigneeValid = requests?.isAssigneeValid({doc: ir, userId: currUser});
					if (isCurrAssigneeValid) ir.setAssignedTo(currUser);
					else {
						let manager = ir.type === 'verification' ? getUsersInRole({scope: 'settlin-analysis-ops', role: 'manager', limit: 1}) : getUsersInRole({scope: 'settlin-seller-ops', role: 'manager', limit: 1});
						ir.setAssignedTo(manager._id);
					}
					break;
				}
				case 'buyer': {
					let isCurrAssigneeValid = requests?.isAssigneeValid({doc: ir});
					if (!isCurrAssigneeValid) isCurrAssigneeValid = requests?.isAssigneeValid({doc: ir, userId: currUser});
					// if current user is valid, then assignee him/her
					if (isCurrAssigneeValid) ir.setAssignedTo(currUser);
					else {
						const {getAssigneeOne} = require('/common/utils/getAssignee');
						let assignedTo = getAssigneeOne({collection: 'buyers', docId: group._id, teams: ir.team});
						if (assignedTo?.userIds?.[0]) {
							ir.setAssignedTo(assignedTo.userIds?.[0], {forceReAssign: true});
						}
					}
					break;
				}
				default: ir.setAssignedTo(currUser);
			}

			let type = source === 'preLaunchExternal' ? 'preLaunch' : newReq ? 'incoming' : ir.type || 'outgoing';
			let team = relationship === 'seller' ? '' : ['project', 'preLaunch'].includes(type) ? 'b2c' : 'c2c';
			ir.set({relationship, source, group: groupData, individual: individualData, type, 'info.remarks': remarks, team});
			ir.location = ir.location || {};
			ir.location.city = city;

			if (ir.assignedTo?.userId) {
				let isAssigneePrimaryOps = Roles.userIsInRole(ir.assignedTo?.userId, ['telecaller', 'manager'], 'settlin-primary-ops');
				if (isAssigneePrimaryOps) {
					ir.team = 'b2c';
				}
			}
			if (remarks) ir.set({'followUp.remarks': remarks});
			if (newReq) {
				ir.followUp.at = new Date();
				ir.mode = 'portal';
			}
			ir.save();

			//updating conversation with this request id.
			const {default: Conversations} = require('/common/db/conversation');
			Conversations.update({'request.id': ir._id}, {$set: {group: groupData, individual: individualData}});
			if (requestIds.length > 1 && newReq) requestStatus[ir._id] = {new: ir._id};
			else if (requestIds.length === 1 && newReq) {
				requestStatus.new = newReq;
				requestStatus.id = ir._id;
			}
			if (setAlert) {
				requestStatus.setAlert = setAlert;
				requestStatus.savedRelationship = ir.relationship;
			}
		});
		return requestStatus;
	},
	'request.findSimilarRequestsCount'({phone = '', currentGroupId = '', currentIndividualId = '', requestId = ''}) {
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.findSimilarrequestsCount');
		check([phone, currentGroupId, currentIndividualId, requestId], [String]);
		const {default: IncomingRequests} = require('/common/db/request');
		return IncomingRequests.find({
			_id: {$ne: requestId},
			'status.open': true,
			$or: [
				{'info.contact.phone.value': phone},
				{'individual.id': currentIndividualId},
			],
			'assignedTo.userId': this.userId,
			relationship: 'unknown',
		}).fetch().map(ir => ir._id);
	},
	'request.addUnitsAndRawUnits'({units = [], rawUnits = [], groupId, relationship, requestId}) {
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.addUnitsAndRawUnits');
		check([requestId, groupId], [String]);
		const {default: IncomingRequest} = require('/common/db/request/server');
		const ir = IncomingRequest.findOne({_id: requestId});
		if (!ir) throw new Meteor.Error('notFatal', 'No request found!');
		ir.set({units, rawUnits, relationship});
		ir.save();
		const {default: Buyer} = require('/common/db/buyer/portal');
		let buyer = Buyer.findOne({_id: groupId, relationship: 'buyer'}, {defaults: false});
		if (buyer) {
			if ((ir.units[0] || {}).id) {
				Meteor.call('notification.buyer.modification', {groupId, unitId: ir.units[0].id, requestId: ir._id, process: 'inception', status: 'enquired'}, function(err) {
					if (err) Log.fatal('Problem Making Notification', err);
				});
			}
		}
		return {relationship, groupId, requestId: ir._id};
	},
	'request.createIncomingNotification'({userId} = {}) {
		const {default: IncomingRequests} = require('/common/db/request');
		check(userId, String);
		const currentDate = addSecs(-2 * 60);
		return IncomingRequests.findOne({'assignedTo.userId': userId, 'status.open': true, type: {$in: ['incoming', 'whatsapp']}, createdAt: {$gte: currentDate}}, {sort: {createdAt: -1}});
	},

	'request.updateFollowUp'({relationship, followUp, groupId, individualId}) {
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.updateFollowUp');
		const {default: IncomingRequest} = require('/common/db/request/server');
		if (!groupId && !individualId) return;
		let query = {};
		if (groupId) query['group.id'] = groupId;
		if (individualId) query['individual.id'] = individualId;
		const incomingRequests = IncomingRequest.find({...query, removed: {$ne: true}, relationship, 'status.open': true, 'assignedTo.userId': this.userId}).fetch();
		incomingRequests.forEach((p) => {
			p.followUp = {
				remarks: followUp.remarks || 'Triggered Automatically by request.updateFollowUp',
				at: followUp.at || require('/common/utils/date/infinity').infinity,
			};
			p.save();
		});
	},
	'request.getCount'({groupId, relationship, docId, individualId}) {
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.getCounts');
		const {default: IncomingRequests} = require('/common/db/request');
		check([relationship, docId], [String]);
		if (!groupId && !individualId) return 0;
		let query = {};
		if (groupId) query['group.id'] = groupId;
		if (individualId) query['individual.id'] = individualId;
		return {requestsCount: IncomingRequests.find({...query, _id: {$ne: docId}, relationship, 'status.open': true, 'assignedTo.userId': this.userId}).count()};
	},
	'request.getReceivedRequests'({unitId}) {
		if (!Meteor.users.findOne({_id: this.userId, uses: 'portal'})) throw new Meteor.Error(403, 'Not authorized to do this operation - request.getReceivedRequests');
		if (!unitId) return [];
		const {default: Requests} = require('/common/db/request');
		return  Requests.find({'units.id': unitId}, {fields: {_id: 1, units: 1}}).fetch();
	},
});