Skip to content

Commit

Permalink
feat: add task object
Browse files Browse the repository at this point in the history
  • Loading branch information
Iamsidar07 committed Aug 19, 2024
1 parent 38fc155 commit 1901c94
Show file tree
Hide file tree
Showing 3 changed files with 334 additions and 0 deletions.
115 changes: 115 additions & 0 deletions packages/api/src/crm/task/services/leadsquared/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { EncryptionService } from '@@core/@core-services/encryption/encryption.service';
import { LoggerService } from '@@core/@core-services/logger/logger.service';
import { PrismaService } from '@@core/@core-services/prisma/prisma.service';
import { ApiResponse } from '@@core/utils/types';
import { SyncParam } from '@@core/utils/types/interface';
import { CrmObject } from '@crm/@lib/@types';
import { ITaskService } from '@crm/task/types';
import { Injectable } from '@nestjs/common';
import axios from 'axios';
import { ServiceRegistry } from '../registry.service';
import { LeadSquaredTaskInput, LeadSquaredTaskOutput } from './types';

@Injectable()
export class LeadSquaredService implements ITaskService {
constructor(
private prisma: PrismaService,
private logger: LoggerService,
private cryptoService: EncryptionService,
private registry: ServiceRegistry,
) {
this.logger.setContext(
CrmObject.task.toUpperCase() + ':' + LeadSquaredService.name,
);
this.registry.registerService('leadsquared', this);
}

async addTask(
taskData: LeadSquaredTaskInput,
linkedUserId: string,
): Promise<ApiResponse<LeadSquaredTaskOutput>> {
try {
const connection = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
provider_slug: 'leadsquared',
vertical: 'crm',
},
});

const headers = {
'Content-Type': 'application/json',
'x-LSQ-AccessKey': this.cryptoService.decrypt(connection.access_token),
'x-LSQ-SecretKey': this.cryptoService.decrypt(connection.secret_token),
};

const resp = await axios.post(
`${connection.account_url}/v2/Task.svc/Create`,
taskData,
{
headers,
},
);
const taskId = resp.data['Message']['Id'];
const taskResponse = await axios.get(
`${connection.account_url}/v2/Task.svc/Retrieve.GetById?id=${taskId}`,
{
headers,
},
);
return {
data: taskResponse.data[0],
message: 'Leadsquared task created',
statusCode: 201,
};
} catch (error) {
throw error;
}
}

async sync(data: SyncParam): Promise<ApiResponse<LeadSquaredTaskOutput[]>> {
try {
const { linkedUserId, ownerEmailAddress, statusCode } = data;

const connection = await this.prisma.connections.findFirst({
where: {
id_linked_user: linkedUserId,
provider_slug: 'leadsquared',
vertical: 'crm',
},
});

const payload = {
Parameter: {
LookupName: 'ownerEmailAddress',
LookupValue: ownerEmailAddress,
StatusCode: statusCode, // 0 = incomplete, 1 = completed
},
};

const resp = await axios.post(
`${connection.account_url}/v2/Task.svc/Retrieve`,
payload,
{
headers: {
'Content-Type': 'application/json',
'x-LSQ-AccessKey': this.cryptoService.decrypt(
connection.access_token,
),
'x-LSQ-SecretKey': this.cryptoService.decrypt(
connection.secret_token,
),
},
},
);
this.logger.log(`Synced leadsquared tasks !`);
return {
data: resp.data['List'],
message: 'Leadsquared tasks retrieved',
statusCode: 200,
};
} catch (error) {
throw error;
}
}
}
178 changes: 178 additions & 0 deletions packages/api/src/crm/task/services/leadsquared/mappers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { LeadSquaredTaskInput, LeadSquaredTaskOutput } from './types';
import {
UnifiedCrmTaskInput,
UnifiedCrmTaskOutput,
} from '@crm/task/types/model.unified';
import { ITaskMapper } from '@crm/task/types';
import { Utils } from '@crm/@lib/@utils';
import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry';
import { Injectable } from '@nestjs/common';

@Injectable()
export class LeadSquaredTaskMapper implements ITaskMapper {
constructor(
private mappersRegistry: MappersRegistry,
private utils: Utils,
) {
this.mappersRegistry.registerService('crm', 'task', 'leadsquared', this);
}

formatToRequiredDateString(date: Date): string {
return `${date.getUTCFullYear()}-${date.getUTCMonth()}-${date.getUTCDate()} ${date.getUTCHours()}:${date.getUTCMinutes()}:${date.getUTCMilliseconds()}`;
}

async desunify(
source: UnifiedCrmTaskInput,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): Promise<LeadSquaredTaskInput> {
// Asuming deal_id = opportunity & company_id = lead
const result: LeadSquaredTaskInput = {
StatusCode: source.status === 'COMPLETED' ? '1' : '0',
Name: source.subject,
Description: source.content,
RelatedEntity: '0',
};

if (source.due_date) {
result.DueDate = this.formatToRequiredDateString(source.due_date);
}

if (source.finished_date) {
result.EndDate = this.formatToRequiredDateString(source.due_date);
}

// deal -> opportunity
if (source.deal_id) {
const opportunity_id = await this.utils.getRemoteIdFromDealUuid(
source.deal_id,
);
if (opportunity_id) {
(result.RelatedEntity = '5'), (result.RelatedEntityId = opportunity_id);
}
}
if (source.user_id) {
const owner_id = await this.utils.getRemoteIdFromUserUuid(source.user_id);
if (owner_id) {
result.OwnerId = owner_id;
}
}

// company -> lead
if (source.company_id) {
const lead_id = await this.utils.getRemoteIdFromCompanyUuid(
source.company_id,
);
if (lead_id) {
(result.RelatedEntity = '1'), (result.RelatedEntityId = lead_id);
}
}

if (customFieldMappings && source.field_mappings) {
for (const [k, v] of Object.entries(source.field_mappings)) {
const mapping = customFieldMappings.find(
(mapping) => mapping.slug === k,
);
if (mapping) {
result[mapping.remote_id] = v;
}
}
}

return result;
}

async unify(
source: LeadSquaredTaskOutput | LeadSquaredTaskOutput[],
connectionId: string,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): Promise<UnifiedCrmTaskOutput | UnifiedCrmTaskOutput[]> {
if (!Array.isArray(source)) {
return await this.mapSingleTaskToUnified(
source,
connectionId,
customFieldMappings,
);
}

return Promise.all(
source.map((task) =>
this.mapSingleTaskToUnified(task, connectionId, customFieldMappings),
),
);
}

private async mapSingleTaskToUnified(
task: LeadSquaredTaskOutput,
connectionId: string,
customFieldMappings?: {
slug: string;
remote_id: string;
}[],
): Promise<UnifiedCrmTaskOutput> {
const field_mappings: { [key: string]: any } = {};
if (customFieldMappings) {
for (const mapping of customFieldMappings) {
field_mappings[mapping.slug] = task[mapping.remote_id];
}
}

let opts: any = {};

// Means RelatedEntityId = LeadId
if (task.RelatedEntity === '1') {
const company_id = await this.utils.getCompanyUuidFromRemoteId(
task.RelatedEntityId,
connectionId,
);
if (company_id) {
opts = {
...opts,
company_id: company_id,
};
}
}
// Means RelatedEntityId = OpportunityId
if (task.RelatedEntity === '5') {
const deal_id = await this.utils.getDealUuidFromRemoteId(
task.RelatedEntityId,
connectionId,
);
if (deal_id) {
opts = {
...opts,
deal_id,
};
}
}

if (task.OwnerId) {
const user_id = await this.utils.getUserUuidFromRemoteId(
task.OwnerId,
connectionId,
);
if (user_id) {
opts = {
...opts,
user_id: user_id,
};
}
}

return {
remote_id: task.UserTaskId,
remote_data: task,
content: task.Description,
status: task.StatusCode === '1' ? 'COMPLETED' : 'PENDING',
finished_date: task.EndDate ? new Date(task.EndDate.split(' ')[0]) : null,
due_date: task.DueDate ? new Date(task.DueDate.split(' ')[0]) : null,
field_mappings,
...opts,
};
}
}
41 changes: 41 additions & 0 deletions packages/api/src/crm/task/services/leadsquared/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
interface KeyValuePair {
[key: string]: unknown;
}

interface LeadSquaredTask {
UserTaskId: string;
Name: string;
Category: number;
Description: string;
// "1" if you're passing the LeadId
// "5" if you're passing the OpportunityId
// "0" if you're not passing any value
RelatedEntity: '0' | '1' | '5';
RelatedEntityId: string;
DueDate: string; //"2022-02-23 13:10:00.000"
Reminder: number;
ReminderBeforeDays: number;
NotifyBy: '1100' | '1000'; // "1100" if you want the task owner to notify by email
// "1000" Do'nt want any notification
StatusCode: '0' | '1'; // 0 = incomplete, 1 = completed
OwnerId: string;
OwnerName: string;
CreatedBy: string;
CreatedByName: string;
CreatedOn: string; //"2021-11-24 09:43:28.000",
ModifiedBy: string;
ModifiedByName: string;
ModifiedOn: string; //"2022-02-23 07:00:41.000",
RelatedEntityIdName: string;
CompletedOn: string; //"0001-01-01 00:00:00.000",
TaskType: KeyValuePair;
OwnerEmailAddress: string;
EndDate: string; //"2022-02-23 13:15:00",
PercentCompleted: number;
Priority: string;
Location: string;
CustomFields: KeyValuePair;
}

export type LeadSquaredTaskInput = Partial<LeadSquaredTask>;
export type LeadSquaredTaskOutput = LeadSquaredTaskInput;

0 comments on commit 1901c94

Please sign in to comment.