diff --git a/lib/close.com.d.ts b/lib/close.com.d.ts index 4d1595a..59b3901 100644 --- a/lib/close.com.d.ts +++ b/lib/close.com.d.ts @@ -196,6 +196,24 @@ declare module 'closecrm-node' { [key: string]: any; } + export interface WhatsAppMessage extends Activity { + _type: 'WhatsAppMessage'; + external_whatsapp_message_id: string; + message_markdown: string; + message_html?: string; + direction?: 'incoming' | 'outgoing'; + status?: string; + response_to_id?: string; + integration_link?: string; + attachments?: Array<{ + url: string; + filename: string; + content_type: string; + size?: number; + }>; + [key: string]: any; + } + export interface Task { id: string; lead_id?: string; @@ -221,6 +239,7 @@ declare module 'closecrm-node' { accepts_multiple_values?: boolean; editable_with_roles?: string[]; required?: boolean; + custom_object_type_id?: string; organization_id: string; created_by?: string; date_created: string; @@ -228,6 +247,33 @@ declare module 'closecrm-node' { [key: string]: any; } + export interface CustomObjectType { + id: string; + name: string; + name_plural: string; + description?: string; + api_create_only?: boolean; + editable_with_roles?: string[]; + fields?: CustomField[]; + back_reference_fields?: CustomField[]; + organization_id: string; + date_created: string; + date_updated: string; + [key: string]: any; + } + + export interface CustomObject { + id: string; + custom_object_type_id: string; + lead_id: string; + name: string; + custom?: Record; + date_created: string; + date_updated: string; + organization_id: string; + [key: string]: any; + } + export interface User { id: string; email: string; @@ -438,6 +484,14 @@ declare module 'closecrm-node' { delete(id: string): Promise; } + export interface WhatsAppMessageResource { + search(options?: SearchOptions): Promise>; + create(data: Partial, queryParams?: { send_to_inbox?: boolean }): Promise; + read(id: string): Promise; + update(id: string, data: Partial): Promise; + delete(id: string): Promise; + } + export interface ActivityResource { search(options?: SearchOptions): Promise>; note: NoteResource; @@ -445,6 +499,7 @@ declare module 'closecrm-node' { call: CallResource; sms: SMSResource; meeting: MeetingResource; + whatsapp_message: WhatsAppMessageResource; } export interface OpportunityResource { @@ -476,6 +531,7 @@ declare module 'closecrm-node' { contact: CustomFieldSubResource; opportunity: CustomFieldSubResource; activity: CustomFieldSubResource; + custom_object_type: CustomFieldSubResource; } export interface CustomActivityResource { @@ -486,6 +542,22 @@ declare module 'closecrm-node' { delete(type: string, id: string): Promise; } + export interface CustomObjectTypeResource { + list(options?: SearchOptions): Promise>; + create(data: Partial): Promise; + read(id: string): Promise; + update(id: string, data: Partial): Promise; + delete(id: string): Promise; + } + + export interface CustomObjectResource { + list(options?: SearchOptions): Promise>; + create(data: Partial): Promise; + read(id: string): Promise; + update(id: string, data: Partial): Promise; + delete(id: string): Promise; + } + export interface UserResource { me(): Promise; list(options?: SearchOptions): Promise>; @@ -553,8 +625,87 @@ declare module 'closecrm-node' { } export interface ReportResource { - list(options?: SearchOptions): Promise>; - read(id: string, options?: SearchOptions): Promise; + // List predefined metrics used in activity reports + activity_metrics(): Promise>; + + // Get an activity report + activity(data: { + datetime_range?: { start: string; end: string }; + relative_range?: string; + query?: any; + users?: string[]; + type: 'overview' | 'comparison'; + metrics: string[]; + }): Promise; + + // Get sent emails report grouped by template + sent_emails(organizationId: string, options?: { + user_id?: string; + date_start?: string; + date_end?: string; + }): Promise; + + // Get lead status change report + lead_statuses(organizationId: string, options?: { + date_start?: string; + date_end?: string; + query?: string; + smart_view_id?: string; + }): Promise; + + // Get opportunity status change report + opportunity_statuses(organizationId: string, options?: { + user_id?: string; + date_start?: string; + date_end?: string; + query?: string; + smart_view_id?: string; + }): Promise; + + // Get custom report + custom(organizationId: string, options?: { + query?: string; + x?: string; + y?: string; + interval?: string; + transform_y?: 'sum' | 'avg' | 'min' | 'max'; + group_by?: string; + start?: string; + end?: string; + }): Promise; + + // Get custom report fields + custom_fields(): Promise; + + // Get opportunity funnel report (totals) + funnel_totals(data: { + pipeline: string; + type: 'created-cohort' | 'active-stage-cohort'; + report_relative_range?: string; + report_datetime_range?: { start: string; end: string }; + cohort_relative_range?: string; + cohort_datetime_range?: { start: string; end: string }; + compared_relative_range?: string; + compared_datetime_range?: string; + compared_custom_range?: { start: string; end: string }; + query?: any; + users?: string[]; + }): Promise; + + // Get opportunity funnel report (stages) + funnel_stages(data: { + pipeline: string; + type: 'created-cohort' | 'active-stage-cohort'; + report_relative_range?: string; + report_datetime_range?: { start: string; end: string }; + cohort_relative_range?: string; + cohort_datetime_range?: { start: string; end: string }; + compared_relative_range?: string; + compared_datetime_range?: string; + compared_custom_range?: { start: string; end: string }; + query?: any; + users?: string[]; + }): Promise; } export interface EventResource { @@ -618,6 +769,8 @@ declare module 'closecrm-node' { task: TaskResource; custom_field: CustomFieldResource; custom_activity: CustomActivityResource; + custom_object_type: CustomObjectTypeResource; + custom_object: CustomObjectResource; user: UserResource; organization: OrganizationResource; pipeline: PipelineResource; diff --git a/lib/close.com.js b/lib/close.com.js index 851b320..7f9a3b6 100644 --- a/lib/close.com.js +++ b/lib/close.com.js @@ -14,6 +14,7 @@ class Closecom { this._initTasks(); this._initCustomFields(); this._initCustomActivities(); + this._initCustomObjects(); this._initUsers(); this._initOrganizations(); this._initPipelines(); @@ -138,6 +139,17 @@ class Closecom { read: (id) => this._get(`/activity/meeting/${id}/`), update: (id, data) => this._put(`/activity/meeting/${id}/`, data), delete: (id) => this._delete(`/activity/meeting/${id}/`) + }, + + whatsapp_message: { + search: (options = {}) => this._get('/activity/whatsapp_message/', this._normalizeSearchParams({ ...options })), + create: (data, queryParams = {}) => { + const path = '/activity/whatsapp_message/'; + return this._request({ method: 'POST', path, body: data, params: queryParams }); + }, + read: (id) => this._get(`/activity/whatsapp_message/${id}/`), + update: (id, data) => this._put(`/activity/whatsapp_message/${id}/`, data), + delete: (id) => this._delete(`/activity/whatsapp_message/${id}/`) } }; } @@ -194,6 +206,14 @@ class Closecom { read: (id) => this._get(`/custom_field/activity/${id}/`), update: (id, data) => this._put(`/custom_field/activity/${id}/`, data), delete: (id) => this._delete(`/custom_field/activity/${id}/`) + }, + + custom_object_type: { + list: () => this._get('/custom_field/custom_object_type/'), + create: (data) => this._post('/custom_field/custom_object_type/', data), + read: (id) => this._get(`/custom_field/custom_object_type/${id}/`), + update: (id, data) => this._put(`/custom_field/custom_object_type/${id}/`, data), + delete: (id) => this._delete(`/custom_field/custom_object_type/${id}/`) } }; } @@ -208,6 +228,24 @@ class Closecom { }; } + _initCustomObjects() { + this.custom_object_type = { + list: (options = {}) => this._get('/custom_object_type/', this._normalizeSearchParams({ ...options })), + create: (data) => this._post('/custom_object_type/', data), + read: (id) => this._get(`/custom_object_type/${id}/`), + update: (id, data) => this._put(`/custom_object_type/${id}/`, data), + delete: (id) => this._delete(`/custom_object_type/${id}/`) + }; + + this.custom_object = { + list: (options = {}) => this._get('/custom_object/', this._normalizeSearchParams({ ...options })), + create: (data) => this._post('/custom_object/', data), + read: (id) => this._get(`/custom_object/${id}/`), + update: (id, data) => this._put(`/custom_object/${id}/`, data), + delete: (id) => this._delete(`/custom_object/${id}/`) + }; + } + _initUsers() { this.user = { me: () => this._get('/me/'), @@ -299,8 +337,36 @@ class Closecom { _initReports() { this.report = { - list: (options = {}) => this._get('/report/', this._normalizeSearchParams({ ...options })), - read: (id, options = {}) => this._get(`/report/${id}/`, this._normalizeSearchParams({ ...options })) + // List predefined metrics used in activity reports + activity_metrics: () => this._get('/report/activity/metrics/'), + + // Get an activity report (POST) + activity: (data) => this._post('/report/activity/', data), + + // Get sent emails report grouped by template + sent_emails: (organizationId, options = {}) => + this._get(`/report/sent_emails/${organizationId}/`, this._normalizeSearchParams({ ...options })), + + // Get lead status change report + lead_statuses: (organizationId, options = {}) => + this._get(`/report/statuses/lead/${organizationId}/`, this._normalizeSearchParams({ ...options })), + + // Get opportunity status change report + opportunity_statuses: (organizationId, options = {}) => + this._get(`/report/statuses/opportunity/${organizationId}/`, this._normalizeSearchParams({ ...options })), + + // Get custom report + custom: (organizationId, options = {}) => + this._get(`/report/custom/${organizationId}/`, this._normalizeSearchParams({ ...options })), + + // Get custom report fields + custom_fields: () => this._get('/report/custom/fields/'), + + // Get opportunity funnel report (totals) + funnel_totals: (data) => this._post('/report/funnel/opportunity/totals/', data), + + // Get opportunity funnel report (stages) + funnel_stages: (data) => this._post('/report/funnel/opportunity/stages/', data) }; } diff --git a/package.json b/package.json index 07dc317..9ae5395 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "closecrm-node", - "version": "1.0.2", + "version": "1.0.3", "description": "Unofficial Close.com API client for Node.js with TypeScript support", "main": "lib/close.com.js", "types": "lib/close.com.d.ts", diff --git a/test/closecom.test.js b/test/closecom.test.js index b2f7ba6..090659d 100644 --- a/test/closecom.test.js +++ b/test/closecom.test.js @@ -71,6 +71,7 @@ describe('Closecom', () => { expect(api.activity.call).to.be.an('object'); expect(api.activity.sms).to.be.an('object'); expect(api.activity.meeting).to.be.an('object'); + expect(api.activity.whatsapp_message).to.be.an('object'); }); it('should initialize opportunity resource', () => { @@ -89,6 +90,34 @@ describe('Closecom', () => { expect(api.custom_field.contact).to.be.an('object'); expect(api.custom_field.opportunity).to.be.an('object'); expect(api.custom_field.activity).to.be.an('object'); + expect(api.custom_field.custom_object_type).to.be.an('object'); + }); + + it('should initialize custom_activity resource', () => { + expect(api.custom_activity).to.be.an('object'); + expect(api.custom_activity.search).to.be.a('function'); + expect(api.custom_activity.create).to.be.a('function'); + expect(api.custom_activity.read).to.be.a('function'); + expect(api.custom_activity.update).to.be.a('function'); + expect(api.custom_activity.delete).to.be.a('function'); + }); + + it('should initialize custom_object_type resource', () => { + expect(api.custom_object_type).to.be.an('object'); + expect(api.custom_object_type.list).to.be.a('function'); + expect(api.custom_object_type.create).to.be.a('function'); + expect(api.custom_object_type.read).to.be.a('function'); + expect(api.custom_object_type.update).to.be.a('function'); + expect(api.custom_object_type.delete).to.be.a('function'); + }); + + it('should initialize custom_object resource', () => { + expect(api.custom_object).to.be.an('object'); + expect(api.custom_object.list).to.be.a('function'); + expect(api.custom_object.create).to.be.a('function'); + expect(api.custom_object.read).to.be.a('function'); + expect(api.custom_object.update).to.be.a('function'); + expect(api.custom_object.delete).to.be.a('function'); }); it('should initialize user resource', () => { @@ -103,6 +132,95 @@ describe('Closecom', () => { expect(api.webhook.create).to.be.a('function'); }); + it('should initialize email_thread resource', () => { + expect(api.email_thread).to.be.an('object'); + expect(api.email_thread.list).to.be.a('function'); + expect(api.email_thread.read).to.be.a('function'); + }); + + it('should initialize connected_account resource', () => { + expect(api.connected_account).to.be.an('object'); + expect(api.connected_account.list).to.be.a('function'); + expect(api.connected_account.read).to.be.a('function'); + }); + + it('should initialize event resource', () => { + expect(api.event).to.be.an('object'); + expect(api.event.search).to.be.a('function'); + expect(api.event.read).to.be.a('function'); + }); + + it('should initialize report resource', () => { + expect(api.report).to.be.an('object'); + expect(api.report.activity_metrics).to.be.a('function'); + expect(api.report.activity).to.be.a('function'); + expect(api.report.sent_emails).to.be.a('function'); + expect(api.report.lead_statuses).to.be.a('function'); + expect(api.report.opportunity_statuses).to.be.a('function'); + expect(api.report.custom).to.be.a('function'); + expect(api.report.custom_fields).to.be.a('function'); + expect(api.report.funnel_totals).to.be.a('function'); + expect(api.report.funnel_stages).to.be.a('function'); + }); + + it('should initialize sequence resource', () => { + expect(api.sequence).to.be.an('object'); + expect(api.sequence.search).to.be.a('function'); + expect(api.sequence.create).to.be.a('function'); + expect(api.sequence.read).to.be.a('function'); + expect(api.sequence.update).to.be.a('function'); + expect(api.sequence.delete).to.be.a('function'); + }); + + it('should initialize sequence_subscription resource', () => { + expect(api.sequence_subscription).to.be.an('object'); + expect(api.sequence_subscription.list).to.be.a('function'); + expect(api.sequence_subscription.create).to.be.a('function'); + expect(api.sequence_subscription.read).to.be.a('function'); + expect(api.sequence_subscription.update).to.be.a('function'); + expect(api.sequence_subscription.delete).to.be.a('function'); + }); + + it('should initialize saved_search resource', () => { + expect(api.saved_search).to.be.an('object'); + expect(api.saved_search.list).to.be.a('function'); + expect(api.saved_search.create).to.be.a('function'); + expect(api.saved_search.read).to.be.a('function'); + expect(api.saved_search.update).to.be.a('function'); + expect(api.saved_search.delete).to.be.a('function'); + }); + + it('should initialize email_template resource', () => { + expect(api.email_template).to.be.an('object'); + expect(api.email_template.search).to.be.a('function'); + expect(api.email_template.create).to.be.a('function'); + expect(api.email_template.read).to.be.a('function'); + expect(api.email_template.update).to.be.a('function'); + expect(api.email_template.delete).to.be.a('function'); + }); + + it('should initialize status resource', () => { + expect(api.status).to.be.an('object'); + expect(api.status.lead).to.be.an('object'); + expect(api.status.opportunity).to.be.an('object'); + }); + + it('should initialize pipeline resource', () => { + expect(api.pipeline).to.be.an('object'); + expect(api.pipeline.list).to.be.a('function'); + expect(api.pipeline.create).to.be.a('function'); + expect(api.pipeline.read).to.be.a('function'); + expect(api.pipeline.update).to.be.a('function'); + expect(api.pipeline.delete).to.be.a('function'); + }); + + it('should initialize organization resource', () => { + expect(api.organization).to.be.an('object'); + expect(api.organization.list).to.be.a('function'); + expect(api.organization.read).to.be.a('function'); + expect(api.organization.update).to.be.a('function'); + }); + it('should initialize bulk resource', () => { expect(api.bulk).to.be.an('object'); expect(api.bulk.delete).to.be.a('function'); @@ -328,4 +446,114 @@ describe('Closecom', () => { } }); }); -}); \ No newline at end of file + + + describe('Activity Sub-Resources', () => { + it('should have all CRUD methods for note', () => { + expect(api.activity.note.search).to.be.a('function'); + expect(api.activity.note.create).to.be.a('function'); + expect(api.activity.note.read).to.be.a('function'); + expect(api.activity.note.update).to.be.a('function'); + expect(api.activity.note.delete).to.be.a('function'); + }); + + it('should have all CRUD methods for email', () => { + expect(api.activity.email.search).to.be.a('function'); + expect(api.activity.email.create).to.be.a('function'); + expect(api.activity.email.read).to.be.a('function'); + expect(api.activity.email.update).to.be.a('function'); + expect(api.activity.email.delete).to.be.a('function'); + }); + + it('should have all CRUD methods for call', () => { + expect(api.activity.call.search).to.be.a('function'); + expect(api.activity.call.create).to.be.a('function'); + expect(api.activity.call.read).to.be.a('function'); + expect(api.activity.call.update).to.be.a('function'); + expect(api.activity.call.delete).to.be.a('function'); + }); + + it('should have all methods for sms (no update)', () => { + expect(api.activity.sms.search).to.be.a('function'); + expect(api.activity.sms.create).to.be.a('function'); + expect(api.activity.sms.read).to.be.a('function'); + expect(api.activity.sms.delete).to.be.a('function'); + }); + + it('should have all CRUD methods for meeting', () => { + expect(api.activity.meeting.search).to.be.a('function'); + expect(api.activity.meeting.create).to.be.a('function'); + expect(api.activity.meeting.read).to.be.a('function'); + expect(api.activity.meeting.update).to.be.a('function'); + expect(api.activity.meeting.delete).to.be.a('function'); + }); + + it('should have all CRUD methods for whatsapp_message', () => { + expect(api.activity.whatsapp_message.search).to.be.a('function'); + expect(api.activity.whatsapp_message.create).to.be.a('function'); + expect(api.activity.whatsapp_message.read).to.be.a('function'); + expect(api.activity.whatsapp_message.update).to.be.a('function'); + expect(api.activity.whatsapp_message.delete).to.be.a('function'); + }); + }); + + describe('Custom Field Sub-Resources', () => { + it('should have all methods for lead custom fields', () => { + expect(api.custom_field.lead.list).to.be.a('function'); + expect(api.custom_field.lead.create).to.be.a('function'); + expect(api.custom_field.lead.read).to.be.a('function'); + expect(api.custom_field.lead.update).to.be.a('function'); + expect(api.custom_field.lead.delete).to.be.a('function'); + }); + + it('should have all methods for contact custom fields', () => { + expect(api.custom_field.contact.list).to.be.a('function'); + expect(api.custom_field.contact.create).to.be.a('function'); + expect(api.custom_field.contact.read).to.be.a('function'); + expect(api.custom_field.contact.update).to.be.a('function'); + expect(api.custom_field.contact.delete).to.be.a('function'); + }); + + it('should have all methods for opportunity custom fields', () => { + expect(api.custom_field.opportunity.list).to.be.a('function'); + expect(api.custom_field.opportunity.create).to.be.a('function'); + expect(api.custom_field.opportunity.read).to.be.a('function'); + expect(api.custom_field.opportunity.update).to.be.a('function'); + expect(api.custom_field.opportunity.delete).to.be.a('function'); + }); + + it('should have all methods for activity custom fields', () => { + expect(api.custom_field.activity.list).to.be.a('function'); + expect(api.custom_field.activity.create).to.be.a('function'); + expect(api.custom_field.activity.read).to.be.a('function'); + expect(api.custom_field.activity.update).to.be.a('function'); + expect(api.custom_field.activity.delete).to.be.a('function'); + }); + + it('should have all methods for custom_object_type custom fields', () => { + expect(api.custom_field.custom_object_type.list).to.be.a('function'); + expect(api.custom_field.custom_object_type.create).to.be.a('function'); + expect(api.custom_field.custom_object_type.read).to.be.a('function'); + expect(api.custom_field.custom_object_type.update).to.be.a('function'); + expect(api.custom_field.custom_object_type.delete).to.be.a('function'); + }); + }); + + describe('Status Sub-Resources', () => { + it('should have all methods for lead status', () => { + expect(api.status.lead.list).to.be.a('function'); + expect(api.status.lead.create).to.be.a('function'); + expect(api.status.lead.read).to.be.a('function'); + expect(api.status.lead.update).to.be.a('function'); + expect(api.status.lead.delete).to.be.a('function'); + }); + + it('should have all methods for opportunity status', () => { + expect(api.status.opportunity.list).to.be.a('function'); + expect(api.status.opportunity.create).to.be.a('function'); + expect(api.status.opportunity.read).to.be.a('function'); + expect(api.status.opportunity.update).to.be.a('function'); + expect(api.status.opportunity.delete).to.be.a('function'); + }); + }); +}); diff --git a/test/manual-test.js b/test/manual-test.js index d290e9e..88ed23c 100644 --- a/test/manual-test.js +++ b/test/manual-test.js @@ -119,8 +119,71 @@ async function runTests() { console.log('✅ Streamed leads:', streamCount); console.log(); - // Test 11: Error Handling - console.log('1️⃣1️⃣ Testing: Error Handling'); + // Test 11: WhatsApp Message Activity + console.log('1️⃣1️⃣ Testing: api.activity.whatsapp_message.search()'); + const whatsappResult = await api.activity.whatsapp_message.search({ _limit: 5 }); + console.log('✅ WhatsApp messages found:', whatsappResult.data.length); + console.log(); + + // Test 12: Custom Objects + console.log('1️⃣2️⃣ Testing: api.custom_object_type.list()'); + const customObjectTypes = await api.custom_object_type.list(); + console.log('✅ Custom Object Types found:', customObjectTypes.data.length); + if (customObjectTypes.data.length > 0) { + console.log(' First type:', customObjectTypes.data[0].name); + } + console.log(); + + // Test 13: Email Threads + console.log('1️⃣3️⃣ Testing: api.email_thread.list()'); + const emailThreads = await api.email_thread.list({ _limit: 5 }); + console.log('✅ Email threads found:', emailThreads.data.length); + console.log(); + + // Test 14: Connected Accounts + console.log('1️⃣4️⃣ Testing: api.connected_account.list()'); + const connectedAccounts = await api.connected_account.list(); + console.log('✅ Connected accounts found:', connectedAccounts.data.length); + console.log(); + + // Test 15: Events + console.log('1️⃣5️⃣ Testing: api.event.search()'); + const events = await api.event.search({ _limit: 5 }); + console.log('✅ Events found:', events.data.length); + console.log(); + + // Test 16: Reports (Activity Metrics) + console.log('1️⃣6️⃣ Testing: api.report.activity_metrics()'); + const activityMetrics = await api.report.activity_metrics(); + console.log('✅ Activity metrics available:', Array.isArray(activityMetrics) ? activityMetrics.length : 'Response received'); + console.log(); + + // Test 17: Sequences + console.log('1️⃣7️⃣ Testing: api.sequence.search()'); + const sequences = await api.sequence.search({ _limit: 5 }); + console.log('✅ Sequences found:', sequences.data.length); + console.log(); + + // Test 18: Email Templates + console.log('1️⃣8️⃣ Testing: api.email_template.search()'); + const templates = await api.email_template.search({ _limit: 5 }); + console.log('✅ Email templates found:', templates.data.length); + console.log(); + + // Test 19: Pipelines + console.log('1️⃣9️⃣ Testing: api.pipeline.list()'); + const pipelines = await api.pipeline.list(); + console.log('✅ Pipelines found:', pipelines.data.length); + console.log(); + + // Test 20: Lead Status + console.log('2️⃣0️⃣ Testing: api.status.lead.list()'); + const leadStatuses = await api.status.lead.list(); + console.log('✅ Lead statuses found:', leadStatuses.data.length); + console.log(); + + // Test 21: Error Handling + console.log('2️⃣1️⃣ Testing: Error Handling'); try { await api.lead.read('invalid_id_12345'); } catch (error) { @@ -130,8 +193,8 @@ async function runTests() { } console.log(); - // Test 12: Validation - console.log('1️⃣2️⃣ Testing: Input Validation'); + // Test 22: Validation + console.log('2️⃣2️⃣ Testing: Input Validation'); try { await api.lead.create({}); } catch (error) {