@memberjunction/actions-bizapps-crm
Version:
CRM system integration actions for MemberJunction
282 lines • 12.1 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GetDealsByContactAction = void 0;
const global_1 = require("@memberjunction/global");
const hubspot_base_action_1 = require("../hubspot-base.action");
const actions_1 = require("@memberjunction/actions");
/**
* Action to get all deals associated with a contact in HubSpot
*/
let GetDealsByContactAction = class GetDealsByContactAction extends hubspot_base_action_1.HubSpotBaseAction {
/**
* Get all deals for a specific contact
*/
async InternalRunAction(params) {
const { Params, ContextUser } = params;
this.params = Params; // Set params for base class to use
try {
// Extract and validate parameters
const contactId = this.getParamValue(Params, 'ContactId');
const includeDetails = this.getParamValue(Params, 'IncludeDetails') ?? true;
const onlyOpen = this.getParamValue(Params, 'OnlyOpen') ?? false;
const sortBy = this.getParamValue(Params, 'SortBy') || 'closedate';
const sortOrder = this.getParamValue(Params, 'SortOrder') || 'DESC';
if (!contactId) {
return {
Success: false,
ResultCode: 'VALIDATION_ERROR',
Message: 'Contact ID is required',
Params
};
}
// First, get the contact to verify it exists and get basic info
let contact;
try {
contact = await this.makeHubSpotRequest(`objects/contacts/${contactId}?properties=firstname,lastname,email,company`, 'GET', undefined, ContextUser);
}
catch (error) {
return {
Success: false,
ResultCode: 'CONTACT_NOT_FOUND',
Message: `Contact with ID ${contactId} not found`,
Params
};
}
// Get associated deals
const associations = await this.makeHubSpotRequest(`objects/contacts/${contactId}/associations/deals`, 'GET', undefined, ContextUser);
// Extract deal IDs
const dealIds = associations.results.map((assoc) => assoc.id);
if (dealIds.length === 0) {
// No deals found
const contactInfo = this.mapHubSpotProperties(contact);
const summary = {
contactId: contactId,
contactName: `${contactInfo.firstname || ''} ${contactInfo.lastname || ''}`.trim(),
contactEmail: contactInfo.email,
totalDeals: 0,
openDeals: 0,
closedWonDeals: 0,
closedLostDeals: 0,
totalValue: 0,
wonValue: 0,
averageDealSize: 0,
deals: []
};
const outputParams = [...Params];
const dealsParam = outputParams.find(p => p.Name === 'Deals');
if (dealsParam)
dealsParam.Value = [];
const summaryParam = outputParams.find(p => p.Name === 'Summary');
if (summaryParam)
summaryParam.Value = summary;
return {
Success: true,
ResultCode: 'SUCCESS',
Message: `No deals found for contact ${contactInfo.email || contactId}`,
Params: outputParams
};
}
// Get deal details if requested
let deals = [];
if (includeDetails) {
// Batch request for all deals
const batchRequest = {
inputs: dealIds.map((id) => ({
id: id,
properties: [
'dealname', 'dealstage', 'pipeline', 'amount', 'closedate',
'dealtype', 'hs_priority', 'hubspot_owner_id', 'createdate',
'hs_lastmodifieddate', 'description', 'hs_is_closed',
'hs_is_closed_won', 'closed_won_reason', 'closed_lost_reason'
]
}))
};
const batchResponse = await this.makeHubSpotRequest('objects/deals/batch/read', 'POST', batchRequest, ContextUser);
deals = batchResponse.results.map((deal) => this.mapHubSpotProperties(deal));
}
else {
// Just return basic deal info
deals = dealIds.map((id) => ({ id }));
}
// Filter open deals if requested
if (onlyOpen && includeDetails) {
deals = deals.filter(deal => !deal.hs_is_closed || deal.hs_is_closed === 'false');
}
// Sort deals
if (includeDetails && sortBy) {
deals.sort((a, b) => {
const aValue = a[sortBy] || '';
const bValue = b[sortBy] || '';
if (sortOrder === 'ASC') {
return aValue > bValue ? 1 : -1;
}
else {
return aValue < bValue ? 1 : -1;
}
});
}
// Calculate summary statistics
const contactInfo = this.mapHubSpotProperties(contact);
const stats = {
totalDeals: deals.length,
openDeals: 0,
closedWonDeals: 0,
closedLostDeals: 0,
totalValue: 0,
wonValue: 0,
lostValue: 0,
openValue: 0,
averageDealSize: 0,
averageTimeToClose: 0,
stageBreakdown: {},
pipelineBreakdown: {}
};
// Calculate statistics if we have details
if (includeDetails) {
let totalDaysToClose = 0;
let closedDealsCount = 0;
deals.forEach((deal) => {
// Count deal states
if (deal.hs_is_closed_won === 'true') {
stats.closedWonDeals++;
}
else if (deal.hs_is_closed === 'true') {
stats.closedLostDeals++;
}
else {
stats.openDeals++;
}
// Sum values
if (deal.amount) {
const amount = parseFloat(deal.amount);
stats.totalValue += amount;
if (deal.hs_is_closed_won === 'true') {
stats.wonValue += amount;
}
else if (deal.hs_is_closed === 'true') {
stats.lostValue += amount;
}
else {
stats.openValue += amount;
}
}
// Track stages and pipelines
if (deal.dealstage) {
stats.stageBreakdown[deal.dealstage] = (stats.stageBreakdown[deal.dealstage] || 0) + 1;
}
if (deal.pipeline) {
stats.pipelineBreakdown[deal.pipeline] = (stats.pipelineBreakdown[deal.pipeline] || 0) + 1;
}
// Calculate time to close for closed deals
if (deal.hs_is_closed === 'true' && deal.createdate && deal.closedate) {
const createDate = new Date(deal.createdate);
const closeDate = new Date(deal.closedate);
const daysToClose = Math.floor((closeDate.getTime() - createDate.getTime()) / (1000 * 60 * 60 * 24));
totalDaysToClose += daysToClose;
closedDealsCount++;
}
});
// Calculate averages
if (deals.length > 0) {
stats.averageDealSize = stats.totalValue / deals.length;
}
if (closedDealsCount > 0) {
stats.averageTimeToClose = totalDaysToClose / closedDealsCount;
}
}
// Create summary
const summary = {
contactId: contactId,
contactName: `${contactInfo.firstname || ''} ${contactInfo.lastname || ''}`.trim(),
contactEmail: contactInfo.email,
contactCompany: contactInfo.company,
...stats,
winRate: stats.closedWonDeals > 0 ? (stats.closedWonDeals / (stats.closedWonDeals + stats.closedLostDeals)) * 100 : 0
};
// Update output parameters
const outputParams = [...Params];
const dealsParam = outputParams.find(p => p.Name === 'Deals');
if (dealsParam)
dealsParam.Value = deals;
const summaryParam = outputParams.find(p => p.Name === 'Summary');
if (summaryParam)
summaryParam.Value = summary;
return {
Success: true,
ResultCode: 'SUCCESS',
Message: `Found ${deals.length} deals for contact ${contactInfo.email || contactId}`,
Params: outputParams
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
return {
Success: false,
ResultCode: 'ERROR',
Message: `Error retrieving deals for contact: ${errorMessage}`,
Params
};
}
}
/**
* Define the parameters this action expects
*/
get Params() {
const baseParams = this.getCommonCRMParams();
const specificParams = [
{
Name: 'ContactId',
Type: 'Input',
Value: null
},
{
Name: 'IncludeDetails',
Type: 'Input',
Value: true
},
{
Name: 'OnlyOpen',
Type: 'Input',
Value: false
},
{
Name: 'SortBy',
Type: 'Input',
Value: 'closedate'
},
{
Name: 'SortOrder',
Type: 'Input',
Value: 'DESC'
},
{
Name: 'Deals',
Type: 'Output',
Value: null
},
{
Name: 'Summary',
Type: 'Output',
Value: null
}
];
return [...baseParams, ...specificParams];
}
/**
* Metadata about this action
*/
get Description() {
return 'Retrieves all deals associated with a specific contact including detailed statistics';
}
};
exports.GetDealsByContactAction = GetDealsByContactAction;
exports.GetDealsByContactAction = GetDealsByContactAction = __decorate([
(0, global_1.RegisterClass)(actions_1.BaseAction, 'GetDealsByContactAction')
], GetDealsByContactAction);
//# sourceMappingURL=get-deals-by-contact.action.js.map