From 8a2d3935630b329613feb287fa146b421a191d9e Mon Sep 17 00:00:00 2001 From: Guilherme Sborz Date: Wed, 14 Aug 2024 18:29:26 -0300 Subject: [PATCH 01/49] fix: add missing columns to connector_sets migration scripts --- packages/api/scripts/init.sql | 2 ++ packages/api/scripts/seed.sql | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index ead5ee43c..534ed146f 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -545,6 +545,8 @@ CREATE TABLE connector_sets ecom_shopify boolean NULL, ecom_amazon boolean NULL, ecom_squarespace boolean NULL, + hris_gusto boolean NULL, + ats_bamboohr boolean NULL, CONSTRAINT PK_project_connector PRIMARY KEY ( id_connector_set ) ); diff --git a/packages/api/scripts/seed.sql b/packages/api/scripts/seed.sql index 0bee28a94..dd3e6c570 100644 --- a/packages/api/scripts/seed.sql +++ b/packages/api/scripts/seed.sql @@ -2,9 +2,9 @@ INSERT INTO users (id_user, identification_strategy, email, password_hash, first ('0ce39030-2901-4c56-8db0-5e326182ec6b', 'b2c','local@panora.dev', '$2b$10$Y7Q8TWGyGuc5ecdIASbBsuXMo3q/Rs3/cnY.mLZP4tUgfGUOCUBlG', 'local', 'Panora'); INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github) VALUES - ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); + ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); INSERT INTO projects (id_project, name, sync_mode, id_user, id_connector_set) VALUES ('1e468c15-aa57-4448-aa2b-7fed640d1e3d', 'Project 1', 'pull', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'), From e02fb493f78fef7aa8ceb0f9de56b0806a2760f0 Mon Sep 17 00:00:00 2001 From: speakeasybot Date: Fri, 16 Aug 2024 00:05:19 +0000 Subject: [PATCH 02/49] ci: regenerated with Speakeasy CLI v1.372.0 --- .speakeasy/workflow.lock | 6 +- .../swagger/openapi-with-code-samples.yaml | 9867 ++++++++++++++--- 2 files changed, 8463 insertions(+), 1410 deletions(-) diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index 0ab14de46..c8370252e 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -1,9 +1,9 @@ -speakeasyVersion: 1.366.0 +speakeasyVersion: 1.372.0 sources: merge-code-samples-into-spec: sourceNamespace: merge-code-samples-into-spec - sourceRevisionDigest: sha256:61d763ced26b2104e5f13eb6e4b32bd0f598073da2505d842baaa9efd4c78ff1 - sourceBlobDigest: sha256:d53140203694ab341c7f9b60bcfce1995e2c287358ec29504fcb45b677d38918 + sourceRevisionDigest: sha256:62bd8d64f8278dcb7b23d28bffcffbb549e57b5b9dc09bae93ac4c1112dbd09a + sourceBlobDigest: sha256:ebdf0ba69a79a32d558c774cfe0f0c32d742319ed3c3d3d887bf14ac67f417d8 tags: - latest - main diff --git a/packages/api/swagger/openapi-with-code-samples.yaml b/packages/api/swagger/openapi-with-code-samples.yaml index 63f5d5778..eac26bebd 100644 --- a/packages/api/swagger/openapi-with-code-samples.yaml +++ b/packages/api/swagger/openapi-with-code-samples.yaml @@ -483,8 +483,6 @@ paths: schema: type: string responses: - '200': - description: '' '201': description: '' content: @@ -568,8 +566,6 @@ paths: schema: type: string responses: - '200': - description: '' '201': description: '' content: @@ -663,8 +659,6 @@ paths: type: object additionalProperties: true description: Dynamic event payload - '201': - description: '' tags: *ref_0 x-speakeasy-group: webhooks x-codeSamples: @@ -6782,12 +6776,6 @@ paths: application/json: schema: type: object - '201': - description: '' - content: - application/json: - schema: - type: object tags: &ref_21 - passthrough x-speakeasy-group: passthrough @@ -8125,6 +8113,12 @@ paths: label: createHrisEmployee source: |- import { Panora } from "@panora/sdk"; + import { + UnifiedHrisEmployeeInputEmploymentStatus, + UnifiedHrisEmployeeInputEthnicity, + UnifiedHrisEmployeeInputGender, + UnifiedHrisEmployeeInputMaritalStatus, + } from "@panora/sdk/models/components"; const panora = new Panora({ apiKey: "", @@ -8133,7 +8127,40 @@ paths: async function run() { const result = await panora.hris.employees.create({ xConnectionToken: "", - unifiedHrisEmployeeInput: {}, + unifiedHrisEmployeeInput: { + groups: [ + "Group1", + "Group2", + ], + locations: [ + "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + ], + employeeNumber: "EMP001", + companyId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + firstName: "John", + lastName: "Doe", + preferredName: "Johnny", + displayFullName: "John Doe", + username: "johndoe", + workEmail: "john.doe@company.com", + personalEmail: "john.doe@personal.com", + mobilePhoneNumber: "+1234567890", + employments: [ + "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + ], + ssn: "123-45-6789", + gender: UnifiedHrisEmployeeInputGender.Male, + ethnicity: UnifiedHrisEmployeeInputEthnicity.AmericanIndianOrAlaskaNative, + maritalStatus: UnifiedHrisEmployeeInputMaritalStatus.MarriedFilingSeparately, + dateOfBirth: new Date("1990-01-01"), + startDate: new Date("2020-01-01"), + employmentStatus: UnifiedHrisEmployeeInputEmploymentStatus.Active, + terminationDate: new Date("2025-01-01"), + avatarUrl: "https://example.com/avatar.jpg", + managerId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + fieldMappings: {}, + }, }); // Handle the result @@ -9695,6 +9722,11 @@ paths: label: createHrisTimeoff source: |- import { Panora } from "@panora/sdk"; + import { + UnifiedHrisTimeoffInputRequestType, + UnifiedHrisTimeoffInputStatus, + UnifiedHrisTimeoffInputUnits, + } from "@panora/sdk/models/components"; const panora = new Panora({ apiKey: "", @@ -9703,7 +9735,18 @@ paths: async function run() { const result = await panora.hris.timeoffs.create({ xConnectionToken: "", - unifiedHrisTimeoffInput: {}, + unifiedHrisTimeoffInput: { + employee: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + approver: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + status: UnifiedHrisTimeoffInputStatus.Requested, + employeeNote: "Annual vacation", + units: UnifiedHrisTimeoffInputUnits.Days, + amount: 5, + requestType: UnifiedHrisTimeoffInputRequestType.Vacation, + startTime: new Date("2024-07-01T09:00:00Z"), + endTime: new Date("2024-07-05T17:00:00Z"), + fieldMappings: {}, + }, }); // Handle the result @@ -10071,6 +10114,204 @@ paths: - lang: ruby label: retrieveHrisTimeoffbalance source: "require 'panora'\n\n\ns = ::OpenApiSDK::Panora.new\ns.config_security(\n ::OpenApiSDK::Shared::Security.new(\n api_key: \"\",\n )\n)\n\n \nres = s.hris_timeoffbalances.retrieve(x_connection_token=\"\", id=\"801f9ede-c698-4e66-a7fc-48d19eebaa4f\", remote_data=false)\n\nif ! res.unified_hris_timeoffbalance_output.nil?\n # handle response\nend" + /hris/timesheetentries: + get: + operationId: listHrisTimesheetentries + summary: List Timesheetentries + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + example: true + description: Set to true to include data from the original software. + schema: + type: boolean + - name: limit + required: false + in: query + example: 10 + description: Set to get the number of records. + schema: + default: 50 + type: number + - name: cursor + required: false + in: query + example: 1b8b05bb-5273-4012-b520-8657b0b90874 + description: Set to get the number of records after this cursor. + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/PaginatedDto' + - properties: + data: + type: array + items: + $ref: '#/components/schemas/UnifiedHrisTimesheetEntryOutput' + tags: &ref_36 + - hris/timesheetentries + x-speakeasy-group: hris.timesheetentries + x-speakeasy-pagination: + type: cursor + inputs: + - name: cursor + in: parameters + type: cursor + outputs: + nextCursor: $.next_cursor + x-codeSamples: + - lang: typescript + label: listHrisTimesheetentries + source: |- + import { Panora } from "@panora/sdk"; + + const panora = new Panora({ + apiKey: "", + }); + + async function run() { + const result = await panora.hris.timesheetentries.list({ + xConnectionToken: "", + remoteData: true, + limit: 10, + cursor: "1b8b05bb-5273-4012-b520-8657b0b90874", + }); + + for await (const page of result) { + // handle page + } + } + + run(); + post: + operationId: createHrisTimesheetentry + summary: Create Timesheetentrys + description: Create Timesheetentrys in any supported Hris software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: remote_data + required: false + in: query + description: Set to true to include data from the original Hris software. + schema: + type: boolean + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedHrisTimesheetEntryInput' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedHrisTimesheetEntryOutput' + tags: *ref_36 + x-speakeasy-group: hris.timesheetentries + x-codeSamples: + - lang: typescript + label: createHrisTimesheetentry + source: |- + import { Panora } from "@panora/sdk"; + + const panora = new Panora({ + apiKey: "", + }); + + async function run() { + const result = await panora.hris.timesheetentries.create({ + xConnectionToken: "", + unifiedHrisTimesheetEntryInput: { + hoursWorked: 40, + startTime: new Date("2024-10-01T08:00:00Z"), + endTime: new Date("2024-10-01T16:00:00Z"), + employeeId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + remoteWasDeleted: false, + fieldMappings: {}, + }, + }); + + // Handle the result + console.log(result) + } + + run(); + /hris/timesheetentries/{id}: + get: + operationId: retrieveHrisTimesheetentry + summary: Retrieve Timesheetentry + description: Retrieve an Timesheetentry from any connected Hris software + parameters: + - name: x-connection-token + required: true + in: header + description: The connection token + schema: + type: string + - name: id + required: true + in: path + description: id of the timesheetentry you want to retrieve. + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + schema: + type: string + - name: remote_data + required: false + in: query + description: Set to true to include data from the original Hris software. + example: false + schema: + type: boolean + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/UnifiedHrisTimesheetEntryOutput' + tags: *ref_36 + x-speakeasy-group: hris.timesheetentries + x-codeSamples: + - lang: typescript + label: retrieveHrisTimesheetentry + source: |- + import { Panora } from "@panora/sdk"; + + const panora = new Panora({ + apiKey: "", + }); + + async function run() { + const result = await panora.hris.timesheetentries.retrieve({ + xConnectionToken: "", + id: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + remoteData: false, + }); + + // Handle the result + console.log(result) + } + + run(); /marketingautomation/actions: get: operationId: listMarketingautomationAction @@ -10118,7 +10359,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedMarketingautomationActionOutput - tags: &ref_36 + tags: &ref_37 - marketingautomation/actions x-speakeasy-group: marketingautomation.actions x-speakeasy-pagination: @@ -10210,7 +10451,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationActionOutput' - tags: *ref_36 + tags: *ref_37 x-speakeasy-group: marketingautomation.actions x-codeSamples: - lang: typescript @@ -10316,7 +10557,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationActionOutput' - tags: *ref_36 + tags: *ref_37 x-speakeasy-group: marketingautomation.actions x-codeSamples: - lang: typescript @@ -10434,7 +10675,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedMarketingautomationAutomationOutput - tags: &ref_37 + tags: &ref_38 - marketingautomation/automations x-speakeasy-group: marketingautomation.automations x-speakeasy-pagination: @@ -10527,7 +10768,7 @@ paths: schema: $ref: >- #/components/schemas/UnifiedMarketingautomationAutomationOutput - tags: *ref_37 + tags: *ref_38 x-speakeasy-group: marketingautomation.automations x-codeSamples: - lang: typescript @@ -10634,7 +10875,7 @@ paths: schema: $ref: >- #/components/schemas/UnifiedMarketingautomationAutomationOutput - tags: *ref_37 + tags: *ref_38 x-speakeasy-group: marketingautomation.automations x-codeSamples: - lang: typescript @@ -10752,7 +10993,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedMarketingautomationCampaignOutput - tags: &ref_38 + tags: &ref_39 - marketingautomation/campaigns x-speakeasy-group: marketingautomation.campaigns x-speakeasy-pagination: @@ -10844,7 +11085,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationCampaignOutput' - tags: *ref_38 + tags: *ref_39 x-speakeasy-group: marketingautomation.campaigns x-codeSamples: - lang: typescript @@ -10950,7 +11191,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationCampaignOutput' - tags: *ref_38 + tags: *ref_39 x-speakeasy-group: marketingautomation.campaigns x-codeSamples: - lang: typescript @@ -11068,7 +11309,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedMarketingautomationContactOutput - tags: &ref_39 + tags: &ref_40 - marketingautomation/contacts x-speakeasy-group: marketingautomation.contacts x-speakeasy-pagination: @@ -11160,7 +11401,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationContactOutput' - tags: *ref_39 + tags: *ref_40 x-speakeasy-group: marketingautomation.contacts x-codeSamples: - lang: typescript @@ -11266,7 +11507,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationContactOutput' - tags: *ref_39 + tags: *ref_40 x-speakeasy-group: marketingautomation.contacts x-codeSamples: - lang: typescript @@ -11384,7 +11625,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedMarketingautomationEmailOutput - tags: &ref_40 + tags: &ref_41 - marketingautomation/emails x-speakeasy-group: marketingautomation.emails x-speakeasy-pagination: @@ -11478,7 +11719,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationEmailOutput' - tags: *ref_40 + tags: *ref_41 x-speakeasy-group: marketingautomation.emails x-codeSamples: - lang: typescript @@ -11596,7 +11837,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedMarketingautomationEventOutput - tags: &ref_41 + tags: &ref_42 - marketingautomation/events x-speakeasy-group: marketingautomation.events x-speakeasy-pagination: @@ -11690,7 +11931,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationEventOutput' - tags: *ref_41 + tags: *ref_42 x-speakeasy-group: marketingautomation.events x-codeSamples: - lang: typescript @@ -11808,7 +12049,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedMarketingautomationListOutput - tags: &ref_42 + tags: &ref_43 - marketingautomation/lists x-speakeasy-group: marketingautomation.lists x-speakeasy-pagination: @@ -11899,7 +12140,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationListOutput' - tags: *ref_42 + tags: *ref_43 x-speakeasy-group: marketingautomation.lists x-codeSamples: - lang: typescript @@ -12002,7 +12243,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationListOutput' - tags: *ref_42 + tags: *ref_43 x-speakeasy-group: marketingautomation.lists x-codeSamples: - lang: typescript @@ -12120,7 +12361,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedMarketingautomationMessageOutput - tags: &ref_43 + tags: &ref_44 - marketingautomation/messages x-speakeasy-group: marketingautomation.messages x-speakeasy-pagination: @@ -12214,7 +12455,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationMessageOutput' - tags: *ref_43 + tags: *ref_44 x-speakeasy-group: marketingautomation.messages x-codeSamples: - lang: typescript @@ -12332,7 +12573,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedMarketingautomationTemplateOutput - tags: &ref_44 + tags: &ref_45 - marketingautomation/templates x-speakeasy-group: marketingautomation.templates x-speakeasy-pagination: @@ -12423,7 +12664,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationTemplateOutput' - tags: *ref_44 + tags: *ref_45 x-speakeasy-group: marketingautomation.templates x-codeSamples: - lang: typescript @@ -12526,7 +12767,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationTemplateOutput' - tags: *ref_44 + tags: *ref_45 x-speakeasy-group: marketingautomation.templates x-codeSamples: - lang: typescript @@ -12644,7 +12885,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedMarketingautomationUserOutput - tags: &ref_45 + tags: &ref_46 - marketingautomation/users x-speakeasy-group: marketingautomation.users x-speakeasy-pagination: @@ -12738,7 +12979,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedMarketingautomationUserOutput' - tags: *ref_45 + tags: *ref_46 x-speakeasy-group: marketingautomation.users x-codeSamples: - lang: typescript @@ -12855,7 +13096,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsActivityOutput' - tags: &ref_46 + tags: &ref_47 - ats/activities x-speakeasy-group: ats.activities x-speakeasy-pagination: @@ -12946,7 +13187,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsActivityOutput' - tags: *ref_46 + tags: *ref_47 x-speakeasy-group: ats.activities x-codeSamples: - lang: typescript @@ -13088,7 +13329,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsActivityOutput' - tags: *ref_46 + tags: *ref_47 x-speakeasy-group: ats.activities x-codeSamples: - lang: typescript @@ -13205,7 +13446,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsApplicationOutput' - tags: &ref_47 + tags: &ref_48 - ats/applications x-speakeasy-group: ats.applications x-speakeasy-pagination: @@ -13296,7 +13537,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsApplicationOutput' - tags: *ref_47 + tags: *ref_48 x-speakeasy-group: ats.applications x-codeSamples: - lang: typescript @@ -13454,7 +13695,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsApplicationOutput' - tags: *ref_47 + tags: *ref_48 x-speakeasy-group: ats.applications x-codeSamples: - lang: typescript @@ -13571,7 +13812,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsAttachmentOutput' - tags: &ref_48 + tags: &ref_49 - ats/attachments x-speakeasy-group: ats.attachments x-speakeasy-pagination: @@ -13662,7 +13903,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsAttachmentOutput' - tags: *ref_48 + tags: *ref_49 x-speakeasy-group: ats.attachments x-codeSamples: - lang: typescript @@ -13804,7 +14045,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsAttachmentOutput' - tags: *ref_48 + tags: *ref_49 x-speakeasy-group: ats.attachments x-codeSamples: - lang: typescript @@ -13921,7 +14162,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsCandidateOutput' - tags: &ref_49 + tags: &ref_50 - ats/candidates x-speakeasy-group: ats.candidates x-speakeasy-pagination: @@ -14012,7 +14253,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsCandidateOutput' - tags: *ref_49 + tags: *ref_50 x-speakeasy-group: ats.candidates x-codeSamples: - lang: typescript @@ -14258,7 +14499,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsCandidateOutput' - tags: *ref_49 + tags: *ref_50 x-speakeasy-group: ats.candidates x-codeSamples: - lang: typescript @@ -14375,7 +14616,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsDepartmentOutput' - tags: &ref_50 + tags: &ref_51 - ats/departments x-speakeasy-group: ats.departments x-speakeasy-pagination: @@ -14468,7 +14709,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsDepartmentOutput' - tags: *ref_50 + tags: *ref_51 x-speakeasy-group: ats.departments x-codeSamples: - lang: typescript @@ -14585,7 +14826,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsInterviewOutput' - tags: &ref_51 + tags: &ref_52 - ats/interviews x-speakeasy-group: ats.interviews x-speakeasy-pagination: @@ -14676,7 +14917,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsInterviewOutput' - tags: *ref_51 + tags: *ref_52 x-speakeasy-group: ats.interviews x-codeSamples: - lang: typescript @@ -14836,7 +15077,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsInterviewOutput' - tags: *ref_51 + tags: *ref_52 x-speakeasy-group: ats.interviews x-codeSamples: - lang: typescript @@ -14954,7 +15195,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAtsJobinterviewstageOutput - tags: &ref_52 + tags: &ref_53 - ats/jobinterviewstages x-speakeasy-group: ats.jobinterviewstages x-speakeasy-pagination: @@ -15047,7 +15288,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsJobinterviewstageOutput' - tags: *ref_52 + tags: *ref_53 x-speakeasy-group: ats.jobinterviewstages x-codeSamples: - lang: typescript @@ -15164,7 +15405,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsJobOutput' - tags: &ref_53 + tags: &ref_54 - ats/jobs x-speakeasy-group: ats.jobs x-speakeasy-pagination: @@ -15257,7 +15498,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsJobOutput' - tags: *ref_53 + tags: *ref_54 x-speakeasy-group: ats.jobs x-codeSamples: - lang: typescript @@ -15374,7 +15615,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsOfferOutput' - tags: &ref_54 + tags: &ref_55 - ats/offers x-speakeasy-group: ats.offers x-speakeasy-pagination: @@ -15467,7 +15708,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsOfferOutput' - tags: *ref_54 + tags: *ref_55 x-speakeasy-group: ats.offers x-codeSamples: - lang: typescript @@ -15584,7 +15825,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsOfficeOutput' - tags: &ref_55 + tags: &ref_56 - ats/offices x-speakeasy-group: ats.offices x-speakeasy-pagination: @@ -15677,7 +15918,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsOfficeOutput' - tags: *ref_55 + tags: *ref_56 x-speakeasy-group: ats.offices x-codeSamples: - lang: typescript @@ -15794,7 +16035,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsRejectreasonOutput' - tags: &ref_56 + tags: &ref_57 - ats/rejectreasons x-speakeasy-group: ats.rejectreasons x-speakeasy-pagination: @@ -15887,7 +16128,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsRejectreasonOutput' - tags: *ref_56 + tags: *ref_57 x-speakeasy-group: ats.rejectreasons x-codeSamples: - lang: typescript @@ -16004,7 +16245,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsScorecardOutput' - tags: &ref_57 + tags: &ref_58 - ats/scorecards x-speakeasy-group: ats.scorecards x-speakeasy-pagination: @@ -16097,7 +16338,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsScorecardOutput' - tags: *ref_57 + tags: *ref_58 x-speakeasy-group: ats.scorecards x-codeSamples: - lang: typescript @@ -16214,7 +16455,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsTagOutput' - tags: &ref_58 + tags: &ref_59 - ats/tags x-speakeasy-group: ats.tags x-speakeasy-pagination: @@ -16307,7 +16548,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsTagOutput' - tags: *ref_58 + tags: *ref_59 x-speakeasy-group: ats.tags x-codeSamples: - lang: typescript @@ -16424,7 +16665,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsUserOutput' - tags: &ref_59 + tags: &ref_60 - ats/users x-speakeasy-group: ats.users x-speakeasy-pagination: @@ -16517,7 +16758,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsUserOutput' - tags: *ref_59 + tags: *ref_60 x-speakeasy-group: ats.users x-codeSamples: - lang: typescript @@ -16634,7 +16875,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAtsEeocsOutput' - tags: &ref_60 + tags: &ref_61 - ats/eeocs x-speakeasy-group: ats.eeocs x-speakeasy-pagination: @@ -16725,7 +16966,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAtsEeocsOutput' - tags: *ref_60 + tags: *ref_61 x-speakeasy-group: ats.eeocs x-codeSamples: - lang: typescript @@ -16839,7 +17080,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAccountingAccountOutput' - tags: &ref_61 + tags: &ref_62 - accounting/accounts x-speakeasy-group: accounting.accounts x-speakeasy-pagination: @@ -16930,13 +17171,14 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingAccountOutput' - tags: *ref_61 + tags: *ref_62 x-speakeasy-group: accounting.accounts x-codeSamples: - lang: typescript label: createAccountingAccount source: |- import { Panora } from "@panora/sdk"; + import { UnifiedAccountingAccountInputCurrency } from "@panora/sdk/models/components"; const panora = new Panora({ apiKey: "", @@ -16946,7 +17188,19 @@ paths: const result = await panora.accounting.accounts.create({ xConnectionToken: "", remoteData: false, - unifiedAccountingAccountInput: {}, + unifiedAccountingAccountInput: { + name: "Cash", + description: "Main cash account for daily operations", + classification: "Asset", + type: "Current Asset", + status: "Active", + currentBalance: 10000, + currency: UnifiedAccountingAccountInputCurrency.Usd, + accountNumber: "1000", + parentAccount: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + companyInfoId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + fieldMappings: {}, + }, }); // Handle the result @@ -17035,7 +17289,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingAccountOutput' - tags: *ref_61 + tags: *ref_62 x-speakeasy-group: accounting.accounts x-codeSamples: - lang: typescript @@ -17152,7 +17406,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAccountingAddressOutput' - tags: &ref_62 + tags: &ref_63 - accounting/addresses x-speakeasy-group: accounting.addresses x-speakeasy-pagination: @@ -17245,7 +17499,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingAddressOutput' - tags: *ref_62 + tags: *ref_63 x-speakeasy-group: accounting.addresses x-codeSamples: - lang: typescript @@ -17363,7 +17617,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingAttachmentOutput - tags: &ref_63 + tags: &ref_64 - accounting/attachments x-speakeasy-group: accounting.attachments x-speakeasy-pagination: @@ -17454,7 +17708,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingAttachmentOutput' - tags: *ref_63 + tags: *ref_64 x-speakeasy-group: accounting.attachments x-codeSamples: - lang: typescript @@ -17470,7 +17724,12 @@ paths: const result = await panora.accounting.attachments.create({ xConnectionToken: "", remoteData: false, - unifiedAccountingAttachmentInput: {}, + unifiedAccountingAttachmentInput: { + fileName: "invoice.pdf", + fileUrl: "https://example.com/files/invoice.pdf", + accountId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + fieldMappings: {}, + }, }); // Handle the result @@ -17559,7 +17818,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingAttachmentOutput' - tags: *ref_63 + tags: *ref_64 x-speakeasy-group: accounting.attachments x-codeSamples: - lang: typescript @@ -17677,7 +17936,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingBalancesheetOutput - tags: &ref_64 + tags: &ref_65 - accounting/balancesheets x-speakeasy-group: accounting.balancesheets x-speakeasy-pagination: @@ -17770,7 +18029,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingBalancesheetOutput' - tags: *ref_64 + tags: *ref_65 x-speakeasy-group: accounting.balancesheets x-codeSamples: - lang: typescript @@ -17888,7 +18147,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingCashflowstatementOutput - tags: &ref_65 + tags: &ref_66 - accounting/cashflowstatements x-speakeasy-group: accounting.cashflowstatements x-speakeasy-pagination: @@ -17981,7 +18240,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingCashflowstatementOutput' - tags: *ref_65 + tags: *ref_66 x-speakeasy-group: accounting.cashflowstatements x-codeSamples: - lang: typescript @@ -18099,7 +18358,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingCompanyinfoOutput - tags: &ref_66 + tags: &ref_67 - accounting/companyinfos x-speakeasy-group: accounting.companyinfos x-speakeasy-pagination: @@ -18192,7 +18451,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingCompanyinfoOutput' - tags: *ref_66 + tags: *ref_67 x-speakeasy-group: accounting.companyinfos x-codeSamples: - lang: typescript @@ -18309,7 +18568,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAccountingContactOutput' - tags: &ref_67 + tags: &ref_68 - accounting/contacts x-speakeasy-group: accounting.contacts x-speakeasy-pagination: @@ -18400,13 +18659,14 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingContactOutput' - tags: *ref_67 + tags: *ref_68 x-speakeasy-group: accounting.contacts x-codeSamples: - lang: typescript label: createAccountingContact source: |- import { Panora } from "@panora/sdk"; + import { UnifiedAccountingContactInputCurrency } from "@panora/sdk/models/components"; const panora = new Panora({ apiKey: "", @@ -18416,7 +18676,18 @@ paths: const result = await panora.accounting.contacts.create({ xConnectionToken: "", remoteData: false, - unifiedAccountingContactInput: {}, + unifiedAccountingContactInput: { + name: "John Doe", + isSupplier: true, + isCustomer: false, + emailAddress: "john.doe@example.com", + taxNumber: "123456789", + status: "Active", + currency: UnifiedAccountingContactInputCurrency.Usd, + remoteUpdatedAt: "2024-06-15T12:00:00Z", + companyInfoId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + fieldMappings: {}, + }, }); // Handle the result @@ -18505,7 +18776,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingContactOutput' - tags: *ref_67 + tags: *ref_68 x-speakeasy-group: accounting.contacts x-codeSamples: - lang: typescript @@ -18623,7 +18894,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingCreditnoteOutput - tags: &ref_68 + tags: &ref_69 - accounting/creditnotes x-speakeasy-group: accounting.creditnotes x-speakeasy-pagination: @@ -18716,7 +18987,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingCreditnoteOutput' - tags: *ref_68 + tags: *ref_69 x-speakeasy-group: accounting.creditnotes x-codeSamples: - lang: typescript @@ -18833,7 +19104,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAccountingExpenseOutput' - tags: &ref_69 + tags: &ref_70 - accounting/expenses x-speakeasy-group: accounting.expenses x-speakeasy-pagination: @@ -18924,13 +19195,14 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingExpenseOutput' - tags: *ref_69 + tags: *ref_70 x-speakeasy-group: accounting.expenses x-codeSamples: - lang: typescript label: createAccountingExpense source: |- import { Panora } from "@panora/sdk"; + import { UnifiedAccountingExpenseInputCurrency } from "@panora/sdk/models/components"; const panora = new Panora({ apiKey: "", @@ -18940,7 +19212,35 @@ paths: const result = await panora.accounting.expenses.create({ xConnectionToken: "", remoteData: false, - unifiedAccountingExpenseInput: {}, + unifiedAccountingExpenseInput: { + transactionDate: new Date("2024-06-15T12:00:00Z"), + totalAmount: 10000, + subTotal: 9000, + totalTaxAmount: 1000, + currency: UnifiedAccountingExpenseInputCurrency.Usd, + exchangeRate: "1.2", + memo: "Business lunch with client", + accountId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + contactId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + companyInfoId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + trackingCategories: [ + "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + ], + lineItems: [ + { + name: "Net Income", + value: 100000, + type: "Operating Activities", + parentItem: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + remoteId: "report_item_1234", + remoteGeneratedAt: new Date("2024-07-01T12:00:00Z"), + companyInfoId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + createdAt: new Date("2024-06-15T12:00:00Z"), + modifiedAt: new Date("2024-06-15T12:00:00Z"), + }, + ], + fieldMappings: {}, + }, }); // Handle the result @@ -19029,7 +19329,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingExpenseOutput' - tags: *ref_69 + tags: *ref_70 x-speakeasy-group: accounting.expenses x-codeSamples: - lang: typescript @@ -19147,7 +19447,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingIncomestatementOutput - tags: &ref_70 + tags: &ref_71 - accounting/incomestatements x-speakeasy-group: accounting.incomestatements x-speakeasy-pagination: @@ -19240,7 +19540,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingIncomestatementOutput' - tags: *ref_70 + tags: *ref_71 x-speakeasy-group: accounting.incomestatements x-codeSamples: - lang: typescript @@ -19357,7 +19657,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAccountingInvoiceOutput' - tags: &ref_71 + tags: &ref_72 - accounting/invoices x-speakeasy-group: accounting.invoices x-speakeasy-pagination: @@ -19448,13 +19748,14 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingInvoiceOutput' - tags: *ref_71 + tags: *ref_72 x-speakeasy-group: accounting.invoices x-codeSamples: - lang: typescript label: createAccountingInvoice source: |- import { Panora } from "@panora/sdk"; + import { UnifiedAccountingInvoiceInputCurrency } from "@panora/sdk/models/components"; const panora = new Panora({ apiKey: "", @@ -19464,7 +19765,42 @@ paths: const result = await panora.accounting.invoices.create({ xConnectionToken: "", remoteData: false, - unifiedAccountingInvoiceInput: {}, + unifiedAccountingInvoiceInput: { + type: "Sales", + number: "INV-001", + issueDate: new Date("2024-06-15T12:00:00Z"), + dueDate: new Date("2024-07-15T12:00:00Z"), + paidOnDate: new Date("2024-07-10T12:00:00Z"), + memo: "Payment for services rendered", + currency: UnifiedAccountingInvoiceInputCurrency.Usd, + exchangeRate: "1.2", + totalDiscount: 1000, + subTotal: 10000, + status: "Paid", + totalTaxAmount: 1000, + totalAmount: 11000, + balance: 0, + contactId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + accountingPeriodId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + trackingCategories: [ + "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + ], + lineItems: [ + { + name: "Net Income", + value: 100000, + type: "Operating Activities", + parentItem: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + remoteId: "report_item_1234", + remoteGeneratedAt: new Date("2024-07-01T12:00:00Z"), + companyInfoId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + createdAt: new Date("2024-06-15T12:00:00Z"), + modifiedAt: new Date("2024-06-15T12:00:00Z"), + }, + ], + fieldMappings: {}, + }, }); // Handle the result @@ -19553,7 +19889,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingInvoiceOutput' - tags: *ref_71 + tags: *ref_72 x-speakeasy-group: accounting.invoices x-codeSamples: - lang: typescript @@ -19670,7 +20006,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAccountingItemOutput' - tags: &ref_72 + tags: &ref_73 - accounting/items x-speakeasy-group: accounting.items x-speakeasy-pagination: @@ -19763,7 +20099,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingItemOutput' - tags: *ref_72 + tags: *ref_73 x-speakeasy-group: accounting.items x-codeSamples: - lang: typescript @@ -19881,7 +20217,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingJournalentryOutput - tags: &ref_73 + tags: &ref_74 - accounting/journalentries x-speakeasy-group: accounting.journalentries x-speakeasy-pagination: @@ -19972,13 +20308,14 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingJournalentryOutput' - tags: *ref_73 + tags: *ref_74 x-speakeasy-group: accounting.journalentries x-codeSamples: - lang: typescript label: createAccountingJournalEntry source: |- import { Panora } from "@panora/sdk"; + import { UnifiedAccountingJournalentryInputCurrency } from "@panora/sdk/models/components"; const panora = new Panora({ apiKey: "", @@ -19988,7 +20325,41 @@ paths: const result = await panora.accounting.journalentries.create({ xConnectionToken: "", remoteData: false, - unifiedAccountingJournalentryInput: {}, + unifiedAccountingJournalentryInput: { + transactionDate: new Date("2024-06-15T12:00:00Z"), + payments: [ + "payment1", + "payment2", + ], + appliedPayments: [ + "appliedPayment1", + "appliedPayment2", + ], + memo: "Monthly expense journal entry", + currency: UnifiedAccountingJournalentryInputCurrency.Usd, + exchangeRate: "1.2", + idAccCompanyInfo: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + journalNumber: "JE-001", + trackingCategories: [ + "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + ], + idAccAccountingPeriod: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + postingStatus: "Posted", + lineItems: [ + { + name: "Net Income", + value: 100000, + type: "Operating Activities", + parentItem: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + remoteId: "report_item_1234", + remoteGeneratedAt: new Date("2024-07-01T12:00:00Z"), + companyInfoId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + createdAt: new Date("2024-06-15T12:00:00Z"), + modifiedAt: new Date("2024-06-15T12:00:00Z"), + }, + ], + fieldMappings: {}, + }, }); // Handle the result @@ -20077,7 +20448,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingJournalentryOutput' - tags: *ref_73 + tags: *ref_74 x-speakeasy-group: accounting.journalentries x-codeSamples: - lang: typescript @@ -20194,7 +20565,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAccountingPaymentOutput' - tags: &ref_74 + tags: &ref_75 - accounting/payments x-speakeasy-group: accounting.payments x-speakeasy-pagination: @@ -20285,13 +20656,14 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingPaymentOutput' - tags: *ref_74 + tags: *ref_75 x-speakeasy-group: accounting.payments x-codeSamples: - lang: typescript label: createAccountingPayment source: |- import { Panora } from "@panora/sdk"; + import { UnifiedAccountingPaymentInputCurrency } from "@panora/sdk/models/components"; const panora = new Panora({ apiKey: "", @@ -20301,7 +20673,35 @@ paths: const result = await panora.accounting.payments.create({ xConnectionToken: "", remoteData: false, - unifiedAccountingPaymentInput: {}, + unifiedAccountingPaymentInput: { + invoiceId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + transactionDate: new Date("2024-06-15T12:00:00Z"), + contactId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + accountId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + currency: UnifiedAccountingPaymentInputCurrency.Usd, + exchangeRate: "1.2", + totalAmount: 10000, + type: "Credit Card", + companyInfoId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + accountingPeriodId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + trackingCategories: [ + "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + ], + lineItems: [ + { + name: "Net Income", + value: 100000, + type: "Operating Activities", + parentItem: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + remoteId: "report_item_1234", + remoteGeneratedAt: new Date("2024-07-01T12:00:00Z"), + companyInfoId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + createdAt: new Date("2024-06-15T12:00:00Z"), + modifiedAt: new Date("2024-06-15T12:00:00Z"), + }, + ], + fieldMappings: {}, + }, }); // Handle the result @@ -20390,7 +20790,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingPaymentOutput' - tags: *ref_74 + tags: *ref_75 x-speakeasy-group: accounting.payments x-codeSamples: - lang: typescript @@ -20508,7 +20908,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingPhonenumberOutput - tags: &ref_75 + tags: &ref_76 - accounting/phonenumbers x-speakeasy-group: accounting.phonenumbers x-speakeasy-pagination: @@ -20601,7 +21001,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingPhonenumberOutput' - tags: *ref_75 + tags: *ref_76 x-speakeasy-group: accounting.phonenumbers x-codeSamples: - lang: typescript @@ -20719,7 +21119,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingPurchaseorderOutput - tags: &ref_76 + tags: &ref_77 - accounting/purchaseorders x-speakeasy-group: accounting.purchaseorders x-speakeasy-pagination: @@ -20810,13 +21210,14 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingPurchaseorderOutput' - tags: *ref_76 + tags: *ref_77 x-speakeasy-group: accounting.purchaseorders x-codeSamples: - lang: typescript label: createAccountingPurchaseOrder source: |- import { Panora } from "@panora/sdk"; + import { UnifiedAccountingPurchaseorderInputCurrency } from "@panora/sdk/models/components"; const panora = new Panora({ apiKey: "", @@ -20826,7 +21227,38 @@ paths: const result = await panora.accounting.purchaseorders.create({ xConnectionToken: "", remoteData: false, - unifiedAccountingPurchaseorderInput: {}, + unifiedAccountingPurchaseorderInput: { + status: "Pending", + issueDate: new Date("2024-06-15T12:00:00Z"), + purchaseOrderNumber: "PO-001", + deliveryDate: new Date("2024-07-15T12:00:00Z"), + deliveryAddress: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + customer: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + vendor: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + memo: "Purchase order for Q3 inventory", + companyId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + totalAmount: 100000, + currency: UnifiedAccountingPurchaseorderInputCurrency.Usd, + exchangeRate: "1.2", + trackingCategories: [ + "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + ], + accountingPeriodId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + lineItems: [ + { + name: "Net Income", + value: 100000, + type: "Operating Activities", + parentItem: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + remoteId: "report_item_1234", + remoteGeneratedAt: new Date("2024-07-01T12:00:00Z"), + companyInfoId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + createdAt: new Date("2024-06-15T12:00:00Z"), + modifiedAt: new Date("2024-06-15T12:00:00Z"), + }, + ], + fieldMappings: {}, + }, }); // Handle the result @@ -20915,7 +21347,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingPurchaseorderOutput' - tags: *ref_76 + tags: *ref_77 x-speakeasy-group: accounting.purchaseorders x-codeSamples: - lang: typescript @@ -21032,7 +21464,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedAccountingTaxrateOutput' - tags: &ref_77 + tags: &ref_78 - accounting/taxrates x-speakeasy-group: accounting.taxrates x-speakeasy-pagination: @@ -21125,7 +21557,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingTaxrateOutput' - tags: *ref_77 + tags: *ref_78 x-speakeasy-group: accounting.taxrates x-codeSamples: - lang: typescript @@ -21243,7 +21675,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingTrackingcategoryOutput - tags: &ref_78 + tags: &ref_79 - accounting/trackingcategories x-speakeasy-group: accounting.trackingcategories x-speakeasy-pagination: @@ -21336,7 +21768,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingTrackingcategoryOutput' - tags: *ref_78 + tags: *ref_79 x-speakeasy-group: accounting.trackingcategories x-codeSamples: - lang: typescript @@ -21454,7 +21886,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingTransactionOutput - tags: &ref_79 + tags: &ref_80 - accounting/transactions x-speakeasy-group: accounting.transactions x-speakeasy-pagination: @@ -21547,7 +21979,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingTransactionOutput' - tags: *ref_79 + tags: *ref_80 x-speakeasy-group: accounting.transactions x-codeSamples: - lang: typescript @@ -21665,7 +22097,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedAccountingVendorcreditOutput - tags: &ref_80 + tags: &ref_81 - accounting/vendorcredits x-speakeasy-group: accounting.vendorcredits x-speakeasy-pagination: @@ -21758,7 +22190,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedAccountingVendorcreditOutput' - tags: *ref_80 + tags: *ref_81 x-speakeasy-group: accounting.vendorcredits x-codeSamples: - lang: typescript @@ -21875,7 +22307,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedFilestorageDriveOutput' - tags: &ref_81 + tags: &ref_82 - filestorage/drives x-speakeasy-group: filestorage.drives x-speakeasy-pagination: @@ -21968,7 +22400,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedFilestorageDriveOutput' - tags: *ref_81 + tags: *ref_82 x-speakeasy-group: filestorage.drives x-codeSamples: - lang: typescript @@ -22085,7 +22517,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedFilestorageFileOutput' - tags: &ref_82 + tags: &ref_83 - filestorage/files x-speakeasy-group: filestorage.files x-speakeasy-pagination: @@ -22176,7 +22608,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedFilestorageFileOutput' - tags: *ref_82 + tags: *ref_83 x-speakeasy-group: filestorage.files x-codeSamples: - lang: typescript @@ -22317,7 +22749,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedFilestorageFileOutput' - tags: *ref_82 + tags: *ref_83 x-speakeasy-group: filestorage.files x-codeSamples: - lang: typescript @@ -22434,7 +22866,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedFilestorageFolderOutput' - tags: &ref_83 + tags: &ref_84 - filestorage/folders x-speakeasy-group: filestorage.folders x-speakeasy-pagination: @@ -22525,7 +22957,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedFilestorageFolderOutput' - tags: *ref_83 + tags: *ref_84 x-speakeasy-group: filestorage.folders x-codeSamples: - lang: typescript @@ -22669,7 +23101,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedFilestorageFolderOutput' - tags: *ref_83 + tags: *ref_84 x-speakeasy-group: filestorage.folders x-codeSamples: - lang: typescript @@ -22786,7 +23218,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedFilestorageGroupOutput' - tags: &ref_84 + tags: &ref_85 - filestorage/groups x-speakeasy-group: filestorage.groups x-speakeasy-pagination: @@ -22879,7 +23311,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedFilestorageGroupOutput' - tags: *ref_84 + tags: *ref_85 x-speakeasy-group: filestorage.groups x-codeSamples: - lang: typescript @@ -22996,7 +23428,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedFilestorageUserOutput' - tags: &ref_85 + tags: &ref_86 - filestorage/users x-speakeasy-group: filestorage.users x-speakeasy-pagination: @@ -23089,7 +23521,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedFilestorageUserOutput' - tags: *ref_85 + tags: *ref_86 x-speakeasy-group: filestorage.users x-codeSamples: - lang: typescript @@ -23206,7 +23638,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedEcommerceProductOutput' - tags: &ref_86 + tags: &ref_87 - ecommerce/products x-speakeasy-group: ecommerce.products x-speakeasy-pagination: @@ -23297,7 +23729,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedEcommerceProductOutput' - tags: *ref_86 + tags: *ref_87 x-speakeasy-group: ecommerce.products x-codeSamples: - lang: typescript @@ -23417,7 +23849,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedEcommerceProductOutput' - tags: *ref_86 + tags: *ref_87 x-speakeasy-group: ecommerce.products x-codeSamples: - lang: typescript @@ -23531,7 +23963,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedEcommerceOrderOutput' - tags: &ref_87 + tags: &ref_88 - ecommerce/orders x-speakeasy-group: ecommerce.orders x-speakeasy-pagination: @@ -23622,7 +24054,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedEcommerceOrderOutput' - tags: *ref_87 + tags: *ref_88 x-speakeasy-group: ecommerce.orders x-codeSamples: - lang: typescript @@ -23656,7 +24088,17 @@ paths: fulfillmentStatus: UnifiedEcommerceOrderInputFulfillmentStatus.Pending, customerId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", items: [ - {}, + { + name: "Net Income", + value: 100000, + type: "Operating Activities", + parentItem: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + remoteId: "12345", + remoteGeneratedAt: new Date("2024-07-01T12:00:00Z"), + companyInfoId: "801f9ede-c698-4e66-a7fc-48d19eebaa4f", + createdAt: new Date("2024-06-15T12:00:00Z"), + modifiedAt: new Date("2024-06-15T12:00:00Z"), + }, ], fieldMappings: {}, }, @@ -23746,7 +24188,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedEcommerceOrderOutput' - tags: *ref_87 + tags: *ref_88 x-speakeasy-group: ecommerce.orders x-codeSamples: - lang: typescript @@ -23860,7 +24302,7 @@ paths: type: array items: $ref: '#/components/schemas/UnifiedEcommerceCustomerOutput' - tags: &ref_88 + tags: &ref_89 - ecommerce/customers x-speakeasy-group: ecommerce.customers x-speakeasy-pagination: @@ -23951,7 +24393,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedEcommerceCustomerOutput' - tags: *ref_88 + tags: *ref_89 x-speakeasy-group: ecommerce.customers x-codeSamples: - lang: typescript @@ -24066,7 +24508,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedEcommerceFulfillmentOutput - tags: &ref_89 + tags: &ref_90 - ecommerce/fulfillments x-speakeasy-group: ecommerce.fulfillments x-speakeasy-pagination: @@ -24157,7 +24599,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedEcommerceFulfillmentOutput' - tags: *ref_89 + tags: *ref_90 x-speakeasy-group: ecommerce.fulfillments x-codeSamples: - lang: typescript @@ -24272,7 +24714,7 @@ paths: items: $ref: >- #/components/schemas/UnifiedTicketingAttachmentOutput - tags: &ref_90 + tags: &ref_91 - ticketing/attachments x-speakeasy-group: ticketing.attachments x-speakeasy-pagination: @@ -24362,7 +24804,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedTicketingAttachmentOutput' - tags: *ref_90 + tags: *ref_91 x-speakeasy-group: ticketing.attachments x-codeSamples: - lang: typescript @@ -24494,7 +24936,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UnifiedTicketingAttachmentOutput' - tags: *ref_90 + tags: *ref_91 x-speakeasy-group: ticketing.attachments x-codeSamples: - lang: typescript @@ -24817,7 +25259,7 @@ components: type: string nullable: true example: USER - enum: &ref_120 + enum: &ref_121 - USER - CONTACT description: >- @@ -24841,12 +25283,12 @@ components: The UUID of the user which the comment belongs to (if no contact_id specified) attachments: type: array - items: &ref_121 + items: &ref_122 oneOf: - type: string - $ref: '#/components/schemas/UnifiedTicketingAttachmentOutput' nullable: true - example: &ref_122 + example: &ref_123 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f description: The attachements UUIDs tied to the comment required: @@ -24862,7 +25304,7 @@ components: status: type: string example: OPEN - enum: &ref_91 + enum: &ref_92 - OPEN - CLOSED nullable: true @@ -24881,7 +25323,7 @@ components: type: type: string example: BUG - enum: &ref_92 + enum: &ref_93 - BUG - SUBTASK - TASK @@ -24896,21 +25338,21 @@ components: description: The UUID of the parent ticket collections: type: array - items: &ref_93 + items: &ref_94 oneOf: - type: string - $ref: '#/components/schemas/UnifiedTicketingCollectionOutput' - example: &ref_94 + example: &ref_95 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: The collection UUIDs the ticket belongs to tags: type: array - items: &ref_95 + items: &ref_96 oneOf: - type: string - $ref: '#/components/schemas/UnifiedTicketingTagOutput' - example: &ref_96 + example: &ref_97 - my_tag - urgent_tag nullable: true @@ -24924,7 +25366,7 @@ components: priority: type: string example: HIGH - enum: &ref_97 + enum: &ref_98 - HIGH - MEDIUM - LOW @@ -24932,7 +25374,7 @@ components: description: >- The priority of the ticket. Authorized values are HIGH, MEDIUM or LOW. assigned_to: - example: &ref_98 + example: &ref_99 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: The users UUIDs the ticket is assigned to @@ -24940,7 +25382,7 @@ components: items: type: string comment: - example: &ref_99 + example: &ref_100 content: Assigned the issue ! nullable: true description: The comment of the ticket @@ -24958,17 +25400,17 @@ components: description: The UUID of the contact which the ticket belongs to attachments: type: array - items: &ref_100 + items: &ref_101 oneOf: - type: string - $ref: '#/components/schemas/UnifiedTicketingAttachmentInput' - example: &ref_101 + example: &ref_102 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f description: The attachements UUIDs tied to the ticket nullable: true field_mappings: type: object - example: &ref_102 + example: &ref_103 fav_dish: broccoli fav_color: red nullable: true @@ -25020,7 +25462,7 @@ components: status: type: string example: OPEN - enum: *ref_91 + enum: *ref_92 nullable: true description: The status of the ticket. Authorized values are OPEN or CLOSED. description: @@ -25037,7 +25479,7 @@ components: type: type: string example: BUG - enum: *ref_92 + enum: *ref_93 nullable: true description: >- The type of the ticket. Authorized values are PROBLEM, QUESTION, or TASK @@ -25048,14 +25490,14 @@ components: description: The UUID of the parent ticket collections: type: array - items: *ref_93 - example: *ref_94 + items: *ref_94 + example: *ref_95 nullable: true description: The collection UUIDs the ticket belongs to tags: type: array - items: *ref_95 - example: *ref_96 + items: *ref_96 + example: *ref_97 nullable: true description: The tags names of the ticket completed_at: @@ -25067,19 +25509,19 @@ components: priority: type: string example: HIGH - enum: *ref_97 + enum: *ref_98 nullable: true description: >- The priority of the ticket. Authorized values are HIGH, MEDIUM or LOW. assigned_to: - example: *ref_98 + example: *ref_99 nullable: true description: The users UUIDs the ticket is assigned to type: array items: type: string comment: - example: *ref_99 + example: *ref_100 nullable: true description: The comment of the ticket allOf: @@ -25096,13 +25538,13 @@ components: description: The UUID of the contact which the ticket belongs to attachments: type: array - items: *ref_100 - example: *ref_101 + items: *ref_101 + example: *ref_102 description: The attachements UUIDs tied to the ticket nullable: true field_mappings: type: object - example: *ref_102 + example: *ref_103 nullable: true description: >- The custom field mappings of the ticket between the remote 3rd party & Panora @@ -25451,7 +25893,7 @@ components: industry: type: string example: ACCOUNTING - enum: &ref_103 + enum: &ref_104 - ACCOUNTING - AIRLINES_AVIATION - ALTERNATIVE_DISPUTE_RESOLUTION @@ -25614,7 +26056,7 @@ components: nullable: true email_addresses: description: The email addresses of the company - example: &ref_104 + example: &ref_105 - email_address: acme@gmail.com email_address_type: WORK nullable: true @@ -25623,7 +26065,7 @@ components: $ref: '#/components/schemas/Email' addresses: description: The addresses of the company - example: &ref_105 + example: &ref_106 - street_1: 5th Avenue city: New York state: NY @@ -25635,7 +26077,7 @@ components: $ref: '#/components/schemas/Address' phone_numbers: description: The phone numbers of the company - example: &ref_106 + example: &ref_107 - phone_number: '+33660606067' phone_type: WORK nullable: true @@ -25644,7 +26086,7 @@ components: $ref: '#/components/schemas/Phone' field_mappings: type: object - example: &ref_107 + example: &ref_108 fav_dish: broccoli fav_color: red description: >- @@ -25692,7 +26134,7 @@ components: industry: type: string example: ACCOUNTING - enum: *ref_103 + enum: *ref_104 description: >- The industry of the company. Authorized values can be found in the Industry enum. nullable: true @@ -25708,28 +26150,28 @@ components: nullable: true email_addresses: description: The email addresses of the company - example: *ref_104 + example: *ref_105 nullable: true type: array items: $ref: '#/components/schemas/Email' addresses: description: The addresses of the company - example: *ref_105 + example: *ref_106 nullable: true type: array items: $ref: '#/components/schemas/Address' phone_numbers: description: The phone numbers of the company - example: *ref_106 + example: *ref_107 nullable: true type: array items: $ref: '#/components/schemas/Phone' field_mappings: type: object - example: *ref_107 + example: *ref_108 description: >- The custom field mappings of the company between the remote 3rd party & Panora nullable: true @@ -25752,7 +26194,7 @@ components: email_addresses: nullable: true description: The email addresses of the contact - example: &ref_108 + example: &ref_109 - email: john.doe@example.com type: WORK type: array @@ -25761,7 +26203,7 @@ components: phone_numbers: nullable: true description: The phone numbers of the contact - example: &ref_109 + example: &ref_110 - phone: '1234567890' type: WORK type: array @@ -25770,7 +26212,7 @@ components: addresses: nullable: true description: The addresses of the contact - example: &ref_110 + example: &ref_111 - street: 123 Main St city: Anytown state: CA @@ -25787,7 +26229,7 @@ components: example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f field_mappings: type: object - example: &ref_111 + example: &ref_112 fav_dish: broccoli fav_color: red nullable: true @@ -25843,21 +26285,21 @@ components: email_addresses: nullable: true description: The email addresses of the contact - example: *ref_108 + example: *ref_109 type: array items: $ref: '#/components/schemas/Email' phone_numbers: nullable: true description: The phone numbers of the contact - example: *ref_109 + example: *ref_110 type: array items: $ref: '#/components/schemas/Phone' addresses: nullable: true description: The addresses of the contact - example: *ref_110 + example: *ref_111 type: array items: $ref: '#/components/schemas/Address' @@ -25868,7 +26310,7 @@ components: example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f field_mappings: type: object - example: *ref_111 + example: *ref_112 nullable: true description: >- The custom field mappings of the contact between the remote 3rd party & Panora @@ -25912,7 +26354,7 @@ components: field_mappings: type: object nullable: true - example: &ref_112 + example: &ref_113 fav_dish: broccoli fav_color: red description: >- @@ -25988,7 +26430,7 @@ components: field_mappings: type: object nullable: true - example: *ref_112 + example: *ref_113 description: >- The custom field mappings of the company between the remote 3rd party & Panora additionalProperties: true @@ -26008,7 +26450,7 @@ components: type: string nullable: true example: INBOUND - enum: &ref_113 + enum: &ref_114 - INBOUND - OUTBOUND description: >- @@ -26034,7 +26476,7 @@ components: type: string nullable: true example: MEETING - enum: &ref_114 + enum: &ref_115 - EMAIL - CALL - MEETING @@ -26052,7 +26494,7 @@ components: description: The UUID of the company tied to the engagement contacts: nullable: true - example: &ref_115 + example: &ref_116 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f description: The UUIDs of contacts tied to the engagement object type: array @@ -26061,7 +26503,7 @@ components: field_mappings: type: object nullable: true - example: &ref_116 + example: &ref_117 fav_dish: broccoli fav_color: red description: >- @@ -26112,7 +26554,7 @@ components: type: string nullable: true example: INBOUND - enum: *ref_113 + enum: *ref_114 description: >- The direction of the engagement. Authorized values are INBOUND or OUTBOUND subject: @@ -26136,7 +26578,7 @@ components: type: string nullable: true example: MEETING - enum: *ref_114 + enum: *ref_115 description: >- The type of the engagement. Authorized values are EMAIL, CALL or MEETING user_id: @@ -26151,7 +26593,7 @@ components: description: The UUID of the company tied to the engagement contacts: nullable: true - example: *ref_115 + example: *ref_116 description: The UUIDs of contacts tied to the engagement object type: array items: @@ -26159,7 +26601,7 @@ components: field_mappings: type: object nullable: true - example: *ref_116 + example: *ref_117 description: >- The custom field mappings of the engagement between the remote 3rd party & Panora additionalProperties: true @@ -26195,7 +26637,7 @@ components: description: The UUID of the deal tied to the note field_mappings: type: object - example: &ref_117 + example: &ref_118 fav_dish: broccoli fav_color: red nullable: true @@ -26264,7 +26706,7 @@ components: description: The UUID of the deal tied to the note field_mappings: type: object - example: *ref_117 + example: *ref_118 nullable: true description: >- The custom field mappings of the note between the remote 3rd party & Panora @@ -26334,7 +26776,7 @@ components: status: type: string example: PENDING - enum: &ref_118 + enum: &ref_119 - PENDING - COMPLETED description: The status of the task. Authorized values are PENDING, COMPLETED. @@ -26366,7 +26808,7 @@ components: nullable: true field_mappings: type: object - example: &ref_119 + example: &ref_120 fav_dish: broccoli fav_color: red description: >- @@ -26424,7 +26866,7 @@ components: status: type: string example: PENDING - enum: *ref_118 + enum: *ref_119 description: The status of the task. Authorized values are PENDING, COMPLETED. nullable: true due_date: @@ -26454,7 +26896,7 @@ components: nullable: true field_mappings: type: object - example: *ref_119 + example: *ref_120 description: >- The custom field mappings of the task between the remote 3rd party & Panora nullable: true @@ -26593,7 +27035,7 @@ components: type: string nullable: true example: USER - enum: *ref_120 + enum: *ref_121 description: >- The creator type of the comment. Authorized values are either USER or CONTACT ticket_id: @@ -26615,9 +27057,9 @@ components: The UUID of the user which the comment belongs to (if no contact_id specified) attachments: type: array - items: *ref_121 + items: *ref_122 nullable: true - example: *ref_122 + example: *ref_123 description: The attachements UUIDs tied to the comment id: type: string @@ -27243,149 +27685,41 @@ components: - method - path UnifiedHrisBankinfoOutput: - type: object - properties: {} - UnifiedHrisBenefitOutput: - type: object - properties: {} - UnifiedHrisCompanyOutput: - type: object - properties: {} - UnifiedHrisDependentOutput: - type: object - properties: {} - UnifiedHrisEmployeepayrollrunOutput: - type: object - properties: {} - UnifiedHrisEmployeeOutput: - type: object - properties: {} - UnifiedHrisEmployeeInput: - type: object - properties: {} - UnifiedHrisEmployerbenefitOutput: - type: object - properties: {} - UnifiedHrisEmploymentOutput: - type: object - properties: {} - UnifiedHrisGroupOutput: - type: object - properties: {} - UnifiedHrisLocationOutput: - type: object - properties: {} - UnifiedHrisPaygroupOutput: - type: object - properties: {} - UnifiedHrisPayrollrunOutput: - type: object - properties: {} - UnifiedHrisTimeoffOutput: - type: object - properties: {} - UnifiedHrisTimeoffInput: - type: object - properties: {} - UnifiedHrisTimeoffbalanceOutput: - type: object - properties: {} - UnifiedMarketingautomationActionOutput: - type: object - properties: {} - UnifiedMarketingautomationActionInput: - type: object - properties: {} - UnifiedMarketingautomationAutomationOutput: - type: object - properties: {} - UnifiedMarketingautomationAutomationInput: - type: object - properties: {} - UnifiedMarketingautomationCampaignOutput: - type: object - properties: {} - UnifiedMarketingautomationCampaignInput: - type: object - properties: {} - UnifiedMarketingautomationContactOutput: - type: object - properties: {} - UnifiedMarketingautomationContactInput: - type: object - properties: {} - UnifiedMarketingautomationEmailOutput: - type: object - properties: {} - UnifiedMarketingautomationEventOutput: - type: object - properties: {} - UnifiedMarketingautomationListOutput: - type: object - properties: {} - UnifiedMarketingautomationListInput: - type: object - properties: {} - UnifiedMarketingautomationMessageOutput: - type: object - properties: {} - UnifiedMarketingautomationTemplateOutput: - type: object - properties: {} - UnifiedMarketingautomationTemplateInput: - type: object - properties: {} - UnifiedMarketingautomationUserOutput: - type: object - properties: {} - UnifiedAtsActivityOutput: type: object properties: - activity_type: + account_type: type: string - enum: &ref_123 - - NOTE - - EMAIL - - OTHER - example: NOTE + example: CHECKING + enum: + - SAVINGS + - CHECKING nullable: true - description: The type of activity - subject: + description: The type of the bank account + bank_name: type: string - example: Email subject + example: Bank of America nullable: true - description: The subject of the activity - body: + description: The name of the bank + account_number: type: string - example: Dear Diana, I love you + example: '1234567890' nullable: true - description: The body of the activity - visibility: + description: The account number + routing_number: type: string - enum: &ref_124 - - ADMIN_ONLY - - PUBLIC - - PRIVATE - example: PUBLIC + example: '021000021' nullable: true - description: The visibility of the activity - candidate_id: + description: The routing number of the bank + employee_id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the candidate - remote_created_at: - type: string - format: date-time - example: '2024-10-01T12:00:00Z' - nullable: true - description: The remote creation date of the activity + description: The UUID of the associated employee field_mappings: type: object - example: &ref_125 - fav_dish: broccoli - fav_color: red - additionalProperties: true + example: + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora @@ -27393,281 +27727,287 @@ components: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the activity + description: The UUID of the bank info record remote_id: type: string example: id_1 nullable: true - description: The remote ID of the activity in the context of the 3rd Party + description: The remote ID of the bank info in the context of the 3rd Party remote_data: type: object example: - fav_dish: broccoli - fav_color: red + raw_data: + additional_field: some value nullable: true - additionalProperties: true - description: The remote data of the activity in the context of the 3rd Party + description: The remote data of the bank info in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The date when the bank info was created in the 3rd party system created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object + description: The created date of the bank info record modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - UnifiedAtsActivityInput: + description: The last modified date of the bank info record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: Indicates if the bank info was deleted in the remote system + required: + - id + - created_at + - modified_at + - remote_was_deleted + UnifiedHrisBenefitOutput: type: object properties: - activity_type: + provider_name: type: string - enum: *ref_123 - example: NOTE + example: Health Insurance Provider nullable: true - description: The type of activity - subject: + description: The name of the benefit provider + employee_id: type: string - example: Email subject + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The subject of the activity - body: - type: string - example: Dear Diana, I love you + description: The UUID of the associated employee + employee_contribution: + type: number + example: 100 nullable: true - description: The body of the activity - visibility: + description: The employee contribution amount + company_contribution: + type: number + example: 200 + nullable: true + description: The company contribution amount + start_date: + format: date-time type: string - enum: *ref_124 - example: PUBLIC + example: '2024-01-01T00:00:00Z' nullable: true - description: The visibility of the activity - candidate_id: + description: The start date of the benefit + end_date: + format: date-time type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: '2024-12-31T23:59:59Z' nullable: true - description: The UUID of the candidate - remote_created_at: + description: The end date of the benefit + employer_benefit_id: type: string - format: date-time - example: '2024-10-01T12:00:00Z' + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The remote creation date of the activity + description: The UUID of the associated employer benefit field_mappings: type: object - example: *ref_125 - additionalProperties: true + example: + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora - UnifiedAtsApplicationOutput: - type: object - properties: - applied_at: - format: date-time + id: type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The application date - example: '2024-10-01T12:00:00Z' - rejected_at: - format: date-time + description: The UUID of the benefit record + remote_id: type: string + example: benefit_1234 nullable: true - description: The rejection date - example: '2024-10-01T12:00:00Z' - offers: + description: The remote ID of the benefit in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value nullable: true - description: The offers UUIDs for the application - example: &ref_126 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - - 12345678-1234-1234-1234-123456789012 - type: array - items: - type: string - source: + description: The remote data of the benefit in the context of the 3rd Party + remote_created_at: + format: date-time type: string + example: '2024-10-01T12:00:00Z' nullable: true - description: The source of the application - example: Source Name - credited_to: + description: The date when the benefit was created in the 3rd party system + created_at: + format: date-time type: string + example: '2024-10-01T12:00:00Z' nullable: true - description: The UUID of the person credited for the application - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - current_stage: + description: The created date of the benefit record + modified_at: + format: date-time type: string + example: '2024-10-01T12:00:00Z' nullable: true - description: The UUID of the current stage of the application - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - reject_reason: - type: string + description: The last modified date of the benefit record + remote_was_deleted: + type: boolean + example: false nullable: true - description: The rejection reason for the application - example: Candidate not experienced enough - candidate_id: + description: Indicates if the benefit was deleted in the remote system + UnifiedHrisCompanyOutput: + type: object + properties: + legal_name: type: string + example: Acme Corporation nullable: true - description: The UUID of the candidate - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - job_id: + description: The legal name of the company + locations: + example: + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: UUIDs of the of the Location associated with the company + type: array + items: + type: string + display_name: type: string - description: The UUID of the job - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: Acme Corp + nullable: true + description: The display name of the company + eins: + example: + - 12-3456789 + - 98-7654321 + nullable: true + description: The Employer Identification Numbers (EINs) of the company + type: array + items: + type: string field_mappings: type: object - example: &ref_127 - fav_dish: broccoli - fav_color: red - additionalProperties: true + example: + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora id: type: string - nullable: true - description: The UUID of the application example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the company record remote_id: type: string + example: company_1234 nullable: true - description: The remote ID of the application in the context of the 3rd Party - example: id_1 + description: The remote ID of the company in the context of the 3rd Party remote_data: type: object example: - fav_dish: broccoli - fav_color: red + raw_data: + additional_field: some value nullable: true - additionalProperties: true - description: The remote data of the application in the context of the 3rd Party - created_at: + description: The remote data of the company in the context of the 3rd Party + remote_created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object - modified_at: + description: The date when the company was created in the 3rd party system + created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - remote_created_at: + description: The created date of the company record + modified_at: format: date-time type: string + example: '2024-10-01T12:00:00Z' nullable: true - description: The remote created date of the object - remote_modified_at: - format: date-time - type: string + description: The last modified date of the company record + remote_was_deleted: + type: boolean + example: false nullable: true - description: The remote modified date of the object - UnifiedAtsApplicationInput: + description: Indicates if the company was deleted in the remote system + UnifiedHrisDependentOutput: type: object properties: - applied_at: - format: date-time + first_name: type: string + example: John nullable: true - description: The application date - example: '2024-10-01T12:00:00Z' - rejected_at: - format: date-time + description: The first name of the dependent + last_name: type: string + example: Doe nullable: true - description: The rejection date - example: '2024-10-01T12:00:00Z' - offers: - nullable: true - description: The offers UUIDs for the application - example: *ref_126 - type: array - items: - type: string - source: + description: The last name of the dependent + middle_name: type: string + example: Michael nullable: true - description: The source of the application - example: Source Name - credited_to: + description: The middle name of the dependent + relationship: type: string + example: CHILD + enum: + - CHILD + - SPOUSE + - DOMESTIC_PARTNER nullable: true - description: The UUID of the person credited for the application - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - current_stage: + description: The relationship of the dependent to the employee + date_of_birth: + format: date-time type: string + example: '2020-01-01' nullable: true - description: The UUID of the current stage of the application - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - reject_reason: + description: The date of birth of the dependent + gender: type: string + example: MALE + enum: + - MALE + - FEMALE + - NON-BINARY + - OTHER + - PREFER_NOT_TO_DISCLOSE nullable: true - description: The rejection reason for the application - example: Candidate not experienced enough - candidate_id: + description: The gender of the dependent + phone_number: type: string + example: '+1234567890' nullable: true - description: The UUID of the candidate - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - job_id: + description: The phone number of the dependent + home_location: type: string - description: The UUID of the job example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - field_mappings: - type: object - example: *ref_127 - additionalProperties: true - nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party & Panora - UnifiedAtsAttachmentOutput: - type: object - properties: - file_url: - type: string - example: https://example.com/file.pdf - nullable: true - description: The URL of the file - file_name: - type: string - example: file.pdf - nullable: true - description: The name of the file - attachment_type: - type: string - example: RESUME - enum: &ref_128 - - RESUME - - COVER_LETTER - - OFFER_LETTER - - OTHER nullable: true - description: The type of the file - remote_created_at: - type: string - example: '2024-10-01T12:00:00Z' - format: date-time + description: The UUID of the home location + is_student: + type: boolean + example: true nullable: true - description: The remote creation date of the attachment - remote_modified_at: + description: Indicates if the dependent is a student + ssn: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: 123-45-6789 nullable: true - description: The remote modification date of the attachment - candidate_id: + description: The Social Security Number of the dependent + employee_id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the candidate + description: The UUID of the associated employee field_mappings: type: object - example: &ref_129 - fav_dish: broccoli - fav_color: red - additionalProperties: true + example: + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora @@ -27675,210 +28015,155 @@ components: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the attachment + description: The UUID of the dependent record remote_id: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: dependent_1234 nullable: true - description: The remote ID of the attachment + description: The remote ID of the dependent in the context of the 3rd Party remote_data: type: object example: - fav_dish: broccoli - fav_color: red + raw_data: + additional_field: some value nullable: true - additionalProperties: true - description: The remote data of the attachment in the context of the 3rd Party + description: The remote data of the dependent in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The date when the dependent was created in the 3rd party system created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object + description: The created date of the dependent record modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - UnifiedAtsAttachmentInput: + description: The last modified date of the dependent record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: Indicates if the dependent was deleted in the remote system + DeductionItem: type: object properties: - file_url: + name: type: string - example: https://example.com/file.pdf + example: Health Insurance nullable: true - description: The URL of the file - file_name: - type: string - example: file.pdf + description: The name of the deduction + employee_deduction: + type: number + example: 100 nullable: true - description: The name of the file - attachment_type: - type: string - example: RESUME - enum: *ref_128 + description: The amount of employee deduction + company_deduction: + type: number + example: 200 nullable: true - description: The type of the file - remote_created_at: - type: string - example: '2024-10-01T12:00:00Z' - format: date-time + description: The amount of company deduction + EarningItem: + type: object + properties: + amount: + type: number + example: 1000 nullable: true - description: The remote creation date of the attachment - remote_modified_at: + description: The amount of the earning + type: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: Salary nullable: true - description: The remote modification date of the attachment - candidate_id: + description: The type of the earning + TaxItem: + type: object + properties: + name: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: Federal Income Tax nullable: true - description: The UUID of the candidate - field_mappings: - type: object - example: *ref_129 - additionalProperties: true + description: The name of the tax + amount: + type: number + example: 250 nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party & Panora - Url: + description: The amount of the tax + employer_tax: + type: boolean + example: true + nullable: true + description: Indicates if this is an employer tax + UnifiedHrisEmployeepayrollrunOutput: type: object properties: - url: + employee_id: type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The url. - url_type: + description: The UUID of the associated employee + payroll_run_id: type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The url type. It takes [WEBSITE | BLOG | LINKEDIN | GITHUB | OTHER] - required: - - url - - url_type - UnifiedAtsCandidateOutput: - type: object - properties: - first_name: - type: string - example: Joe - nullable: true - description: The first name of the candidate - last_name: - type: string - example: Doe - nullable: true - description: The last name of the candidate - company: - type: string - example: Acme + description: The UUID of the associated payroll run + gross_pay: + type: number + example: 5000 nullable: true - description: The company of the candidate - title: - type: string - example: Analyst + description: The gross pay amount + net_pay: + type: number + example: 4000 nullable: true - description: The title of the candidate - locations: + description: The net pay amount + start_date: + format: date-time type: string - example: New York - nullable: true - description: The locations of the candidate - is_private: - type: boolean - example: false - nullable: true - description: Whether the candidate is private - email_reachable: - type: boolean - example: true + example: '2023-01-01T00:00:00Z' nullable: true - description: Whether the candidate is reachable by email - remote_created_at: - type: string - example: '2024-10-01T12:00:00Z' + description: The start date of the pay period + end_date: format: date-time - nullable: true - description: The remote creation date of the candidate - remote_modified_at: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: '2023-01-15T23:59:59Z' nullable: true - description: The remote modification date of the candidate - last_interaction_at: - type: string - example: '2024-10-01T12:00:00Z' + description: The end date of the pay period + check_date: format: date-time + type: string + example: '2023-01-20T00:00:00Z' nullable: true - description: The last interaction date with the candidate - attachments: - type: array - items: &ref_130 - oneOf: - - type: string - - $ref: '#/components/schemas/UnifiedAtsAttachmentOutput' - example: &ref_131 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The attachments UUIDs of the candidate - applications: - type: array - items: &ref_132 - oneOf: - - type: string - - $ref: '#/components/schemas/UnifiedAtsApplicationOutput' - example: &ref_133 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The applications UUIDs of the candidate - tags: - type: array - items: &ref_134 - oneOf: - - type: string - - $ref: '#/components/schemas/UnifiedAtsTagOutput' - example: &ref_135 - - tag_1 - - tag_2 - nullable: true - description: The tags of the candidate - urls: - example: &ref_136 - - url: mywebsite.com - url_type: WEBSITE + description: The date the check was issued + deductions: nullable: true - description: >- - The urls of the candidate, possible values for Url type are WEBSITE, BLOG, LINKEDIN, GITHUB, or OTHER + description: The list of deductions for this payroll run type: array items: - $ref: '#/components/schemas/Url' - phone_numbers: - example: &ref_137 - - phone_number: '+33660688899' - phone_type: WORK + $ref: '#/components/schemas/DeductionItem' + earnings: nullable: true - description: The phone numbers of the candidate + description: The list of earnings for this payroll run type: array items: - $ref: '#/components/schemas/Phone' - email_addresses: - example: &ref_138 - - email_address: joedoe@gmail.com - email_address_type: WORK + $ref: '#/components/schemas/EarningItem' + taxes: nullable: true - description: The email addresses of the candidate + description: The list of taxes for this payroll run type: array items: - $ref: '#/components/schemas/Email' + $ref: '#/components/schemas/TaxItem' field_mappings: type: object - example: &ref_139 - fav_dish: broccoli - fav_color: red - additionalProperties: true + example: + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora @@ -27886,149 +28171,208 @@ components: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the candidate + description: The UUID of the employee payroll run record remote_id: type: string - example: id_1 + example: payroll_run_1234 nullable: true - description: The id of the candidate in the context of the 3rd Party + description: >- + The remote ID of the employee payroll run in the context of the 3rd Party remote_data: type: object example: - fav_dish: broccoli - fav_color: red + raw_data: + additional_field: some value nullable: true - additionalProperties: true - description: The remote data of the candidate in the context of the 3rd Party + description: >- + The remote data of the employee payroll run in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: >- + The date when the employee payroll run was created in the 3rd party system created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object + description: The created date of the employee payroll run record modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - UnifiedAtsCandidateInput: + description: The last modified date of the employee payroll run record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: >- + Indicates if the employee payroll run was deleted in the remote system + UnifiedHrisEmployeeOutput: type: object properties: + groups: + example: &ref_124 + - Group1 + - Group2 + nullable: true + description: The groups the employee belongs to + type: array + items: + type: string + locations: + example: &ref_125 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: UUIDs of the of the Location associated with the company + type: array + items: + type: string + employee_number: + type: string + example: EMP001 + nullable: true + description: The employee number + company_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company first_name: type: string - example: Joe + example: John nullable: true - description: The first name of the candidate + description: The first name of the employee last_name: type: string example: Doe nullable: true - description: The last name of the candidate - company: + description: The last name of the employee + preferred_name: type: string - example: Acme + example: Johnny nullable: true - description: The company of the candidate - title: + description: The preferred name of the employee + display_full_name: type: string - example: Analyst + example: John Doe nullable: true - description: The title of the candidate - locations: + description: The full display name of the employee + username: type: string - example: New York + example: johndoe nullable: true - description: The locations of the candidate - is_private: - type: boolean - example: false + description: The username of the employee + work_email: + type: string + example: john.doe@company.com nullable: true - description: Whether the candidate is private - email_reachable: - type: boolean - example: true + description: The work email of the employee + personal_email: + type: string + example: john.doe@personal.com nullable: true - description: Whether the candidate is reachable by email - remote_created_at: + description: The personal email of the employee + mobile_phone_number: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: '+1234567890' nullable: true - description: The remote creation date of the candidate - remote_modified_at: + description: The mobile phone number of the employee + employments: + example: &ref_126 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The employments of the employee + type: array + items: + type: string + ssn: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: 123-45-6789 nullable: true - description: The remote modification date of the candidate - last_interaction_at: + description: The Social Security Number of the employee + gender: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: MALE + enum: &ref_127 + - MALE + - FEMALE + - NON-BINARY + - OTHER + - PREFER_NOT_TO_DISCLOSE nullable: true - description: The last interaction date with the candidate - attachments: - type: array - items: *ref_130 - example: *ref_131 + description: The gender of the employee + ethnicity: + type: string + example: AMERICAN_INDIAN_OR_ALASKA_NATIVE + enum: &ref_128 + - AMERICAN_INDIAN_OR_ALASKA_NATIVE + - ASIAN_OR_INDIAN_SUBCONTINENT + - BLACK_OR_AFRICAN_AMERICAN + - HISPANIC_OR_LATINO + - NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER + - TWO_OR_MORE_RACES + - WHITE + - PREFER_NOT_TO_DISCLOSE nullable: true - description: The attachments UUIDs of the candidate - applications: - type: array - items: *ref_132 - example: *ref_133 + description: The ethnicity of the employee + marital_status: + type: string + example: Married + enum: &ref_129 + - SINGLE + - MARRIED_FILING_JOINTLY + - MARRIED_FILING_SEPARATELY + - HEAD_OF_HOUSEHOLD + - QUALIFYING_WIDOW_OR_WIDOWER_WITH_DEPENDENT_CHILD nullable: true - description: The applications UUIDs of the candidate - tags: - type: array - items: *ref_134 - example: *ref_135 + description: The marital status of the employee + date_of_birth: + format: date-time + type: string + example: '1990-01-01' nullable: true - description: The tags of the candidate - urls: - example: *ref_136 + description: The date of birth of the employee + start_date: + format: date-time + type: string + example: '2020-01-01' nullable: true - description: >- - The urls of the candidate, possible values for Url type are WEBSITE, BLOG, LINKEDIN, GITHUB, or OTHER - type: array - items: - $ref: '#/components/schemas/Url' - phone_numbers: - example: *ref_137 + description: The start date of the employee + employment_status: + type: string + example: ACTIVE + enum: &ref_130 + - ACTIVE + - PENDING + - INACTIVE nullable: true - description: The phone numbers of the candidate - type: array - items: - $ref: '#/components/schemas/Phone' - email_addresses: - example: *ref_138 + description: The employment status of the employee + termination_date: + format: date-time + type: string + example: '2025-01-01' nullable: true - description: The email addresses of the candidate - type: array - items: - $ref: '#/components/schemas/Email' - field_mappings: - type: object - example: *ref_139 - additionalProperties: true + description: The termination date of the employee + avatar_url: + type: string + example: https://example.com/avatar.jpg nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party & Panora - UnifiedAtsDepartmentOutput: - type: object - properties: - name: + description: The URL of the employee's avatar + manager_id: type: string - example: Sales + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The name of the department + description: UUID of the manager (employee) of the employee field_mappings: type: object - example: - fav_dish: broccoli - fav_color: red - additionalProperties: true + example: &ref_131 + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora @@ -28036,227 +28380,213 @@ components: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the department + description: The UUID of the employee record remote_id: type: string - example: id_1 + example: employee_1234 nullable: true - description: The remote ID of the department in the context of the 3rd Party + description: The remote ID of the employee in the context of the 3rd Party remote_data: type: object example: - key1: value1 - key2: 42 - key3: true + raw_data: + additional_field: some value nullable: true - additionalProperties: true - description: The remote data of the department in the context of the 3rd Party + description: The remote data of the employee in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The date when the employee was created in the 3rd party system created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object + description: The created date of the employee record modified_at: format: date-time type: string - example: '2023-10-01T12:00:00Z' + example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - UnifiedAtsInterviewOutput: + description: The last modified date of the employee record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: Indicates if the employee was deleted in the remote system + UnifiedHrisEmployeeInput: type: object properties: - status: - type: string - enum: &ref_140 - - SCHEDULED - - AWAITING_FEEDBACK - - COMPLETED - example: SCHEDULED - nullable: true - description: The status of the interview - application_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the application - job_interview_stage_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the job interview stage - organized_by: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + groups: + example: *ref_124 nullable: true - description: The UUID of the organizer - interviewers: - example: &ref_141 - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The groups the employee belongs to + type: array + items: + type: string + locations: + example: *ref_125 nullable: true - description: The UUIDs of the interviewers + description: UUIDs of the of the Location associated with the company type: array items: type: string - location: + employee_number: type: string - example: San Francisco + example: EMP001 nullable: true - description: The location of the interview - start_at: - format: date-time + description: The employee number + company_id: type: string - example: '2024-10-01T12:00:00Z' + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The start date and time of the interview - end_at: - format: date-time + description: The UUID of the associated company + first_name: type: string - example: '2024-10-01T12:00:00Z' + example: John nullable: true - description: The end date and time of the interview - remote_created_at: - format: date-time + description: The first name of the employee + last_name: type: string - example: '2024-10-01T12:00:00Z' + example: Doe nullable: true - description: The remote creation date of the interview - remote_updated_at: - format: date-time + description: The last name of the employee + preferred_name: type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The remote modification date of the interview - field_mappings: - type: object - example: &ref_142 - fav_dish: broccoli - fav_color: red - additionalProperties: true + example: Johnny nullable: true - description: >- - The custom field mappings of the object between the remote 3rd party & Panora - id: + description: The preferred name of the employee + display_full_name: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: John Doe nullable: true - description: The UUID of the interview - remote_id: + description: The full display name of the employee + username: type: string - example: id_1 - nullable: true - description: The remote ID of the interview in the context of the 3rd Party - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red + example: johndoe nullable: true - additionalProperties: true - description: The remote data of the interview in the context of the 3rd Party - created_at: - format: date-time + description: The username of the employee + work_email: type: string - example: '2024-10-01T12:00:00Z' + example: john.doe@company.com nullable: true - description: The created date of the object - modified_at: - format: date-time + description: The work email of the employee + personal_email: type: string - example: '2024-10-01T12:00:00Z' + example: john.doe@personal.com nullable: true - description: The modified date of the object - UnifiedAtsInterviewInput: - type: object - properties: - status: + description: The personal email of the employee + mobile_phone_number: type: string - enum: *ref_140 - example: SCHEDULED + example: '+1234567890' nullable: true - description: The status of the interview - application_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The mobile phone number of the employee + employments: + example: *ref_126 nullable: true - description: The UUID of the application - job_interview_stage_id: + description: The employments of the employee + type: array + items: + type: string + ssn: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: 123-45-6789 nullable: true - description: The UUID of the job interview stage - organized_by: + description: The Social Security Number of the employee + gender: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: MALE + enum: *ref_127 nullable: true - description: The UUID of the organizer - interviewers: - example: *ref_141 + description: The gender of the employee + ethnicity: + type: string + example: AMERICAN_INDIAN_OR_ALASKA_NATIVE + enum: *ref_128 nullable: true - description: The UUIDs of the interviewers - type: array - items: - type: string - location: + description: The ethnicity of the employee + marital_status: type: string - example: San Francisco + example: Married + enum: *ref_129 nullable: true - description: The location of the interview - start_at: + description: The marital status of the employee + date_of_birth: format: date-time type: string - example: '2024-10-01T12:00:00Z' + example: '1990-01-01' nullable: true - description: The start date and time of the interview - end_at: + description: The date of birth of the employee + start_date: format: date-time type: string - example: '2024-10-01T12:00:00Z' + example: '2020-01-01' nullable: true - description: The end date and time of the interview - remote_created_at: - format: date-time + description: The start date of the employee + employment_status: type: string - example: '2024-10-01T12:00:00Z' + example: ACTIVE + enum: *ref_130 nullable: true - description: The remote creation date of the interview - remote_updated_at: + description: The employment status of the employee + termination_date: format: date-time type: string - example: '2024-10-01T12:00:00Z' + example: '2025-01-01' nullable: true - description: The remote modification date of the interview + description: The termination date of the employee + avatar_url: + type: string + example: https://example.com/avatar.jpg + nullable: true + description: The URL of the employee's avatar + manager_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: UUID of the manager (employee) of the employee field_mappings: type: object - example: *ref_142 - additionalProperties: true + example: *ref_131 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora - UnifiedAtsJobinterviewstageOutput: + UnifiedHrisEmployerbenefitOutput: type: object properties: + benefit_plan_type: + type: string + example: Health Insurance + enum: + - MEDICAL + - HEALTH_SAVINGS + - INSURANCE + - RETIREMENT + - OTHER + nullable: true + description: The type of the benefit plan name: type: string - example: Second Call + example: Company Health Plan nullable: true - description: The name of the job interview stage - stage_order: - type: number - example: 1 + description: The name of the employer benefit + description: + type: string + example: Comprehensive health insurance coverage for employees nullable: true - description: The order of the stage - job_id: + description: The description of the employer benefit + deduction_code: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: HEALTH-001 nullable: true - description: The UUID of the job + description: The deduction code for the employer benefit field_mappings: type: object example: - fav_dish: broccoli - fav_color: red - additionalProperties: true + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora @@ -28264,127 +28594,298 @@ components: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the job interview stage + description: The UUID of the employer benefit record remote_id: type: string - example: id_1 + example: benefit_1234 nullable: true description: >- - The remote ID of the job interview stage in the context of the 3rd Party + The remote ID of the employer benefit in the context of the 3rd Party remote_data: type: object example: - fav_dish: broccoli - fav_color: red + raw_data: + additional_field: some value nullable: true - additionalProperties: true description: >- - The remote data of the job interview stage in the context of the 3rd Party + The remote data of the employer benefit in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: >- + The date when the employer benefit was created in the 3rd party system created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object + description: The created date of the employer benefit record modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - UnifiedAtsJobOutput: + description: The last modified date of the employer benefit record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: Indicates if the employer benefit was deleted in the remote system + UnifiedHrisEmploymentOutput: type: object properties: - name: - type: string - example: Financial Analyst - nullable: true - description: The name of the job - description: + job_title: type: string - example: Extract financial data and write detailed investment thesis + example: Software Engineer nullable: true - description: The description of the job - code: - type: string - example: JOB123 + description: The job title of the employment + pay_rate: + type: number + example: 100000 nullable: true - description: The code of the job - status: + description: The pay rate of the employment + pay_period: type: string + example: MONTHLY enum: - - OPEN - - CLOSED - - DRAFT - - ARCHIVED - - PENDING - example: OPEN - nullable: true - description: The status of the job - type: - type: string - example: POSTING + - HOUR + - DAY + - WEEK + - EVERY_TWO_WEEKS + - SEMIMONTHLY + - MONTH + - QUARTER + - EVERY_SIX_MONTHS + - YEAR + nullable: true + description: The pay period of the employment + pay_frequency: + type: string + example: WEEKLY enum: - - POSTING - - REQUISITION - - PROFILE - nullable: true - description: The type of the job - confidential: - type: boolean - example: true - nullable: true - description: Whether the job is confidential - departments: - example: - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The departments UUIDs associated with the job - type: array - items: - type: string - offices: - example: - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The offices UUIDs associated with the job - type: array - items: - type: string - managers: - example: - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The managers UUIDs associated with the job - type: array - items: - type: string - recruiters: - example: - - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + - WEEKLY + - BIWEEKLY + - MONTHLY + - QUARTERLY + - SEMIANNUALLY + - ANNUALLY + - THIRTEEN-MONTHLY + - PRO_RATA + - SEMIMONTHLY + nullable: true + description: The pay frequency of the employment + pay_currency: + type: string + example: USD + enum: + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL nullable: true - description: The recruiters UUIDs associated with the job - type: array - items: - type: string - remote_created_at: + description: The currency of the pay + flsa_status: type: string - example: '2024-10-01T12:00:00Z' + example: EXEMPT + enum: + - EXEMPT + - SALARIED_NONEXEMPT + - NONEXEMPT + - OWNER + nullable: true + description: The FLSA status of the employment + effective_date: format: date-time + type: string + example: '2023-01-01' nullable: true - description: The remote creation date of the job - remote_updated_at: + description: The effective date of the employment + employment_type: type: string - example: '2024-10-01T12:00:00Z' - format: date-time + example: FULL_TIME + enum: + - FULL_TIME + - PART_TIME + - INTERN + - CONTRACTOR + - FREELANCE nullable: true - description: The remote modification date of the job + description: The type of employment + pay_group_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated pay group + employee_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated employee field_mappings: type: object example: - fav_dish: broccoli - fav_color: red - additionalProperties: true + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora @@ -28392,313 +28893,450 @@ components: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the job + description: The UUID of the employment record remote_id: type: string - example: id_1 + example: employment_1234 nullable: true - description: The remote ID of the job in the context of the 3rd Party + description: The remote ID of the employment in the context of the 3rd Party remote_data: type: object example: - key1: value1 - key2: 42 - key3: true + raw_data: + additional_field: some value nullable: true - additionalProperties: true - description: The remote data of the job in the context of the 3rd Party + description: The remote data of the employment in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The date when the employment was created in the 3rd party system created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object + description: The created date of the employment record modified_at: format: date-time type: string - example: '2023-10-01T12:00:00Z' + example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - UnifiedAtsOfferOutput: + description: The last modified date of the employment record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: Indicates if the employment was deleted in the remote system + UnifiedHrisGroupOutput: type: object properties: - created_by: + parent_group: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the creator nullable: true + description: The UUID of the parent group + name: + type: string + example: Engineering Team + nullable: true + description: The name of the group + type: + type: string + example: DEPARTMENT + enum: + - TEAM + - DEPARTMENT + - COST_CENTER + - BUSINESS_UNIT + - GROUP + nullable: true + description: The type of the group + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the group record + remote_id: + type: string + example: group_1234 + nullable: true + description: The remote ID of the group in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the group in the context of the 3rd Party remote_created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' - description: The remote creation date of the offer nullable: true - closed_at: + description: The date when the group was created in the 3rd party system + created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' - description: The closing date of the offer nullable: true - sent_at: + description: The created date of the group record + modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' - description: The sending date of the offer nullable: true - start_date: - format: date-time + description: The last modified date of the group record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: Indicates if the group was deleted in the remote system + UnifiedHrisLocationOutput: + type: object + properties: + name: type: string - example: '2024-10-01T12:00:00Z' - description: The start date of the offer + example: Headquarters nullable: true - status: + description: The name of the location + phone_number: type: string - example: DRAFT - enum: - - DRAFT - - APPROVAL_SENT - - APPROVED - - SENT - - SENT_MANUALLY - - OPENED - - DENIED - - SIGNED - - DEPRECATED - description: The status of the offer + example: '+1234567890' nullable: true - application_id: + description: The phone number of the location + street_1: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the application + example: 123 Main St nullable: true - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - description: >- - The custom field mappings of the object between the remote 3rd party & Panora + description: The first line of the street address + street_2: + type: string + example: Suite 456 nullable: true - additionalProperties: true - id: + description: The second line of the street address + city: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the offer + example: San Francisco nullable: true - remote_id: + description: The city of the location + state: type: string - example: id_1 - description: The remote ID of the offer in the context of the 3rd Party + example: CA nullable: true - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - description: The remote data of the offer in the context of the 3rd Party + description: The state or region of the location + zip_code: + type: string + example: '94105' nullable: true - additionalProperties: true - created_at: - type: object - example: '2024-10-01T12:00:00Z' - description: The created date of the object + description: The zip or postal code of the location + country: + type: string + example: USA nullable: true - modified_at: - type: object - example: '2024-10-01T12:00:00Z' - description: The modified date of the object + description: The country of the location + location_type: + type: string + example: WORK + enum: + - WORK + - HOME nullable: true - UnifiedAtsOfficeOutput: - type: object - properties: - name: + description: The type of the location + company_id: type: string - example: Condo Office 5th + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The name of the office - location: + description: The UUID of the company associated with the location + employee_id: type: string - example: New York + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The location of the office + description: The UUID of the employee associated with the location field_mappings: type: object example: - fav_dish: broccoli - fav_color: red - additionalProperties: true + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the office + nullable: true + description: The UUID of the location record remote_id: type: string - example: id_1 + example: location_1234 nullable: true - description: The remote ID of the office in the context of the 3rd Party + description: The remote ID of the location in the context of the 3rd Party remote_data: type: object example: - fav_dish: broccoli - fav_color: red + raw_data: + additional_field: some value nullable: true - additionalProperties: true - description: The remote data of the office in the context of the 3rd Party + description: The remote data of the location in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The date when the location was created in the 3rd party system created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object + description: The created date of the location record modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - UnifiedAtsRejectreasonOutput: + description: The last modified date of the location record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: Indicates if the location was deleted in the remote system + UnifiedHrisPaygroupOutput: type: object properties: - name: + pay_group_name: type: string - example: Candidate inexperienced + example: Monthly Salaried nullable: true - description: The name of the reject reason + description: The name of the pay group field_mappings: type: object example: - fav_dish: broccoli - fav_color: red - additionalProperties: true + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora id: type: string - nullable: true - description: The UUID of the reject reason example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the pay group record remote_id: type: string + example: paygroup_1234 nullable: true - description: The remote ID of the reject reason in the context of the 3rd Party - example: id_1 + description: The remote ID of the pay group in the context of the 3rd Party remote_data: type: object example: - fav_dish: broccoli - fav_color: red + raw_data: + additional_field: some value nullable: true - additionalProperties: true - description: The remote data of the reject reason in the context of the 3rd Party + description: The remote data of the pay group in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The date when the pay group was created in the 3rd party system created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object + description: The created date of the pay group record modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - UnifiedAtsScorecardOutput: + description: The last modified date of the pay group record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: Indicates if the pay group was deleted in the remote system + UnifiedHrisPayrollrunOutput: type: object properties: - overall_recommendation: + run_state: type: string + example: PAID enum: - - DEFINITELY_NO - - 'NO' - - 'YES' - - STRONG_YES - - NO_DECISION - example: 'YES' + - PAID + - DRAFT + - APPROVED + - FAILED + - CLOSE nullable: true - description: The overall recommendation - application_id: + description: The state of the payroll run + run_type: type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: REGULAR + enum: + - REGULAR + - OFF_CYCLE + - CORRECTION + - TERMINATION + - SIGN_ON_BONUS nullable: true - description: The UUID of the application - interview_id: + description: The type of the payroll run + start_date: + format: date-time type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + example: '2024-01-01T00:00:00Z' nullable: true - description: The UUID of the interview - remote_created_at: - type: string - example: '2024-10-01T12:00:00Z' + description: The start date of the payroll run + end_date: format: date-time - nullable: true - description: The remote creation date of the scorecard - submitted_at: type: string - example: '2024-10-01T12:00:00Z' + example: '2024-01-15T23:59:59Z' + nullable: true + description: The end date of the payroll run + check_date: format: date-time + type: string + example: '2024-01-20T00:00:00Z' nullable: true - description: The submission date of the scorecard + description: The check date of the payroll run field_mappings: type: object example: - fav_dish: broccoli - fav_color: red - additionalProperties: true + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the scorecard + nullable: true + description: The UUID of the payroll run record remote_id: type: string - example: id_1 + example: payroll_run_1234 nullable: true - description: The remote ID of the scorecard in the context of the 3rd Party + description: The remote ID of the payroll run in the context of the 3rd Party remote_data: type: object example: - fav_dish: broccoli - fav_color: red + raw_data: + additional_field: some value nullable: true - additionalProperties: true - description: The remote data of the scorecard in the context of the 3rd Party + description: The remote data of the payroll run in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The date when the payroll run was created in the 3rd party system created_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The created date of the object + description: The created date of the payroll run record modified_at: format: date-time type: string example: '2024-10-01T12:00:00Z' nullable: true - description: The modified date of the object - UnifiedAtsTagOutput: + description: The last modified date of the payroll run record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: Indicates if the payroll run was deleted in the remote system + employee_payroll_runs: + example: + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: >- + The UUIDs of the employee payroll runs associated with this payroll run + type: array + items: + type: string + UnifiedHrisTimeoffOutput: type: object properties: - name: + employee: type: string - example: Important + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The name of the tag - id_ats_candidate: + description: The UUID of the employee taking time off + approver: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the candidate + description: The UUID of the approver for the time off request + status: + type: string + example: REQUESTED + enum: &ref_132 + - REQUESTED + - APPROVED + - DECLINED + - CANCELLED + - DELETED + nullable: true + description: The status of the time off request + employee_note: + type: string + example: Annual vacation + nullable: true + description: A note from the employee about the time off request + units: + type: string + example: DAYS + enum: &ref_133 + - HOURS + - DAYS + nullable: true + description: The units used for the time off (e.g., Days, Hours) + amount: + type: number + example: 5 + nullable: true + description: The amount of time off requested + request_type: + type: string + example: VACATION + enum: &ref_134 + - VACATION + - SICK + - PERSONAL + - JURY_DUTY + - VOLUNTEER + - BEREAVEMENT + nullable: true + description: The type of time off request + start_time: + format: date-time + type: string + example: '2024-07-01T09:00:00Z' + nullable: true + description: The start time of the time off + end_time: + format: date-time + type: string + example: '2024-07-05T17:00:00Z' + nullable: true + description: The end time of the time off field_mappings: type: object - example: - fav_dish: broccoli - fav_color: red - additionalProperties: true + example: &ref_135 + custom_field_1: value1 + custom_field_2: value2 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora @@ -28706,298 +29344,6716 @@ components: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - description: The UUID of the tag + description: The UUID of the time off record remote_id: type: string - example: id_1 + example: timeoff_1234 nullable: true - description: The remote ID of the tag in the context of the 3rd Party + description: The remote ID of the time off in the context of the 3rd Party remote_data: type: object example: - fav_dish: broccoli - fav_color: red + raw_data: + additional_field: some value nullable: true - additionalProperties: true - description: The remote data of the tag in the context of the 3rd Party + description: The remote data of the time off in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the time off was created in the 3rd party system created_at: format: date-time type: string + example: '2024-06-15T12:00:00Z' nullable: true - example: '2024-10-01T12:00:00Z' - description: The creation date of the tag + description: The created date of the time off record modified_at: format: date-time type: string + example: '2024-06-15T12:00:00Z' nullable: true - example: '2024-10-01T12:00:00Z' - description: The modification date of the tag - UnifiedAtsUserOutput: + description: The last modified date of the time off record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: Indicates if the time off was deleted in the remote system + UnifiedHrisTimeoffInput: type: object properties: - first_name: + employee: type: string - example: John - description: The first name of the user + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - last_name: + description: The UUID of the employee taking time off + approver: type: string - example: Doe - description: The last name of the user + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the approver for the time off request + status: + type: string + example: REQUESTED + enum: *ref_132 + nullable: true + description: The status of the time off request + employee_note: + type: string + example: Annual vacation + nullable: true + description: A note from the employee about the time off request + units: + type: string + example: DAYS + enum: *ref_133 + nullable: true + description: The units used for the time off (e.g., Days, Hours) + amount: + type: number + example: 5 + nullable: true + description: The amount of time off requested + request_type: + type: string + example: VACATION + enum: *ref_134 + nullable: true + description: The type of time off request + start_time: + format: date-time + type: string + example: '2024-07-01T09:00:00Z' + nullable: true + description: The start time of the time off + end_time: + format: date-time + type: string + example: '2024-07-05T17:00:00Z' + nullable: true + description: The end time of the time off + field_mappings: + type: object + example: *ref_135 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedHrisTimeoffbalanceOutput: + type: object + properties: + balance: + type: number + example: 80 + nullable: true + description: The current balance of time off + employee_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated employee + used: + type: number + example: 40 + nullable: true + description: The amount of time off used + policy_type: + type: string + example: VACATION + enum: + - VACATION + - SICK + - PERSONAL + - JURY_DUTY + - VOLUNTEER + - BEREAVEMENT + nullable: true + description: The type of time off policy + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the time off balance record + remote_id: + type: string + example: timeoff_balance_1234 + nullable: true + description: >- + The remote ID of the time off balance in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: >- + The remote data of the time off balance in the context of the 3rd Party + remote_created_at: + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: >- + The date when the time off balance was created in the 3rd party system + created_at: + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the time off balance record + modified_at: + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the time off balance record + remote_was_deleted: + type: boolean + example: false + nullable: true + description: Indicates if the time off balance was deleted in the remote system + UnifiedHrisTimesheetEntryOutput: + type: object + properties: + hours_worked: + type: number + example: 40 + nullable: true + description: The number of hours worked + start_time: + format: date-time + type: string + example: '2024-10-01T08:00:00Z' + nullable: true + description: The start time of the timesheet entry + end_time: + format: date-time + type: string + example: '2024-10-01T16:00:00Z' + nullable: true + description: The end time of the timesheet entry + employee_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated employee + remote_was_deleted: + type: boolean + example: false + description: Indicates if the timesheet entry was deleted in the remote system + field_mappings: + type: object + example: &ref_136 + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the timesheet entry record + remote_id: + type: string + example: id_1 + nullable: true + description: The remote ID of the timesheet entry + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The date when the timesheet entry was created in the remote system + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: The created date of the timesheet entry + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: The last modified date of the timesheet entry + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: >- + The remote data of the timesheet entry in the context of the 3rd Party + UnifiedHrisTimesheetEntryInput: + type: object + properties: + hours_worked: + type: number + example: 40 + nullable: true + description: The number of hours worked + start_time: + format: date-time + type: string + example: '2024-10-01T08:00:00Z' + nullable: true + description: The start time of the timesheet entry + end_time: + format: date-time + type: string + example: '2024-10-01T16:00:00Z' + nullable: true + description: The end time of the timesheet entry + employee_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated employee + remote_was_deleted: + type: boolean + example: false + description: Indicates if the timesheet entry was deleted in the remote system + field_mappings: + type: object + example: *ref_136 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedMarketingautomationActionOutput: + type: object + properties: {} + UnifiedMarketingautomationActionInput: + type: object + properties: {} + UnifiedMarketingautomationAutomationOutput: + type: object + properties: {} + UnifiedMarketingautomationAutomationInput: + type: object + properties: {} + UnifiedMarketingautomationCampaignOutput: + type: object + properties: {} + UnifiedMarketingautomationCampaignInput: + type: object + properties: {} + UnifiedMarketingautomationContactOutput: + type: object + properties: {} + UnifiedMarketingautomationContactInput: + type: object + properties: {} + UnifiedMarketingautomationEmailOutput: + type: object + properties: {} + UnifiedMarketingautomationEventOutput: + type: object + properties: {} + UnifiedMarketingautomationListOutput: + type: object + properties: {} + UnifiedMarketingautomationListInput: + type: object + properties: {} + UnifiedMarketingautomationMessageOutput: + type: object + properties: {} + UnifiedMarketingautomationTemplateOutput: + type: object + properties: {} + UnifiedMarketingautomationTemplateInput: + type: object + properties: {} + UnifiedMarketingautomationUserOutput: + type: object + properties: {} + UnifiedAtsActivityOutput: + type: object + properties: + activity_type: + type: string + enum: &ref_137 + - NOTE + - EMAIL + - OTHER + example: NOTE + nullable: true + description: The type of activity + subject: + type: string + example: Email subject + nullable: true + description: The subject of the activity + body: + type: string + example: Dear Diana, I love you + nullable: true + description: The body of the activity + visibility: + type: string + enum: &ref_138 + - ADMIN_ONLY + - PUBLIC + - PRIVATE + example: PUBLIC + nullable: true + description: The visibility of the activity + candidate_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the candidate + remote_created_at: + type: string + format: date-time + example: '2024-10-01T12:00:00Z' + nullable: true + description: The remote creation date of the activity + field_mappings: + type: object + example: &ref_139 + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the activity + remote_id: + type: string + example: id_1 + nullable: true + description: The remote ID of the activity in the context of the 3rd Party + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + additionalProperties: true + description: The remote data of the activity in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + UnifiedAtsActivityInput: + type: object + properties: + activity_type: + type: string + enum: *ref_137 + example: NOTE + nullable: true + description: The type of activity + subject: + type: string + example: Email subject + nullable: true + description: The subject of the activity + body: + type: string + example: Dear Diana, I love you + nullable: true + description: The body of the activity + visibility: + type: string + enum: *ref_138 + example: PUBLIC + nullable: true + description: The visibility of the activity + candidate_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the candidate + remote_created_at: + type: string + format: date-time + example: '2024-10-01T12:00:00Z' + nullable: true + description: The remote creation date of the activity + field_mappings: + type: object + example: *ref_139 + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedAtsApplicationOutput: + type: object + properties: + applied_at: + format: date-time + type: string + nullable: true + description: The application date + example: '2024-10-01T12:00:00Z' + rejected_at: + format: date-time + type: string + nullable: true + description: The rejection date + example: '2024-10-01T12:00:00Z' + offers: + nullable: true + description: The offers UUIDs for the application + example: &ref_140 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + - 12345678-1234-1234-1234-123456789012 + type: array + items: + type: string + source: + type: string + nullable: true + description: The source of the application + example: Source Name + credited_to: + type: string + nullable: true + description: The UUID of the person credited for the application + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + current_stage: + type: string + nullable: true + description: The UUID of the current stage of the application + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + reject_reason: + type: string + nullable: true + description: The rejection reason for the application + example: Candidate not experienced enough + candidate_id: + type: string + nullable: true + description: The UUID of the candidate + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + job_id: + type: string + description: The UUID of the job + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + field_mappings: + type: object + example: &ref_141 + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + nullable: true + description: The UUID of the application + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + remote_id: + type: string + nullable: true + description: The remote ID of the application in the context of the 3rd Party + example: id_1 + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + additionalProperties: true + description: The remote data of the application in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + remote_created_at: + format: date-time + type: string + nullable: true + description: The remote created date of the object + remote_modified_at: + format: date-time + type: string + nullable: true + description: The remote modified date of the object + UnifiedAtsApplicationInput: + type: object + properties: + applied_at: + format: date-time + type: string + nullable: true + description: The application date + example: '2024-10-01T12:00:00Z' + rejected_at: + format: date-time + type: string + nullable: true + description: The rejection date + example: '2024-10-01T12:00:00Z' + offers: + nullable: true + description: The offers UUIDs for the application + example: *ref_140 + type: array + items: + type: string + source: + type: string + nullable: true + description: The source of the application + example: Source Name + credited_to: + type: string + nullable: true + description: The UUID of the person credited for the application + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + current_stage: + type: string + nullable: true + description: The UUID of the current stage of the application + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + reject_reason: + type: string + nullable: true + description: The rejection reason for the application + example: Candidate not experienced enough + candidate_id: + type: string + nullable: true + description: The UUID of the candidate + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + job_id: + type: string + description: The UUID of the job + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + field_mappings: + type: object + example: *ref_141 + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedAtsAttachmentOutput: + type: object + properties: + file_url: + type: string + example: https://example.com/file.pdf + nullable: true + description: The URL of the file + file_name: + type: string + example: file.pdf + nullable: true + description: The name of the file + attachment_type: + type: string + example: RESUME + enum: &ref_142 + - RESUME + - COVER_LETTER + - OFFER_LETTER + - OTHER + nullable: true + description: The type of the file + remote_created_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The remote creation date of the attachment + remote_modified_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The remote modification date of the attachment + candidate_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the candidate + field_mappings: + type: object + example: &ref_143 + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the attachment + remote_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The remote ID of the attachment + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + additionalProperties: true + description: The remote data of the attachment in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + UnifiedAtsAttachmentInput: + type: object + properties: + file_url: + type: string + example: https://example.com/file.pdf + nullable: true + description: The URL of the file + file_name: + type: string + example: file.pdf + nullable: true + description: The name of the file + attachment_type: + type: string + example: RESUME + enum: *ref_142 + nullable: true + description: The type of the file + remote_created_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The remote creation date of the attachment + remote_modified_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The remote modification date of the attachment + candidate_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the candidate + field_mappings: + type: object + example: *ref_143 + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + Url: + type: object + properties: + url: + type: string + nullable: true + description: The url. + url_type: + type: string + nullable: true + description: The url type. It takes [WEBSITE | BLOG | LINKEDIN | GITHUB | OTHER] + required: + - url + - url_type + UnifiedAtsCandidateOutput: + type: object + properties: + first_name: + type: string + example: Joe + nullable: true + description: The first name of the candidate + last_name: + type: string + example: Doe + nullable: true + description: The last name of the candidate + company: + type: string + example: Acme + nullable: true + description: The company of the candidate + title: + type: string + example: Analyst + nullable: true + description: The title of the candidate + locations: + type: string + example: New York + nullable: true + description: The locations of the candidate + is_private: + type: boolean + example: false + nullable: true + description: Whether the candidate is private + email_reachable: + type: boolean + example: true + nullable: true + description: Whether the candidate is reachable by email + remote_created_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The remote creation date of the candidate + remote_modified_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The remote modification date of the candidate + last_interaction_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The last interaction date with the candidate + attachments: + type: array + items: &ref_144 + oneOf: + - type: string + - $ref: '#/components/schemas/UnifiedAtsAttachmentOutput' + example: &ref_145 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The attachments UUIDs of the candidate + applications: + type: array + items: &ref_146 + oneOf: + - type: string + - $ref: '#/components/schemas/UnifiedAtsApplicationOutput' + example: &ref_147 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The applications UUIDs of the candidate + tags: + type: array + items: &ref_148 + oneOf: + - type: string + - $ref: '#/components/schemas/UnifiedAtsTagOutput' + example: &ref_149 + - tag_1 + - tag_2 + nullable: true + description: The tags of the candidate + urls: + example: &ref_150 + - url: mywebsite.com + url_type: WEBSITE + nullable: true + description: >- + The urls of the candidate, possible values for Url type are WEBSITE, BLOG, LINKEDIN, GITHUB, or OTHER + type: array + items: + $ref: '#/components/schemas/Url' + phone_numbers: + example: &ref_151 + - phone_number: '+33660688899' + phone_type: WORK + nullable: true + description: The phone numbers of the candidate + type: array + items: + $ref: '#/components/schemas/Phone' + email_addresses: + example: &ref_152 + - email_address: joedoe@gmail.com + email_address_type: WORK + nullable: true + description: The email addresses of the candidate + type: array + items: + $ref: '#/components/schemas/Email' + field_mappings: + type: object + example: &ref_153 + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the candidate + remote_id: + type: string + example: id_1 + nullable: true + description: The id of the candidate in the context of the 3rd Party + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + additionalProperties: true + description: The remote data of the candidate in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + UnifiedAtsCandidateInput: + type: object + properties: + first_name: + type: string + example: Joe + nullable: true + description: The first name of the candidate + last_name: + type: string + example: Doe + nullable: true + description: The last name of the candidate + company: + type: string + example: Acme + nullable: true + description: The company of the candidate + title: + type: string + example: Analyst + nullable: true + description: The title of the candidate + locations: + type: string + example: New York + nullable: true + description: The locations of the candidate + is_private: + type: boolean + example: false + nullable: true + description: Whether the candidate is private + email_reachable: + type: boolean + example: true + nullable: true + description: Whether the candidate is reachable by email + remote_created_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The remote creation date of the candidate + remote_modified_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The remote modification date of the candidate + last_interaction_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The last interaction date with the candidate + attachments: + type: array + items: *ref_144 + example: *ref_145 + nullable: true + description: The attachments UUIDs of the candidate + applications: + type: array + items: *ref_146 + example: *ref_147 + nullable: true + description: The applications UUIDs of the candidate + tags: + type: array + items: *ref_148 + example: *ref_149 + nullable: true + description: The tags of the candidate + urls: + example: *ref_150 + nullable: true + description: >- + The urls of the candidate, possible values for Url type are WEBSITE, BLOG, LINKEDIN, GITHUB, or OTHER + type: array + items: + $ref: '#/components/schemas/Url' + phone_numbers: + example: *ref_151 + nullable: true + description: The phone numbers of the candidate + type: array + items: + $ref: '#/components/schemas/Phone' + email_addresses: + example: *ref_152 + nullable: true + description: The email addresses of the candidate + type: array + items: + $ref: '#/components/schemas/Email' + field_mappings: + type: object + example: *ref_153 + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedAtsDepartmentOutput: + type: object + properties: + name: + type: string + example: Sales + nullable: true + description: The name of the department + field_mappings: + type: object + example: + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the department + remote_id: + type: string + example: id_1 + nullable: true + description: The remote ID of the department in the context of the 3rd Party + remote_data: + type: object + example: + key1: value1 + key2: 42 + key3: true + nullable: true + additionalProperties: true + description: The remote data of the department in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2023-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + UnifiedAtsInterviewOutput: + type: object + properties: + status: + type: string + enum: &ref_154 + - SCHEDULED + - AWAITING_FEEDBACK + - COMPLETED + example: SCHEDULED + nullable: true + description: The status of the interview + application_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the application + job_interview_stage_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the job interview stage + organized_by: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the organizer + interviewers: + example: &ref_155 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUIDs of the interviewers + type: array + items: + type: string + location: + type: string + example: San Francisco + nullable: true + description: The location of the interview + start_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The start date and time of the interview + end_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The end date and time of the interview + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The remote creation date of the interview + remote_updated_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The remote modification date of the interview + field_mappings: + type: object + example: &ref_156 + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the interview + remote_id: + type: string + example: id_1 + nullable: true + description: The remote ID of the interview in the context of the 3rd Party + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + additionalProperties: true + description: The remote data of the interview in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + UnifiedAtsInterviewInput: + type: object + properties: + status: + type: string + enum: *ref_154 + example: SCHEDULED + nullable: true + description: The status of the interview + application_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the application + job_interview_stage_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the job interview stage + organized_by: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the organizer + interviewers: + example: *ref_155 + nullable: true + description: The UUIDs of the interviewers + type: array + items: + type: string + location: + type: string + example: San Francisco + nullable: true + description: The location of the interview + start_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The start date and time of the interview + end_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The end date and time of the interview + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The remote creation date of the interview + remote_updated_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The remote modification date of the interview + field_mappings: + type: object + example: *ref_156 + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedAtsJobinterviewstageOutput: + type: object + properties: + name: + type: string + example: Second Call + nullable: true + description: The name of the job interview stage + stage_order: + type: number + example: 1 + nullable: true + description: The order of the stage + job_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the job + field_mappings: + type: object + example: + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the job interview stage + remote_id: + type: string + example: id_1 + nullable: true + description: >- + The remote ID of the job interview stage in the context of the 3rd Party + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + additionalProperties: true + description: >- + The remote data of the job interview stage in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + UnifiedAtsJobOutput: + type: object + properties: + name: + type: string + example: Financial Analyst + nullable: true + description: The name of the job + description: + type: string + example: Extract financial data and write detailed investment thesis + nullable: true + description: The description of the job + code: + type: string + example: JOB123 + nullable: true + description: The code of the job + status: + type: string + enum: + - OPEN + - CLOSED + - DRAFT + - ARCHIVED + - PENDING + example: OPEN + nullable: true + description: The status of the job + type: + type: string + example: POSTING + enum: + - POSTING + - REQUISITION + - PROFILE + nullable: true + description: The type of the job + confidential: + type: boolean + example: true + nullable: true + description: Whether the job is confidential + departments: + example: + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The departments UUIDs associated with the job + type: array + items: + type: string + offices: + example: + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The offices UUIDs associated with the job + type: array + items: + type: string + managers: + example: + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The managers UUIDs associated with the job + type: array + items: + type: string + recruiters: + example: + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The recruiters UUIDs associated with the job + type: array + items: + type: string + remote_created_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The remote creation date of the job + remote_updated_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The remote modification date of the job + field_mappings: + type: object + example: + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the job + remote_id: + type: string + example: id_1 + nullable: true + description: The remote ID of the job in the context of the 3rd Party + remote_data: + type: object + example: + key1: value1 + key2: 42 + key3: true + nullable: true + additionalProperties: true + description: The remote data of the job in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2023-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + UnifiedAtsOfferOutput: + type: object + properties: + created_by: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the creator + nullable: true + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: The remote creation date of the offer + nullable: true + closed_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: The closing date of the offer + nullable: true + sent_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: The sending date of the offer + nullable: true + start_date: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: The start date of the offer + nullable: true + status: + type: string + example: DRAFT + enum: + - DRAFT + - APPROVAL_SENT + - APPROVED + - SENT + - SENT_MANUALLY + - OPENED + - DENIED + - SIGNED + - DEPRECATED + description: The status of the offer + nullable: true + application_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the application + nullable: true + field_mappings: + type: object + example: + fav_dish: broccoli + fav_color: red + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + nullable: true + additionalProperties: true + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the offer + nullable: true + remote_id: + type: string + example: id_1 + description: The remote ID of the offer in the context of the 3rd Party + nullable: true + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + description: The remote data of the offer in the context of the 3rd Party + nullable: true + additionalProperties: true + created_at: + type: object + example: '2024-10-01T12:00:00Z' + description: The created date of the object + nullable: true + modified_at: + type: object + example: '2024-10-01T12:00:00Z' + description: The modified date of the object + nullable: true + UnifiedAtsOfficeOutput: + type: object + properties: + name: + type: string + example: Condo Office 5th + nullable: true + description: The name of the office + location: + type: string + example: New York + nullable: true + description: The location of the office + field_mappings: + type: object + example: + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the office + remote_id: + type: string + example: id_1 + nullable: true + description: The remote ID of the office in the context of the 3rd Party + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + additionalProperties: true + description: The remote data of the office in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + UnifiedAtsRejectreasonOutput: + type: object + properties: + name: + type: string + example: Candidate inexperienced + nullable: true + description: The name of the reject reason + field_mappings: + type: object + example: + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + nullable: true + description: The UUID of the reject reason + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + remote_id: + type: string + nullable: true + description: The remote ID of the reject reason in the context of the 3rd Party + example: id_1 + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + additionalProperties: true + description: The remote data of the reject reason in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + UnifiedAtsScorecardOutput: + type: object + properties: + overall_recommendation: + type: string + enum: + - DEFINITELY_NO + - 'NO' + - 'YES' + - STRONG_YES + - NO_DECISION + example: 'YES' + nullable: true + description: The overall recommendation + application_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the application + interview_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the interview + remote_created_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The remote creation date of the scorecard + submitted_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The submission date of the scorecard + field_mappings: + type: object + example: + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the scorecard + remote_id: + type: string + example: id_1 + nullable: true + description: The remote ID of the scorecard in the context of the 3rd Party + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + additionalProperties: true + description: The remote data of the scorecard in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + UnifiedAtsTagOutput: + type: object + properties: + name: + type: string + example: Important + nullable: true + description: The name of the tag + id_ats_candidate: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the candidate + field_mappings: + type: object + example: + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the tag + remote_id: + type: string + example: id_1 + nullable: true + description: The remote ID of the tag in the context of the 3rd Party + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + additionalProperties: true + description: The remote data of the tag in the context of the 3rd Party + created_at: + format: date-time + type: string + nullable: true + example: '2024-10-01T12:00:00Z' + description: The creation date of the tag + modified_at: + format: date-time + type: string + nullable: true + example: '2024-10-01T12:00:00Z' + description: The modification date of the tag + UnifiedAtsUserOutput: + type: object + properties: + first_name: + type: string + example: John + description: The first name of the user + nullable: true + last_name: + type: string + example: Doe + description: The last name of the user nullable: true email: type: string - example: john.doe@example.com - description: The email of the user - nullable: true - disabled: - type: boolean - example: false - description: Whether the user is disabled + example: john.doe@example.com + description: The email of the user + nullable: true + disabled: + type: boolean + example: false + description: Whether the user is disabled + nullable: true + access_role: + type: string + example: ADMIN + enum: + - SUPER_ADMIN + - ADMIN + - TEAM_MEMBER + - LIMITED_TEAM_MEMBER + - INTERVIEWER + description: The access role of the user + nullable: true + remote_created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: The remote creation date of the user + nullable: true + remote_modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: The remote modification date of the user + nullable: true + field_mappings: + type: object + example: + fav_dish: broccoli + fav_color: red + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + nullable: true + additionalProperties: true + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + description: The UUID of the user + nullable: true + remote_id: + type: string + example: id_1 + description: The remote ID of the user in the context of the 3rd Party + nullable: true + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + description: The remote data of the user in the context of the 3rd Party + nullable: true + additionalProperties: true + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: The created date of the object + nullable: true + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + description: The modified date of the object + nullable: true + UnifiedAtsEeocsOutput: + type: object + properties: + candidate_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the candidate + submitted_at: + type: string + example: '2024-10-01T12:00:00Z' + format: date-time + nullable: true + description: The submission date of the EEOC + race: + type: string + enum: + - AMERICAN_INDIAN_OR_ALASKAN_NATIVE + - ASIAN + - BLACK_OR_AFRICAN_AMERICAN + - HISPANIC_OR_LATINO + - WHITE + - NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER + - TWO_OR_MORE_RACES + - DECLINE_TO_SELF_IDENTIFY + example: AMERICAN_INDIAN_OR_ALASKAN_NATIVE + nullable: true + description: The race of the candidate + gender: + type: string + example: MALE + enum: + - MALE + - FEMALE + - NON_BINARY + - OTHER + - DECLINE_TO_SELF_IDENTIFY + nullable: true + description: The gender of the candidate + veteran_status: + type: string + example: I_AM_NOT_A_PROTECTED_VETERAN + enum: + - I_AM_NOT_A_PROTECTED_VETERAN + - >- + I_IDENTIFY_AS_ONE_OR_MORE_OF_THE_CLASSIFICATIONS_OF_A_PROTECTED_VETERAN + - I_DONT_WISH_TO_ANSWER + nullable: true + description: The veteran status of the candidate + disability_status: + type: string + enum: + - YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY + - NO_I_DONT_HAVE_A_DISABILITY + - I_DONT_WISH_TO_ANSWER + example: YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY + nullable: true + description: The disability status of the candidate + field_mappings: + type: object + example: + fav_dish: broccoli + fav_color: red + additionalProperties: true + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the EEOC + remote_id: + type: string + example: id_1 + nullable: true + description: The remote ID of the EEOC in the context of the 3rd Party + remote_data: + type: object + example: + fav_dish: broccoli + fav_color: red + nullable: true + additionalProperties: true + description: The remote data of the EEOC in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The created date of the object + modified_at: + format: date-time + type: string + example: '2024-10-01T12:00:00Z' + nullable: true + description: The modified date of the object + UnifiedAccountingAccountOutput: + type: object + properties: + name: + type: string + example: Cash + nullable: true + description: The name of the account + description: + type: string + example: Main cash account for daily operations + nullable: true + description: A description of the account + classification: + type: string + example: Asset + nullable: true + description: The classification of the account + type: + type: string + example: Current Asset + nullable: true + description: The type of the account + status: + type: string + example: Active + nullable: true + description: The status of the account + current_balance: + type: number + example: 10000 + nullable: true + description: The current balance of the account + currency: + type: string + example: USD + enum: &ref_157 + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency of the account + account_number: + type: string + example: '1000' + nullable: true + description: The account number + parent_account: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the parent account + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + field_mappings: + type: object + example: &ref_158 + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the account record + remote_id: + type: string + example: account_1234 + nullable: true + description: The remote ID of the account in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the account in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the account record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the account record + UnifiedAccountingAccountInput: + type: object + properties: + name: + type: string + example: Cash + nullable: true + description: The name of the account + description: + type: string + example: Main cash account for daily operations + nullable: true + description: A description of the account + classification: + type: string + example: Asset + nullable: true + description: The classification of the account + type: + type: string + example: Current Asset + nullable: true + description: The type of the account + status: + type: string + example: Active + nullable: true + description: The status of the account + current_balance: + type: number + example: 10000 + nullable: true + description: The current balance of the account + currency: + type: string + example: USD + enum: *ref_157 + nullable: true + description: The currency of the account + account_number: + type: string + example: '1000' + nullable: true + description: The account number + parent_account: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the parent account + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + field_mappings: + type: object + example: *ref_158 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedAccountingAddressOutput: + type: object + properties: + type: + type: string + example: Billing + nullable: true + description: The type of the address + street_1: + type: string + example: 123 Main St + nullable: true + description: The first line of the street address + street_2: + type: string + example: Apt 4B + nullable: true + description: The second line of the street address + city: + type: string + example: New York + nullable: true + description: The city of the address + state: + type: string + example: NY + nullable: true + description: The state of the address + country_subdivision: + type: string + example: New York + nullable: true + description: The country subdivision (e.g., province or state) of the address + country: + type: string + example: USA + nullable: true + description: The country of the address + zip: + type: string + example: '10001' + nullable: true + description: The zip or postal code of the address + contact_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated contact + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the address record + remote_id: + type: string + example: address_1234 + nullable: true + description: The remote ID of the address in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the address in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the address record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the address record + UnifiedAccountingAttachmentOutput: + type: object + properties: + file_name: + type: string + example: invoice.pdf + nullable: true + description: The name of the attached file + file_url: + type: string + example: https://example.com/files/invoice.pdf + nullable: true + description: The URL where the file can be accessed + account_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated account + field_mappings: + type: object + example: &ref_159 + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the attachment record + remote_id: + type: string + example: attachment_1234 + nullable: true + description: The remote ID of the attachment in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the attachment in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the attachment record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the attachment record + UnifiedAccountingAttachmentInput: + type: object + properties: + file_name: + type: string + example: invoice.pdf + nullable: true + description: The name of the attached file + file_url: + type: string + example: https://example.com/files/invoice.pdf + nullable: true + description: The URL where the file can be accessed + account_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated account + field_mappings: + type: object + example: *ref_159 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + LineItem: + type: object + properties: + name: + type: string + example: Net Income + nullable: true + description: The name of the report item + value: + type: number + example: 100000 + nullable: true + description: The value of the report item + type: + type: string + example: Operating Activities + nullable: true + description: The type of the report item + parent_item: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the parent item + remote_id: + type: string + example: report_item_1234 + nullable: true + description: The remote ID of the report item + remote_generated_at: + format: date-time + type: string + example: '2024-07-01T12:00:00Z' + nullable: true + description: The date when the report item was generated in the remote system + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info object + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + description: The created date of the report item + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + description: The last modified date of the report item + UnifiedAccountingBalancesheetOutput: + type: object + properties: + name: + type: string + example: Q2 2024 Balance Sheet + nullable: true + description: The name of the balance sheet + currency: + type: string + example: USD + enum: + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency used in the balance sheet + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + date: + format: date-time + type: string + example: '2024-06-30T23:59:59Z' + nullable: true + description: The date of the balance sheet + net_assets: + type: number + example: 1000000 + nullable: true + description: The net assets value + assets: + example: + - Cash + - Accounts Receivable + - Inventory + nullable: true + description: The list of assets + type: array + items: + type: string + liabilities: + example: + - Accounts Payable + - Long-term Debt + nullable: true + description: The list of liabilities + type: array + items: + type: string + equity: + example: + - Common Stock + - Retained Earnings + nullable: true + description: The list of equity items + type: array + items: + type: string + remote_generated_at: + format: date-time + type: string + example: '2024-07-01T12:00:00Z' + nullable: true + description: The date when the balance sheet was generated in the remote system + line_items: + description: The report items associated with this balance sheet + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the balance sheet record + remote_id: + type: string + example: balancesheet_1234 + nullable: true + description: The remote ID of the balance sheet in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the balance sheet in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the balance sheet record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the balance sheet record + UnifiedAccountingCashflowstatementOutput: + type: object + properties: + name: + type: string + example: Q2 2024 Cash Flow Statement + nullable: true + description: The name of the cash flow statement + currency: + type: string + example: USD + enum: + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency used in the cash flow statement + company_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company + start_period: + format: date-time + type: string + example: '2024-04-01T00:00:00Z' + nullable: true + description: The start date of the period covered by the cash flow statement + end_period: + format: date-time + type: string + example: '2024-06-30T23:59:59Z' + nullable: true + description: The end date of the period covered by the cash flow statement + cash_at_beginning_of_period: + type: number + example: 1000000 + nullable: true + description: The cash balance at the beginning of the period + cash_at_end_of_period: + type: number + example: 1200000 + nullable: true + description: The cash balance at the end of the period + remote_generated_at: + format: date-time + type: string + example: '2024-07-01T12:00:00Z' + nullable: true + description: >- + The date when the cash flow statement was generated in the remote system + line_items: + description: The report items associated with this cash flow statement + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the cash flow statement record + remote_id: + type: string + example: cashflowstatement_1234 + nullable: true + description: >- + The remote ID of the cash flow statement in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: >- + The remote data of the cash flow statement in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the cash flow statement record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the cash flow statement record + UnifiedAccountingCompanyinfoOutput: + type: object + properties: + name: + type: string + example: Acme Corporation + nullable: true + description: The name of the company + legal_name: + type: string + example: Acme Corporation LLC + nullable: true + description: The legal name of the company + tax_number: + type: string + example: '123456789' + nullable: true + description: The tax number of the company + fiscal_year_end_month: + type: number + example: 12 + nullable: true + description: The month of the fiscal year end (1-12) + fiscal_year_end_day: + type: number + example: 31 + nullable: true + description: The day of the fiscal year end (1-31) + currency: + type: string + example: USD + enum: + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency used by the company + urls: + example: + - https://www.acmecorp.com + - https://store.acmecorp.com + nullable: true + description: The URLs associated with the company + type: array + items: + type: string + tracking_categories: + example: + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUIDs of the tracking categories used by the company + type: array + items: + type: string + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the company info record + remote_id: + type: string + example: company_1234 + nullable: true + description: The remote ID of the company info in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the company info in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the company info was created in the remote system + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the company info record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the company info record + UnifiedAccountingContactOutput: + type: object + properties: + name: + type: string + example: John Doe + nullable: true + description: The name of the contact + is_supplier: + type: boolean + example: true + nullable: true + description: Indicates if the contact is a supplier + is_customer: + type: boolean + example: false + nullable: true + description: Indicates if the contact is a customer + email_address: + type: string + example: john.doe@example.com + nullable: true + description: The email address of the contact + tax_number: + type: string + example: '123456789' + nullable: true + description: The tax number of the contact + status: + type: string + example: Active + nullable: true + description: The status of the contact + currency: + type: string + example: USD + nullable: true + enum: &ref_160 + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + description: The currency associated with the contact + remote_updated_at: + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the contact was last updated in the remote system + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + field_mappings: + type: object + example: &ref_161 + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the contact record + remote_id: + type: string + example: contact_1234 + nullable: true + description: The remote ID of the contact in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the contact in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the contact record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the contact record + UnifiedAccountingContactInput: + type: object + properties: + name: + type: string + example: John Doe + nullable: true + description: The name of the contact + is_supplier: + type: boolean + example: true + nullable: true + description: Indicates if the contact is a supplier + is_customer: + type: boolean + example: false + nullable: true + description: Indicates if the contact is a customer + email_address: + type: string + example: john.doe@example.com + nullable: true + description: The email address of the contact + tax_number: + type: string + example: '123456789' + nullable: true + description: The tax number of the contact + status: + type: string + example: Active + nullable: true + description: The status of the contact + currency: + type: string + example: USD + nullable: true + enum: *ref_160 + description: The currency associated with the contact + remote_updated_at: + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the contact was last updated in the remote system + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + field_mappings: + type: object + example: *ref_161 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedAccountingCreditnoteOutput: + type: object + properties: + transaction_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date of the credit note transaction + status: + type: string + example: Issued + nullable: true + description: The status of the credit note + number: + type: string + example: CN-001 + nullable: true + description: The number of the credit note + contact_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated contact + company_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the credit note + total_amount: + type: number + example: 10000 + nullable: true + description: The total amount of the credit note + remaining_credit: + type: number + example: 5000 + nullable: true + description: The remaining credit on the credit note + tracking_categories: + example: + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUIDs of the tracking categories associated with the credit note + type: array + items: + type: string + currency: + type: string + example: USD + enum: + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency of the credit note + payments: + example: + - PAYMENT-001 + - PAYMENT-002 + nullable: true + description: The payments associated with the credit note + type: array + items: + type: string + applied_payments: + example: + - APPLIED-001 + - APPLIED-002 + nullable: true + description: The applied payments associated with the credit note + type: array + items: + type: string + accounting_period_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated accounting period + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the credit note record + remote_id: + type: string + example: creditnote_1234 + nullable: true + description: The remote ID of the credit note in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the credit note in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the credit note was created in the remote system + remote_updated_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the credit note was last updated in the remote system + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the credit note record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the credit note record + UnifiedAccountingExpenseOutput: + type: object + properties: + transaction_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date of the expense transaction + total_amount: + type: number + example: 10000 + nullable: true + description: The total amount of the expense + sub_total: + type: number + example: 9000 + nullable: true + description: The sub-total amount of the expense (before tax) + total_tax_amount: + type: number + example: 1000 + nullable: true + description: The total tax amount of the expense + currency: + type: string + example: USD + enum: &ref_162 + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency of the expense + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the expense + memo: + type: string + example: Business lunch with client + nullable: true + description: A memo or description for the expense + account_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated account + contact_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated contact + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + tracking_categories: + example: &ref_163 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUIDs of the tracking categories associated with the expense + type: array + items: + type: string + line_items: + description: The line items associated with this expense + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: &ref_164 + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the expense record + remote_id: + type: string + example: expense_1234 + nullable: true + description: The remote ID of the expense in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the expense in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the expense was created in the remote system + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the expense record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the expense record + UnifiedAccountingExpenseInput: + type: object + properties: + transaction_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date of the expense transaction + total_amount: + type: number + example: 10000 + nullable: true + description: The total amount of the expense + sub_total: + type: number + example: 9000 + nullable: true + description: The sub-total amount of the expense (before tax) + total_tax_amount: + type: number + example: 1000 + nullable: true + description: The total tax amount of the expense + currency: + type: string + example: USD + enum: *ref_162 + nullable: true + description: The currency of the expense + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the expense + memo: + type: string + example: Business lunch with client + nullable: true + description: A memo or description for the expense + account_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated account + contact_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated contact + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + tracking_categories: + example: *ref_163 + nullable: true + description: The UUIDs of the tracking categories associated with the expense + type: array + items: + type: string + line_items: + description: The line items associated with this expense + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: *ref_164 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedAccountingIncomestatementOutput: + type: object + properties: + name: + type: string + example: Q2 2024 Income Statement + nullable: true + description: The name of the income statement + currency: + type: string + example: USD + enum: + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency used in the income statement + start_period: + format: date-time + type: string + example: '2024-04-01T00:00:00Z' + nullable: true + description: The start date of the period covered by the income statement + end_period: + format: date-time + type: string + example: '2024-06-30T23:59:59Z' + nullable: true + description: The end date of the period covered by the income statement + gross_profit: + type: number + example: 1000000 + nullable: true + description: The gross profit for the period + net_operating_income: + type: number + example: 800000 + nullable: true + description: The net operating income for the period + net_income: + type: number + example: 750000 + nullable: true + description: The net income for the period + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the income statement record + remote_id: + type: string + example: incomestatement_1234 + nullable: true + description: >- + The remote ID of the income statement in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: >- + The remote data of the income statement in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the income statement record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the income statement record + UnifiedAccountingInvoiceOutput: + type: object + properties: + type: + type: string + example: Sales + nullable: true + description: The type of the invoice + number: + type: string + example: INV-001 + nullable: true + description: The invoice number + issue_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date the invoice was issued + due_date: + format: date-time + type: string + example: '2024-07-15T12:00:00Z' + nullable: true + description: The due date of the invoice + paid_on_date: + format: date-time + type: string + example: '2024-07-10T12:00:00Z' + nullable: true + description: The date the invoice was paid + memo: + type: string + example: Payment for services rendered + nullable: true + description: A memo or note on the invoice + currency: + type: string + example: USD + enum: &ref_165 + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency of the invoice + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the invoice + total_discount: + type: number + example: 1000 + nullable: true + description: The total discount applied to the invoice + sub_total: + type: number + example: 10000 + nullable: true + description: The subtotal of the invoice + status: + type: string + example: Paid + nullable: true + description: The status of the invoice + total_tax_amount: + type: number + example: 1000 + nullable: true + description: The total tax amount on the invoice + total_amount: + type: number + example: 11000 + nullable: true + description: The total amount of the invoice + balance: + type: number + example: 0 + nullable: true + description: The remaining balance on the invoice + contact_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated contact + accounting_period_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated accounting period + tracking_categories: + example: &ref_166 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUIDs of the tracking categories associated with the invoice + type: array + items: + type: string + line_items: + description: The line items associated with this invoice + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: &ref_167 + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the invoice record + remote_id: + type: string + example: invoice_1234 + nullable: true + description: The remote ID of the invoice in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the invoice in the context of the 3rd Party + remote_updated_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the invoice was last updated in the remote system + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the invoice record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the invoice record + UnifiedAccountingInvoiceInput: + type: object + properties: + type: + type: string + example: Sales + nullable: true + description: The type of the invoice + number: + type: string + example: INV-001 + nullable: true + description: The invoice number + issue_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date the invoice was issued + due_date: + format: date-time + type: string + example: '2024-07-15T12:00:00Z' + nullable: true + description: The due date of the invoice + paid_on_date: + format: date-time + type: string + example: '2024-07-10T12:00:00Z' + nullable: true + description: The date the invoice was paid + memo: + type: string + example: Payment for services rendered + nullable: true + description: A memo or note on the invoice + currency: + type: string + example: USD + enum: *ref_165 + nullable: true + description: The currency of the invoice + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the invoice + total_discount: + type: number + example: 1000 + nullable: true + description: The total discount applied to the invoice + sub_total: + type: number + example: 10000 + nullable: true + description: The subtotal of the invoice + status: + type: string + example: Paid + nullable: true + description: The status of the invoice + total_tax_amount: + type: number + example: 1000 + nullable: true + description: The total tax amount on the invoice + total_amount: + type: number + example: 11000 + nullable: true + description: The total amount of the invoice + balance: + type: number + example: 0 + nullable: true + description: The remaining balance on the invoice + contact_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated contact + accounting_period_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated accounting period + tracking_categories: + example: *ref_166 + nullable: true + description: The UUIDs of the tracking categories associated with the invoice + type: array + items: + type: string + line_items: + description: The line items associated with this invoice + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: *ref_167 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedAccountingItemOutput: + type: object + properties: + name: + type: string + example: Product A + nullable: true + description: The name of the accounting item + status: + type: string + example: Active + nullable: true + description: The status of the accounting item + unit_price: + type: number + example: 1000 + nullable: true + description: The unit price of the item in cents + purchase_price: + type: number + example: 800 + nullable: true + description: The purchase price of the item in cents + sales_account: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated sales account + purchase_account: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated purchase account + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the accounting item record + remote_id: + type: string + example: item_1234 + nullable: true + description: The remote ID of the item in the context of the 3rd Party + remote_updated_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the item was last updated in the remote system + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the item in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the accounting item record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the accounting item record + UnifiedAccountingJournalentryOutput: + type: object + properties: + transaction_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date of the transaction + payments: + example: &ref_168 + - payment1 + - payment2 + nullable: true + description: The payments associated with the journal entry + type: array + items: + type: string + applied_payments: + example: &ref_169 + - appliedPayment1 + - appliedPayment2 + nullable: true + description: The applied payments for the journal entry + type: array + items: + type: string + memo: + type: string + example: Monthly expense journal entry + nullable: true + description: A memo or note for the journal entry + currency: + type: string + example: USD + enum: &ref_170 + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency of the journal entry + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the journal entry + id_acc_company_info: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: false + description: The UUID of the associated company info + journal_number: + type: string + example: JE-001 + nullable: true + description: The journal number + tracking_categories: + example: &ref_171 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: >- + The UUIDs of the tracking categories associated with the journal entry + type: array + items: + type: string + id_acc_accounting_period: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated accounting period + posting_status: + type: string + example: Posted + nullable: true + description: The posting status of the journal entry + line_items: + description: The line items associated with this journal entry + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: &ref_172 + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the journal entry record + remote_id: + type: string + example: journal_entry_1234 + nullable: false + description: The remote ID of the journal entry in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the journal entry was created in the remote system + remote_modiified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: >- + The date when the journal entry was last modified in the remote system + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the journal entry in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the journal entry record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the journal entry record + UnifiedAccountingJournalentryInput: + type: object + properties: + transaction_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date of the transaction + payments: + example: *ref_168 + nullable: true + description: The payments associated with the journal entry + type: array + items: + type: string + applied_payments: + example: *ref_169 + nullable: true + description: The applied payments for the journal entry + type: array + items: + type: string + memo: + type: string + example: Monthly expense journal entry + nullable: true + description: A memo or note for the journal entry + currency: + type: string + example: USD + enum: *ref_170 + nullable: true + description: The currency of the journal entry + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the journal entry + id_acc_company_info: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: false + description: The UUID of the associated company info + journal_number: + type: string + example: JE-001 + nullable: true + description: The journal number + tracking_categories: + example: *ref_171 + nullable: true + description: >- + The UUIDs of the tracking categories associated with the journal entry + type: array + items: + type: string + id_acc_accounting_period: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated accounting period + posting_status: + type: string + example: Posted + nullable: true + description: The posting status of the journal entry + line_items: + description: The line items associated with this journal entry + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: *ref_172 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedAccountingPaymentOutput: + type: object + properties: + invoice_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated invoice + transaction_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date of the transaction + contact_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated contact + account_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated account + currency: + type: string + example: USD + enum: &ref_173 + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency of the payment + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the payment + total_amount: + type: number + example: 10000 + nullable: true + description: The total amount of the payment in cents + type: + type: string + example: Credit Card + nullable: true + description: The type of payment + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + accounting_period_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated accounting period + tracking_categories: + example: &ref_174 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUIDs of the tracking categories associated with the payment + type: array + items: + type: string + line_items: + description: The line items associated with this payment + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: &ref_175 + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the payment record + remote_id: + type: string + example: payment_1234 + nullable: true + description: The remote ID of the payment in the context of the 3rd Party + remote_updated_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the payment was last updated in the remote system + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the payment in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the payment record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the payment record + UnifiedAccountingPaymentInput: + type: object + properties: + invoice_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated invoice + transaction_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date of the transaction + contact_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated contact + account_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated account + currency: + type: string + example: USD + enum: *ref_173 + nullable: true + description: The currency of the payment + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the payment + total_amount: + type: number + example: 10000 + nullable: true + description: The total amount of the payment in cents + type: + type: string + example: Credit Card + nullable: true + description: The type of payment + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + accounting_period_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated accounting period + tracking_categories: + example: *ref_174 + nullable: true + description: The UUIDs of the tracking categories associated with the payment + type: array + items: + type: string + line_items: + description: The line items associated with this payment + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: *ref_175 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedAccountingPhonenumberOutput: + type: object + properties: + number: + type: string + example: '+1234567890' + nullable: true + description: The phone number + type: + type: string + example: Mobile + nullable: true + description: The type of phone number + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + contact_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: false + description: The UUID of the associated contact + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the phone number record + remote_id: + type: string + example: phone_1234 + nullable: true + description: The remote ID of the phone number in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the phone number in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the phone number record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the phone number record + UnifiedAccountingPurchaseorderOutput: + type: object + properties: + status: + type: string + example: Pending + nullable: true + description: The status of the purchase order + issue_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The issue date of the purchase order + purchase_order_number: + type: string + example: PO-001 + nullable: true + description: The purchase order number + delivery_date: + format: date-time + type: string + example: '2024-07-15T12:00:00Z' + nullable: true + description: The delivery date for the purchase order + delivery_address: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the delivery address + customer: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the customer + vendor: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the vendor + memo: + type: string + example: Purchase order for Q3 inventory + nullable: true + description: A memo or note for the purchase order + company_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the company + total_amount: + type: number + example: 100000 + nullable: true + description: The total amount of the purchase order in cents + currency: + type: string + example: USD + enum: &ref_176 + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency of the purchase order + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the purchase order + tracking_categories: + example: &ref_177 + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: >- + The UUIDs of the tracking categories associated with the purchase order + type: array + items: + type: string + accounting_period_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated accounting period + line_items: + description: The line items associated with this purchase order + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: &ref_178 + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the purchase order record + remote_id: + type: string + example: po_1234 + nullable: true + description: The remote ID of the purchase order in the context of the 3rd Party + remote_created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the purchase order was created in the remote system + remote_updated_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: >- + The date when the purchase order was last updated in the remote system + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: >- + The remote data of the purchase order in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the purchase order record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the purchase order record + UnifiedAccountingPurchaseorderInput: + type: object + properties: + status: + type: string + example: Pending + nullable: true + description: The status of the purchase order + issue_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The issue date of the purchase order + purchase_order_number: + type: string + example: PO-001 + nullable: true + description: The purchase order number + delivery_date: + format: date-time + type: string + example: '2024-07-15T12:00:00Z' + nullable: true + description: The delivery date for the purchase order + delivery_address: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the delivery address + customer: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the customer + vendor: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the vendor + memo: + type: string + example: Purchase order for Q3 inventory + nullable: true + description: A memo or note for the purchase order + company_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the company + total_amount: + type: number + example: 100000 + nullable: true + description: The total amount of the purchase order in cents + currency: + type: string + example: USD + enum: *ref_176 + nullable: true + description: The currency of the purchase order + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the purchase order + tracking_categories: + example: *ref_177 + nullable: true + description: >- + The UUIDs of the tracking categories associated with the purchase order + type: array + items: + type: string + accounting_period_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated accounting period + line_items: + description: The line items associated with this purchase order + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: *ref_178 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + UnifiedAccountingTaxrateOutput: + type: object + properties: + description: + type: string + example: VAT 20% + nullable: true + description: The description of the tax rate + total_tax_ratge: + type: number + example: 2000 + nullable: true + description: The total tax rate in basis points (e.g., 2000 for 20%) + effective_tax_rate: + type: number + example: 1900 + nullable: true + description: The effective tax rate in basis points (e.g., 1900 for 19%) + company_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the tax rate record + remote_id: + type: string + example: tax_rate_1234 + nullable: true + description: The remote ID of the tax rate in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: The remote data of the tax rate in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the tax rate record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the tax rate record + UnifiedAccountingTrackingcategoryOutput: + type: object + properties: + name: + type: string + example: Department + nullable: true + description: The name of the tracking category + status: + type: string + example: Active + nullable: true + description: The status of the tracking category + category_type: + type: string + example: Expense + nullable: true + description: The type of the tracking category + parent_category: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the parent category, if applicable + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the tracking category record + remote_id: + type: string + example: tracking_category_1234 + nullable: true + description: >- + The remote ID of the tracking category in the context of the 3rd Party + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: >- + The remote data of the tracking category in the context of the 3rd Party + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The created date of the tracking category record + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The last modified date of the tracking category record + UnifiedAccountingTransactionOutput: + type: object + properties: + transaction_type: + type: string + example: Sale + nullable: true + description: The type of the transaction + number: + type: string + example: '1001' + nullable: true + description: The transaction number + transaction_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date of the transaction + total_amount: + type: string + example: '1000' + nullable: true + description: The total amount of the transaction + exchange_rate: + type: string + example: '1.2' + nullable: true + description: The exchange rate applied to the transaction + currency: + type: string + example: USD + enum: + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + nullable: true + description: The currency of the transaction + tracking_categories: + example: + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUIDs of the tracking categories associated with the transaction + type: array + items: + type: string + account_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated account + contact_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated contact + company_info_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated company info + accounting_period_id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the associated accounting period + line_items: + description: The line items associated with this transaction + type: array + items: + $ref: '#/components/schemas/LineItem' + field_mappings: + type: object + example: + custom_field_1: value1 + custom_field_2: value2 + nullable: true + description: >- + The custom field mappings of the object between the remote 3rd party & Panora + id: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the transaction record + remote_id: + type: string + example: remote_id_1234 + nullable: false + description: The remote ID of the transaction + created_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: false + description: The created date of the transaction + remote_data: + type: object + example: + raw_data: + additional_field: some value + nullable: true + description: >- + The remote data of the tracking category in the context of the 3rd Party + modified_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: false + description: The last modified date of the transaction + remote_updated_at: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date when the transaction was last updated in the remote system + UnifiedAccountingVendorcreditOutput: + type: object + properties: + number: + type: string + example: VC-001 + nullable: true + description: The number of the vendor credit + transaction_date: + format: date-time + type: string + example: '2024-06-15T12:00:00Z' + nullable: true + description: The date of the transaction + vendor: + type: string + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f + nullable: true + description: The UUID of the vendor associated with the credit + total_amount: + type: string + example: '1000' + nullable: true + description: The total amount of the vendor credit + currency: + type: string + example: USD + nullable: true + enum: + - AED + - AFN + - ALL + - AMD + - ANG + - AOA + - ARS + - AUD + - AWG + - AZN + - BAM + - BBD + - BDT + - BGN + - BHD + - BIF + - BMD + - BND + - BOB + - BRL + - BSD + - BTN + - BWP + - BYN + - BZD + - CAD + - CDF + - CHF + - CLP + - CNY + - COP + - CRC + - CUP + - CVE + - CZK + - DJF + - DKK + - DOP + - DZD + - EGP + - ERN + - ETB + - EUR + - FJD + - FKP + - FOK + - GBP + - GEL + - GGP + - GHS + - GIP + - GMD + - GNF + - GTQ + - GYD + - HKD + - HNL + - HRK + - HTG + - HUF + - IDR + - ILS + - IMP + - INR + - IQD + - IRR + - ISK + - JEP + - JMD + - JOD + - JPY + - KES + - KGS + - KHR + - KID + - KMF + - KRW + - KWD + - KYD + - KZT + - LAK + - LBP + - LKR + - LRD + - LSL + - LYD + - MAD + - MDL + - MGA + - MKD + - MMK + - MNT + - MOP + - MRU + - MUR + - MVR + - MWK + - MXN + - MYR + - MZN + - NAD + - NGN + - NIO + - NOK + - NPR + - NZD + - OMR + - PAB + - PEN + - PGK + - PHP + - PKR + - PLN + - PYG + - QAR + - RON + - RSD + - RUB + - RWF + - SAR + - SBD + - SCR + - SDG + - SEK + - SGD + - SHP + - SLE + - SLL + - SOS + - SRD + - SSP + - STN + - SYP + - SZL + - THB + - TJS + - TMT + - TND + - TOP + - TRY + - TTD + - TVD + - TWD + - TZS + - UAH + - UGX + - USD + - UYU + - UZS + - VES + - VND + - VUV + - WST + - XAF + - XCD + - XDR + - XOF + - XPF + - YER + - ZAR + - ZMW + - ZWL + description: The currency of the vendor credit + exchange_rate: + type: string + example: '1.2' nullable: true - access_role: + description: The exchange rate applied to the vendor credit + company_id: type: string - example: ADMIN - enum: - - SUPER_ADMIN - - ADMIN - - TEAM_MEMBER - - LIMITED_TEAM_MEMBER - - INTERVIEWER - description: The access role of the user + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - remote_created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - description: The remote creation date of the user + description: The UUID of the associated company + tracking_categories: + example: + - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true - remote_modified_at: - format: date-time + description: >- + The UUID of the tracking categories associated with the vendor credit + type: array + items: + type: string + accounting_period_id: type: string - example: '2024-10-01T12:00:00Z' - description: The remote modification date of the user + example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true + description: The UUID of the associated accounting period + line_items: + description: The line items associated with this vendor credit + type: array + items: + $ref: '#/components/schemas/LineItem' field_mappings: type: object example: - fav_dish: broccoli - fav_color: red + custom_field_1: value1 + custom_field_2: value2 + nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora - nullable: true - additionalProperties: true id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - description: The UUID of the user nullable: true + description: The UUID of the vendor credit record remote_id: type: string - example: id_1 - description: The remote ID of the user in the context of the 3rd Party - nullable: true - remote_data: - type: object - example: - fav_dish: broccoli - fav_color: red - description: The remote data of the user in the context of the 3rd Party + example: remote_id_1234 nullable: true - additionalProperties: true + description: The remote ID of the vendor credit created_at: format: date-time type: string - example: '2024-10-01T12:00:00Z' - description: The created date of the object - nullable: true + example: '2024-06-15T12:00:00Z' + nullable: false + description: The created date of the vendor credit modified_at: format: date-time type: string - example: '2024-10-01T12:00:00Z' - description: The modified date of the object - nullable: true - UnifiedAtsEeocsOutput: - type: object - properties: - candidate_id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the candidate - submitted_at: - type: string - example: '2024-10-01T12:00:00Z' + example: '2024-06-15T12:00:00Z' + nullable: false + description: The last modified date of the vendor credit + remote_updated_at: format: date-time - nullable: true - description: The submission date of the EEOC - race: - type: string - enum: - - AMERICAN_INDIAN_OR_ALASKAN_NATIVE - - ASIAN - - BLACK_OR_AFRICAN_AMERICAN - - HISPANIC_OR_LATINO - - WHITE - - NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER - - TWO_OR_MORE_RACES - - DECLINE_TO_SELF_IDENTIFY - example: AMERICAN_INDIAN_OR_ALASKAN_NATIVE - nullable: true - description: The race of the candidate - gender: - type: string - example: MALE - enum: - - MALE - - FEMALE - - NON_BINARY - - OTHER - - DECLINE_TO_SELF_IDENTIFY - nullable: true - description: The gender of the candidate - veteran_status: - type: string - example: I_AM_NOT_A_PROTECTED_VETERAN - enum: - - I_AM_NOT_A_PROTECTED_VETERAN - - >- - I_IDENTIFY_AS_ONE_OR_MORE_OF_THE_CLASSIFICATIONS_OF_A_PROTECTED_VETERAN - - I_DONT_WISH_TO_ANSWER - nullable: true - description: The veteran status of the candidate - disability_status: type: string - enum: - - YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY - - NO_I_DONT_HAVE_A_DISABILITY - - I_DONT_WISH_TO_ANSWER - example: YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY - nullable: true - description: The disability status of the candidate - field_mappings: - type: object - example: - fav_dish: broccoli - fav_color: red - additionalProperties: true + example: '2024-06-15T12:00:00Z' nullable: true description: >- - The custom field mappings of the object between the remote 3rd party & Panora - id: - type: string - example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f - nullable: true - description: The UUID of the EEOC - remote_id: - type: string - example: id_1 - nullable: true - description: The remote ID of the EEOC in the context of the 3rd Party + The date when the vendor credit was last updated in the remote system remote_data: type: object example: - fav_dish: broccoli - fav_color: red - nullable: true - additionalProperties: true - description: The remote data of the EEOC in the context of the 3rd Party - created_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' + raw_data: + additional_field: some value nullable: true - description: The created date of the object - modified_at: - format: date-time - type: string - example: '2024-10-01T12:00:00Z' - nullable: true - description: The modified date of the object - UnifiedAccountingAccountOutput: - type: object - properties: {} - UnifiedAccountingAccountInput: - type: object - properties: {} - UnifiedAccountingAddressOutput: - type: object - properties: {} - UnifiedAccountingAttachmentOutput: - type: object - properties: {} - UnifiedAccountingAttachmentInput: - type: object - properties: {} - UnifiedAccountingBalancesheetOutput: - type: object - properties: {} - UnifiedAccountingCashflowstatementOutput: - type: object - properties: {} - UnifiedAccountingCompanyinfoOutput: - type: object - properties: {} - UnifiedAccountingContactOutput: - type: object - properties: {} - UnifiedAccountingContactInput: - type: object - properties: {} - UnifiedAccountingCreditnoteOutput: - type: object - properties: {} - UnifiedAccountingExpenseOutput: - type: object - properties: {} - UnifiedAccountingExpenseInput: - type: object - properties: {} - UnifiedAccountingIncomestatementOutput: - type: object - properties: {} - UnifiedAccountingInvoiceOutput: - type: object - properties: {} - UnifiedAccountingInvoiceInput: - type: object - properties: {} - UnifiedAccountingItemOutput: - type: object - properties: {} - UnifiedAccountingJournalentryOutput: - type: object - properties: {} - UnifiedAccountingJournalentryInput: - type: object - properties: {} - UnifiedAccountingPaymentOutput: - type: object - properties: {} - UnifiedAccountingPaymentInput: - type: object - properties: {} - UnifiedAccountingPhonenumberOutput: - type: object - properties: {} - UnifiedAccountingPurchaseorderOutput: - type: object - properties: {} - UnifiedAccountingPurchaseorderInput: - type: object - properties: {} - UnifiedAccountingTaxrateOutput: - type: object - properties: {} - UnifiedAccountingTrackingcategoryOutput: - type: object - properties: {} - UnifiedAccountingTransactionOutput: - type: object - properties: {} - UnifiedAccountingVendorcreditOutput: - type: object - properties: {} + description: The remote data of the vendor credit in the context of the 3rd Party UnifiedFilestorageDriveOutput: type: object properties: @@ -29099,7 +36155,7 @@ components: nullable: true field_mappings: type: object - example: &ref_143 + example: &ref_179 fav_dish: broccoli fav_color: red description: >- @@ -29184,7 +36240,7 @@ components: nullable: true field_mappings: type: object - example: *ref_143 + example: *ref_179 description: >- The custom field mappings of the object between the remote 3rd party & Panora nullable: true @@ -29241,7 +36297,7 @@ components: description: The UUID of the permission tied to the folder field_mappings: type: object - example: &ref_144 + example: &ref_180 fav_dish: broccoli fav_color: red additionalProperties: true @@ -29331,7 +36387,7 @@ components: description: The UUID of the permission tied to the folder field_mappings: type: object - example: *ref_144 + example: *ref_180 additionalProperties: true nullable: true description: >- @@ -29492,13 +36548,13 @@ components: type: string example: ACTIVE nullable: true - enum: &ref_145 + enum: &ref_181 - ARCHIVED - ACTIVE - DRAFT description: The status of the product. Either ACTIVE, DRAFT OR ARCHIVED. images_urls: - example: &ref_146 + example: &ref_182 - https://myproduct/image nullable: true description: The URLs of the product images @@ -29516,7 +36572,7 @@ components: nullable: true description: The vendor of the product variants: - example: &ref_147 + example: &ref_183 - title: teeshirt price: 20 sku: '3' @@ -29528,7 +36584,7 @@ components: items: $ref: '#/components/schemas/Variant' tags: - example: &ref_148 + example: &ref_184 - tag_1 nullable: true description: The tags associated with the product @@ -29537,7 +36593,7 @@ components: type: string field_mappings: type: object - example: &ref_149 + example: &ref_185 fav_dish: broccoli fav_color: red nullable: true @@ -29587,10 +36643,10 @@ components: type: string example: ACTIVE nullable: true - enum: *ref_145 + enum: *ref_181 description: The status of the product. Either ACTIVE, DRAFT OR ARCHIVED. images_urls: - example: *ref_146 + example: *ref_182 nullable: true description: The URLs of the product images type: array @@ -29607,13 +36663,13 @@ components: nullable: true description: The vendor of the product variants: - example: *ref_147 + example: *ref_183 description: The variants of the product type: array items: $ref: '#/components/schemas/Variant' tags: - example: *ref_148 + example: *ref_184 nullable: true description: The tags associated with the product type: array @@ -29621,20 +36677,17 @@ components: type: string field_mappings: type: object - example: *ref_149 + example: *ref_185 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora - LineItem: - type: object - properties: {} UnifiedEcommerceOrderOutput: type: object properties: order_status: type: string example: UNSHIPPED - enum: &ref_150 + enum: &ref_186 - PENDING - UNSHIPPED - SHIPPED @@ -29649,7 +36702,7 @@ components: payment_status: type: string example: SUCCESS - enum: &ref_151 + enum: &ref_187 - SUCCESS - FAIL nullable: true @@ -29658,7 +36711,7 @@ components: type: string nullable: true example: AUD - enum: &ref_152 + enum: &ref_188 - AED - AFN - ALL @@ -29847,7 +36900,7 @@ components: type: string nullable: true example: PENDING - enum: &ref_153 + enum: &ref_189 - PENDING - FULFILLED - CANCELED @@ -29859,7 +36912,7 @@ components: description: The UUID of the customer associated with the order items: nullable: true - example: &ref_154 + example: &ref_190 - remote_id: '12345' product_id: prod_001 variant_id: var_001 @@ -29890,7 +36943,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_155 + example: &ref_191 fav_dish: broccoli fav_color: red nullable: true @@ -29929,7 +36982,7 @@ components: order_status: type: string example: UNSHIPPED - enum: *ref_150 + enum: *ref_186 nullable: true description: The status of the order order_number: @@ -29940,14 +36993,14 @@ components: payment_status: type: string example: SUCCESS - enum: *ref_151 + enum: *ref_187 nullable: true description: The payment status of the order currency: type: string nullable: true example: AUD - enum: *ref_152 + enum: *ref_188 description: >- The currency of the order. Authorized value must be of type CurrencyCode (ISO 4217) total_price: @@ -29974,7 +37027,7 @@ components: type: string nullable: true example: PENDING - enum: *ref_153 + enum: *ref_189 description: The fulfillment status of the order customer_id: type: string @@ -29983,14 +37036,14 @@ components: description: The UUID of the customer associated with the order items: nullable: true - example: *ref_154 + example: *ref_190 description: The items in the order type: array items: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_155 + example: *ref_191 nullable: true description: >- The custom field mappings of the object between the remote 3rd party & Panora @@ -30164,7 +37217,7 @@ components: field_mappings: type: object nullable: true - example: &ref_156 + example: &ref_192 fav_dish: broccoli fav_color: red description: >- @@ -30235,7 +37288,7 @@ components: field_mappings: type: object nullable: true - example: *ref_156 + example: *ref_192 description: >- The custom field mappings of the attachment between the remote 3rd party & Panora additionalProperties: true From 2e62b82a3fe2149a5ec8a1baaaa17d0e9cf397a5 Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 16 Aug 2024 02:26:22 +0200 Subject: [PATCH 03/49] :sparkles: Deel + sage hris integrations --- .../hris/hris.connection.module.ts | 2 + .../hris/services/sage/sage.service.ts | 143 +++++++++++++++ .../utils/types/original/original.hris.ts | 31 +++- packages/api/src/hris/@lib/@utils/index.ts | 15 ++ .../api/src/hris/company/company.module.ts | 4 + .../src/hris/company/services/deel/index.ts | 72 ++++++++ .../src/hris/company/services/deel/mappers.ts | 70 ++++++++ .../src/hris/company/services/deel/types.ts | 7 + .../api/src/hris/employee/employee.module.ts | 8 + .../src/hris/employee/services/deel/index.ts | 60 +++++++ .../hris/employee/services/deel/mappers.ts | 170 ++++++++++++++++++ .../src/hris/employee/services/deel/types.ts | 100 +++++++++++ .../src/hris/employee/services/sage/index.ts | 58 ++++++ .../hris/employee/services/sage/mappers.ts | 115 ++++++++++++ .../src/hris/employee/services/sage/types.ts | 55 ++++++ .../src/hris/employment/employment.module.ts | 2 + .../hris/employment/services/deel/mappers.ts | 136 ++++++++++++++ .../hris/employment/services/deel/types.ts | 50 ++++++ packages/api/src/hris/group/group.module.ts | 8 + .../api/src/hris/group/services/deel/index.ts | 66 +++++++ .../src/hris/group/services/deel/mappers.ts | 61 +++++++ .../api/src/hris/group/services/deel/types.ts | 4 + .../api/src/hris/group/services/sage/index.ts | 66 +++++++ .../src/hris/group/services/sage/mappers.ts | 61 +++++++ .../api/src/hris/group/services/sage/types.ts | 6 + .../api/src/hris/location/location.module.ts | 2 + .../hris/location/services/deel/mappers.ts | 70 ++++++++ .../src/hris/location/services/deel/types.ts | 8 + .../src/hris/timeoff/services/sage/index.ts | 69 +++++++ .../src/hris/timeoff/services/sage/mappers.ts | 115 ++++++++++++ .../src/hris/timeoff/services/sage/types.ts | 30 ++++ .../api/src/hris/timeoff/timeoff.module.ts | 4 + .../timeoffbalance/services/sage/index.ts | 72 ++++++++ .../timeoffbalance/services/sage/mappers.ts | 73 ++++++++ .../timeoffbalance/services/sage/types.ts | 5 + .../timeoffbalance/timeoffbalance.module.ts | 4 + packages/shared/src/connectors/metadata.ts | 44 +++-- 37 files changed, 1846 insertions(+), 20 deletions(-) create mode 100644 packages/api/src/@core/connections/hris/services/sage/sage.service.ts create mode 100644 packages/api/src/hris/company/services/deel/index.ts create mode 100644 packages/api/src/hris/company/services/deel/mappers.ts create mode 100644 packages/api/src/hris/company/services/deel/types.ts create mode 100644 packages/api/src/hris/employee/services/deel/index.ts create mode 100644 packages/api/src/hris/employee/services/deel/mappers.ts create mode 100644 packages/api/src/hris/employee/services/deel/types.ts create mode 100644 packages/api/src/hris/employee/services/sage/index.ts create mode 100644 packages/api/src/hris/employee/services/sage/mappers.ts create mode 100644 packages/api/src/hris/employee/services/sage/types.ts create mode 100644 packages/api/src/hris/employment/services/deel/mappers.ts create mode 100644 packages/api/src/hris/employment/services/deel/types.ts create mode 100644 packages/api/src/hris/group/services/deel/index.ts create mode 100644 packages/api/src/hris/group/services/deel/mappers.ts create mode 100644 packages/api/src/hris/group/services/deel/types.ts create mode 100644 packages/api/src/hris/group/services/sage/index.ts create mode 100644 packages/api/src/hris/group/services/sage/mappers.ts create mode 100644 packages/api/src/hris/group/services/sage/types.ts create mode 100644 packages/api/src/hris/location/services/deel/mappers.ts create mode 100644 packages/api/src/hris/location/services/deel/types.ts create mode 100644 packages/api/src/hris/timeoff/services/sage/index.ts create mode 100644 packages/api/src/hris/timeoff/services/sage/mappers.ts create mode 100644 packages/api/src/hris/timeoff/services/sage/types.ts create mode 100644 packages/api/src/hris/timeoffbalance/services/sage/index.ts create mode 100644 packages/api/src/hris/timeoffbalance/services/sage/mappers.ts create mode 100644 packages/api/src/hris/timeoffbalance/services/sage/types.ts diff --git a/packages/api/src/@core/connections/hris/hris.connection.module.ts b/packages/api/src/@core/connections/hris/hris.connection.module.ts index 6c70292b3..da5e50d4a 100644 --- a/packages/api/src/@core/connections/hris/hris.connection.module.ts +++ b/packages/api/src/@core/connections/hris/hris.connection.module.ts @@ -13,6 +13,7 @@ import { NamelyConnectionService } from './services/namely/namely.service'; import { PayfitConnectionService } from './services/payfit/payfit.service'; import { ServiceRegistry } from './services/registry.service'; import { RipplingConnectionService } from './services/rippling/rippling.service'; +import { SageConnectionService } from './services/sage/sage.service'; @Module({ imports: [WebhookModule, BullQueueModule], @@ -30,6 +31,7 @@ import { RipplingConnectionService } from './services/rippling/rippling.service' FactorialConnectionService, NamelyConnectionService, BamboohrConnectionService, + SageConnectionService, ], exports: [HrisConnectionsService], }) diff --git a/packages/api/src/@core/connections/hris/services/sage/sage.service.ts b/packages/api/src/@core/connections/hris/services/sage/sage.service.ts new file mode 100644 index 000000000..cbcdf1199 --- /dev/null +++ b/packages/api/src/@core/connections/hris/services/sage/sage.service.ts @@ -0,0 +1,143 @@ +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 { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; +import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; +import { ConnectionUtils } from '@@core/connections/@utils'; +import { + AbstractBaseConnectionService, + OAuthCallbackParams, + PassthroughInput, + RefreshParams, +} from '@@core/connections/@utils/types'; +import { PassthroughResponse } from '@@core/passthrough/types'; +import { Injectable } from '@nestjs/common'; +import { + AuthStrategy, + CONNECTORS_METADATA, + DynamicApiUrl, + providerToType, +} from '@panora/shared'; +import { v4 as uuidv4 } from 'uuid'; +import { ServiceRegistry } from '../registry.service'; + +@Injectable() +export class SageConnectionService extends AbstractBaseConnectionService { + private readonly type: string; + + constructor( + protected prisma: PrismaService, + private logger: LoggerService, + protected cryptoService: EncryptionService, + private registry: ServiceRegistry, + private connectionUtils: ConnectionUtils, + private cService: ConnectionsStrategiesService, + private retryService: RetryHandler, + ) { + super(prisma, cryptoService); + this.logger.setContext(SageConnectionService.name); + this.registry.registerService('sage', this); + this.type = providerToType('sage', 'hris', AuthStrategy.oauth2); + } + + async passthrough( + input: PassthroughInput, + connectionId: string, + ): Promise { + try { + const { headers } = input; + const config = await this.constructPassthrough(input, connectionId); + + const connection = await this.prisma.connections.findUnique({ + where: { + id_connection: connectionId, + }, + }); + + config.headers = { + ...config.headers, + ...headers, + 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), + }; + + return await this.retryService.makeRequest( + { + method: config.method, + url: config.url, + data: config.data, + headers: config.headers, + }, + 'hris.sage.passthrough', + config.linkedUserId, + ); + } catch (error) { + throw error; + } + } + + async handleCallback(opts: OAuthCallbackParams) { + try { + const { linkedUserId, projectId, body } = opts; + const { api_key, subdomain } = body; + const isNotUnique = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'sage', + vertical: 'hris', + }, + }); + + let db_res; + const connection_token = uuidv4(); + const BASE_API_URL = ( + CONNECTORS_METADATA['hris']['sage'].urls.apiUrl as DynamicApiUrl + )(subdomain); + + if (isNotUnique) { + db_res = await this.prisma.connections.update({ + where: { + id_connection: isNotUnique.id_connection, + }, + data: { + access_token: this.cryptoService.encrypt(api_key), + account_url: BASE_API_URL, + status: 'valid', + created_at: new Date(), + }, + }); + } else { + db_res = await this.prisma.connections.create({ + data: { + id_connection: uuidv4(), + connection_token: connection_token, + provider_slug: 'sage', + vertical: 'hris', + token_type: 'basic', + account_url: BASE_API_URL, + access_token: this.cryptoService.encrypt(api_key), + status: 'valid', + created_at: new Date(), + projects: { + connect: { id_project: projectId }, + }, + linked_users: { + connect: { + id_linked_user: await this.connectionUtils.getLinkedUserId( + projectId, + linkedUserId, + ), + }, + }, + }, + }); + } + return db_res; + } catch (error) { + throw error; + } + } + + handleTokenRefresh?(opts: RefreshParams): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/packages/api/src/@core/utils/types/original/original.hris.ts b/packages/api/src/@core/utils/types/original/original.hris.ts index e85d40144..df980dafa 100644 --- a/packages/api/src/@core/utils/types/original/original.hris.ts +++ b/packages/api/src/@core/utils/types/original/original.hris.ts @@ -1,12 +1,21 @@ /* INPUT */ import { GustoBenefitOutput } from '@hris/benefit/services/gusto/types'; +import { DeelCompanyOutput } from '@hris/company/services/deel/types'; import { GustoCompanyOutput } from '@hris/company/services/gusto/types'; +import { DeelEmployeeOutput } from '@hris/employee/services/deel/types'; import { GustoEmployeeOutput } from '@hris/employee/services/gusto/types'; +import { SageEmployeeOutput } from '@hris/employee/services/sage/types'; import { GustoEmployerbenefitOutput } from '@hris/employerbenefit/services/gusto/types'; +import { DeelEmploymentOutput } from '@hris/employment/services/deel/types'; import { GustoEmploymentOutput } from '@hris/employment/services/gusto/types'; +import { DeelGroupOutput } from '@hris/group/services/deel/types'; import { GustoGroupOutput } from '@hris/group/services/gusto/types'; +import { SageGroupOutput } from '@hris/group/services/sage/types'; +import { DeelLocationOutput } from '@hris/location/services/deel/types'; import { GustoLocationOutput } from '@hris/location/services/gusto/types'; +import { SageTimeoffOutput } from '@hris/timeoff/services/sage/types'; +import { SageTimeoffbalanceOutput } from '@hris/timeoffbalance/services/sage/types'; /* bankinfo */ export type OriginalBankInfoInput = any; @@ -79,13 +88,16 @@ export type OriginalBankInfoOutput = any; export type OriginalBenefitOutput = GustoBenefitOutput; /* company */ -export type OriginalCompanyOutput = GustoCompanyOutput; +export type OriginalCompanyOutput = GustoCompanyOutput | DeelCompanyOutput; /* dependent */ export type OriginalDependentOutput = any; /* employee */ -export type OriginalEmployeeOutput = GustoEmployeeOutput; +export type OriginalEmployeeOutput = + | GustoEmployeeOutput + | SageEmployeeOutput + | DeelEmployeeOutput; /* employeepayrollrun */ export type OriginalEmployeePayrollRunOutput = any; @@ -94,13 +106,18 @@ export type OriginalEmployeePayrollRunOutput = any; export type OriginalEmployerBenefitOutput = GustoEmployerbenefitOutput; /* employment */ -export type OriginalEmploymentOutput = GustoEmploymentOutput; +export type OriginalEmploymentOutput = + | GustoEmploymentOutput + | DeelEmploymentOutput; /* group */ -export type OriginalGroupOutput = GustoGroupOutput; +export type OriginalGroupOutput = + | GustoGroupOutput + | DeelGroupOutput + | SageGroupOutput; /* location */ -export type OriginalLocationOutput = GustoLocationOutput; +export type OriginalLocationOutput = GustoLocationOutput | DeelLocationOutput; /* paygroup */ export type OriginalPayGroupOutput = any; @@ -109,10 +126,10 @@ export type OriginalPayGroupOutput = any; export type OriginalPayrollRunOutput = any; /* timeoff */ -export type OriginalTimeoffOutput = any; +export type OriginalTimeoffOutput = SageTimeoffOutput; /* timeoffbalance */ -export type OriginalTimeoffBalanceOutput = any; +export type OriginalTimeoffBalanceOutput = SageTimeoffbalanceOutput; /* timesheetentry */ export type OriginalTimesheetentryOutput = any; diff --git a/packages/api/src/hris/@lib/@utils/index.ts b/packages/api/src/hris/@lib/@utils/index.ts index 23b09c261..d4b9be7af 100644 --- a/packages/api/src/hris/@lib/@utils/index.ts +++ b/packages/api/src/hris/@lib/@utils/index.ts @@ -35,6 +35,21 @@ export class Utils { } } + async getGroupUuidFromRemoteId(id: string, connection_id: string) { + try { + const res = await this.prisma.hris_groups.findFirst({ + where: { + remote_id: id, + id_connection: connection_id, + }, + }); + if (!res) return; + return res.id_hris_group; + } catch (error) { + throw error; + } + } + async getEmployerBenefitUuidFromRemoteId(id: string, connection_id: string) { try { const res = await this.prisma.hris_employer_benefits.findFirst({ diff --git a/packages/api/src/hris/company/company.module.ts b/packages/api/src/hris/company/company.module.ts index eae8227f4..a1cfc0614 100644 --- a/packages/api/src/hris/company/company.module.ts +++ b/packages/api/src/hris/company/company.module.ts @@ -9,6 +9,8 @@ import { CoreUnification } from '@@core/@core-services/unification/core-unificat import { GustoCompanyMapper } from './services/gusto/mappers'; import { GustoService } from './services/gusto'; import { Utils } from '@hris/@lib/@utils'; +import { DeelService } from './services/deel'; +import { DeelCompanyMapper } from './services/deel/mappers'; @Module({ controllers: [CompanyController], providers: [ @@ -20,8 +22,10 @@ import { Utils } from '@hris/@lib/@utils'; ServiceRegistry, IngestDataService, GustoCompanyMapper, + DeelCompanyMapper, /* PROVIDERS SERVICES */ GustoService, + DeelService, ], exports: [SyncService], }) diff --git a/packages/api/src/hris/company/services/deel/index.ts b/packages/api/src/hris/company/services/deel/index.ts new file mode 100644 index 000000000..6eb47d4ea --- /dev/null +++ b/packages/api/src/hris/company/services/deel/index.ts @@ -0,0 +1,72 @@ +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { EnvironmentService } from '@@core/@core-services/environment/environment.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 { HrisObject } from '@hris/@lib/@types'; +import { ICompanyService } from '@hris/company/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { DeelCompanyOutput } from './types'; +import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; +import { OriginalCompanyOutput } from '@@core/utils/types/original/original.hris'; + +@Injectable() +export class DeelService implements ICompanyService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private env: EnvironmentService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + HrisObject.company.toUpperCase() + ':' + DeelService.name, + ); + this.registry.registerService('deel', this); + } + + addCompany( + companyData: DesunifyReturnType, + linkedUserId: string, + ): Promise> { + throw new Error('Method not implemented.'); + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'deel', + vertical: 'hris', + }, + }); + + const resp = await axios.get( + `${connection.account_url}/rest/v2/legal-entities`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + this.logger.log(`Synced deel companys !`); + + return { + data: resp.data.data, + message: 'Deel companys retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/hris/company/services/deel/mappers.ts b/packages/api/src/hris/company/services/deel/mappers.ts new file mode 100644 index 000000000..5bdc9293c --- /dev/null +++ b/packages/api/src/hris/company/services/deel/mappers.ts @@ -0,0 +1,70 @@ +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { Injectable } from '@nestjs/common'; +import { DeelCompanyOutput } from './types'; +import { + UnifiedHrisCompanyInput, + UnifiedHrisCompanyOutput, +} from '@hris/company/types/model.unified'; +import { ICompanyMapper } from '@hris/company/types'; +import { Utils } from '@hris/@lib/@utils'; + +@Injectable() +export class DeelCompanyMapper implements ICompanyMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private ingestService: IngestDataService, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService('hris', 'company', 'deel', this); + } + + async desunify( + source: UnifiedHrisCompanyInput, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + // Implementation for desunify (if needed) + return; + } + + async unify( + source: DeelCompanyOutput | DeelCompanyOutput[], + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleCompanyToUnified( + source, + connectionId, + customFieldMappings, + ); + } + return Promise.all( + source.map((company) => + this.mapSingleCompanyToUnified( + company, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async mapSingleCompanyToUnified( + company: DeelCompanyOutput, + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + return { + remote_id: company.id || null, + legal_name: company.name || null, + display_name: company.name || null, // Using name for display_name as well + eins: [], // Deel doesn't provide EINs in this data structure + remote_data: company, + locations: [], // Deel doesn't provide locations in this data structure + field_mappings: {}, + }; + } +} diff --git a/packages/api/src/hris/company/services/deel/types.ts b/packages/api/src/hris/company/services/deel/types.ts new file mode 100644 index 000000000..ec793698b --- /dev/null +++ b/packages/api/src/hris/company/services/deel/types.ts @@ -0,0 +1,7 @@ +export interface DeelCompanyOutput { + id: string; + name: string; + country: string; + entity_type: 'individual' | string; + entity_subtype: string; +} diff --git a/packages/api/src/hris/employee/employee.module.ts b/packages/api/src/hris/employee/employee.module.ts index ed03dd83f..e01e841db 100644 --- a/packages/api/src/hris/employee/employee.module.ts +++ b/packages/api/src/hris/employee/employee.module.ts @@ -9,6 +9,10 @@ import { CoreUnification } from '@@core/@core-services/unification/core-unificat import { GustoEmployeeMapper } from './services/gusto/mappers'; import { GustoService } from './services/gusto'; import { Utils } from '@hris/@lib/@utils'; +import { SageEmployeeMapper } from './services/sage/mappers'; +import { SageService } from './services/sage'; +import { DeelService } from './services/deel'; +import { DeelEmployeeMapper } from './services/deel/mappers'; @Module({ controllers: [EmployeeController], providers: [ @@ -20,8 +24,12 @@ import { Utils } from '@hris/@lib/@utils'; ServiceRegistry, IngestDataService, GustoEmployeeMapper, + SageEmployeeMapper, + DeelEmployeeMapper, /* PROVIDERS SERVICES */ GustoService, + SageService, + DeelService, ], exports: [SyncService], }) diff --git a/packages/api/src/hris/employee/services/deel/index.ts b/packages/api/src/hris/employee/services/deel/index.ts new file mode 100644 index 000000000..966c3ff66 --- /dev/null +++ b/packages/api/src/hris/employee/services/deel/index.ts @@ -0,0 +1,60 @@ +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { EnvironmentService } from '@@core/@core-services/environment/environment.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 { HrisObject } from '@hris/@lib/@types'; +import { IEmployeeService } from '@hris/employee/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { DeelEmployeeOutput } from './types'; + +@Injectable() +export class DeelService implements IEmployeeService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private env: EnvironmentService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + HrisObject.employee.toUpperCase() + ':' + DeelService.name, + ); + this.registry.registerService('deel', this); + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'deel', + vertical: 'hris', + }, + }); + + const resp = await axios.get(`${connection.account_url}/rest/v2/people`, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced deel employees !`); + + return { + data: resp.data.data, + message: 'Deel employees retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/hris/employee/services/deel/mappers.ts b/packages/api/src/hris/employee/services/deel/mappers.ts new file mode 100644 index 000000000..0912bdb4d --- /dev/null +++ b/packages/api/src/hris/employee/services/deel/mappers.ts @@ -0,0 +1,170 @@ +import { Injectable } from '@nestjs/common'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { DeelEmployeeOutput } from './types'; +import { + UnifiedHrisEmployeeInput, + UnifiedHrisEmployeeOutput, +} from '@hris/employee/types/model.unified'; +import { IEmployeeMapper } from '@hris/employee/types'; +import { Utils } from '@hris/@lib/@utils'; +import { HrisObject } from '@panora/shared'; +import { UnifiedHrisEmploymentOutput } from '@hris/employment/types/model.unified'; +import { DeelEmploymentOutput } from '@hris/employment/services/deel/types'; +import { DeelLocationOutput } from '@hris/location/services/deel/types'; +import { UnifiedHrisLocationOutput } from '@hris/location/types/model.unified'; + +@Injectable() +export class DeelEmployeeMapper implements IEmployeeMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private ingestService: IngestDataService, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService('hris', 'employee', 'deel', this); + } + + async desunify( + source: UnifiedHrisEmployeeInput, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + // Implementation for desunify (if needed) + return; + } + + async unify( + source: DeelEmployeeOutput | DeelEmployeeOutput[], + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleEmployeeToUnified( + source, + connectionId, + customFieldMappings, + ); + } + return Promise.all( + source.map((employee) => + this.mapSingleEmployeeToUnified( + employee, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async mapSingleEmployeeToUnified( + employee: DeelEmployeeOutput, + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + const opts: any = {}; + + if (employee.client_legal_entity) { + const company_id = await this.utils.getCompanyUuidFromRemoteId( + employee.client_legal_entity.id, // Assuming client_legal_entity has an id field + connectionId, + ); + if (company_id) { + opts.company_id = company_id; + } + } + + if (employee.direct_manager) { + const manager_id = await this.utils.getEmployeeUuidFromRemoteId( + employee.direct_manager.id, + connectionId, + ); + if (manager_id) { + opts.manager_id = manager_id; + } + } + + if (employee.employments) { + const employments = await this.ingestService.ingestData< + UnifiedHrisEmploymentOutput, + DeelEmploymentOutput + >( + employee.employments, + 'deel', + connectionId, + 'hris', + HrisObject.employment, + [], + ); + if (employments) { + opts.employments = employments.map((emp) => emp.id_hris_employment); + } + } + + if (employee.addresses) { + const addresses = await this.ingestService.ingestData< + UnifiedHrisLocationOutput, + DeelLocationOutput + >( + employee.addresses.map((add) => { + return { + ...add, + type: 'HOME', + }; + }), + 'deel', + connectionId, + 'hris', + HrisObject.location, + [], + ); + if (addresses) { + opts.locations = addresses.map((add) => add.id_hris_location); + } + } + + const primaryEmployment = employee.employments.find((emp) => !emp.is_ended); + + return { + remote_id: employee.id, + remote_data: employee, + first_name: employee.first_name, + last_name: employee.last_name, + preferred_name: null, // Deel doesn't provide this information + display_full_name: employee.full_name, + work_email: + employee.emails.find((email) => email.type === 'work')?.value || null, + personal_email: + employee.emails.find((email) => email.type === 'personal')?.value || + null, + mobile_phone_number: null, // Deel doesn't provide this information in the given structure + start_date: primaryEmployment + ? new Date(primaryEmployment.start_date) + : null, + termination_date: employee.completion_date + ? new Date(employee.completion_date) + : null, + employment_status: this.mapEmploymentStatus(employee.hiring_status), + date_of_birth: employee.birth_date ? new Date(employee.birth_date) : null, + avatar_url: null, // Deel doesn't provide this information in the given structure + gender: null, // Deel doesn't provide this information in the given structure + ethnicity: null, // Deel doesn't provide this information in the given structure + marital_status: null, // Deel doesn't provide this information in the given structure + job_title: primaryEmployment ? primaryEmployment.job_title : null, + ...opts, + }; + } + + private mapEmploymentStatus( + status: string, + ): 'ACTIVE' | 'PENDING' | 'INACTIVE' { + switch (status.toUpperCase()) { + case 'ACTIVE': + return 'ACTIVE'; + case 'PENDING': + return 'PENDING'; + default: + return 'INACTIVE'; + } + } +} diff --git a/packages/api/src/hris/employee/services/deel/types.ts b/packages/api/src/hris/employee/services/deel/types.ts new file mode 100644 index 000000000..65eb03f3d --- /dev/null +++ b/packages/api/src/hris/employee/services/deel/types.ts @@ -0,0 +1,100 @@ +export type DeelEmployeeOutput = Partial<{ + id: string; + created_at: string; // Date-time in string format + first_name: string; + last_name: string; + full_name: string; + addresses: DeelAddress[]; + emails: DeelEmail[]; + birth_date: string; + start_date: string; // Date in string format + nationalities: string[]; + client_legal_entity: DeelClientLegalEntity; + state: string; + seniority: string; + completion_date: string | null; + direct_manager: DeelDirectManager | null; + direct_reports: DeelDirectManager[] | null; + direct_reports_count: number; + employments: DeelEmployment[]; + hiring_status: string; + new_hiring_status: string; + hiring_type: string; + job_title: string; + country: string; + timezone: string; + department: DeelDepartment; + work_location: string; + updated_at: string | null; // Date-time in string format +}>; + +export interface DeelAddress { + streetAddress: string; + locality: string; + region: string; + postalCode: string; + country: string; +} + +export interface DeelEmail { + type: string; + value: string; +} + +export interface DeelClientLegalEntity { + id: string; + name: string; +} + +export interface DeelDirectManager { + id: string; + last_name: string; + first_name: string; + work_email: string; +} + +export interface DeelTeam { + id: string; + name: string; +} + +export interface DeelPayment { + rate: number; + scale: string; + currency: string; + contract_name: string; +} +export interface DeelDepartment { + id: string; + name: string; + parent: string; +} + +export interface DeelEmployment { + id: string; + name: string; + team: DeelTeam; + email: string; + state: string; + country: string; + payment: DeelPayment; + is_ended: boolean; + timezone: string; + job_title: string; + seniority: string; + start_date: string; // Date in string format + work_email: string; + hiring_type: string; + hiring_status: string; + completion_date: string; + contract_status: string; + voluntarily_left: string; + contract_coverage: string[]; + new_hiring_status: string; + client_legal_entity: DeelClientLegalEntity; + has_eor_termination: string; + contract_is_archived: boolean; + contract_has_contractor: boolean; + is_user_contract_deleted: boolean; + hris_direct_employee_invitation: string; +} diff --git a/packages/api/src/hris/employee/services/sage/index.ts b/packages/api/src/hris/employee/services/sage/index.ts new file mode 100644 index 000000000..be8e2937c --- /dev/null +++ b/packages/api/src/hris/employee/services/sage/index.ts @@ -0,0 +1,58 @@ +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { EnvironmentService } from '@@core/@core-services/environment/environment.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 { HrisObject } from '@hris/@lib/@types'; +import { IEmployeeService } from '@hris/employee/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { SageEmployeeOutput } from './types'; + +@Injectable() +export class SageService implements IEmployeeService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private env: EnvironmentService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + HrisObject.employee.toUpperCase() + ':' + SageService.name, + ); + this.registry.registerService('sage', this); + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'sage', + vertical: 'hris', + }, + }); + + const resp = await axios.get(`${connection.account_url}/api/employees`, { + headers: { + 'Content-Type': 'application/json', + 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), + }, + }); + this.logger.log(`Synced sage employees !`); + + return { + data: resp.data.data, + message: 'Sage employees retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/hris/employee/services/sage/mappers.ts b/packages/api/src/hris/employee/services/sage/mappers.ts new file mode 100644 index 000000000..3c0064346 --- /dev/null +++ b/packages/api/src/hris/employee/services/sage/mappers.ts @@ -0,0 +1,115 @@ +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { Utils } from '@hris/@lib/@utils'; +import { IEmployeeMapper } from '@hris/employee/types'; +import { + EmploymentStatus, + Gender, + MartialStatus, + UnifiedHrisEmployeeInput, + UnifiedHrisEmployeeOutput, +} from '@hris/employee/types/model.unified'; +import { Injectable } from '@nestjs/common'; +import { SageEmployeeOutput } from './types'; + +@Injectable() +export class SageEmployeeMapper implements IEmployeeMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private ingestService: IngestDataService, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService('hris', 'employee', 'sage', this); + } + + async desunify( + source: UnifiedHrisEmployeeInput, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + // Implementation for desunify (if needed) + return; + } + + async unify( + source: SageEmployeeOutput | SageEmployeeOutput[], + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleEmployeeToUnified( + source, + connectionId, + customFieldMappings, + ); + } + return Promise.all( + source.map((employee) => + this.mapSingleEmployeeToUnified( + employee, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async mapSingleEmployeeToUnified( + employee: SageEmployeeOutput, + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + return { + remote_id: employee.id.toString(), + remote_data: employee, + first_name: employee.first_name, + last_name: employee.last_name, + display_full_name: `${employee.first_name} ${employee.last_name}`, + work_email: employee.email, + mobile_phone_number: employee.mobile_phone, + employments: [], // We would need to process employment history to populate this + groups: [employee.team], + start_date: new Date(employee.employment_start_date), + employment_status: this.mapEmploymentStatus(employee.employment_status), + date_of_birth: new Date(employee.date_of_birth), + gender: this.mapGender(employee.gender), + marital_status: this.mapMaritalStatus(employee.marital_status), + avatar_url: employee.picture_url, + ssn: employee.personal_identification_number, + employee_number: employee.employee_number, + }; + } + + private mapGender(sageGender: string): Gender { + switch (sageGender.toLowerCase()) { + case 'male': + return 'MALE'; + case 'female': + return 'FEMALE'; + default: + return 'OTHER'; + } + } + + private mapMaritalStatus(sageMaritalStatus: string): MartialStatus { + switch (sageMaritalStatus.toLowerCase()) { + case 'married': + return 'MARRIED_FILING_JOINTLY'; + case 'single': + return 'SINGLE'; + default: + return 'SINGLE'; // Default to single if unknown + } + } + + private mapEmploymentStatus(sageEmploymentStatus: string): EmploymentStatus { + switch (sageEmploymentStatus.toLowerCase()) { + case 'full-time': + case 'part-time': + return 'ACTIVE'; + default: + return 'INACTIVE'; + } + } +} diff --git a/packages/api/src/hris/employee/services/sage/types.ts b/packages/api/src/hris/employee/services/sage/types.ts new file mode 100644 index 000000000..c4f986e70 --- /dev/null +++ b/packages/api/src/hris/employee/services/sage/types.ts @@ -0,0 +1,55 @@ +export type SageEmployeeOutput = Partial<{ + id: number; + email: string; + first_name: string; + last_name: string; + picture_url: string; + employment_start_date: string; + date_of_birth: string; + team: string; + team_id: number; + position: string; + position_id: number; + reports_to_employee_id: number; + work_phone: string; + home_phone: string; + mobile_phone: string; + gender: string; + street_first: string; + street_second: string; + city: string; + post_code: number; + country: string; + employee_number: string; + employment_status: string; + nationality: string; + marital_status: string; + personal_identification_number: string; + tax_number: string; + irregular_contract_worker: boolean; + team_history: SageTeamHistory[]; + employment_status_history: SageEmploymentStatusHistory[]; + position_history: SagePositionHistory[]; +}>; + +export interface SageTeamHistory { + team_id: number; + start_date: string; + end_date: string; + team_name: string; +} + +export interface SageEmploymentStatusHistory { + employment_status_id: number; + start_date: string; + end_date: string; + employment_statu_name: string; // Note: This seems to be a typo in the original data +} + +export interface SagePositionHistory { + position_id: number; + start_date: string; + end_date: string; + position_name: string; + position_code: string; +} diff --git a/packages/api/src/hris/employment/employment.module.ts b/packages/api/src/hris/employment/employment.module.ts index e5d64d523..2ef02de82 100644 --- a/packages/api/src/hris/employment/employment.module.ts +++ b/packages/api/src/hris/employment/employment.module.ts @@ -8,6 +8,7 @@ import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/w import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; import { GustoEmploymentMapper } from './services/gusto/mappers'; import { Utils } from '@hris/@lib/@utils'; +import { DeelEmploymentMapper } from './services/deel/mappers'; @Module({ controllers: [EmploymentController], providers: [ @@ -19,6 +20,7 @@ import { Utils } from '@hris/@lib/@utils'; IngestDataService, Utils, GustoEmploymentMapper, + DeelEmploymentMapper, /* PROVIDERS SERVICES */ ], exports: [SyncService], diff --git a/packages/api/src/hris/employment/services/deel/mappers.ts b/packages/api/src/hris/employment/services/deel/mappers.ts new file mode 100644 index 000000000..bb892abf0 --- /dev/null +++ b/packages/api/src/hris/employment/services/deel/mappers.ts @@ -0,0 +1,136 @@ +import { Injectable } from '@nestjs/common'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { Utils } from '@hris/@lib/@utils'; +import { IEmploymentMapper } from '@hris/employment/types'; +import { + FlsaStatus, + UnifiedHrisEmploymentInput, + UnifiedHrisEmploymentOutput, + EmploymentType, + PayFrequency, + PayPeriod, +} from '@hris/employment/types/model.unified'; +import { DeelEmploymentOutput } from './types'; +import { CurrencyCode } from '@@core/utils/types'; + +@Injectable() +export class DeelEmploymentMapper implements IEmploymentMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private ingestService: IngestDataService, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService('hris', 'employment', 'deel', this); + } + + async desunify( + source: UnifiedHrisEmploymentInput, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + // Implementation for desunify (if needed) + return; + } + + async unify( + source: DeelEmploymentOutput | DeelEmploymentOutput[], + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleEmploymentToUnified( + source, + connectionId, + customFieldMappings, + ); + } + return Promise.all( + source.map((employment) => + this.mapSingleEmploymentToUnified( + employment, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async mapSingleEmploymentToUnified( + employment: DeelEmploymentOutput, + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + return { + remote_id: employment.id, + remote_data: employment, + job_title: employment.job_title, + pay_rate: employment.payment?.rate, + pay_period: this.mapPayPeriod(employment.payment?.scale), + pay_frequency: this.mapPayFrequency(employment.payment?.scale), + pay_currency: employment.payment?.currency as CurrencyCode, + flsa_status: this.mapFlsaStatus(employment.hiring_type), + effective_date: employment.start_date + ? new Date(employment.start_date) + : null, + employment_type: this.mapEmploymentType(employment.hiring_type), + }; + } + + private mapPayPeriod(scale?: string): PayPeriod | undefined { + switch (scale?.toLowerCase()) { + case 'yearly': + return 'YEAR'; + case 'monthly': + return 'MONTH'; + case 'weekly': + return 'WEEK'; + case 'daily': + return 'DAY'; + case 'hourly': + return 'HOUR'; + default: + return undefined; + } + } + + private mapPayFrequency(scale?: string): PayFrequency | undefined { + switch (scale?.toLowerCase()) { + case 'yearly': + return 'ANNUALLY'; + case 'monthly': + return 'MONTHLY'; + case 'weekly': + return 'WEEKLY'; + case 'daily': + return 'WEEKLY'; // Assuming daily payment is done weekly + case 'hourly': + return 'WEEKLY'; // Assuming hourly payment is done weekly + default: + return undefined; + } + } + + private mapFlsaStatus(hiringType?: string): FlsaStatus | undefined { + switch (hiringType?.toLowerCase()) { + case 'employee': + return 'EXEMPT'; // Assuming employees are exempt + case 'contractor': + return 'NONEXEMPT'; // Assuming contractors are nonexempt + default: + return undefined; + } + } + + private mapEmploymentType(hiringType?: string): EmploymentType | undefined { + switch (hiringType?.toLowerCase()) { + case 'employee': + return 'FULL_TIME'; // Assuming employees are full-time + case 'contractor': + return 'CONTRACTOR'; + default: + return undefined; + } + } +} diff --git a/packages/api/src/hris/employment/services/deel/types.ts b/packages/api/src/hris/employment/services/deel/types.ts new file mode 100644 index 000000000..8b4ad5ae9 --- /dev/null +++ b/packages/api/src/hris/employment/services/deel/types.ts @@ -0,0 +1,50 @@ +export type DeelEmploymentOutput = Partial<{ + id: string; + name: string; + team: DeelTeam; + email: string; + state: string; + country: string; + payment: DeelPayment; + is_ended: boolean; + timezone: string; + job_title: string; + seniority: string; + start_date: string; // Date in string format + work_email: string; + hiring_type: string; + hiring_status: string; + completion_date: string; + contract_status: string; + voluntarily_left: string; + contract_coverage: string[]; + new_hiring_status: string; + client_legal_entity: DeelClientLegalEntity; + has_eor_termination: string; + contract_is_archived: boolean; + contract_has_contractor: boolean; + is_user_contract_deleted: boolean; + hris_direct_employee_invitation: string; +}>; + +export interface DeelTeam { + id: string; + name: string; +} + +export interface DeelPayment { + rate: number; + scale: string; + currency: string; + contract_name: string; +} +export interface DeelDepartment { + id: string; + name: string; + parent: string; +} + +export interface DeelClientLegalEntity { + id: string; + name: string; +} diff --git a/packages/api/src/hris/group/group.module.ts b/packages/api/src/hris/group/group.module.ts index 9e1bdaa36..78f6a35ee 100644 --- a/packages/api/src/hris/group/group.module.ts +++ b/packages/api/src/hris/group/group.module.ts @@ -9,6 +9,10 @@ import { CoreUnification } from '@@core/@core-services/unification/core-unificat import { GustoGroupMapper } from './services/gusto/mappers'; import { GustoService } from './services/gusto'; import { Utils } from '@hris/@lib/@utils'; +import { SageService } from './services/sage'; +import { SageGroupMapper } from './services/sage/mappers'; +import { DeelService } from './services/deel'; +import { DeelGroupMapper } from './services/deel/mappers'; @Module({ controllers: [GroupController], providers: [ @@ -20,8 +24,12 @@ import { Utils } from '@hris/@lib/@utils'; IngestDataService, GustoGroupMapper, Utils, + SageGroupMapper, + DeelGroupMapper, /* PROVIDERS SERVICES */ GustoService, + SageService, + DeelService, ], exports: [SyncService], }) diff --git a/packages/api/src/hris/group/services/deel/index.ts b/packages/api/src/hris/group/services/deel/index.ts new file mode 100644 index 000000000..f593070b1 --- /dev/null +++ b/packages/api/src/hris/group/services/deel/index.ts @@ -0,0 +1,66 @@ +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { EnvironmentService } from '@@core/@core-services/environment/environment.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 { HrisObject } from '@hris/@lib/@types'; +import { IGroupService } from '@hris/group/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { DeelGroupOutput } from './types'; +import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; +import { OriginalGroupOutput } from '@@core/utils/types/original/original.hris'; + +@Injectable() +export class DeelService implements IGroupService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private env: EnvironmentService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + HrisObject.group.toUpperCase() + ':' + DeelService.name, + ); + this.registry.registerService('deel', this); + } + addGroup( + groupData: DesunifyReturnType, + linkedUserId: string, + ): Promise> { + throw new Error('Method not implemented.'); + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'deel', + vertical: 'hris', + }, + }); + + const resp = await axios.get(`${connection.account_url}/rest/v2/teams`, { + headers: { + 'Content-Type': 'application/json', + 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), + }, + }); + this.logger.log(`Synced deel groups !`); + + return { + data: resp.data.data, + message: 'Deel groups retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/hris/group/services/deel/mappers.ts b/packages/api/src/hris/group/services/deel/mappers.ts new file mode 100644 index 000000000..356c44695 --- /dev/null +++ b/packages/api/src/hris/group/services/deel/mappers.ts @@ -0,0 +1,61 @@ +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { Utils } from '@hris/@lib/@utils'; +import { IGroupMapper } from '@hris/group/types'; +import { + UnifiedHrisGroupInput, + UnifiedHrisGroupOutput, +} from '@hris/group/types/model.unified'; +import { Injectable } from '@nestjs/common'; +import { DeelGroupOutput } from './types'; + +@Injectable() +export class DeelGroupMapper implements IGroupMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private ingestService: IngestDataService, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService('hris', 'group', 'deel', this); + } + + async desunify( + source: UnifiedHrisGroupInput, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + return; + } + + async unify( + source: DeelGroupOutput | DeelGroupOutput[], + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleGroupToUnified( + source, + connectionId, + customFieldMappings, + ); + } + return Promise.all( + source.map((group) => + this.mapSingleGroupToUnified(group, connectionId, customFieldMappings), + ), + ); + } + + private async mapSingleGroupToUnified( + group: DeelGroupOutput, + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + return { + remote_id: group.id, + remote_data: group, + name: group.name, + }; + } +} diff --git a/packages/api/src/hris/group/services/deel/types.ts b/packages/api/src/hris/group/services/deel/types.ts new file mode 100644 index 000000000..8964f34e3 --- /dev/null +++ b/packages/api/src/hris/group/services/deel/types.ts @@ -0,0 +1,4 @@ +export type DeelGroupOutput = { + id: string; + name: string; +}; diff --git a/packages/api/src/hris/group/services/sage/index.ts b/packages/api/src/hris/group/services/sage/index.ts new file mode 100644 index 000000000..08dff20ec --- /dev/null +++ b/packages/api/src/hris/group/services/sage/index.ts @@ -0,0 +1,66 @@ +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { EnvironmentService } from '@@core/@core-services/environment/environment.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 { HrisObject } from '@hris/@lib/@types'; +import { IGroupService } from '@hris/group/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { SageGroupOutput } from './types'; +import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; +import { OriginalGroupOutput } from '@@core/utils/types/original/original.hris'; + +@Injectable() +export class SageService implements IGroupService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private env: EnvironmentService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + HrisObject.group.toUpperCase() + ':' + SageService.name, + ); + this.registry.registerService('sage', this); + } + addGroup( + groupData: DesunifyReturnType, + linkedUserId: string, + ): Promise> { + throw new Error('Method not implemented.'); + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'sage', + vertical: 'hris', + }, + }); + + const resp = await axios.get(`${connection.account_url}/api/teams`, { + headers: { + 'Content-Type': 'application/json', + 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), + }, + }); + this.logger.log(`Synced sage groups !`); + + return { + data: resp.data.data, + message: 'Sage groups retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/hris/group/services/sage/mappers.ts b/packages/api/src/hris/group/services/sage/mappers.ts new file mode 100644 index 000000000..c40072b9f --- /dev/null +++ b/packages/api/src/hris/group/services/sage/mappers.ts @@ -0,0 +1,61 @@ +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { Utils } from '@hris/@lib/@utils'; +import { IGroupMapper } from '@hris/group/types'; +import { + UnifiedHrisGroupInput, + UnifiedHrisGroupOutput, +} from '@hris/group/types/model.unified'; +import { Injectable } from '@nestjs/common'; +import { SageGroupOutput } from './types'; + +@Injectable() +export class SageGroupMapper implements IGroupMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private ingestService: IngestDataService, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService('hris', 'group', 'sage', this); + } + + async desunify( + source: UnifiedHrisGroupInput, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + return; + } + + async unify( + source: SageGroupOutput | SageGroupOutput[], + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleGroupToUnified( + source, + connectionId, + customFieldMappings, + ); + } + return Promise.all( + source.map((group) => + this.mapSingleGroupToUnified(group, connectionId, customFieldMappings), + ), + ); + } + + private async mapSingleGroupToUnified( + group: SageGroupOutput, + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + return { + remote_id: String(group.id), + remote_data: group, + name: group.name, + }; + } +} diff --git a/packages/api/src/hris/group/services/sage/types.ts b/packages/api/src/hris/group/services/sage/types.ts new file mode 100644 index 000000000..dcd2d590b --- /dev/null +++ b/packages/api/src/hris/group/services/sage/types.ts @@ -0,0 +1,6 @@ +export type SageGroupOutput = { + id: number; + name: string; + manager_ids: string[]; + employee_ids: string[]; +}; diff --git a/packages/api/src/hris/location/location.module.ts b/packages/api/src/hris/location/location.module.ts index 443570585..c2f147465 100644 --- a/packages/api/src/hris/location/location.module.ts +++ b/packages/api/src/hris/location/location.module.ts @@ -9,6 +9,7 @@ import { CoreUnification } from '@@core/@core-services/unification/core-unificat import { Utils } from '@hris/@lib/@utils'; import { GustoLocationMapper } from './services/gusto/mappers'; import { GustoService } from './services/gusto'; +import { DeelLocationMapper } from './services/deel/mappers'; @Module({ controllers: [LocationController], providers: [ @@ -20,6 +21,7 @@ import { GustoService } from './services/gusto'; ServiceRegistry, IngestDataService, GustoLocationMapper, + DeelLocationMapper, /* PROVIDERS SERVICES */ GustoService, ], diff --git a/packages/api/src/hris/location/services/deel/mappers.ts b/packages/api/src/hris/location/services/deel/mappers.ts new file mode 100644 index 000000000..cecf44401 --- /dev/null +++ b/packages/api/src/hris/location/services/deel/mappers.ts @@ -0,0 +1,70 @@ +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { Injectable } from '@nestjs/common'; +import { DeelLocationOutput } from './types'; +import { + UnifiedHrisLocationInput, + UnifiedHrisLocationOutput, +} from '@hris/location/types/model.unified'; +import { ILocationMapper } from '@hris/location/types'; +import { Utils } from '@hris/@lib/@utils'; + +@Injectable() +export class DeelLocationMapper implements ILocationMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private ingestService: IngestDataService, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService('hris', 'location', 'deel', this); + } + + async desunify( + source: UnifiedHrisLocationInput, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + return; + } + + async unify( + source: DeelLocationOutput | DeelLocationOutput[], + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleLocationToUnified( + source, + connectionId, + customFieldMappings, + ); + } + return Promise.all( + source.map((location) => + this.mapSingleLocationToUnified( + location, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async mapSingleLocationToUnified( + location: DeelLocationOutput, + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + return { + remote_id: null, + remote_data: location, + street_1: location.streetAddress, + city: location.locality, + state: location.region, + zip_code: location.postalCode, + country: location.country, + location_type: location.type, + }; + } +} diff --git a/packages/api/src/hris/location/services/deel/types.ts b/packages/api/src/hris/location/services/deel/types.ts new file mode 100644 index 000000000..98db26aa7 --- /dev/null +++ b/packages/api/src/hris/location/services/deel/types.ts @@ -0,0 +1,8 @@ +export type DeelLocationOutput = Partial<{ + streetAddress: string; + locality: string; + region: string; + postalCode: string; + country: string; + type: 'WORK' | 'HOME'; +}>; diff --git a/packages/api/src/hris/timeoff/services/sage/index.ts b/packages/api/src/hris/timeoff/services/sage/index.ts new file mode 100644 index 000000000..c83e2d187 --- /dev/null +++ b/packages/api/src/hris/timeoff/services/sage/index.ts @@ -0,0 +1,69 @@ +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { EnvironmentService } from '@@core/@core-services/environment/environment.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 { HrisObject } from '@hris/@lib/@types'; +import { ITimeoffService } from '@hris/timeoff/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { SageTimeoffOutput } from './types'; +import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; +import { OriginalTimeoffOutput } from '@@core/utils/types/original/original.hris'; + +@Injectable() +export class SageService implements ITimeoffService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private env: EnvironmentService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + HrisObject.timeoff.toUpperCase() + ':' + SageService.name, + ); + this.registry.registerService('sage', this); + } + addTimeoff( + timeoffData: DesunifyReturnType, + linkedUserId: string, + ): Promise> { + throw new Error('Method not implemented.'); + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'sage', + vertical: 'hris', + }, + }); + + const resp = await axios.get( + `${connection.account_url}/api/leave-management/requests`, + { + headers: { + 'Content-Type': 'application/json', + 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), + }, + }, + ); + this.logger.log(`Synced sage timeoffs !`); + + return { + data: resp.data.data, + message: 'Sage timeoffs retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/hris/timeoff/services/sage/mappers.ts b/packages/api/src/hris/timeoff/services/sage/mappers.ts new file mode 100644 index 000000000..d8eeff03d --- /dev/null +++ b/packages/api/src/hris/timeoff/services/sage/mappers.ts @@ -0,0 +1,115 @@ +import { Injectable } from '@nestjs/common'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { Utils } from '@hris/@lib/@utils'; +import { ITimeoffMapper } from '@hris/timeoff/types'; +import { + UnifiedHrisTimeoffInput, + UnifiedHrisTimeoffOutput, + Status, + RequestType, +} from '@hris/timeoff/types/model.unified'; +import { SageTimeoffOutput } from './types'; + +@Injectable() +export class SageTimeoffMapper implements ITimeoffMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private ingestService: IngestDataService, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService('hris', 'timeoff', 'sage', this); + } + + async desunify( + source: UnifiedHrisTimeoffInput, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + // Implementation for desunify (if needed) + return; + } + + async unify( + source: SageTimeoffOutput | SageTimeoffOutput[], + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleTimeoffToUnified( + source, + connectionId, + customFieldMappings, + ); + } + return Promise.all( + source.map((timeoff) => + this.mapSingleTimeoffToUnified( + timeoff, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async mapSingleTimeoffToUnified( + timeoff: SageTimeoffOutput, + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + const employee = await this.utils.getEmployeeUuidFromRemoteId( + timeoff.employee_id.toString(), + connectionId, + ); + + return { + remote_id: timeoff.id.toString(), + remote_data: timeoff, + employee: employee, + status: this.mapStatus(timeoff.status_code), + employee_note: timeoff.details, + units: 'HOURS', + amount: timeoff.hours, + request_type: this.inferRequestType(timeoff.details), + start_time: new Date(timeoff.start_date), + end_time: new Date(timeoff.end_date), + }; + } + + private mapStatus(statusCode: string): Status { + switch (statusCode.toLowerCase()) { + case 'approved': + return 'APPROVED'; + case 'declined': + return 'DECLINED'; + case 'cancelled': + return 'CANCELLED'; + case 'deleted': + return 'DELETED'; + default: + return 'REQUESTED'; + } + } + + private inferRequestType(details: string): RequestType { + const lowerDetails = details.toLowerCase(); + if (lowerDetails.includes('vacation') || lowerDetails.includes('holiday')) { + return 'VACATION'; + } else if ( + lowerDetails.includes('sick') || + lowerDetails.includes('illness') + ) { + return 'SICK'; + } else if (lowerDetails.includes('jury')) { + return 'JURY_DUTY'; + } else if (lowerDetails.includes('bereavement')) { + return 'BEREAVEMENT'; + } else if (lowerDetails.includes('volunteer')) { + return 'VOLUNTEER'; + } else { + return 'PERSONAL'; + } + } +} diff --git a/packages/api/src/hris/timeoff/services/sage/types.ts b/packages/api/src/hris/timeoff/services/sage/types.ts new file mode 100644 index 000000000..f7b59bd94 --- /dev/null +++ b/packages/api/src/hris/timeoff/services/sage/types.ts @@ -0,0 +1,30 @@ +export interface SageTimeoffReplacement { + id: number; + full_name: string; +} + +export interface SageTimeoffField { + title: string; + answer: string; +} + +export type SageTimeoffOutput = Partial<{ + id: number; + status: string; + status_code: string; + policy_id: number; + employee_id: number; + replacement: SageTimeoffReplacement; + details: string; + is_multi_date: boolean; + is_single_day: boolean; + is_part_of_day: boolean; + first_part_of_day: boolean; + second_part_of_day: boolean; + start_date: string; + end_date: string; + request_date: string; + approval_date: string | null; + hours: number; + fields: SageTimeoffField[]; +}>; diff --git a/packages/api/src/hris/timeoff/timeoff.module.ts b/packages/api/src/hris/timeoff/timeoff.module.ts index 86d017aa9..0f66654af 100644 --- a/packages/api/src/hris/timeoff/timeoff.module.ts +++ b/packages/api/src/hris/timeoff/timeoff.module.ts @@ -7,6 +7,8 @@ import { IngestDataService } from '@@core/@core-services/unification/ingest-data import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; import { Utils } from '@hris/@lib/@utils'; +import { SageTimeoffMapper } from './services/sage/mappers'; +import { SageService } from './services/sage'; @Module({ controllers: [TimeoffController], providers: [ @@ -17,7 +19,9 @@ import { Utils } from '@hris/@lib/@utils'; WebhookService, ServiceRegistry, IngestDataService, + SageTimeoffMapper, /* PROVIDERS SERVICES */ + SageService, ], exports: [SyncService], }) diff --git a/packages/api/src/hris/timeoffbalance/services/sage/index.ts b/packages/api/src/hris/timeoffbalance/services/sage/index.ts new file mode 100644 index 000000000..61f928029 --- /dev/null +++ b/packages/api/src/hris/timeoffbalance/services/sage/index.ts @@ -0,0 +1,72 @@ +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { EnvironmentService } from '@@core/@core-services/environment/environment.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 { HrisObject } from '@hris/@lib/@types'; +import { ITimeoffBalanceService } from '@hris/timeoffbalance/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { SageTimeoffbalanceOutput } from './types'; + +@Injectable() +export class SageService implements ITimeoffBalanceService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private env: EnvironmentService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + HrisObject.timeoffbalance.toUpperCase() + ':' + SageService.name, + ); + this.registry.registerService('sage', this); + } + + async sync( + data: SyncParam, + ): Promise> { + try { + const { linkedUserId, id_employee } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'sage', + vertical: 'hris', + }, + }); + + const employee = await this.prisma.hris_employees.findUnique({ + where: { + id_hris_employee: id_employee as string, + }, + select: { + remote_id: true, + }, + }); + + const resp = await axios.get( + `${connection.account_url}/api/employees/${employee.remote_id}/leave-management/balances`, + { + headers: { + 'Content-Type': 'application/json', + 'X-Auth-Token': this.cryptoService.decrypt(connection.access_token), + }, + }, + ); + this.logger.log(`Synced sage timeoffbalances !`); + + return { + data: resp.data.data, + message: 'Sage timeoffbalances retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/hris/timeoffbalance/services/sage/mappers.ts b/packages/api/src/hris/timeoffbalance/services/sage/mappers.ts new file mode 100644 index 000000000..448a08afd --- /dev/null +++ b/packages/api/src/hris/timeoffbalance/services/sage/mappers.ts @@ -0,0 +1,73 @@ +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { Utils } from '@hris/@lib/@utils'; +import { ITimeoffBalanceMapper } from '@hris/timeoffbalance/types'; +import { + UnifiedHrisTimeoffbalanceInput, + UnifiedHrisTimeoffbalanceOutput, +} from '@hris/timeoffbalance/types/model.unified'; +import { Injectable } from '@nestjs/common'; +import { SageTimeoffbalanceOutput } from './types'; + +@Injectable() +export class SageTimeoffbalanceMapper implements ITimeoffBalanceMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private ingestService: IngestDataService, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService( + 'hris', + 'timeoffbalance', + 'sage', + this, + ); + } + + async desunify( + source: UnifiedHrisTimeoffbalanceInput, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + return; + } + + async unify( + source: SageTimeoffbalanceOutput | SageTimeoffbalanceOutput[], + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise< + UnifiedHrisTimeoffbalanceOutput | UnifiedHrisTimeoffbalanceOutput[] + > { + if (!Array.isArray(source)) { + return this.mapSingleTimeoffbalanceToUnified( + source, + connectionId, + customFieldMappings, + ); + } + return Promise.all( + source.map((timeoffbalance) => + this.mapSingleTimeoffbalanceToUnified( + timeoffbalance, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async mapSingleTimeoffbalanceToUnified( + timeoffbalance: SageTimeoffbalanceOutput, + connectionId: string, + customFieldMappings?: { slug: string; remote_id: string }[], + ): Promise { + return { + remote_id: null, + remote_data: timeoffbalance, + balance: timeoffbalance.available, + used: timeoffbalance.used, + }; + } +} diff --git a/packages/api/src/hris/timeoffbalance/services/sage/types.ts b/packages/api/src/hris/timeoffbalance/services/sage/types.ts new file mode 100644 index 000000000..b22f8a766 --- /dev/null +++ b/packages/api/src/hris/timeoffbalance/services/sage/types.ts @@ -0,0 +1,5 @@ +export type SageTimeoffbalanceOutput = Partial<{ + policy_id: number; + used: number; + available: number; +}>; diff --git a/packages/api/src/hris/timeoffbalance/timeoffbalance.module.ts b/packages/api/src/hris/timeoffbalance/timeoffbalance.module.ts index 106e1bdec..12f5213b3 100644 --- a/packages/api/src/hris/timeoffbalance/timeoffbalance.module.ts +++ b/packages/api/src/hris/timeoffbalance/timeoffbalance.module.ts @@ -7,6 +7,8 @@ import { IngestDataService } from '@@core/@core-services/unification/ingest-data import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; import { Utils } from '@hris/@lib/@utils'; +import { SageTimeoffbalanceMapper } from './services/sage/mappers'; +import { SageService } from './services/sage'; @Module({ controllers: [TimeoffBalanceController], providers: [ @@ -17,7 +19,9 @@ import { Utils } from '@hris/@lib/@utils'; WebhookService, ServiceRegistry, IngestDataService, + SageTimeoffbalanceMapper, /* PROVIDERS SERVICES */ + SageService, ], exports: [SyncService], }) diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index 9f0368312..ef65b5d6c 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -1110,6 +1110,20 @@ export const CONNECTORS_METADATA: ProvidersConfig = { strategy: AuthStrategy.oauth2 } }, + 'apollo': { + urls: { + docsUrl: 'https://apolloio.github.io/apollo-api-docs/?shell#introduction', + apiUrl: 'https://api.apollo.io' + }, + logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSM4trwPJLoj7tPkFYZG6TyMVzgCX1fn2zUyA&s', + description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', + active: false, + primaryColor: '#ffcf40', + authStrategy: { + strategy: AuthStrategy.api_key, + properties: ["api_key"] + } + }, 'hubspot_marketing_hub': { scopes: '', urls: { @@ -1251,7 +1265,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { scopes: 'openid+email', urls: { docsUrl: 'https://documentation.bamboohr.com/docs/getting-started', - apiUrl: (companySubdomain) => `https://api.bamboohr.com/api/gateway.php/${companySubdomain}`, + apiUrl: (subdomain) => `https://api.bamboohr.com/api/gateway.php/${subdomain}`, }, logoPath: 'https://play-lh.googleusercontent.com/c4BW9wr_QAiIeVBYHhP7rs06w99xJzxgLvmL5I1mkucC3_ATMyL1t7Doz0_LQ0X-qS0', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', @@ -1259,7 +1273,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { primaryColor: '#599D16', authStrategy: { strategy: AuthStrategy.basic, - properties: ['username', 'company_subdomain'] + properties: ['username', 'subdomain'] }, }, 'breezy': { @@ -1685,7 +1699,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { strategy: AuthStrategy.api_key } }, - 'sage_hr': { + 'sage': { scopes: '', urls: { docsUrl: '', @@ -2449,11 +2463,13 @@ export const CONNECTORS_METADATA: ProvidersConfig = { docsUrl: '', apiUrl: '' }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', + logoPath: 'https://assets.wheelhouse.com/media/_solution_logo_04042023_58844144.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, + primaryColor: '#F8A22D', authStrategy: { - strategy: AuthStrategy.api_key + strategy: AuthStrategy.api_key, + properties: ["api_key"] } }, 'payfit': { @@ -2549,18 +2565,20 @@ export const CONNECTORS_METADATA: ProvidersConfig = { strategy: AuthStrategy.api_key } }, - 'sage_hr': { + 'sage': { scopes: '', urls: { - docsUrl: '', - apiUrl: '' + docsUrl: 'https://sagehr.docs.apiary.io/#reference', + apiUrl: (subdomain) => `https://${subdomain}.sage.hr` }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', + logoPath: 'https://appexchange.salesforce.com/partners/servlet/servlet.FileDownload?file=00P4V00000xPZsjUAG', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, + primaryColor: '#00d639', authStrategy: { - strategy: AuthStrategy.api_key - } + strategy: AuthStrategy.api_key, + properties: ["api_key", "subdomain"] + }, }, 'sap_successfactors': { scopes: '', From ba8471a3954eecfde6b1143557be76f13c3a1ea8 Mon Sep 17 00:00:00 2001 From: Manoj Kumar <95513071+Iamsidar07@users.noreply.github.com> Date: Fri, 16 Aug 2024 07:31:12 +0530 Subject: [PATCH 04/49] Change whixch to which --- docs/open-source/contributors/build.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/open-source/contributors/build.mdx b/docs/open-source/contributors/build.mdx index 279be5f54..273d34136 100644 --- a/docs/open-source/contributors/build.mdx +++ b/docs/open-source/contributors/build.mdx @@ -34,7 +34,7 @@ You can find all categories inside [`packages/shared/src/categories.ts`](https:/ Actually an integration is built in 2 parts : - the **authentication part** (oauth, api key, basic etc) which is built by the Panora team -- the **service integration** where the mapping with our unified model is created whixch is what you'll build in the next steps +- the **service integration** where the mapping with our unified model is created which is what you'll build in the next steps # Step 2: Build your provider service @@ -186,4 +186,4 @@ For the sake of this guide, let's map the common object `contact` under `crm` ca The script will automatically scan the `/crm/contact/services` folder and detect any new service folder so all dependencies and imports are updated across the codebase. - \ No newline at end of file + From 5052c8ed70aef4864072c7f545da59665cee0767 Mon Sep 17 00:00:00 2001 From: Manoj Kumar <95513071+Iamsidar07@users.noreply.github.com> Date: Fri, 16 Aug 2024 07:44:27 +0530 Subject: [PATCH 05/49] discord link was expired change to https://discord.com/invite/PH5k7gGubt --- docs/support/get_help.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/support/get_help.mdx b/docs/support/get_help.mdx index a87780feb..f130c991c 100644 --- a/docs/support/get_help.mdx +++ b/docs/support/get_help.mdx @@ -4,4 +4,4 @@ icon: circle-info --- Our team can help you - click [here](https://app.cal.com/rflih/30) to book some time to chat with an integrations expert. -You can also ask the community: just go on our [discord](https://discord.gg/2SCC2jxN) +You can also ask the community: just go on our [discord](https://discord.com/invite/PH5k7gGubt) From aef952658d25ec8b15538a1abd3b7d00200cc330 Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 16 Aug 2024 04:40:34 +0200 Subject: [PATCH 06/49] :sparkles: Added connections for all cybersec integrations --- .../@core/connections/connections.module.ts | 3 + .../cybersecurity.connection.module.ts | 38 ++++ .../crowdstrike/crowdstrike.service.ts | 195 +++++++++++++++++ .../cybersecurity.connection.service.ts | 129 ++++++++++++ .../microsoftdefender.service.ts | 199 ++++++++++++++++++ .../services/qualys/qualys.service.ts | 156 ++++++++++++++ .../rapid7insightvm/rapid7.service.ts | 145 +++++++++++++ .../services/registry.service.ts | 25 +++ .../services/semgrep/semgrep.service.ts | 143 +++++++++++++ .../sentinelone/sentinelone.service.ts | 149 +++++++++++++ .../services/snyk/snyk.service.ts | 191 +++++++++++++++++ .../services/tenable/tenable.service.ts | 147 +++++++++++++ packages/shared/src/categories.ts | 3 +- packages/shared/src/connectors/metadata.ts | 119 +++++++++++ 14 files changed, 1641 insertions(+), 1 deletion(-) create mode 100644 packages/api/src/@core/connections/cybersecurity/cybersecurity.connection.module.ts create mode 100644 packages/api/src/@core/connections/cybersecurity/services/crowdstrike/crowdstrike.service.ts create mode 100644 packages/api/src/@core/connections/cybersecurity/services/cybersecurity.connection.service.ts create mode 100644 packages/api/src/@core/connections/cybersecurity/services/microsoftdefender/microsoftdefender.service.ts create mode 100644 packages/api/src/@core/connections/cybersecurity/services/qualys/qualys.service.ts create mode 100644 packages/api/src/@core/connections/cybersecurity/services/rapid7insightvm/rapid7.service.ts create mode 100644 packages/api/src/@core/connections/cybersecurity/services/registry.service.ts create mode 100644 packages/api/src/@core/connections/cybersecurity/services/semgrep/semgrep.service.ts create mode 100644 packages/api/src/@core/connections/cybersecurity/services/sentinelone/sentinelone.service.ts create mode 100644 packages/api/src/@core/connections/cybersecurity/services/snyk/snyk.service.ts create mode 100644 packages/api/src/@core/connections/cybersecurity/services/tenable/tenable.service.ts diff --git a/packages/api/src/@core/connections/connections.module.ts b/packages/api/src/@core/connections/connections.module.ts index 80c2a0d71..188d25c6c 100644 --- a/packages/api/src/@core/connections/connections.module.ts +++ b/packages/api/src/@core/connections/connections.module.ts @@ -12,6 +12,7 @@ import { ProductivityConnectionsModule } from './productivity/productivity.conne import { MarketingAutomationConnectionsModule } from './marketingautomation/marketingautomation.connection.module'; import { TicketingConnectionModule } from './ticketing/ticketing.connection.module'; import { EcommerceConnectionModule } from './ecommerce/ecommerce.connection.module'; +import { CybersecurityConnectionsModule } from './cybersecurity/cybersecurity.connection.module'; @Module({ controllers: [ConnectionsController], @@ -25,6 +26,7 @@ import { EcommerceConnectionModule } from './ecommerce/ecommerce.connection.modu FilestorageConnectionModule, HrisConnectionModule, EcommerceConnectionModule, + CybersecurityConnectionsModule, SyncModule, ], providers: [ValidateUserService, OAuthTokenRefreshService], @@ -40,6 +42,7 @@ import { EcommerceConnectionModule } from './ecommerce/ecommerce.connection.modu EcommerceConnectionModule, HrisConnectionModule, ProductivityConnectionsModule, + CybersecurityConnectionsModule, ], }) export class ConnectionsModule {} diff --git a/packages/api/src/@core/connections/cybersecurity/cybersecurity.connection.module.ts b/packages/api/src/@core/connections/cybersecurity/cybersecurity.connection.module.ts new file mode 100644 index 000000000..65508349f --- /dev/null +++ b/packages/api/src/@core/connections/cybersecurity/cybersecurity.connection.module.ts @@ -0,0 +1,38 @@ +import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; +import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; +import { WebhookModule } from '@@core/@core-services/webhooks/panora-webhooks/webhook.module'; +import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; +import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; +import { Module } from '@nestjs/common'; +import { CybersecurityConnectionsService } from './services/cybersecurity.connection.service'; +import { ServiceRegistry } from './services/registry.service'; +import { TenableConnectionService } from './services/tenable/tenable.service'; +import { QualysConnectionService } from './services/qualys/qualys.service'; +import { SemgrepConnectionService } from './services/semgrep/semgrep.service'; +import { SentineloneConnectionService } from './services/sentinelone/sentinelone.service'; +import { Rapid7ConnectionService } from './services/rapid7insightvm/rapid7.service'; +import { SnykConnectionService } from './services/snyk/snyk.service'; +import { CrowdstrikeConnectionService } from './services/crowdstrike/crowdstrike.service'; +import { MicrosoftdefenderConnectionService } from './services/microsoftdefender/microsoftdefender.service'; + +@Module({ + imports: [WebhookModule, BullQueueModule], + providers: [ + CybersecurityConnectionsService, + WebhookService, + EnvironmentService, + ServiceRegistry, + ConnectionsStrategiesService, + //PROVIDERS SERVICES + SemgrepConnectionService, + TenableConnectionService, + QualysConnectionService, + SentineloneConnectionService, + Rapid7ConnectionService, + SnykConnectionService, + CrowdstrikeConnectionService, + MicrosoftdefenderConnectionService, + ], + exports: [CybersecurityConnectionsService], +}) +export class CybersecurityConnectionsModule {} diff --git a/packages/api/src/@core/connections/cybersecurity/services/crowdstrike/crowdstrike.service.ts b/packages/api/src/@core/connections/cybersecurity/services/crowdstrike/crowdstrike.service.ts new file mode 100644 index 000000000..4fcff71f9 --- /dev/null +++ b/packages/api/src/@core/connections/cybersecurity/services/crowdstrike/crowdstrike.service.ts @@ -0,0 +1,195 @@ +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 { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; +import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; +import { ConnectionUtils } from '@@core/connections/@utils'; +import { + AbstractBaseConnectionService, + OAuthCallbackParams, + PassthroughInput, + RefreshParams, +} from '@@core/connections/@utils/types'; +import { PassthroughResponse } from '@@core/passthrough/types'; +import { Injectable } from '@nestjs/common'; +import { + AuthStrategy, + CONNECTORS_METADATA, + OAuth2AuthData, + providerToType, +} from '@panora/shared'; +import { v4 as uuidv4 } from 'uuid'; +import { ServiceRegistry } from '../registry.service'; +import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; +import axios from 'axios'; + +export interface CrowdstrikeOAuthResponse { + access_token: string; + token_type: string; + scope: string; +} + +@Injectable() +export class CrowdstrikeConnectionService extends AbstractBaseConnectionService { + private readonly type: string; + + constructor( + protected prisma: PrismaService, + private logger: LoggerService, + protected cryptoService: EncryptionService, + private env: EnvironmentService, + private registry: ServiceRegistry, + private connectionUtils: ConnectionUtils, + private cService: ConnectionsStrategiesService, + private retryService: RetryHandler, + ) { + super(prisma, cryptoService); + this.logger.setContext(CrowdstrikeConnectionService.name); + this.registry.registerService('crowdstrike', this); + this.type = providerToType( + 'crowdstrike', + 'cybersecurity', + AuthStrategy.oauth2, + ); + } + + async passthrough( + input: PassthroughInput, + connectionId: string, + ): Promise { + try { + const { headers } = input; + const config = await this.constructPassthrough(input, connectionId); + + const connection = await this.prisma.connections.findUnique({ + where: { + id_connection: connectionId, + }, + }); + + const access_token = JSON.parse( + this.cryptoService.decrypt(connection.access_token), + ); + config.headers = { + ...config.headers, + ...headers, + Authorization: `Bearer ${access_token}`, + }; + + return await this.retryService.makeRequest( + { + method: config.method, + url: config.url, + data: config.data, + headers: config.headers, + }, + 'cybersecurity.crowdstrike.passthrough', + config.linkedUserId, + ); + } catch (error) { + throw error; + } + } + + async handleCallback(opts: OAuthCallbackParams) { + try { + const { linkedUserId, projectId, code } = opts; + const isNotUnique = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'crowdstrike', + vertical: 'cybersecurity', + }, + }); + //reconstruct the redirect URI that was passed in the frontend it must be the same + const REDIRECT_URI = `${ + this.env.getDistributionMode() == 'selfhost' + ? this.env.getTunnelIngress() + : this.env.getPanoraBaseUrl() + }/connections/oauth/callback`; + + const CREDENTIALS = (await this.cService.getCredentials( + projectId, + this.type, + )) as OAuth2AuthData; + + const formData = new URLSearchParams({ + redirect_uri: REDIRECT_URI, + client_id: CREDENTIALS.CLIENT_ID, + client_secret: CREDENTIALS.CLIENT_SECRET, + code: code, + grant_type: 'authorization_code', + }); + const res = await axios.post( + 'https://api.crowdstrike.com/oauth/access_token', + formData.toString(), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + }, + }, + ); + const data: CrowdstrikeOAuthResponse = res.data; + // save tokens for this customer inside our db + let db_res; + const connection_token = uuidv4(); + const BASE_API_URL = CONNECTORS_METADATA['cybersecurity']['crowdstrike'] + .urls.apiUrl as string; + // get the site id for the token + const site = await axios.get('https://api.crowdstrike.com/v2/sites', { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + Authorization: `Bearer ${data.access_token}`, + }, + }); + const site_id = site.data.sites[0].id; + if (isNotUnique) { + // Update existing connection + db_res = await this.prisma.connections.update({ + where: { + id_connection: isNotUnique.id_connection, + }, + data: { + access_token: this.cryptoService.encrypt(data.access_token), + status: 'valid', + created_at: new Date(), + }, + }); + } else { + // Create new connection + db_res = await this.prisma.connections.create({ + data: { + id_connection: uuidv4(), + connection_token: connection_token, + provider_slug: 'crowdstrike', + vertical: 'cybersecurity', + token_type: 'oauth2', + account_url: `${BASE_API_URL}/sites/${site_id}`, + access_token: this.cryptoService.encrypt(data.access_token), + status: 'valid', + created_at: new Date(), + projects: { + connect: { id_project: projectId }, + }, + linked_users: { + connect: { + id_linked_user: await this.connectionUtils.getLinkedUserId( + projectId, + linkedUserId, + ), + }, + }, + }, + }); + } + this.logger.log('Successfully added tokens inside DB ' + db_res); + return db_res; + } catch (error) { + throw error; + } + } + + async handleTokenRefresh(opts: RefreshParams) { + return; + } +} diff --git a/packages/api/src/@core/connections/cybersecurity/services/cybersecurity.connection.service.ts b/packages/api/src/@core/connections/cybersecurity/services/cybersecurity.connection.service.ts new file mode 100644 index 000000000..586f81b2e --- /dev/null +++ b/packages/api/src/@core/connections/cybersecurity/services/cybersecurity.connection.service.ts @@ -0,0 +1,129 @@ +import { LoggerService } from '@@core/@core-services/logger/logger.service'; +import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; +import { + CallbackParams, + IConnectionCategory, + PassthroughInput, + RefreshParams, +} from '@@core/connections/@utils/types'; +import { Injectable } from '@nestjs/common'; +import { connections as Connection } from '@prisma/client'; +import { v4 as uuidv4 } from 'uuid'; +import { ServiceRegistry } from './registry.service'; +import { CategoryConnectionRegistry } from '@@core/@core-services/registries/connections-categories.registry'; +import { PassthroughResponse } from '@@core/passthrough/types'; + +@Injectable() +export class CybersecurityConnectionsService implements IConnectionCategory { + constructor( + private serviceRegistry: ServiceRegistry, + private connectionCategoryRegistry: CategoryConnectionRegistry, + private webhook: WebhookService, + private logger: LoggerService, + private prisma: PrismaService, + ) { + this.logger.setContext(CybersecurityConnectionsService.name); + this.connectionCategoryRegistry.registerService('cybersecurity', this); + } + //STEP 1:[FRONTEND STEP] + //create a frontend SDK snippet in which an authorization embedded link is set up so when users click + // on it to grant access => they grant US the access and then when confirmed + /*const authUrl = + 'https://app.hubspot.com/oauth/authorize' + + `?client_id=${encodeURIComponent(CLIENT_ID)}` + + `&scope=${encodeURIComponent(SCOPES)}` + + `&redirect_uri=${encodeURIComponent(REDIRECT_URI)}`;*/ //oauth/callback + + // oauth server calls this redirect callback + // WE WOULD HAVE CREATED A DEV ACCOUNT IN THE 5 CRMs (Panora dev account) + // we catch the tmp token and swap it against oauth2 server for access/refresh tokens + // to perform actions on his behalf + // this call pass 1. integrationID 2. CustomerId 3. Panora Api Key + async handleCallBack( + providerName: string, + callbackOpts: CallbackParams, + type_strategy: 'oauth2' | 'apikey' | 'basic', + ) { + try { + const serviceName = providerName.toLowerCase(); + + const service = this.serviceRegistry.getService(serviceName); + + if (!service) { + throw new ReferenceError(`Unknown provider, found ${providerName}`); + } + const data: Connection = await service.handleCallback(callbackOpts); + const event = await this.prisma.events.create({ + data: { + id_connection: data.id_connection, + id_project: data.id_project, + id_event: uuidv4(), + status: 'success', + type: 'connection.created', + method: 'GET', + url: `/${type_strategy}/callback`, + provider: providerName.toLowerCase(), + direction: '0', + timestamp: new Date(), + id_linked_user: callbackOpts.linkedUserId, + }, + }); + //directly send the webhook + await this.webhook.dispatchWebhook( + data, + 'connection.created', + callbackOpts.projectId, + event.id_event, + ); + } catch (error) { + throw error; + } + } + + async handleTokensRefresh( + connectionId: string, + providerName: string, + refresh_token: string, + id_project: string, + account_url?: string, + ) { + try { + const serviceName = providerName.toLowerCase(); + const service = this.serviceRegistry.getService(serviceName); + if (!service) { + throw new ReferenceError(`Unknown provider, found ${providerName}`); + } + const refreshOpts: RefreshParams = { + connectionId: connectionId, + refreshToken: refresh_token, + account_url: account_url, + projectId: id_project, + }; + await service.handleTokenRefresh(refreshOpts); + } catch (error) { + throw error; + } + } + + async passthrough( + input: PassthroughInput, + connectionId: string, + ): Promise { + try { + const connection = await this.prisma.connections.findUnique({ + where: { + id_connection: connectionId, + }, + }); + const serviceName = connection.provider_slug.toLowerCase(); + const service = this.serviceRegistry.getService(serviceName); + if (!service) { + throw new ReferenceError(`Unknown provider, found ${serviceName}`); + } + return await service.passthrough(input, connectionId); + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/@core/connections/cybersecurity/services/microsoftdefender/microsoftdefender.service.ts b/packages/api/src/@core/connections/cybersecurity/services/microsoftdefender/microsoftdefender.service.ts new file mode 100644 index 000000000..c874a92ba --- /dev/null +++ b/packages/api/src/@core/connections/cybersecurity/services/microsoftdefender/microsoftdefender.service.ts @@ -0,0 +1,199 @@ +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 { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; +import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; +import { ConnectionUtils } from '@@core/connections/@utils'; +import { + AbstractBaseConnectionService, + OAuthCallbackParams, + PassthroughInput, + RefreshParams, +} from '@@core/connections/@utils/types'; +import { PassthroughResponse } from '@@core/passthrough/types'; +import { Injectable } from '@nestjs/common'; +import { + AuthStrategy, + CONNECTORS_METADATA, + OAuth2AuthData, + providerToType, +} from '@panora/shared'; +import { v4 as uuidv4 } from 'uuid'; +import { ServiceRegistry } from '../registry.service'; +import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; +import axios from 'axios'; + +export interface MicrosoftdefenderOAuthResponse { + access_token: string; + token_type: string; + scope: string; +} + +@Injectable() +export class MicrosoftdefenderConnectionService extends AbstractBaseConnectionService { + private readonly type: string; + + constructor( + protected prisma: PrismaService, + private logger: LoggerService, + protected cryptoService: EncryptionService, + private env: EnvironmentService, + private registry: ServiceRegistry, + private connectionUtils: ConnectionUtils, + private cService: ConnectionsStrategiesService, + private retryService: RetryHandler, + ) { + super(prisma, cryptoService); + this.logger.setContext(MicrosoftdefenderConnectionService.name); + this.registry.registerService('microsoftdefender', this); + this.type = providerToType( + 'microsoftdefender', + 'cybersecurity', + AuthStrategy.oauth2, + ); + } + + async passthrough( + input: PassthroughInput, + connectionId: string, + ): Promise { + try { + const { headers } = input; + const config = await this.constructPassthrough(input, connectionId); + + const connection = await this.prisma.connections.findUnique({ + where: { + id_connection: connectionId, + }, + }); + + const access_token = JSON.parse( + this.cryptoService.decrypt(connection.access_token), + ); + config.headers = { + ...config.headers, + ...headers, + Authorization: `Bearer ${access_token}`, + }; + + return await this.retryService.makeRequest( + { + method: config.method, + url: config.url, + data: config.data, + headers: config.headers, + }, + 'cybersecurity.microsoftdefender.passthrough', + config.linkedUserId, + ); + } catch (error) { + throw error; + } + } + + async handleCallback(opts: OAuthCallbackParams) { + try { + const { linkedUserId, projectId, code } = opts; + const isNotUnique = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdefender', + vertical: 'cybersecurity', + }, + }); + //reconstruct the redirect URI that was passed in the frontend it must be the same + const REDIRECT_URI = `${ + this.env.getDistributionMode() == 'selfhost' + ? this.env.getTunnelIngress() + : this.env.getPanoraBaseUrl() + }/connections/oauth/callback`; + + const CREDENTIALS = (await this.cService.getCredentials( + projectId, + this.type, + )) as OAuth2AuthData; + + const formData = new URLSearchParams({ + redirect_uri: REDIRECT_URI, + client_id: CREDENTIALS.CLIENT_ID, + client_secret: CREDENTIALS.CLIENT_SECRET, + code: code, + grant_type: 'authorization_code', + }); + const res = await axios.post( + 'https://api.microsoftdefender.com/oauth/access_token', + formData.toString(), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + }, + }, + ); + const data: MicrosoftdefenderOAuthResponse = res.data; + // save tokens for this customer inside our db + let db_res; + const connection_token = uuidv4(); + const BASE_API_URL = CONNECTORS_METADATA['cybersecurity'][ + 'microsoftdefender' + ].urls.apiUrl as string; + // get the site id for the token + const site = await axios.get( + 'https://api.microsoftdefender.com/v2/sites', + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + Authorization: `Bearer ${data.access_token}`, + }, + }, + ); + const site_id = site.data.sites[0].id; + if (isNotUnique) { + // Update existing connection + db_res = await this.prisma.connections.update({ + where: { + id_connection: isNotUnique.id_connection, + }, + data: { + access_token: this.cryptoService.encrypt(data.access_token), + status: 'valid', + created_at: new Date(), + }, + }); + } else { + // Create new connection + db_res = await this.prisma.connections.create({ + data: { + id_connection: uuidv4(), + connection_token: connection_token, + provider_slug: 'microsoftdefender', + vertical: 'cybersecurity', + token_type: 'oauth2', + account_url: `${BASE_API_URL}/sites/${site_id}`, + access_token: this.cryptoService.encrypt(data.access_token), + status: 'valid', + created_at: new Date(), + projects: { + connect: { id_project: projectId }, + }, + linked_users: { + connect: { + id_linked_user: await this.connectionUtils.getLinkedUserId( + projectId, + linkedUserId, + ), + }, + }, + }, + }); + } + this.logger.log('Successfully added tokens inside DB ' + db_res); + return db_res; + } catch (error) { + throw error; + } + } + + async handleTokenRefresh(opts: RefreshParams) { + return; + } +} diff --git a/packages/api/src/@core/connections/cybersecurity/services/qualys/qualys.service.ts b/packages/api/src/@core/connections/cybersecurity/services/qualys/qualys.service.ts new file mode 100644 index 000000000..7840147b9 --- /dev/null +++ b/packages/api/src/@core/connections/cybersecurity/services/qualys/qualys.service.ts @@ -0,0 +1,156 @@ +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 { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; +import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; +import { ConnectionUtils } from '@@core/connections/@utils'; +import { + AbstractBaseConnectionService, + OAuthCallbackParams, + PassthroughInput, + RefreshParams, +} from '@@core/connections/@utils/types'; +import { PassthroughResponse } from '@@core/passthrough/types'; +import { Injectable } from '@nestjs/common'; +import { + AuthStrategy, + CONNECTORS_METADATA, + DynamicApiUrl, + providerToType, +} from '@panora/shared'; +import { v4 as uuidv4 } from 'uuid'; +import { ServiceRegistry } from '../registry.service'; + +@Injectable() +export class QualysConnectionService extends AbstractBaseConnectionService { + private readonly type: string; + + constructor( + protected prisma: PrismaService, + private logger: LoggerService, + protected cryptoService: EncryptionService, + private registry: ServiceRegistry, + private connectionUtils: ConnectionUtils, + private cService: ConnectionsStrategiesService, + private retryService: RetryHandler, + ) { + super(prisma, cryptoService); + this.logger.setContext(QualysConnectionService.name); + this.registry.registerService('qualys', this); + this.type = providerToType('qualys', 'cybersecurity', AuthStrategy.oauth2); + } + + async passthrough( + input: PassthroughInput, + connectionId: string, + ): Promise { + try { + const { headers } = input; + const config = await this.constructPassthrough(input, connectionId); + + const connection = await this.prisma.connections.findUnique({ + where: { + id_connection: connectionId, + }, + }); + const decryptedData = JSON.parse( + this.cryptoService.decrypt(connection.access_token), + ); + + const { username, password } = decryptedData; + + config.headers = { + ...config.headers, + ...headers, + 'X-Requested-With': 'Curl Sample', + Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString( + 'base64', + )}`, + }; + + return await this.retryService.makeRequest( + { + method: config.method, + url: config.url, + data: config.data, + headers: config.headers, + }, + 'cybersecurity.qualys.passthrough', + config.linkedUserId, + ); + } catch (error) { + throw error; + } + } + + async handleCallback(opts: OAuthCallbackParams) { + try { + const { linkedUserId, projectId, body } = opts; + const { username, password, api_url } = body; + const isNotUnique = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'qualys', + vertical: 'cybersecurity', + }, + }); + + let db_res; + const connection_token = uuidv4(); + const BASE_API_URL = ( + CONNECTORS_METADATA['cybersecurity']['qualys'].urls + .apiUrl as DynamicApiUrl + )(api_url); + + if (isNotUnique) { + db_res = await this.prisma.connections.update({ + where: { + id_connection: isNotUnique.id_connection, + }, + data: { + access_token: this.cryptoService.encrypt( + JSON.stringify({ username, password }), + ), + account_url: BASE_API_URL, + status: 'valid', + created_at: new Date(), + }, + }); + } else { + db_res = await this.prisma.connections.create({ + data: { + id_connection: uuidv4(), + connection_token: connection_token, + provider_slug: 'qualys', + vertical: 'cybersecurity', + token_type: 'basic', + account_url: BASE_API_URL, + access_token: this.cryptoService.encrypt( + JSON.stringify({ username, password }), + ), + status: 'valid', + created_at: new Date(), + projects: { + connect: { id_project: projectId }, + }, + linked_users: { + connect: { + id_linked_user: await this.connectionUtils.getLinkedUserId( + projectId, + linkedUserId, + ), + }, + }, + }, + }); + } + return db_res; + } catch (error) { + throw error; + } + } + + handleTokenRefresh?(opts: RefreshParams): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/packages/api/src/@core/connections/cybersecurity/services/rapid7insightvm/rapid7.service.ts b/packages/api/src/@core/connections/cybersecurity/services/rapid7insightvm/rapid7.service.ts new file mode 100644 index 000000000..12e5cd767 --- /dev/null +++ b/packages/api/src/@core/connections/cybersecurity/services/rapid7insightvm/rapid7.service.ts @@ -0,0 +1,145 @@ +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 { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; +import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; +import { ConnectionUtils } from '@@core/connections/@utils'; +import { + AbstractBaseConnectionService, + OAuthCallbackParams, + PassthroughInput, + RefreshParams, +} from '@@core/connections/@utils/types'; +import { PassthroughResponse } from '@@core/passthrough/types'; +import { Injectable } from '@nestjs/common'; +import { + AuthStrategy, + CONNECTORS_METADATA, + DynamicApiUrl, + providerToType, +} from '@panora/shared'; +import { v4 as uuidv4 } from 'uuid'; +import { ServiceRegistry } from '../registry.service'; + +@Injectable() +export class Rapid7ConnectionService extends AbstractBaseConnectionService { + private readonly type: string; + + constructor( + protected prisma: PrismaService, + private logger: LoggerService, + protected cryptoService: EncryptionService, + private registry: ServiceRegistry, + private connectionUtils: ConnectionUtils, + private cService: ConnectionsStrategiesService, + private retryService: RetryHandler, + ) { + super(prisma, cryptoService); + this.logger.setContext(Rapid7ConnectionService.name); + this.registry.registerService('rapid7', this); + this.type = providerToType('rapid7', 'cybersecurity', AuthStrategy.oauth2); + } + + async passthrough( + input: PassthroughInput, + connectionId: string, + ): Promise { + try { + const { headers } = input; + const config = await this.constructPassthrough(input, connectionId); + + const connection = await this.prisma.connections.findUnique({ + where: { + id_connection: connectionId, + }, + }); + config.headers = { + ...config.headers, + ...headers, + Authorization: `Basic ${Buffer.from( + `:${connection.access_token}`, + ).toString('base64')}`, + }; + + return await this.retryService.makeRequest( + { + method: config.method, + url: config.url, + data: config.data, + headers: config.headers, + }, + 'cybersecurity.rapid7.passthrough', + config.linkedUserId, + ); + } catch (error) { + throw error; + } + } + + async handleCallback(opts: OAuthCallbackParams) { + try { + const { linkedUserId, projectId, body } = opts; + const { api_key, region } = body; + const isNotUnique = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'rapid7', + vertical: 'cybersecurity', + }, + }); + + let db_res; + const connection_token = uuidv4(); + const BASE_API_URL = ( + CONNECTORS_METADATA['cybersecurity']['rapid7'].urls + .apiUrl as DynamicApiUrl + )(region); + + if (isNotUnique) { + db_res = await this.prisma.connections.update({ + where: { + id_connection: isNotUnique.id_connection, + }, + data: { + access_token: this.cryptoService.encrypt(api_key), + account_url: BASE_API_URL, + status: 'valid', + created_at: new Date(), + }, + }); + } else { + db_res = await this.prisma.connections.create({ + data: { + id_connection: uuidv4(), + connection_token: connection_token, + provider_slug: 'rapid7', + vertical: 'cybersecurity', + token_type: 'basic', + account_url: BASE_API_URL, + access_token: this.cryptoService.encrypt(api_key), + status: 'valid', + created_at: new Date(), + projects: { + connect: { id_project: projectId }, + }, + linked_users: { + connect: { + id_linked_user: await this.connectionUtils.getLinkedUserId( + projectId, + linkedUserId, + ), + }, + }, + }, + }); + } + return db_res; + } catch (error) { + throw error; + } + } + + handleTokenRefresh?(opts: RefreshParams): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/packages/api/src/@core/connections/cybersecurity/services/registry.service.ts b/packages/api/src/@core/connections/cybersecurity/services/registry.service.ts new file mode 100644 index 000000000..2e9e3b100 --- /dev/null +++ b/packages/api/src/@core/connections/cybersecurity/services/registry.service.ts @@ -0,0 +1,25 @@ +import { IConnectionService } from '@@core/connections/@utils/types'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class ServiceRegistry { + private serviceMap: Map; + + constructor() { + this.serviceMap = new Map(); + } + + registerService(serviceKey: string, service: IConnectionService) { + this.serviceMap.set(serviceKey, service); + } + + getService(integrationId: string): IConnectionService { + const service = this.serviceMap.get(integrationId); + if (!service) { + throw new ReferenceError( + `Service not found for integration ID: ${integrationId}`, + ); + } + return service; + } +} diff --git a/packages/api/src/@core/connections/cybersecurity/services/semgrep/semgrep.service.ts b/packages/api/src/@core/connections/cybersecurity/services/semgrep/semgrep.service.ts new file mode 100644 index 000000000..7bd36d5d0 --- /dev/null +++ b/packages/api/src/@core/connections/cybersecurity/services/semgrep/semgrep.service.ts @@ -0,0 +1,143 @@ +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 { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; +import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; +import { ConnectionUtils } from '@@core/connections/@utils'; +import { + AbstractBaseConnectionService, + OAuthCallbackParams, + PassthroughInput, + RefreshParams, +} from '@@core/connections/@utils/types'; +import { PassthroughResponse } from '@@core/passthrough/types'; +import { Injectable } from '@nestjs/common'; +import { + AuthStrategy, + CONNECTORS_METADATA, + DynamicApiUrl, + providerToType, +} from '@panora/shared'; +import { v4 as uuidv4 } from 'uuid'; +import { ServiceRegistry } from '../registry.service'; + +@Injectable() +export class SemgrepConnectionService extends AbstractBaseConnectionService { + private readonly type: string; + + constructor( + protected prisma: PrismaService, + private logger: LoggerService, + protected cryptoService: EncryptionService, + private registry: ServiceRegistry, + private connectionUtils: ConnectionUtils, + private cService: ConnectionsStrategiesService, + private retryService: RetryHandler, + ) { + super(prisma, cryptoService); + this.logger.setContext(SemgrepConnectionService.name); + this.registry.registerService('semgrep', this); + this.type = providerToType('semgrep', 'cybersecurity', AuthStrategy.oauth2); + } + + async passthrough( + input: PassthroughInput, + connectionId: string, + ): Promise { + try { + const { headers } = input; + const config = await this.constructPassthrough(input, connectionId); + + const connection = await this.prisma.connections.findUnique({ + where: { + id_connection: connectionId, + }, + }); + config.headers = { + ...config.headers, + ...headers, + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }; + + return await this.retryService.makeRequest( + { + method: config.method, + url: config.url, + data: config.data, + headers: config.headers, + }, + 'cybersecurity.semgrep.passthrough', + config.linkedUserId, + ); + } catch (error) { + throw error; + } + } + + async handleCallback(opts: OAuthCallbackParams) { + try { + const { linkedUserId, projectId, body } = opts; + const { api_key } = body; + const isNotUnique = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'semgrep', + vertical: 'cybersecurity', + }, + }); + + let db_res; + const connection_token = uuidv4(); + const BASE_API_URL = CONNECTORS_METADATA['cybersecurity']['semgrep'].urls + .apiUrl as string; + + if (isNotUnique) { + db_res = await this.prisma.connections.update({ + where: { + id_connection: isNotUnique.id_connection, + }, + data: { + access_token: this.cryptoService.encrypt(api_key), + account_url: BASE_API_URL, + status: 'valid', + created_at: new Date(), + }, + }); + } else { + db_res = await this.prisma.connections.create({ + data: { + id_connection: uuidv4(), + connection_token: connection_token, + provider_slug: 'semgrep', + vertical: 'cybersecurity', + token_type: 'basic', + account_url: BASE_API_URL, + access_token: this.cryptoService.encrypt(api_key), + status: 'valid', + created_at: new Date(), + projects: { + connect: { id_project: projectId }, + }, + linked_users: { + connect: { + id_linked_user: await this.connectionUtils.getLinkedUserId( + projectId, + linkedUserId, + ), + }, + }, + }, + }); + } + return db_res; + } catch (error) { + throw error; + } + } + + handleTokenRefresh?(opts: RefreshParams): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/packages/api/src/@core/connections/cybersecurity/services/sentinelone/sentinelone.service.ts b/packages/api/src/@core/connections/cybersecurity/services/sentinelone/sentinelone.service.ts new file mode 100644 index 000000000..7d6f02fb5 --- /dev/null +++ b/packages/api/src/@core/connections/cybersecurity/services/sentinelone/sentinelone.service.ts @@ -0,0 +1,149 @@ +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 { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; +import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; +import { ConnectionUtils } from '@@core/connections/@utils'; +import { + AbstractBaseConnectionService, + OAuthCallbackParams, + PassthroughInput, + RefreshParams, +} from '@@core/connections/@utils/types'; +import { PassthroughResponse } from '@@core/passthrough/types'; +import { Injectable } from '@nestjs/common'; +import { + AuthStrategy, + CONNECTORS_METADATA, + DynamicApiUrl, + providerToType, +} from '@panora/shared'; +import { v4 as uuidv4 } from 'uuid'; +import { ServiceRegistry } from '../registry.service'; + +@Injectable() +export class SentineloneConnectionService extends AbstractBaseConnectionService { + private readonly type: string; + + constructor( + protected prisma: PrismaService, + private logger: LoggerService, + protected cryptoService: EncryptionService, + private registry: ServiceRegistry, + private connectionUtils: ConnectionUtils, + private cService: ConnectionsStrategiesService, + private retryService: RetryHandler, + ) { + super(prisma, cryptoService); + this.logger.setContext(SentineloneConnectionService.name); + this.registry.registerService('sentinelone', this); + this.type = providerToType( + 'sentinelone', + 'cybersecurity', + AuthStrategy.oauth2, + ); + } + + async passthrough( + input: PassthroughInput, + connectionId: string, + ): Promise { + try { + const { headers } = input; + const config = await this.constructPassthrough(input, connectionId); + + const connection = await this.prisma.connections.findUnique({ + where: { + id_connection: connectionId, + }, + }); + config.headers = { + ...config.headers, + ...headers, + Authorization: `APIToken ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }; + + return await this.retryService.makeRequest( + { + method: config.method, + url: config.url, + data: config.data, + headers: config.headers, + }, + 'cybersecurity.sentinelone.passthrough', + config.linkedUserId, + ); + } catch (error) { + throw error; + } + } + + async handleCallback(opts: OAuthCallbackParams) { + try { + const { linkedUserId, projectId, body } = opts; + const { api_key, host } = body; + const isNotUnique = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'sentinelone', + vertical: 'cybersecurity', + }, + }); + + let db_res; + const connection_token = uuidv4(); + const BASE_API_URL = ( + CONNECTORS_METADATA['cybersecurity']['sentinelone'].urls + .apiUrl as DynamicApiUrl + )(host); + + if (isNotUnique) { + db_res = await this.prisma.connections.update({ + where: { + id_connection: isNotUnique.id_connection, + }, + data: { + access_token: this.cryptoService.encrypt(api_key), + account_url: BASE_API_URL, + status: 'valid', + created_at: new Date(), + }, + }); + } else { + db_res = await this.prisma.connections.create({ + data: { + id_connection: uuidv4(), + connection_token: connection_token, + provider_slug: 'sentinelone', + vertical: 'cybersecurity', + token_type: 'basic', + account_url: BASE_API_URL, + access_token: this.cryptoService.encrypt(api_key), + status: 'valid', + created_at: new Date(), + projects: { + connect: { id_project: projectId }, + }, + linked_users: { + connect: { + id_linked_user: await this.connectionUtils.getLinkedUserId( + projectId, + linkedUserId, + ), + }, + }, + }, + }); + } + return db_res; + } catch (error) { + throw error; + } + } + + handleTokenRefresh?(opts: RefreshParams): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/packages/api/src/@core/connections/cybersecurity/services/snyk/snyk.service.ts b/packages/api/src/@core/connections/cybersecurity/services/snyk/snyk.service.ts new file mode 100644 index 000000000..ff77211ce --- /dev/null +++ b/packages/api/src/@core/connections/cybersecurity/services/snyk/snyk.service.ts @@ -0,0 +1,191 @@ +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 { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; +import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; +import { ConnectionUtils } from '@@core/connections/@utils'; +import { + AbstractBaseConnectionService, + OAuthCallbackParams, + PassthroughInput, + RefreshParams, +} from '@@core/connections/@utils/types'; +import { PassthroughResponse } from '@@core/passthrough/types'; +import { Injectable } from '@nestjs/common'; +import { + AuthStrategy, + CONNECTORS_METADATA, + OAuth2AuthData, + providerToType, +} from '@panora/shared'; +import { v4 as uuidv4 } from 'uuid'; +import { ServiceRegistry } from '../registry.service'; +import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; +import axios from 'axios'; + +export interface SnykOAuthResponse { + access_token: string; + token_type: string; + scope: string; +} + +@Injectable() +export class SnykConnectionService extends AbstractBaseConnectionService { + private readonly type: string; + + constructor( + protected prisma: PrismaService, + private logger: LoggerService, + protected cryptoService: EncryptionService, + private env: EnvironmentService, + private registry: ServiceRegistry, + private connectionUtils: ConnectionUtils, + private cService: ConnectionsStrategiesService, + private retryService: RetryHandler, + ) { + super(prisma, cryptoService); + this.logger.setContext(SnykConnectionService.name); + this.registry.registerService('snyk', this); + this.type = providerToType('snyk', 'cybersecurity', AuthStrategy.oauth2); + } + + async passthrough( + input: PassthroughInput, + connectionId: string, + ): Promise { + try { + const { headers } = input; + const config = await this.constructPassthrough(input, connectionId); + + const connection = await this.prisma.connections.findUnique({ + where: { + id_connection: connectionId, + }, + }); + + const access_token = JSON.parse( + this.cryptoService.decrypt(connection.access_token), + ); + config.headers = { + ...config.headers, + ...headers, + Authorization: `Bearer ${access_token}`, + }; + + return await this.retryService.makeRequest( + { + method: config.method, + url: config.url, + data: config.data, + headers: config.headers, + }, + 'cybersecurity.snyk.passthrough', + config.linkedUserId, + ); + } catch (error) { + throw error; + } + } + + async handleCallback(opts: OAuthCallbackParams) { + try { + const { linkedUserId, projectId, code } = opts; + const isNotUnique = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'snyk', + vertical: 'cybersecurity', + }, + }); + //reconstruct the redirect URI that was passed in the frontend it must be the same + const REDIRECT_URI = `${ + this.env.getDistributionMode() == 'selfhost' + ? this.env.getTunnelIngress() + : this.env.getPanoraBaseUrl() + }/connections/oauth/callback`; + + const CREDENTIALS = (await this.cService.getCredentials( + projectId, + this.type, + )) as OAuth2AuthData; + + const formData = new URLSearchParams({ + redirect_uri: REDIRECT_URI, + client_id: CREDENTIALS.CLIENT_ID, + client_secret: CREDENTIALS.CLIENT_SECRET, + code: code, + grant_type: 'authorization_code', + }); + const res = await axios.post( + 'https://api.snyk.com/oauth/access_token', + formData.toString(), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + }, + }, + ); + const data: SnykOAuthResponse = res.data; + // save tokens for this customer inside our db + let db_res; + const connection_token = uuidv4(); + const BASE_API_URL = CONNECTORS_METADATA['cybersecurity']['snyk'].urls + .apiUrl as string; + // get the site id for the token + const site = await axios.get('https://api.snyk.com/v2/sites', { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + Authorization: `Bearer ${data.access_token}`, + }, + }); + const site_id = site.data.sites[0].id; + if (isNotUnique) { + // Update existing connection + db_res = await this.prisma.connections.update({ + where: { + id_connection: isNotUnique.id_connection, + }, + data: { + access_token: this.cryptoService.encrypt(data.access_token), + status: 'valid', + created_at: new Date(), + }, + }); + } else { + // Create new connection + db_res = await this.prisma.connections.create({ + data: { + id_connection: uuidv4(), + connection_token: connection_token, + provider_slug: 'snyk', + vertical: 'cybersecurity', + token_type: 'oauth2', + account_url: `${BASE_API_URL}/sites/${site_id}`, + access_token: this.cryptoService.encrypt(data.access_token), + status: 'valid', + created_at: new Date(), + projects: { + connect: { id_project: projectId }, + }, + linked_users: { + connect: { + id_linked_user: await this.connectionUtils.getLinkedUserId( + projectId, + linkedUserId, + ), + }, + }, + }, + }); + } + this.logger.log('Successfully added tokens inside DB ' + db_res); + return db_res; + } catch (error) { + throw error; + } + } + + async handleTokenRefresh(opts: RefreshParams) { + return; + } +} diff --git a/packages/api/src/@core/connections/cybersecurity/services/tenable/tenable.service.ts b/packages/api/src/@core/connections/cybersecurity/services/tenable/tenable.service.ts new file mode 100644 index 000000000..7c125d3d8 --- /dev/null +++ b/packages/api/src/@core/connections/cybersecurity/services/tenable/tenable.service.ts @@ -0,0 +1,147 @@ +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 { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; +import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; +import { ConnectionUtils } from '@@core/connections/@utils'; +import { + AbstractBaseConnectionService, + OAuthCallbackParams, + PassthroughInput, + RefreshParams, +} from '@@core/connections/@utils/types'; +import { PassthroughResponse } from '@@core/passthrough/types'; +import { Injectable } from '@nestjs/common'; +import { + AuthStrategy, + CONNECTORS_METADATA, + DynamicApiUrl, + providerToType, +} from '@panora/shared'; +import { v4 as uuidv4 } from 'uuid'; +import { ServiceRegistry } from '../registry.service'; + +@Injectable() +export class TenableConnectionService extends AbstractBaseConnectionService { + private readonly type: string; + + constructor( + protected prisma: PrismaService, + private logger: LoggerService, + protected cryptoService: EncryptionService, + private registry: ServiceRegistry, + private connectionUtils: ConnectionUtils, + private cService: ConnectionsStrategiesService, + private retryService: RetryHandler, + ) { + super(prisma, cryptoService); + this.logger.setContext(TenableConnectionService.name); + this.registry.registerService('tenable', this); + this.type = providerToType('tenable', 'cybersecurity', AuthStrategy.oauth2); + } + + async passthrough( + input: PassthroughInput, + connectionId: string, + ): Promise { + try { + const { headers } = input; + const config = await this.constructPassthrough(input, connectionId); + + const connection = await this.prisma.connections.findUnique({ + where: { + id_connection: connectionId, + }, + }); + const decryptedData = JSON.parse( + this.cryptoService.decrypt(connection.access_token), + ); + + const { access_key, secret_key } = decryptedData; // todo + + config.headers = { + ...config.headers, + ...headers, + 'X-ApiKeys': `${secret_key}`, + }; + + return await this.retryService.makeRequest( + { + method: config.method, + url: config.url, + data: config.data, + headers: config.headers, + }, + 'cybersecurity.tenable.passthrough', + config.linkedUserId, + ); + } catch (error) { + throw error; + } + } + + async handleCallback(opts: OAuthCallbackParams) { + try { + const { linkedUserId, projectId, body } = opts; + const { access_key, secret_key } = body; + const isNotUnique = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'tenable', + vertical: 'cybersecurity', + }, + }); + + let db_res; + const connection_token = uuidv4(); + const BASE_API_URL = CONNECTORS_METADATA['cybersecurity']['tenable'].urls + .apiUrl as string; + + if (isNotUnique) { + db_res = await this.prisma.connections.update({ + where: { + id_connection: isNotUnique.id_connection, + }, + data: { + access_token: this.cryptoService.encrypt(secret_key), + account_url: BASE_API_URL, + status: 'valid', + created_at: new Date(), + }, + }); + } else { + db_res = await this.prisma.connections.create({ + data: { + id_connection: uuidv4(), + connection_token: connection_token, + provider_slug: 'tenable', + vertical: 'cybersecurity', + token_type: 'basic', + account_url: BASE_API_URL, + access_token: this.cryptoService.encrypt(secret_key), + status: 'valid', + created_at: new Date(), + projects: { + connect: { id_project: projectId }, + }, + linked_users: { + connect: { + id_linked_user: await this.connectionUtils.getLinkedUserId( + projectId, + linkedUserId, + ), + }, + }, + }, + }); + } + return db_res; + } catch (error) { + throw error; + } + } + + handleTokenRefresh?(opts: RefreshParams): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/packages/shared/src/categories.ts b/packages/shared/src/categories.ts index 5cb99075e..00369f15b 100644 --- a/packages/shared/src/categories.ts +++ b/packages/shared/src/categories.ts @@ -7,7 +7,8 @@ export enum ConnectorCategory { MarketingAutomation = 'marketingautomation', FileStorage = 'filestorage', Productivity = 'productivity', - Ecommerce = 'ecommerce' + Ecommerce = 'ecommerce', + Cybersecurity = 'cybersecurity' } export const categoriesVerticals: ConnectorCategory[] = Object.values(ConnectorCategory); diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index ef65b5d6c..687f99313 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -2967,5 +2967,124 @@ export const CONNECTORS_METADATA: ProvidersConfig = { properties: ['username', 'password', 'store_url'] } }, + }, + 'cybersecurity': { + 'semgrep': { + urls: { + docsUrl: 'https://semgrep.dev/api/v1/docs/#section/Introduction', + apiUrl: 'https://semgrep.dev/api', + }, + logoPath: 'https://yt3.googleusercontent.com/NWVXYvuzHDgJJsbda7eyyz21Ba2qnq5WmuGrt9ax1rs6PP-mlDl5LCJ4ZO0Z2ZbiCq4ZoxqiGg=s900-c-k-c0x00ffffff-no-rj', + description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', + active: false, + primaryColor: '#10C096', + authStrategy: { + strategy: AuthStrategy.api_key, + properties: ['api_key'] + } + }, + 'snyk': { + scopes: '', + urls: { + docsUrl: 'https://docs.snyk.io/snyk-api/', + apiUrl: 'https://api.snyk.io', + authBaseUrl: 'https://app.snyk.io/oauth2/authorize' + }, + logoPath: 'https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1215%2Ffe4be452-1e68-444a-bf77-db21bf3a7bdc.png', + description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', + active: false, + authStrategy: { + strategy: AuthStrategy.oauth2 + }, + options: { + local_redirect_uri_in_https: true + } + }, + 'tenable': { + urls: { + docsUrl: 'https://developer.tenable.com/reference/navigate', + apiUrl: 'https://cloud.tenable.com', + }, + logoPath: 'https://pbs.twimg.com/profile_images/1410604377757216768/ocEKYniC_400x400.jpg', + description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', + active: false, + primaryColor: '#0D1E40', + authStrategy: { + strategy: AuthStrategy.basic, + properties: ['access_key', 'secret_key'] + } + }, + 'qualys': { + urls: { + docsUrl: 'https://docs.qualys.com/en/vm/api/scans/index.htm#t=get_started%2Fauthentication.htm', + apiUrl: (baseApi) => `https://${baseApi}/api` + }, + logoPath: 'https://companieslogo.com/img/orig/QLYS-68c2032c.png?t=1720244493', + description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', + active: false, + primaryColor: '#ED2E28', + authStrategy: { + strategy: AuthStrategy.basic, + properties: ['username', 'password', 'api_url'] + } + }, + 'rapid7insightvm': { + urls: { + docsUrl: 'https://help.rapid7.com/insightvm/en-us/api/index.html', + apiUrl: (region) => `https://${region}.api.insight.rapid7.com`, + }, + logoPath: 'https://images.saasworthy.com/insightvm_9113_logo_1635748346_lc0gr.png', + description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', + active: false, + primaryColor: '#E95722', + authStrategy: { + strategy: AuthStrategy.api_key, + properties: ['region', 'api_key'] + } + }, + 'crowdstrike': { + scopes: '', + urls: { + docsUrl: 'https://developer.crowdstrike.com/', + apiUrl: (dotHost) => `https://api${dotHost}.crowdstrike.com`, + authBaseUrl: '' + }, + logoPath: 'https://pbs.twimg.com/profile_images/1451022302578049024/6L-zG5oq_400x400.jpg', + description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', + active: false, + primaryColor: '#FC0001', + authStrategy: { + strategy: AuthStrategy.oauth2, + } + }, + 'sentinelone': { + urls: { + docsUrl: 'https://www.postman.com/api-evangelist/sentinelone/overview', + apiUrl: (host) => `https://${host}.sentinelone.net`, + }, + logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTZHLye2za7fjLiggqC1upKhhM3T-laySJSLQ&s', + description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', + active: false, + primaryColor: '#522E74', + authStrategy: { + strategy: AuthStrategy.api_key, + properties: ['host', 'api_key'] + } + }, + 'microsoftdefender': { + scopes: '', + urls: { + docsUrl: 'https://learn.microsoft.com/en-us/defender-endpoint/api/apis-intro', + apiUrl: '', + authBaseUrl: '' + }, + logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSuR4GZElDP7UNKhXS9jDGpElBTdchjg8hSsA&s', + description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', + active: false, + primaryColor: '#0078D8', + authStrategy: { + strategy: AuthStrategy.oauth2, + } + }, } }; From 0ffb1be959a4ff8ed8050a3a60f14d783a7e6b3c Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 16 Aug 2024 04:51:27 +0200 Subject: [PATCH 07/49] :bug: Added fix --- packages/api/scripts/init.sql | 3 +++ packages/api/scripts/seed.sql | 8 ++++---- packages/shared/src/connectors/metadata.ts | 3 +-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index ead5ee43c..f3bbbc437 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -541,6 +541,9 @@ CREATE TABLE connector_sets crm_close boolean NULL, fs_box boolean NULL, tcg_github boolean NULL, + hris_gusto boolean NULL, + hris_deel boolean NULL, + hris_sage boolean NULL, ecom_woocommerce boolean NULL, ecom_shopify boolean NULL, ecom_amazon boolean NULL, diff --git a/packages/api/scripts/seed.sql b/packages/api/scripts/seed.sql index 0bee28a94..7b29520da 100644 --- a/packages/api/scripts/seed.sql +++ b/packages/api/scripts/seed.sql @@ -1,10 +1,10 @@ INSERT INTO users (id_user, identification_strategy, email, password_hash, first_name, last_name) VALUES ('0ce39030-2901-4c56-8db0-5e326182ec6b', 'b2c','local@panora.dev', '$2b$10$Y7Q8TWGyGuc5ecdIASbBsuXMo3q/Rs3/cnY.mLZP4tUgfGUOCUBlG', 'local', 'Panora'); -INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github) VALUES - ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); +INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github, hris_deel, hris_sage) VALUES + ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); INSERT INTO projects (id_project, name, sync_mode, id_user, id_connector_set) VALUES ('1e468c15-aa57-4448-aa2b-7fed640d1e3d', 'Project 1', 'pull', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'), diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index 687f99313..a0540c9f3 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -2026,7 +2026,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://asset.brandfetch.io/id4NSNrRnG/idXzwlo3iL.jpeg', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -2566,7 +2566,6 @@ export const CONNECTORS_METADATA: ProvidersConfig = { } }, 'sage': { - scopes: '', urls: { docsUrl: 'https://sagehr.docs.apiary.io/#reference', apiUrl: (subdomain) => `https://${subdomain}.sage.hr` From e3a079bf9e7f6fd4bc37df12da77642fe1ddd985 Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 16 Aug 2024 04:53:26 +0200 Subject: [PATCH 08/49] :pencil2: Fix --- packages/api/scripts/init.sql | 1 + packages/api/scripts/seed.sql | 8 ++++---- packages/shared/src/connectors/metadata.ts | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index f3bbbc437..71a8c80ec 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -548,6 +548,7 @@ CREATE TABLE connector_sets ecom_shopify boolean NULL, ecom_amazon boolean NULL, ecom_squarespace boolean NULL, + ats_ashby boolean NULL, CONSTRAINT PK_project_connector PRIMARY KEY ( id_connector_set ) ); diff --git a/packages/api/scripts/seed.sql b/packages/api/scripts/seed.sql index 7b29520da..05c4d1bde 100644 --- a/packages/api/scripts/seed.sql +++ b/packages/api/scripts/seed.sql @@ -1,10 +1,10 @@ INSERT INTO users (id_user, identification_strategy, email, password_hash, first_name, last_name) VALUES ('0ce39030-2901-4c56-8db0-5e326182ec6b', 'b2c','local@panora.dev', '$2b$10$Y7Q8TWGyGuc5ecdIASbBsuXMo3q/Rs3/cnY.mLZP4tUgfGUOCUBlG', 'local', 'Panora'); -INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github, hris_deel, hris_sage) VALUES - ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); +INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github, hris_deel, hris_sage, ats_ashby) VALUES + ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); INSERT INTO projects (id_project, name, sync_mode, id_user, id_connector_set) VALUES ('1e468c15-aa57-4448-aa2b-7fed640d1e3d', 'Project 1', 'pull', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'), diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index a0540c9f3..ccb733c2f 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -1254,7 +1254,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://images.ctfassets.net/p03bi75xct27/2tVvkghDdMJxzkMca2QLnr/31b520c5e07db0103948af171fb54e99/ashby_logo_square.jpeg?q=80&fm=webp&w=2048', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, primaryColor: '#4a3ead', authStrategy: { strategy: AuthStrategy.basic, @@ -1269,7 +1269,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://play-lh.googleusercontent.com/c4BW9wr_QAiIeVBYHhP7rs06w99xJzxgLvmL5I1mkucC3_ATMyL1t7Doz0_LQ0X-qS0', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, primaryColor: '#599D16', authStrategy: { strategy: AuthStrategy.basic, @@ -2465,7 +2465,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://assets.wheelhouse.com/media/_solution_logo_04042023_58844144.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, primaryColor: '#F8A22D', authStrategy: { strategy: AuthStrategy.api_key, From 3d5a922d9a1f5f9d84dd6e452da958f4da6e7ebb Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 16 Aug 2024 05:17:50 +0200 Subject: [PATCH 09/49] :rotating_light: Lint fix --- packages/shared/src/connectors/metadata.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index ccb733c2f..3bc4e8fb3 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -1121,7 +1121,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { primaryColor: '#ffcf40', authStrategy: { strategy: AuthStrategy.api_key, - properties: ["api_key"] + properties: ['api_key'] } }, 'hubspot_marketing_hub': { @@ -2469,7 +2469,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { primaryColor: '#F8A22D', authStrategy: { strategy: AuthStrategy.api_key, - properties: ["api_key"] + properties: ['api_key'] } }, 'payfit': { @@ -2576,7 +2576,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { primaryColor: '#00d639', authStrategy: { strategy: AuthStrategy.api_key, - properties: ["api_key", "subdomain"] + properties: ['api_key', 'subdomain'] }, }, 'sap_successfactors': { From 131d644990d7fba1c7399892535d28154ab7619c Mon Sep 17 00:00:00 2001 From: nael Date: Fri, 16 Aug 2024 19:10:26 +0200 Subject: [PATCH 10/49] :bug: Fix build --- docker-compose.dev.yml | 3 +- docker-compose.source.yml | 3 + docker-compose.yml | 3 + packages/api/prisma/schema.prisma | 820 +++++++++++---------- packages/api/scripts/init.sql | 1 - packages/api/swagger/swagger-spec.yaml | 12 + packages/shared/src/connectors/metadata.ts | 1 + 7 files changed, 433 insertions(+), 410 deletions(-) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 061735d37..3e19f9fd6 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -182,7 +182,8 @@ services: SMTP_PORT: ${SMTP_PORT} SMTP_USER: ${SMTP_USER} SMTP_PASSWORD: ${SMTP_PASSWORD} - + MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID} + MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET} restart: unless-stopped ports: diff --git a/docker-compose.source.yml b/docker-compose.source.yml index ba207adf0..b1a4f161c 100644 --- a/docker-compose.source.yml +++ b/docker-compose.source.yml @@ -182,6 +182,9 @@ services: SMTP_PORT: ${SMTP_PORT} SMTP_USER: ${SMTP_USER} SMTP_PASSWORD: ${SMTP_PASSWORD} + MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID} + MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET} + restart: unless-stopped ports: - 3000:3000 diff --git a/docker-compose.yml b/docker-compose.yml index b03be3f08..c33ef1247 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -176,6 +176,9 @@ services: SMTP_PORT: ${SMTP_PORT} SMTP_USER: ${SMTP_USER} SMTP_PASSWORD: ${SMTP_PASSWORD} + MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID} + MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET} + restart: unless-stopped ports: - 3000:3000 diff --git a/packages/api/prisma/schema.prisma b/packages/api/prisma/schema.prisma index 58997c432..60a66a984 100644 --- a/packages/api/prisma/schema.prisma +++ b/packages/api/prisma/schema.prisma @@ -7,6 +7,403 @@ datasource db { url = env("DATABASE_URL") } +model crm_deals_stages { + id_crm_deals_stage String @id(map: "pk_crm_deal_stages") @db.Uuid + stage_name String? + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + id_linked_user String? @db.Uuid + remote_id String? + remote_platform String? + id_connection String @db.Uuid + crm_deals crm_deals[] +} + +model crm_users { + id_crm_user String @id(map: "pk_crm_users") @db.Uuid + name String? + email String? + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + id_linked_user String? @db.Uuid + remote_id String? + remote_platform String? + id_connection String @db.Uuid + crm_companies crm_companies[] + crm_contacts crm_contacts[] + crm_deals crm_deals[] + crm_engagements crm_engagements[] + crm_tasks crm_tasks[] +} + +model cs_attributes { + id_cs_attribute String @id(map: "pk_ct_attributes") @db.Uuid + attribute_slug String + data_type String + id_cs_entity String @db.Uuid +} + +model cs_entities { + id_cs_entity String @id(map: "pk_ct_entities") @db.Uuid + id_connection_strategy String @db.Uuid +} + +model cs_values { + id_cs_value String @id(map: "pk_ct_values") @db.Uuid + value String + id_cs_attribute String @db.Uuid +} + +model ecom_customers { + id_ecom_customer String @id(map: "pk_ecom_customers") @db.Uuid + remote_id String? + email String? + first_name String? + last_name String? + phone_number String? + modified_at DateTime @db.Timestamptz(6) + created_at DateTime @db.Timestamptz(6) + id_connection String @db.Uuid + remote_deleted Boolean + ecom_addresses ecom_addresses[] + ecom_orders ecom_orders[] +} + +model ecom_fulfilment_orders { + id_ecom_fulfilment_order String @id(map: "pk_ecom_fulfilment_order") @db.Uuid +} + +model ecom_order_line_items { + id_ecom_order_line_item String @id(map: "pk_106") @db.Uuid +} + +model ecom_products { + id_ecom_product String @id(map: "pk_ecom_products") @db.Uuid + remote_id String? + product_url String? + product_type String? + product_status String? + images_urls String[] + description String? + vendor String? + tags String[] + modified_at DateTime @db.Timestamptz(6) + created_at DateTime @db.Timestamptz(6) + id_connection String @db.Uuid + remote_deleted Boolean + ecom_product_variants ecom_product_variants[] +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model entity { + id_entity String @id(map: "pk_entity") @db.Uuid + ressource_owner_id String @db.Uuid + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + attribute attribute[] + value value[] +} + +model fs_drives { + id_fs_drive String @id(map: "pk_fs_drives") @db.Uuid + drive_url String? + name String? + remote_created_at DateTime? @db.Timestamptz(6) + remote_id String? + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + id_connection String @db.Uuid +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model fs_groups { + id_fs_group String @id(map: "pk_fs_groups") @db.Uuid + name String? + users String[] + remote_id String? + remote_was_deleted Boolean + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + id_connection String @db.Uuid +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model fs_permissions { + id_fs_permission String @id(map: "pk_fs_permissions") @db.Uuid + remote_id String? + user String? @db.Uuid + group String? @db.Uuid + type String? + roles String[] + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + id_connection String @db.Uuid +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model fs_shared_links { + id_fs_shared_link String @id(map: "pk_fs_shared_links") @db.Uuid + url String? + download_url String? + scope String? + password_protected Boolean + password String? + expires_at DateTime? @db.Timestamptz(6) + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + id_connection String @db.Uuid + id_fs_folder String? @db.Uuid + id_fs_file String? @db.Uuid + remote_id String? +} + +model fs_users { + id_fs_user String @id(map: "pk_fs_users") @db.Uuid + name String? + email String? + is_me Boolean + remote_id String? + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + id_connection String @db.Uuid +} + +model hris_companies { + id_hris_company String @id(map: "pk_hris_companies") @db.Uuid + legal_name String? + display_name String? + eins String[] + remote_id String? + remote_created_at DateTime? @db.Timestamptz(6) + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + remote_was_deleted Boolean + id_connection String @db.Uuid + hris_employees hris_employees[] +} + +model hris_employer_benefits { + id_hris_employer_benefit String @id(map: "pk_hris_employer_benefits") @db.Uuid + id_connection String @db.Uuid + benefit_plan_type String? + name String? + description String? + deduction_code String? + remote_id String? + remote_created_at DateTime? @db.Timestamptz(6) + remote_was_deleted Boolean + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + hris_benefits hris_benefits[] +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model hris_groups { + id_hris_group String @id(map: "pk_hris_groups") @db.Uuid + parent_group String? @db.Uuid + name String? + type String? + remote_id String + remote_created_at DateTime @db.Timestamptz(6) + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + remote_was_deleted Boolean + id_connection String @db.Uuid +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model hris_locations { + id_hris_location String @id(map: "pk_hris_locations") @db.Uuid + name String? + phone_number String? + street_1 String? + street_2 String? + city String? + state String? + id_hris_company String? @db.Uuid + id_hris_employee String? @db.Uuid + zip_code String? + country String? + location_type String? + remote_id String? + remote_created_at DateTime @db.Timestamptz(6) + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + remote_was_deleted Boolean + id_connection String @db.Uuid +} + +model hris_pay_groups { + id_hris_pay_group String @id(map: "pk_hris_pay_groups") @db.Uuid + pay_group_name String? + remote_id String? + remote_created_at DateTime? @db.Timestamptz(6) + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + remote_was_deleted Boolean + id_connection String @db.Uuid + hris_employments hris_employments[] +} + +model hris_payroll_runs { + id_hris_payroll_run String @id(map: "pk_hris_payroll_runs") @db.Uuid + run_state String? + run_type String? + start_date DateTime? @db.Timestamptz(6) + end_date DateTime? @db.Timestamptz(6) + check_date DateTime? @db.Timestamptz(6) + remote_id String? + remote_created_at DateTime? @db.Timestamptz(6) + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + remote_was_deleted Boolean + id_connection String @db.Uuid + hris_employee_payroll_runs hris_employee_payroll_runs[] +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model hris_time_off { + id_hris_time_off String @id(map: "pk_hris_time_off") @db.Uuid + employee String? @db.Uuid + approver String? @db.Uuid + status String? + employee_note String? + units String? + amount BigInt? + request_type String? + start_time DateTime? @db.Timestamptz(6) + end_time DateTime? @db.Timestamptz(6) + remote_id String? + remote_created_at DateTime? @db.Timestamptz(6) + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + remote_was_deleted Boolean + id_connection String @db.Uuid +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model managed_webhooks { + id_managed_webhook String @id(map: "pk_managed_webhooks") @db.Uuid + active Boolean + id_connection String @db.Uuid + endpoint String @db.Uuid + api_version String? + active_events String[] + remote_signing_secret String? + modified_at DateTime @db.Timestamptz(6) + created_at DateTime @db.Timestamptz(6) +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model remote_data { + id_remote_data String @id(map: "pk_remote_data") @db.Uuid + ressource_owner_id String? @unique(map: "force_unique_ressourceownerid") @db.Uuid + format String? + data String? + created_at DateTime? @db.Timestamptz(6) +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model tcg_accounts { + id_tcg_account String @id(map: "pk_tcg_account") @db.Uuid + remote_id String? + name String? + domains String[] + remote_platform String? + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + id_linked_user String? @db.Uuid + id_connection String @db.Uuid + tcg_contacts tcg_contacts[] +} + +model tcg_collections { + id_tcg_collection String @id(map: "pk_tcg_collections") @db.Uuid + name String? + description String? + remote_id String? + remote_platform String? + collection_type String? + parent_collection String? @db.Uuid + id_tcg_ticket String? @db.Uuid + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + id_linked_user String @db.Uuid + id_connection String @db.Uuid +} + +model tcg_teams { + id_tcg_team String @id(map: "pk_tcg_teams") @db.Uuid + remote_id String? + remote_platform String? + name String? + description String? + created_at DateTime @db.Timestamp(6) + modified_at DateTime @db.Timestamp(6) + id_linked_user String? @db.Uuid + id_connection String @db.Uuid +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model tcg_users { + id_tcg_user String @id(map: "pk_tcg_users") @db.Uuid + name String? + email_address String? + remote_id String? + remote_platform String? + teams String[] + id_linked_user String? @db.Uuid + id_connection String @db.Uuid + created_at DateTime? @db.Timestamptz(6) + modified_at DateTime? @db.Timestamptz(6) + tcg_comments tcg_comments[] +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model users { + id_user String @id(map: "pk_users") @db.Uuid + identification_strategy String + email String? @unique(map: "unique_email") + password_hash String? + first_name String + last_name String + id_stytch String? @unique(map: "force_stytch_id_unique") + created_at DateTime @default(now()) @db.Timestamptz(6) + modified_at DateTime @default(now()) @db.Timestamptz(6) + reset_token String? + reset_token_expires_at DateTime? @db.Timestamptz(6) + api_keys api_keys[] + projects projects[] +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model webhook_endpoints { + id_webhook_endpoint String @id(map: "pk_webhook_endpoint") @db.Uuid + endpoint_description String? + url String + secret String + active Boolean + created_at DateTime @db.Timestamptz(6) + scope String[] + id_project String @db.Uuid + last_update DateTime? @db.Timestamptz(6) + webhook_delivery_attempts webhook_delivery_attempts[] +} + +model webhooks_payloads { + id_webhooks_payload String @id(map: "pk_webhooks_payload") @db.Uuid + data Json @db.Json + webhook_delivery_attempts webhook_delivery_attempts[] +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +model webhooks_reponses { + id_webhooks_reponse String @id(map: "pk_webhooks_reponse") @db.Uuid + http_response_data String + http_status_code String + webhook_delivery_attempts webhook_delivery_attempts[] +} + /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model acc_accounting_periods { id_acc_accounting_period String @id(map: "pk_acc_accounting_periods") @db.Uuid @@ -938,10 +1335,14 @@ model connector_sets { crm_close Boolean? fs_box Boolean? tcg_github Boolean? + hris_gusto Boolean? + hris_deel Boolean? + hris_sage Boolean? ecom_woocommerce Boolean? ecom_shopify Boolean? ecom_amazon Boolean? ecom_squarespace Boolean? + ats_ashby Boolean? projects projects[] } @@ -1039,18 +1440,6 @@ model crm_deals { @@index([id_crm_company], map: "fk_crm_deal_crmcompanyid") } -model crm_deals_stages { - id_crm_deals_stage String @id(map: "pk_crm_deal_stages") @db.Uuid - stage_name String? - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - id_linked_user String? @db.Uuid - remote_id String? - remote_platform String? - id_connection String @db.Uuid - crm_deals crm_deals[] -} - /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model crm_email_addresses { id_crm_email String @id(map: "pk_crm_contact_email_addresses") @db.Uuid @@ -1160,41 +1549,6 @@ model crm_tasks { @@index([id_crm_deal], map: "fk_crmtask_dealid") } -model crm_users { - id_crm_user String @id(map: "pk_crm_users") @db.Uuid - name String? - email String? - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - id_linked_user String? @db.Uuid - remote_id String? - remote_platform String? - id_connection String @db.Uuid - crm_companies crm_companies[] - crm_contacts crm_contacts[] - crm_deals crm_deals[] - crm_engagements crm_engagements[] - crm_tasks crm_tasks[] -} - -model cs_attributes { - id_cs_attribute String @id(map: "pk_ct_attributes") @db.Uuid - attribute_slug String - data_type String - id_cs_entity String @db.Uuid -} - -model cs_entities { - id_cs_entity String @id(map: "pk_ct_entities") @db.Uuid - id_connection_strategy String @db.Uuid -} - -model cs_values { - id_cs_value String @id(map: "pk_ct_values") @db.Uuid - value String - id_cs_attribute String @db.Uuid -} - /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model ecom_addresses { id_ecom_address String @id(map: "pk_ecom_customer_addresses") @db.Uuid @@ -1217,25 +1571,6 @@ model ecom_addresses { @@index([id_ecom_order], map: "fk_index_fk_ecom_order_address") } -model ecom_customers { - id_ecom_customer String @id(map: "pk_ecom_customers") @db.Uuid - remote_id String? - email String? - first_name String? - last_name String? - phone_number String? - modified_at DateTime @db.Timestamptz(6) - created_at DateTime @db.Timestamptz(6) - id_connection String @db.Uuid - remote_deleted Boolean - ecom_addresses ecom_addresses[] - ecom_orders ecom_orders[] -} - -model ecom_fulfilment_orders { - id_ecom_fulfilment_order String @id(map: "pk_ecom_fulfilment_order") @db.Uuid -} - /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model ecom_fulfilments { id_ecom_fulfilment String @id(map: "pk_ecom_fulfilments") @db.Uuid @@ -1254,10 +1589,6 @@ model ecom_fulfilments { @@index([id_ecom_order], map: "fk_index_ecom_order_fulfilment") } -model ecom_order_line_items { - id_ecom_order_line_item String @id(map: "pk_106") @db.Uuid -} - model ecom_orders { id_ecom_order String @id(map: "pk_ecom_orders") @db.Uuid order_status String? @@ -1302,33 +1633,6 @@ model ecom_product_variants { @@index([id_ecom_product], map: "fk_index_ecom_products_variants") } -model ecom_products { - id_ecom_product String @id(map: "pk_ecom_products") @db.Uuid - remote_id String? - product_url String? - product_type String? - product_status String? - images_urls String[] - description String? - vendor String? - tags String[] - modified_at DateTime @db.Timestamptz(6) - created_at DateTime @db.Timestamptz(6) - id_connection String @db.Uuid - remote_deleted Boolean - ecom_product_variants ecom_product_variants[] -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model entity { - id_entity String @id(map: "pk_entity") @db.Uuid - ressource_owner_id String @db.Uuid - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - attribute attribute[] - value value[] -} - /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model events { id_event String @id(map: "pk_jobs") @db.Uuid @@ -1349,17 +1653,6 @@ model events { @@index([id_linked_user], map: "fk_linkeduserid_projectid") } -model fs_drives { - id_fs_drive String @id(map: "pk_fs_drives") @db.Uuid - drive_url String? - name String? - remote_created_at DateTime? @db.Timestamptz(6) - remote_id String? - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - id_connection String @db.Uuid -} - model fs_files { id_fs_file String @id(map: "pk_fs_files") @db.Uuid name String? @@ -1381,71 +1674,18 @@ model fs_folders { id_fs_folder String @id(map: "pk_fs_folders") @db.Uuid folder_url String? size BigInt? - name String? - description String? - parent_folder String? @db.Uuid - remote_id String? - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - id_fs_drive String? @db.Uuid - id_connection String @db.Uuid - id_fs_permission String? @db.Uuid - - @@index([id_fs_drive], map: "fk_fs_folder_driveid") - @@index([id_fs_permission], map: "fk_fs_folder_permissionid") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model fs_groups { - id_fs_group String @id(map: "pk_fs_groups") @db.Uuid - name String? - users String[] - remote_id String? - remote_was_deleted Boolean - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - id_connection String @db.Uuid -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model fs_permissions { - id_fs_permission String @id(map: "pk_fs_permissions") @db.Uuid - remote_id String? - user String? @db.Uuid - group String? @db.Uuid - type String? - roles String[] - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - id_connection String @db.Uuid -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model fs_shared_links { - id_fs_shared_link String @id(map: "pk_fs_shared_links") @db.Uuid - url String? - download_url String? - scope String? - password_protected Boolean - password String? - expires_at DateTime? @db.Timestamptz(6) - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - id_connection String @db.Uuid - id_fs_folder String? @db.Uuid - id_fs_file String? @db.Uuid - remote_id String? -} + name String? + description String? + parent_folder String? @db.Uuid + remote_id String? + created_at DateTime @db.Timestamptz(6) + modified_at DateTime @db.Timestamptz(6) + id_fs_drive String? @db.Uuid + id_connection String @db.Uuid + id_fs_permission String? @db.Uuid -model fs_users { - id_fs_user String @id(map: "pk_fs_users") @db.Uuid - name String? - email String? - is_me Boolean - remote_id String? - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - id_connection String @db.Uuid + @@index([id_fs_drive], map: "fk_fs_folder_driveid") + @@index([id_fs_permission], map: "fk_fs_folder_permissionid") } model hris_bank_infos { @@ -1488,20 +1728,6 @@ model hris_benefits { @@index([id_hris_employee], map: "fkx_hris_benefits_employeeid") } -model hris_companies { - id_hris_company String @id(map: "pk_hris_companies") @db.Uuid - legal_name String? - display_name String? - eins String[] - remote_id String? - remote_created_at DateTime? @db.Timestamptz(6) - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - remote_was_deleted Boolean - id_connection String @db.Uuid - hris_employees hris_employees[] -} - /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model hris_dependents { id_hris_dependents String @id(map: "pk_hris_dependents") @db.Uuid @@ -1636,21 +1862,6 @@ model hris_employees { @@index([id_hris_company], map: "fkx_employee_companyid") } -model hris_employer_benefits { - id_hris_employer_benefit String @id(map: "pk_hris_employer_benefits") @db.Uuid - id_connection String @db.Uuid - benefit_plan_type String? - name String? - description String? - deduction_code String? - remote_id String? - remote_created_at DateTime? @db.Timestamptz(6) - remote_was_deleted Boolean - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - hris_benefits hris_benefits[] -} - /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model hris_employments { id_hris_employment String @id(map: "pk_hris_employments") @db.Uuid @@ -1677,90 +1888,6 @@ model hris_employments { @@index([id_hris_pay_group], map: "fkx_employments_pay_group_id") } -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model hris_groups { - id_hris_group String @id(map: "pk_hris_groups") @db.Uuid - parent_group String? @db.Uuid - name String? - type String? - remote_id String - remote_created_at DateTime @db.Timestamptz(6) - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - remote_was_deleted Boolean - id_connection String @db.Uuid -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model hris_locations { - id_hris_location String @id(map: "pk_hris_locations") @db.Uuid - name String? - phone_number String? - street_1 String? - street_2 String? - city String? - state String? - id_hris_company String? @db.Uuid - id_hris_employee String? @db.Uuid - zip_code String? - country String? - location_type String? - remote_id String? - remote_created_at DateTime @db.Timestamptz(6) - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - remote_was_deleted Boolean - id_connection String @db.Uuid -} - -model hris_pay_groups { - id_hris_pay_group String @id(map: "pk_hris_pay_groups") @db.Uuid - pay_group_name String? - remote_id String? - remote_created_at DateTime? @db.Timestamptz(6) - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - remote_was_deleted Boolean - id_connection String @db.Uuid - hris_employments hris_employments[] -} - -model hris_payroll_runs { - id_hris_payroll_run String @id(map: "pk_hris_payroll_runs") @db.Uuid - run_state String? - run_type String? - start_date DateTime? @db.Timestamptz(6) - end_date DateTime? @db.Timestamptz(6) - check_date DateTime? @db.Timestamptz(6) - remote_id String? - remote_created_at DateTime? @db.Timestamptz(6) - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - remote_was_deleted Boolean - id_connection String @db.Uuid - hris_employee_payroll_runs hris_employee_payroll_runs[] -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model hris_time_off { - id_hris_time_off String @id(map: "pk_hris_time_off") @db.Uuid - employee String? @db.Uuid - approver String? @db.Uuid - status String? - employee_note String? - units String? - amount BigInt? - request_type String? - start_time DateTime? @db.Timestamptz(6) - end_time DateTime? @db.Timestamptz(6) - remote_id String? - remote_created_at DateTime? @db.Timestamptz(6) - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - remote_was_deleted Boolean - id_connection String @db.Uuid -} - model hris_time_off_balances { id_hris_time_off_balance String @id(map: "pk_hris_time_off_balances") @db.Uuid balance BigInt? @@ -1833,19 +1960,6 @@ model linked_users { @@index([id_project], map: "fk_proectid_linked_users") } -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model managed_webhooks { - id_managed_webhook String @id(map: "pk_managed_webhooks") @db.Uuid - active Boolean - id_connection String @db.Uuid - endpoint String @db.Uuid - api_version String? - active_events String[] - remote_signing_secret String? - modified_at DateTime @db.Timestamptz(6) - created_at DateTime @db.Timestamptz(6) -} - /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model projects { id_project String @id(map: "pk_projects") @db.Uuid @@ -1864,29 +1978,6 @@ model projects { @@index([id_connector_set], map: "fk_connectors_sets") } -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model remote_data { - id_remote_data String @id(map: "pk_remote_data") @db.Uuid - ressource_owner_id String? @unique(map: "force_unique_ressourceownerid") @db.Uuid - format String? - data String? - created_at DateTime? @db.Timestamptz(6) -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model tcg_accounts { - id_tcg_account String @id(map: "pk_tcg_account") @db.Uuid - remote_id String? - name String? - domains String[] - remote_platform String? - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - id_linked_user String? @db.Uuid - id_connection String @db.Uuid - tcg_contacts tcg_contacts[] -} - /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model tcg_attachments { id_tcg_attachment String @id(map: "pk_tcg_attachments") @db.Uuid @@ -1908,21 +1999,6 @@ model tcg_attachments { @@index([id_tcg_ticket], map: "fk_tcg_attachment_tcg_ticketid") } -model tcg_collections { - id_tcg_collection String @id(map: "pk_tcg_collections") @db.Uuid - name String? - description String? - remote_id String? - remote_platform String? - collection_type String? - parent_collection String? @db.Uuid - id_tcg_ticket String? @db.Uuid - created_at DateTime @db.Timestamptz(6) - modified_at DateTime @db.Timestamptz(6) - id_linked_user String @db.Uuid - id_connection String @db.Uuid -} - /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model tcg_comments { id_tcg_comment String @id(map: "pk_tcg_comments") @db.Uuid @@ -1984,18 +2060,6 @@ model tcg_tags { @@index([id_tcg_ticket], map: "fk_tcg_tag_tcg_ticketid") } -model tcg_teams { - id_tcg_team String @id(map: "pk_tcg_teams") @db.Uuid - remote_id String? - remote_platform String? - name String? - description String? - created_at DateTime @db.Timestamp(6) - modified_at DateTime @db.Timestamp(6) - id_linked_user String? @db.Uuid - id_connection String @db.Uuid -} - /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model tcg_tickets { id_tcg_ticket String @id(map: "pk_tcg_tickets") @db.Uuid @@ -2025,38 +2089,6 @@ model tcg_tickets { @@index([id_tcg_user], map: "fk_tcg_ticket_tcg_user") } -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model tcg_users { - id_tcg_user String @id(map: "pk_tcg_users") @db.Uuid - name String? - email_address String? - remote_id String? - remote_platform String? - teams String[] - id_linked_user String? @db.Uuid - id_connection String @db.Uuid - created_at DateTime? @db.Timestamptz(6) - modified_at DateTime? @db.Timestamptz(6) - tcg_comments tcg_comments[] -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model users { - id_user String @id(map: "pk_users") @db.Uuid - identification_strategy String - email String? @unique(map: "unique_email") - password_hash String? - first_name String - last_name String - id_stytch String? @unique(map: "force_stytch_id_unique") - created_at DateTime @default(now()) @db.Timestamptz(6) - modified_at DateTime @default(now()) @db.Timestamptz(6) - reset_token String? - reset_token_expires_at DateTime? @db.Timestamptz(6) - api_keys api_keys[] - projects projects[] -} - /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model value { id_value String @id(map: "pk_value") @db.Uuid @@ -2093,31 +2125,3 @@ model webhook_delivery_attempts { @@index([id_event], map: "fk_webhook_delivery_attempt_eventid") @@index([id_webhooks_reponse], map: "fk_webhook_delivery_attempt_webhook_responseid") } - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model webhook_endpoints { - id_webhook_endpoint String @id(map: "pk_webhook_endpoint") @db.Uuid - endpoint_description String? - url String - secret String - active Boolean - created_at DateTime @db.Timestamptz(6) - scope String[] - id_project String @db.Uuid - last_update DateTime? @db.Timestamptz(6) - webhook_delivery_attempts webhook_delivery_attempts[] -} - -model webhooks_payloads { - id_webhooks_payload String @id(map: "pk_webhooks_payload") @db.Uuid - data Json @db.Json - webhook_delivery_attempts webhook_delivery_attempts[] -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -model webhooks_reponses { - id_webhooks_reponse String @id(map: "pk_webhooks_reponse") @db.Uuid - http_response_data String - http_status_code String - webhook_delivery_attempts webhook_delivery_attempts[] -} diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index 4be270a83..71a8c80ec 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -549,7 +549,6 @@ CREATE TABLE connector_sets ecom_amazon boolean NULL, ecom_squarespace boolean NULL, ats_ashby boolean NULL, - hris_gusto boolean NULL, CONSTRAINT PK_project_connector PRIMARY KEY ( id_connector_set ) ); diff --git a/packages/api/swagger/swagger-spec.yaml b/packages/api/swagger/swagger-spec.yaml index 166080fe7..17b806d81 100644 --- a/packages/api/swagger/swagger-spec.yaml +++ b/packages/api/swagger/swagger-spec.yaml @@ -107,6 +107,8 @@ paths: schema: type: string responses: + '200': + description: '' '201': description: '' content: @@ -127,6 +129,8 @@ paths: schema: type: string responses: + '200': + description: '' '201': description: '' content: @@ -157,6 +161,8 @@ paths: type: object additionalProperties: true description: Dynamic event payload + '201': + description: '' tags: *ref_0 x-speakeasy-group: webhooks /ticketing/tickets: @@ -2215,6 +2221,12 @@ paths: application/json: schema: type: object + '201': + description: '' + content: + application/json: + schema: + type: object tags: &ref_21 - passthrough x-speakeasy-group: passthrough diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index 3bc4e8fb3..9ffa15b32 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -215,6 +215,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { logoPath: 'https://play-lh.googleusercontent.com/MC_Aoa7rlMjGtcgAdiLJGeIm3-kpVw7APQmQUrUZtXuoZokiqVOJqR-bTu7idJBD8g', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', active: false, + primaryColor: '#516EE2', options: { end_user_domain: true }, From 2a8d95f2abdbfadc235052771429b8f3444bc161 Mon Sep 17 00:00:00 2001 From: speakeasybot Date: Sat, 17 Aug 2024 00:05:09 +0000 Subject: [PATCH 11/49] ci: regenerated with Speakeasy CLI v1.374.2 --- .speakeasy/workflow.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index c8370252e..665bf259f 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -1,8 +1,8 @@ -speakeasyVersion: 1.372.0 +speakeasyVersion: 1.374.2 sources: merge-code-samples-into-spec: sourceNamespace: merge-code-samples-into-spec - sourceRevisionDigest: sha256:62bd8d64f8278dcb7b23d28bffcffbb549e57b5b9dc09bae93ac4c1112dbd09a + sourceRevisionDigest: sha256:1bcf3a8cca852c571571fad60c90aad27624f86e4f2fb58e3777f0c3a6ec712a sourceBlobDigest: sha256:ebdf0ba69a79a32d558c774cfe0f0c32d742319ed3c3d3d887bf14ac67f417d8 tags: - latest From f9031753fcf1ab267d35e8cf530efa1d77bacc9d Mon Sep 17 00:00:00 2001 From: nael Date: Sun, 18 Aug 2024 01:31:51 +0200 Subject: [PATCH 12/49] :bug: Fix shared package build --- apps/magic-link/src/App.css | 1 + docker-compose.dev.yml | 37 ++++----- docker-compose.source.yml | 6 +- docker-compose.yml | 6 +- .../encryption/encryption.service.ts | 17 +++++ .../api/src/@core/auth/auth.controller.ts | 31 +++++--- .../services/snyk/snyk.service.ts | 76 +++++++++++++++---- packages/shared/package.json | 6 +- packages/shared/src/authUrl.ts | 38 +++++----- packages/shared/src/connectors/metadata.ts | 71 ++++++++--------- packages/shared/tsconfig.json | 2 +- 11 files changed, 188 insertions(+), 103 deletions(-) diff --git a/apps/magic-link/src/App.css b/apps/magic-link/src/App.css index b9d355df2..56229e6af 100644 --- a/apps/magic-link/src/App.css +++ b/apps/magic-link/src/App.css @@ -3,6 +3,7 @@ margin: 0 auto; padding: 2rem; text-align: center; + background: #09090B; } .logo { diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 3e19f9fd6..e82cf3769 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -184,7 +184,10 @@ services: SMTP_PASSWORD: ${SMTP_PASSWORD} MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID} MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET} - + SNYK_CYBERSECURITY_CLOUD_CLIENT_ID: ${SNYK_CYBERSECURITY_CLOUD_CLIENT_ID} + SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET} + CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID} + CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET} restart: unless-stopped ports: - 3000:3000 @@ -277,22 +280,22 @@ services: # volumes: # - pgadmin-data:/var/lib/pgadmin - # ngrok: - # image: ngrok/ngrok:latest - # restart: always - # command: - # - "start" - # - "--all" - # - "--config" - # - "/etc/ngrok.yml" - # volumes: - # - ./ngrok.yml:/etc/ngrok.yml - # ports: - # - 4040:4040 - # depends_on: - # api: - # condition: service_healthy - # network_mode: "host" + ngrok: + image: ngrok/ngrok:latest + restart: always + command: + - "start" + - "--all" + - "--config" + - "/etc/ngrok.yml" + volumes: + - ./ngrok.yml:/etc/ngrok.yml + ports: + - 4040:4040 + depends_on: + api: + condition: service_healthy + network_mode: "host" docs: build: diff --git a/docker-compose.source.yml b/docker-compose.source.yml index b1a4f161c..d81637773 100644 --- a/docker-compose.source.yml +++ b/docker-compose.source.yml @@ -184,7 +184,11 @@ services: SMTP_PASSWORD: ${SMTP_PASSWORD} MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID} MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET} - + SNYK_CYBERSECURITY_CLOUD_CLIENT_ID: ${SNYK_CYBERSECURITY_CLOUD_CLIENT_ID} + SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET} + CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID} + CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET} + restart: unless-stopped ports: - 3000:3000 diff --git a/docker-compose.yml b/docker-compose.yml index c33ef1247..c8603707e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -178,7 +178,11 @@ services: SMTP_PASSWORD: ${SMTP_PASSWORD} MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_ID} MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET: ${MICROSOFTDYNAMICSSALES_CRM_CLOUD_CLIENT_SECRET} - + SNYK_CYBERSECURITY_CLOUD_CLIENT_ID: ${SNYK_CYBERSECURITY_CLOUD_CLIENT_ID} + SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET} + CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID} + CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET} + restart: unless-stopped ports: - 3000:3000 diff --git a/packages/api/src/@core/@core-services/encryption/encryption.service.ts b/packages/api/src/@core/@core-services/encryption/encryption.service.ts index 25be858af..bdf660a9d 100644 --- a/packages/api/src/@core/@core-services/encryption/encryption.service.ts +++ b/packages/api/src/@core/@core-services/encryption/encryption.service.ts @@ -46,4 +46,21 @@ export class EncryptionService { throw error; } } + + generateCodes(): { codeVerifier: string; codeChallenge: string } { + const base64URLEncode = (str: Buffer): string => { + return str + .toString('base64') + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, ''); + }; + + const verifier = base64URLEncode(Buffer.from(crypto.randomBytes(32))); + const challenge = base64URLEncode( + crypto.createHash('sha256').update(Buffer.from(verifier)).digest(), + ); + + return { codeVerifier: verifier, codeChallenge: challenge }; + } } diff --git a/packages/api/src/@core/auth/auth.controller.ts b/packages/api/src/@core/auth/auth.controller.ts index 860ec66ab..5a9895cf9 100644 --- a/packages/api/src/@core/auth/auth.controller.ts +++ b/packages/api/src/@core/auth/auth.controller.ts @@ -1,17 +1,14 @@ +import { LoggerService } from '@@core/@core-services/logger/logger.service'; import { - Controller, - Post, Body, - Get, - UseGuards, - Request, + Controller, Delete, + Get, Param, + Post, + Request, + UseGuards, } from '@nestjs/common'; -import { CreateUserDto } from './dto/create-user.dto'; -import { AuthService } from './auth.service'; -import { JwtAuthGuard } from './guards/jwt-auth.guard'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; import { ApiBody, ApiExcludeEndpoint, @@ -20,23 +17,35 @@ import { ApiResponse, ApiTags, } from '@nestjs/swagger'; +import { AuthService } from './auth.service'; import { ApiKeyDto } from './dto/api-key.dto'; +import { CreateUserDto } from './dto/create-user.dto'; import { LoginDto } from './dto/login.dto'; import { RefreshDto } from './dto/refresh.dto'; -import { ResetPasswordDto } from './dto/reset-password.dto'; import { RequestPasswordResetDto } from './dto/request-password-reset.dto'; -import { ApiKeyAuthGuard } from './guards/api-key.guard'; +import { ResetPasswordDto } from './dto/reset-password.dto'; +import { JwtAuthGuard } from './guards/jwt-auth.guard'; +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; @ApiTags('auth') @Controller('auth') export class AuthController { constructor( private readonly authService: AuthService, + private readonly encryptionService: EncryptionService, private logger: LoggerService, ) { this.logger.setContext(AuthController.name); } + @ApiOperation({ operationId: 'generateCodes', summary: 'generateCodes' }) + @ApiResponse({ status: 200 }) + @Get('s256Codes') + @ApiExcludeEndpoint() + async generateCodes() { + return this.encryptionService.generateCodes(); + } + @ApiOperation({ operationId: 'signUp', summary: 'Register' }) @ApiBody({ type: CreateUserDto }) @ApiResponse({ status: 201 }) diff --git a/packages/api/src/@core/connections/cybersecurity/services/snyk/snyk.service.ts b/packages/api/src/@core/connections/cybersecurity/services/snyk/snyk.service.ts index ff77211ce..f5cb498c0 100644 --- a/packages/api/src/@core/connections/cybersecurity/services/snyk/snyk.service.ts +++ b/packages/api/src/@core/connections/cybersecurity/services/snyk/snyk.service.ts @@ -25,8 +25,10 @@ import axios from 'axios'; export interface SnykOAuthResponse { access_token: string; + refresh_token: string; token_type: string; scope: string; + expires_in: string; } @Injectable() @@ -69,7 +71,7 @@ export class SnykConnectionService extends AbstractBaseConnectionService { config.headers = { ...config.headers, ...headers, - Authorization: `Bearer ${access_token}`, + Authorization: `token ${access_token}`, }; return await this.retryService.makeRequest( @@ -89,7 +91,7 @@ export class SnykConnectionService extends AbstractBaseConnectionService { async handleCallback(opts: OAuthCallbackParams) { try { - const { linkedUserId, projectId, code } = opts; + const { linkedUserId, projectId, code, code_verifier } = opts; const isNotUnique = await this.prisma.connections.findFirst({ where: { id_linked_user: linkedUserId, @@ -115,9 +117,10 @@ export class SnykConnectionService extends AbstractBaseConnectionService { client_secret: CREDENTIALS.CLIENT_SECRET, code: code, grant_type: 'authorization_code', + code_verifier: code_verifier, }); const res = await axios.post( - 'https://api.snyk.com/oauth/access_token', + 'https://api.snyk.io/oauth2/token', formData.toString(), { headers: { @@ -126,19 +129,12 @@ export class SnykConnectionService extends AbstractBaseConnectionService { }, ); const data: SnykOAuthResponse = res.data; + console.log('result is ' + JSON.stringify(data)); // save tokens for this customer inside our db let db_res; const connection_token = uuidv4(); const BASE_API_URL = CONNECTORS_METADATA['cybersecurity']['snyk'].urls .apiUrl as string; - // get the site id for the token - const site = await axios.get('https://api.snyk.com/v2/sites', { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', - Authorization: `Bearer ${data.access_token}`, - }, - }); - const site_id = site.data.sites[0].id; if (isNotUnique) { // Update existing connection db_res = await this.prisma.connections.update({ @@ -147,6 +143,10 @@ export class SnykConnectionService extends AbstractBaseConnectionService { }, data: { access_token: this.cryptoService.encrypt(data.access_token), + refresh_token: this.cryptoService.encrypt(data.refresh_token), + expiration_timestamp: new Date( + new Date().getTime() + Number(data.expires_in) * 1000, + ), status: 'valid', created_at: new Date(), }, @@ -160,8 +160,12 @@ export class SnykConnectionService extends AbstractBaseConnectionService { provider_slug: 'snyk', vertical: 'cybersecurity', token_type: 'oauth2', - account_url: `${BASE_API_URL}/sites/${site_id}`, + account_url: BASE_API_URL, access_token: this.cryptoService.encrypt(data.access_token), + refresh_token: this.cryptoService.encrypt(data.refresh_token), + expiration_timestamp: new Date( + new Date().getTime() + Number(data.expires_in) * 1000, + ), status: 'valid', created_at: new Date(), projects: { @@ -186,6 +190,52 @@ export class SnykConnectionService extends AbstractBaseConnectionService { } async handleTokenRefresh(opts: RefreshParams) { - return; + try { + const { connectionId, refreshToken, projectId } = opts; + const REDIRECT_URI = `${ + this.env.getDistributionMode() == 'selfhost' + ? this.env.getTunnelIngress() + : this.env.getPanoraBaseUrl() + }/connections/oauth/callback`; + + const CREDENTIALS = (await this.cService.getCredentials( + projectId, + this.type, + )) as OAuth2AuthData; + + const formData = new URLSearchParams({ + grant_type: 'refresh_token', + client_id: CREDENTIALS.CLIENT_ID, + client_secret: CREDENTIALS.CLIENT_SECRET, + refresh_token: this.cryptoService.decrypt(refreshToken), + redirect_uri: REDIRECT_URI, + }); + + const res = await axios.post( + `https://app.deel.com/oauth2/tokens`, + formData.toString(), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + }, + }, + ); + const data: SnykOAuthResponse = res.data; + await this.prisma.connections.update({ + where: { + id_connection: connectionId, + }, + data: { + access_token: this.cryptoService.encrypt(data.access_token), + refresh_token: this.cryptoService.encrypt(data.refresh_token), + expiration_timestamp: new Date( + new Date().getTime() + Number(data.expires_in) * 1000, + ), + }, + }); + this.logger.log('OAuth credentials updated : deel '); + } catch (error) { + throw error; + } } } diff --git a/packages/shared/package.json b/packages/shared/package.json index 9fdbd22e2..fc2065363 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -45,10 +45,10 @@ "supertest": "^6.3.3", "ts-jest": "^29.1.0", "ts-node": "^10.9.1", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.1.3", "tsc-watch": "2.2.1", - "tslint": "5.16.0" + "tsconfig-paths": "^4.2.0", + "tslint": "5.16.0", + "typescript": "^5.1.3" }, "jest": { "moduleFileExtensions": [ diff --git a/packages/shared/src/authUrl.ts b/packages/shared/src/authUrl.ts index 6b7d66e72..5a7ae7d8a 100644 --- a/packages/shared/src/authUrl.ts +++ b/packages/shared/src/authUrl.ts @@ -1,4 +1,3 @@ -import * as crypto from 'crypto'; import { CONNECTORS_METADATA } from './connectors/metadata'; import { needsEndUserSubdomain, @@ -31,22 +30,6 @@ interface AuthParams { } } -function generateCodes() { - const base64URLEncode = (str: Buffer): string => { - return str.toString('base64') - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/=/g, ''); - } - - const verifier = base64URLEncode(crypto.randomBytes(32)); - const challenge = base64URLEncode( - crypto.createHash('sha256').update(Buffer.from(verifier)).digest() - ); - - return { codeVerifier: verifier, codeChallenge: challenge }; -} - export const constructAuthUrl = async ({ projectId, linkedUserId, @@ -84,6 +67,17 @@ export const constructAuthUrl = async ({ resource: additionalParams!.end_user_domain! })).toString('base64')); } + const opts: any = {}; + if (['snyk', 'klaviyo'].includes(providerName)) { + const response = await fetch(`${apiUrl}/auth/s256Codes`); + const data = await response.json(); + const { codeChallenge, codeVerifier } = data; + state = encodeURIComponent(JSON.stringify({ + projectId, linkedUserId, providerName, vertical, returnUrl, code_verifier: codeVerifier + })); + opts.codeVerifier = codeVerifier; + opts.codeChallenge = codeChallenge; + } if (vertical === null) { throw new ReferenceError('vertical is null'); @@ -95,7 +89,7 @@ export const constructAuthUrl = async ({ case AuthStrategy.oauth2: return handleOAuth2Url({ providerName, vertical, authStrategy, projectId, config, - encodedRedirectUrl, state, apiUrl, additionalParams + encodedRedirectUrl, state, apiUrl, additionalParams, ...opts }); case AuthStrategy.api_key: return handleApiKeyUrl(); @@ -115,7 +109,8 @@ interface HandleOAuth2Url { apiUrl: string; additionalParams?: { end_user_domain: string; - } + }; + [key: string]: any; } const handleOAuth2Url = async ({ @@ -128,6 +123,7 @@ const handleOAuth2Url = async ({ state, apiUrl, additionalParams, + ...dyn }: HandleOAuth2Url) => { const type = providerToType(providerName, vertical, authStrategy); @@ -225,9 +221,9 @@ const handleOAuth2Url = async ({ case 'notion': params += '&owner=user'; break; + case 'snyk': case 'klaviyo': - const { codeChallenge, codeVerifier } = generateCodes(); - params += `&code_challenge_method=S256&code_challenge=${codeChallenge}`; + params += `&code_challenge_method=S256&code_challenge=${dyn.codeChallenge}`; break; } diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index 9ffa15b32..e3fdab57e 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -130,7 +130,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://media.licdn.com/dms/image/C4D0BAQFOaK6KXEYj_w/company-logo_200_200/0/1630489791871/project_affinity_logo?e=2147483647&v=beta&t=u8j-1u3nO2m6vqgT170WJMCJyFSDiLYS_VguYOllNMI', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, primaryColor: '#244CED', authStrategy: { strategy: AuthStrategy.basic, @@ -458,7 +458,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://asset.brandfetch.io/iduDa181eM/idYYbqOlKi.png', description: 'Sync & Create accounts, tickets, comments, attachments, contacts, tags, teams and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -905,7 +905,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -1020,9 +1020,9 @@ export const CONNECTORS_METADATA: ProvidersConfig = { apiUrl: 'https://quickbooks.api.intuit.com/v3', authBaseUrl: 'https://appcenter.intuit.com/connect/oauth2', }, - logoPath: 'https://i.ibb.co/hMhHt5G/quickbooks-app-tips.png', + logoPath: 'https://media.licdn.com/dms/image/D4D12AQFduz3E6g6COg/article-cover_image-shrink_600_2000/0/1697053235030?e=2147483647&v=beta&t=c2Ia7g8fJ6wAHNlnogLV_Ii765HMqSw16HxgawsxmTw', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -1063,9 +1063,9 @@ export const CONNECTORS_METADATA: ProvidersConfig = { apiUrl: 'https://api.xero.com/api.xro/2.0', authBaseUrl: 'https://login.xero.com/identity/connect/authorize', }, - logoPath: 'https://i.ibb.co/qpc2RQZ/xeroappicon.png', + logoPath: 'https://upload.wikimedia.org/wikipedia/en/archive/9/9f/20171204173437%21Xero_software_logo.svg', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -1270,7 +1270,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://play-lh.googleusercontent.com/c4BW9wr_QAiIeVBYHhP7rs06w99xJzxgLvmL5I1mkucC3_ATMyL1t7Doz0_LQ0X-qS0', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, primaryColor: '#599D16', authStrategy: { strategy: AuthStrategy.basic, @@ -1431,9 +1431,9 @@ export const CONNECTORS_METADATA: ProvidersConfig = { apiUrl: 'https://api.greenhouse.io/v1/partner', authBaseUrl: 'https://api.greenhouse.io/oauth/authorize' }, - logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', + logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSQRGdTs2tatWjail4b3hpHLIyI-6gXZLxhvw&s', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -1591,7 +1591,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQbR9XSB1lbZnYlLWyqMe5Px80ghtEOUqHeqw&s', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -1837,7 +1837,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSZTX2h9yFQ0u4ziDqvfQ224wW4N1s5JvJ5nA&s', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.api_key } @@ -2043,7 +2043,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://avatars.githubusercontent.com/u/19614805?s=280&v=4', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -2719,9 +2719,9 @@ export const CONNECTORS_METADATA: ProvidersConfig = { apiUrl: 'https://www.googleapis.com/drive', authBaseUrl: 'https://accounts.google.com/o/oauth2/v2/auth' }, - logoPath: 'https://static-00.iconduck.com/assets.00/google-drive-icon-2048x2048-j5sa1hcp.png', + logoPath: 'https://upload.wikimedia.org/wikipedia/commons/d/da/Google_Drive_logo.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -2735,7 +2735,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://logowik.com/content/uploads/images/4964-microsoft-onedrive-new.jpg', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -2748,7 +2748,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://cdn2.iconfinder.com/data/icons/metro-ui-dock/512/Dropbox.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -2762,7 +2762,8 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://pnghq.com/wp-content/uploads/pnghq.com-microsoft-sharepoint-logo-9.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, + primaryColor: '', options: { local_redirect_uri_in_https: true }, @@ -2793,7 +2794,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://upload.wikimedia.org/wikipedia/commons/4/45/Notion_app_logo.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -2807,7 +2808,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://assets-global.website-files.com/621c8d7ad9e04933c4e51ffb/65eba5ffa14998827c92cc01_slack-octothorpe.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: false, + active: true, options: { local_redirect_uri_in_https: true }, @@ -2824,7 +2825,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTH_-bQ399xl-yfJYhbLraU-w0yWBcppLf8NA&s', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, primaryColor: '#000001', authStrategy: { strategy: AuthStrategy.api_key, @@ -2840,7 +2841,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://www.logodesignlove.com/images/evolution/ebay-logo-01.jpg', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, options: { local_redirect_uri_in_https: true, oauth_attributes: ['ruvalue'] @@ -2903,7 +2904,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTKRidZq3jXGYOIZ2X2EruCx0pFMkbsHineLg&s', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -2917,7 +2918,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://dailybrand.co.zw/wp-content/uploads/2023/10/webflow-2.png', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -2934,7 +2935,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSSDg6WOKCYRvGFmwtaJ6Gv88PjAGRS9_h9EQ&s', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -2948,7 +2949,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://api.getkoala.com/web/companies/mercadolibre.com/logo', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -2976,7 +2977,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://yt3.googleusercontent.com/NWVXYvuzHDgJJsbda7eyyz21Ba2qnq5WmuGrt9ax1rs6PP-mlDl5LCJ4ZO0Z2ZbiCq4ZoxqiGg=s900-c-k-c0x00ffffff-no-rj', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, primaryColor: '#10C096', authStrategy: { strategy: AuthStrategy.api_key, @@ -2984,7 +2985,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { } }, 'snyk': { - scopes: '', + scopes: 'org.read org.report.read org.project.read', urls: { docsUrl: 'https://docs.snyk.io/snyk-api/', apiUrl: 'https://api.snyk.io', @@ -2992,7 +2993,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1215%2Ffe4be452-1e68-444a-bf77-db21bf3a7bdc.png', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -3007,7 +3008,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://pbs.twimg.com/profile_images/1410604377757216768/ocEKYniC_400x400.jpg', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, primaryColor: '#0D1E40', authStrategy: { strategy: AuthStrategy.basic, @@ -3021,7 +3022,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://companieslogo.com/img/orig/QLYS-68c2032c.png?t=1720244493', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, primaryColor: '#ED2E28', authStrategy: { strategy: AuthStrategy.basic, @@ -3035,7 +3036,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://images.saasworthy.com/insightvm_9113_logo_1635748346_lc0gr.png', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, primaryColor: '#E95722', authStrategy: { strategy: AuthStrategy.api_key, @@ -3051,7 +3052,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://pbs.twimg.com/profile_images/1451022302578049024/6L-zG5oq_400x400.jpg', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, primaryColor: '#FC0001', authStrategy: { strategy: AuthStrategy.oauth2, @@ -3064,7 +3065,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTZHLye2za7fjLiggqC1upKhhM3T-laySJSLQ&s', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, primaryColor: '#522E74', authStrategy: { strategy: AuthStrategy.api_key, @@ -3080,7 +3081,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSuR4GZElDP7UNKhXS9jDGpElBTdchjg8hSsA&s', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, primaryColor: '#0078D8', authStrategy: { strategy: AuthStrategy.oauth2, diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json index 9e6f5265b..7bec40790 100644 --- a/packages/shared/tsconfig.json +++ b/packages/shared/tsconfig.json @@ -10,7 +10,7 @@ "outDir": "./dist", "rootDir": "./src", "baseUrl": "./", - "noLib": false + "noLib": false, }, "include": ["src/**/*.ts"], "exclude": ["node_modules"] From c1b815691d6f832f061f914af31e437a1549353e Mon Sep 17 00:00:00 2001 From: nael Date: Sun, 18 Aug 2024 01:37:01 +0200 Subject: [PATCH 13/49] :fire: Remove true properties to some connectors --- packages/shared/src/connectors/metadata.ts | 58 +++++++++++----------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index e3fdab57e..62e424368 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -130,7 +130,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://media.licdn.com/dms/image/C4D0BAQFOaK6KXEYj_w/company-logo_200_200/0/1630489791871/project_affinity_logo?e=2147483647&v=beta&t=u8j-1u3nO2m6vqgT170WJMCJyFSDiLYS_VguYOllNMI', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, primaryColor: '#244CED', authStrategy: { strategy: AuthStrategy.basic, @@ -458,7 +458,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://asset.brandfetch.io/iduDa181eM/idYYbqOlKi.png', description: 'Sync & Create accounts, tickets, comments, attachments, contacts, tags, teams and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -1022,7 +1022,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://media.licdn.com/dms/image/D4D12AQFduz3E6g6COg/article-cover_image-shrink_600_2000/0/1697053235030?e=2147483647&v=beta&t=c2Ia7g8fJ6wAHNlnogLV_Ii765HMqSw16HxgawsxmTw', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -1065,7 +1065,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://upload.wikimedia.org/wikipedia/en/archive/9/9f/20171204173437%21Xero_software_logo.svg', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -1270,7 +1270,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://play-lh.googleusercontent.com/c4BW9wr_QAiIeVBYHhP7rs06w99xJzxgLvmL5I1mkucC3_ATMyL1t7Doz0_LQ0X-qS0', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, primaryColor: '#599D16', authStrategy: { strategy: AuthStrategy.basic, @@ -1433,7 +1433,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSQRGdTs2tatWjail4b3hpHLIyI-6gXZLxhvw&s', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -1591,7 +1591,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQbR9XSB1lbZnYlLWyqMe5Px80ghtEOUqHeqw&s', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -1837,7 +1837,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSZTX2h9yFQ0u4ziDqvfQ224wW4N1s5JvJ5nA&s', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.api_key } @@ -2043,7 +2043,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://avatars.githubusercontent.com/u/19614805?s=280&v=4', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -2721,7 +2721,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://upload.wikimedia.org/wikipedia/commons/d/da/Google_Drive_logo.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -2735,7 +2735,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://logowik.com/content/uploads/images/4964-microsoft-onedrive-new.jpg', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -2748,7 +2748,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://cdn2.iconfinder.com/data/icons/metro-ui-dock/512/Dropbox.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -2762,7 +2762,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://pnghq.com/wp-content/uploads/pnghq.com-microsoft-sharepoint-logo-9.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, primaryColor: '', options: { local_redirect_uri_in_https: true @@ -2794,7 +2794,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://upload.wikimedia.org/wikipedia/commons/4/45/Notion_app_logo.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 } @@ -2808,7 +2808,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://assets-global.website-files.com/621c8d7ad9e04933c4e51ffb/65eba5ffa14998827c92cc01_slack-octothorpe.png', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, options: { local_redirect_uri_in_https: true }, @@ -2825,7 +2825,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTH_-bQ399xl-yfJYhbLraU-w0yWBcppLf8NA&s', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, primaryColor: '#000001', authStrategy: { strategy: AuthStrategy.api_key, @@ -2841,7 +2841,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://www.logodesignlove.com/images/evolution/ebay-logo-01.jpg', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, options: { local_redirect_uri_in_https: true, oauth_attributes: ['ruvalue'] @@ -2904,7 +2904,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTKRidZq3jXGYOIZ2X2EruCx0pFMkbsHineLg&s', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -2918,7 +2918,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://dailybrand.co.zw/wp-content/uploads/2023/10/webflow-2.png', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -2935,7 +2935,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSSDg6WOKCYRvGFmwtaJ6Gv88PjAGRS9_h9EQ&s', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -2949,7 +2949,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://api.getkoala.com/web/companies/mercadolibre.com/logo', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -2977,7 +2977,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://yt3.googleusercontent.com/NWVXYvuzHDgJJsbda7eyyz21Ba2qnq5WmuGrt9ax1rs6PP-mlDl5LCJ4ZO0Z2ZbiCq4ZoxqiGg=s900-c-k-c0x00ffffff-no-rj', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, primaryColor: '#10C096', authStrategy: { strategy: AuthStrategy.api_key, @@ -2993,7 +2993,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1215%2Ffe4be452-1e68-444a-bf77-db21bf3a7bdc.png', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -3008,7 +3008,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://pbs.twimg.com/profile_images/1410604377757216768/ocEKYniC_400x400.jpg', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, primaryColor: '#0D1E40', authStrategy: { strategy: AuthStrategy.basic, @@ -3022,7 +3022,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://companieslogo.com/img/orig/QLYS-68c2032c.png?t=1720244493', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, primaryColor: '#ED2E28', authStrategy: { strategy: AuthStrategy.basic, @@ -3036,7 +3036,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://images.saasworthy.com/insightvm_9113_logo_1635748346_lc0gr.png', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, primaryColor: '#E95722', authStrategy: { strategy: AuthStrategy.api_key, @@ -3052,7 +3052,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://pbs.twimg.com/profile_images/1451022302578049024/6L-zG5oq_400x400.jpg', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, primaryColor: '#FC0001', authStrategy: { strategy: AuthStrategy.oauth2, @@ -3065,7 +3065,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTZHLye2za7fjLiggqC1upKhhM3T-laySJSLQ&s', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, primaryColor: '#522E74', authStrategy: { strategy: AuthStrategy.api_key, @@ -3081,7 +3081,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSuR4GZElDP7UNKhXS9jDGpElBTdchjg8hSsA&s', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: true, + active: false, primaryColor: '#0078D8', authStrategy: { strategy: AuthStrategy.oauth2, From 66be9acf1a5dcda528e1849a773491bee0c23528 Mon Sep 17 00:00:00 2001 From: nael Date: Sun, 18 Aug 2024 07:29:10 +0200 Subject: [PATCH 14/49] :bug: Fix bamboihr connection --- packages/api/src/@core/auth/auth.controller.ts | 5 ++++- .../ats/services/bamboohr/bamboohr.service.ts | 16 +++++++--------- .../src/filestorage/file/services/box/index.ts | 17 ++++++++--------- packages/shared/src/connectors/metadata.ts | 8 +++++--- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/packages/api/src/@core/auth/auth.controller.ts b/packages/api/src/@core/auth/auth.controller.ts index 5a9895cf9..b0a3a761b 100644 --- a/packages/api/src/@core/auth/auth.controller.ts +++ b/packages/api/src/@core/auth/auth.controller.ts @@ -38,7 +38,10 @@ export class AuthController { this.logger.setContext(AuthController.name); } - @ApiOperation({ operationId: 'generateCodes', summary: 'generateCodes' }) + @ApiOperation({ + operationId: 'generateCodes', + summary: 'generate codes for s256 challenge', + }) @ApiResponse({ status: 200 }) @Get('s256Codes') @ApiExcludeEndpoint() diff --git a/packages/api/src/@core/connections/ats/services/bamboohr/bamboohr.service.ts b/packages/api/src/@core/connections/ats/services/bamboohr/bamboohr.service.ts index 92c56609f..3f0df3d4e 100644 --- a/packages/api/src/@core/connections/ats/services/bamboohr/bamboohr.service.ts +++ b/packages/api/src/@core/connections/ats/services/bamboohr/bamboohr.service.ts @@ -2,6 +2,7 @@ import { EncryptionService } from '@@core/@core-services/encryption/encryption.s import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; import { LoggerService } from '@@core/@core-services/logger/logger.service'; import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; import { ConnectionsStrategiesService } from '@@core/connections-strategies/connections-strategies.service'; import { ConnectionUtils } from '@@core/connections/@utils'; import { @@ -10,6 +11,7 @@ import { PassthroughInput, RefreshParams, } from '@@core/connections/@utils/types'; +import { PassthroughResponse } from '@@core/passthrough/types'; import { Injectable } from '@nestjs/common'; import { AuthStrategy, @@ -17,11 +19,8 @@ import { DynamicApiUrl, providerToType, } from '@panora/shared'; -import axios from 'axios'; import { v4 as uuidv4 } from 'uuid'; import { ServiceRegistry } from '../registry.service'; -import { RetryHandler } from '@@core/@core-services/request-retry/retry.handler'; -import { PassthroughResponse } from '@@core/passthrough/types'; export type BamboohrOAuthResponse = { access_token: string; @@ -92,7 +91,7 @@ export class BamboohrConnectionService extends AbstractBaseConnectionService { async handleCallback(opts: OAuthCallbackParams) { try { const { linkedUserId, projectId, body } = opts; - const { username, company_subdomain } = body; + const { api_key, subdomain } = body; const isNotUnique = await this.prisma.connections.findFirst({ where: { id_linked_user: linkedUserId, @@ -104,16 +103,15 @@ export class BamboohrConnectionService extends AbstractBaseConnectionService { let db_res; const connection_token = uuidv4(); const BASE_API_URL = ( - CONNECTORS_METADATA['ats']['bamboohr'].urls - .apiUrl as DynamicApiUrl - )(company_subdomain); + CONNECTORS_METADATA['ats']['bamboohr'].urls.apiUrl as DynamicApiUrl + )(subdomain); if (isNotUnique) { db_res = await this.prisma.connections.update({ where: { id_connection: isNotUnique.id_connection, }, data: { - access_token: this.cryptoService.encrypt(username), + access_token: this.cryptoService.encrypt(api_key), account_url: BASE_API_URL, status: 'valid', created_at: new Date(), @@ -128,7 +126,7 @@ export class BamboohrConnectionService extends AbstractBaseConnectionService { vertical: 'ats', token_type: 'basic', account_url: BASE_API_URL, - access_token: this.cryptoService.encrypt(username), + access_token: this.cryptoService.encrypt(api_key), status: 'valid', created_at: new Date(), projects: { diff --git a/packages/api/src/filestorage/file/services/box/index.ts b/packages/api/src/filestorage/file/services/box/index.ts index 5c4cf5b12..a5ba5f4c8 100644 --- a/packages/api/src/filestorage/file/services/box/index.ts +++ b/packages/api/src/filestorage/file/services/box/index.ts @@ -1,15 +1,14 @@ -import { Injectable } from '@nestjs/common'; -import { IFileService } from '@filestorage/file/types'; -import { FileStorageObject } from '@filestorage/@lib/@types'; -import axios from 'axios'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; 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 { ServiceRegistry } from '../registry.service'; -import { BoxFileInput, BoxFileOutput } from './types'; import { SyncParam } from '@@core/utils/types/interface'; +import { FileStorageObject } from '@filestorage/@lib/@types'; +import { IFileService } from '@filestorage/file/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { BoxFileOutput } from './types'; @Injectable() export class BoxService implements IFileService { diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index 62e424368..873590f9c 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -905,7 +905,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://play-lh.googleusercontent.com/EMobDJKabP1eY_63QHgPS_-TK3eRfxXaeOnERbcRaWAw573iaV74pXS9xOv997dRZtM', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', - active: true, + active: false, authStrategy: { strategy: AuthStrategy.oauth2 }, @@ -1274,7 +1274,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { primaryColor: '#599D16', authStrategy: { strategy: AuthStrategy.basic, - properties: ['username', 'subdomain'] + properties: ['subdomain', 'api_key'] }, }, 'breezy': { @@ -1838,8 +1838,10 @@ export const CONNECTORS_METADATA: ProvidersConfig = { logoPath: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSZTX2h9yFQ0u4ziDqvfQ224wW4N1s5JvJ5nA&s', description: 'Sync & Create contacts, deals, companies, notes, engagements, stages, tasks and users', active: false, + primaryColor: '#0167AB', authStrategy: { - strategy: AuthStrategy.api_key + strategy: AuthStrategy.api_key, + properties: ['api_key'] } }, 'zoho_recruit': { From 68dae36aa952ebd8f91fd4a87edd361c66fca563 Mon Sep 17 00:00:00 2001 From: Subin C Date: Thu, 22 Aug 2024 11:18:52 +0530 Subject: [PATCH 15/49] =?UTF-8?q?=F0=9F=90=9B=20If=20Column=20contains=20a?= =?UTF-8?q?=20null=20data=20then=20act=20as=20if=20the=20colum=20was=20set?= =?UTF-8?q?=20to=20true=20#561?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../react/src/components/PanoraDynamicCatalog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/embedded-catalog/react/src/components/PanoraDynamicCatalog.tsx b/apps/embedded-catalog/react/src/components/PanoraDynamicCatalog.tsx index 27685e13e..f8208cc8e 100644 --- a/apps/embedded-catalog/react/src/components/PanoraDynamicCatalog.tsx +++ b/apps/embedded-catalog/react/src/components/PanoraDynamicCatalog.tsx @@ -168,7 +168,7 @@ const DynamicCatalog = ({projectId,linkedUserId, category, optionalApiUrl} : Dyn return [{ connector_name: connector_name, category: category, - status: String(value) + status: value === null ? "true" : String(value) }]; } return []; From 4c32cd424bccaa463a222465bcc193dad900fc3a Mon Sep 17 00:00:00 2001 From: Subin C Date: Thu, 22 Aug 2024 15:46:29 +0530 Subject: [PATCH 16/49] =?UTF-8?q?=F0=9F=90=9B=20Removed=20returnurl=20in?= =?UTF-8?q?=20code=20example=20#674?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "removed returnurl in code example #674" --- .../src/components/Configuration/Catalog/CopySnippet.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/webapp/src/components/Configuration/Catalog/CopySnippet.tsx b/apps/webapp/src/components/Configuration/Catalog/CopySnippet.tsx index f8ba24d40..464ebfd40 100644 --- a/apps/webapp/src/components/Configuration/Catalog/CopySnippet.tsx +++ b/apps/webapp/src/components/Configuration/Catalog/CopySnippet.tsx @@ -28,7 +28,7 @@ export const CopySnippet = () => { `` ); @@ -131,7 +131,7 @@ export const CopySnippet = () => { projectId{`={'c9a1b1f8-466d-442d-a95e-11cdd00baf49'}`} - returnUrl{`={'https://acme.inc'}`} + optionalApiUrl{`={'https://acme.inc'}`} linkedUserId{`={'b860d6c1-28f9-485c-86cd-fb09e60f10a2'}`} @@ -191,7 +191,7 @@ export const CopySnippet = () => { projectId{`={'c9a1b1f8-466d-442d-a95e-11cdd00baf49'}`} - returnUrl{`={'https://acme.inc'}`} + optionalApiUrl{`={'https://acme.inc'}`} linkedUserId{`={'b860d6c1-28f9-485c-86cd-fb09e60f10a2'}`} From c08d89ec1babcf6c72515e1e4cd3186dd4a83e8f Mon Sep 17 00:00:00 2001 From: Subin C Date: Thu, 22 Aug 2024 15:48:35 +0530 Subject: [PATCH 17/49] =?UTF-8?q?=F0=9F=90=9B=20Removed=20returnurl=20in?= =?UTF-8?q?=20code=20example=20#674?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit removed returnurl in code example #674 --- .../webapp/src/components/Configuration/Catalog/CopySnippet.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/src/components/Configuration/Catalog/CopySnippet.tsx b/apps/webapp/src/components/Configuration/Catalog/CopySnippet.tsx index 464ebfd40..896f02c2d 100644 --- a/apps/webapp/src/components/Configuration/Catalog/CopySnippet.tsx +++ b/apps/webapp/src/components/Configuration/Catalog/CopySnippet.tsx @@ -50,7 +50,7 @@ export const CopySnippet = () => { name={"hubspot"} category={ConnectorCategory.Crm} projectId={"c9a1b1f8-466d-442d-a95e-11cdd00baf49"} - returnUrl={"https://acme.inc"} + optionalApiUrl={"https://acme.inc"} linkedUserId={"b860d6c1-28f9-485c-86cd-fb09e60f10a2"} />` ); From bdab891c6d3eaba2d0d334cb18ecbf08d7788b62 Mon Sep 17 00:00:00 2001 From: Ram Krishna Singh Date: Sat, 24 Aug 2024 12:07:20 +0530 Subject: [PATCH 18/49] stores account output api data in remote_data --- packages/api/src/ticketing/account/services/front/mappers.ts | 1 + packages/api/src/ticketing/account/services/zendesk/mappers.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/api/src/ticketing/account/services/front/mappers.ts b/packages/api/src/ticketing/account/services/front/mappers.ts index ba545a04d..c467955c8 100644 --- a/packages/api/src/ticketing/account/services/front/mappers.ts +++ b/packages/api/src/ticketing/account/services/front/mappers.ts @@ -63,6 +63,7 @@ export class FrontAccountMapper implements IAccountMapper { name: account.name, domains: account.domains.flat(), field_mappings: field_mappings, + remote_data: account }; return unifiedAccount; diff --git a/packages/api/src/ticketing/account/services/zendesk/mappers.ts b/packages/api/src/ticketing/account/services/zendesk/mappers.ts index d4e128d80..5f40bfd32 100644 --- a/packages/api/src/ticketing/account/services/zendesk/mappers.ts +++ b/packages/api/src/ticketing/account/services/zendesk/mappers.ts @@ -60,6 +60,7 @@ export class ZendeskAccountMapper implements IAccountMapper { remote_id: String(account.id), name: account.name, domains: account.domain_names, + remote_data: account }; return unifiedAccount; From cc1f8ef6ffd0a566d642ba4f67821c5bdd104931 Mon Sep 17 00:00:00 2001 From: mit-27 Date: Mon, 26 Aug 2024 01:31:26 -0600 Subject: [PATCH 19/49] =?UTF-8?q?=E2=9C=A8=20Add=20Microsoft=20Dynamics=20?= =?UTF-8?q?365=20Sales=20CRM=20{Company,=20Contact,=20Deal,=20Note,=20Task?= =?UTF-8?q?,=20User}=20objects?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/scripts/init.sql | 3 +- packages/api/scripts/seed.sql | 8 +- .../utils/types/original/original.crm.ts | 36 +- .../api/src/crm/company/company.module.ts | 4 + .../services/microsoftdynamicssales/index.ts | 97 ++++++ .../microsoftdynamicssales/mappers.ts | 309 ++++++++++++++++++ .../services/microsoftdynamicssales/types.ts | 153 +++++++++ .../api/src/crm/contact/contact.module.ts | 4 + .../services/microsoftdynamicssales/index.ts | 99 ++++++ .../microsoftdynamicssales/mappers.ts | 235 +++++++++++++ .../services/microsoftdynamicssales/types.ts | 228 +++++++++++++ packages/api/src/crm/deal/deal.module.ts | 4 + .../services/microsoftdynamicssales/index.ts | 96 ++++++ .../microsoftdynamicssales/mappers.ts | 130 ++++++++ .../services/microsoftdynamicssales/types.ts | 125 +++++++ packages/api/src/crm/note/note.module.ts | 4 + .../services/microsoftdynamicssales/index.ts | 93 ++++++ .../microsoftdynamicssales/mappers.ts | 151 +++++++++ .../services/microsoftdynamicssales/types.ts | 32 ++ .../services/microsoftdynamicssales/index.ts | 94 ++++++ .../microsoftdynamicssales/mappers.ts | 165 ++++++++++ .../services/microsoftdynamicssales/types.ts | 70 ++++ packages/api/src/crm/task/task.module.ts | 4 + .../services/microsoftdynamicssales/index.ts | 67 ++++ .../microsoftdynamicssales/mappers.ts | 71 ++++ .../services/microsoftdynamicssales/types.ts | 123 +++++++ packages/api/src/crm/user/user.module.ts | 4 + packages/shared/src/authUrl.ts | 4 +- packages/shared/src/connectors/enum.ts | 3 +- packages/shared/src/connectors/index.ts | 2 +- packages/shared/src/connectors/metadata.ts | 2 +- 31 files changed, 2398 insertions(+), 22 deletions(-) create mode 100644 packages/api/src/crm/company/services/microsoftdynamicssales/index.ts create mode 100644 packages/api/src/crm/company/services/microsoftdynamicssales/mappers.ts create mode 100644 packages/api/src/crm/company/services/microsoftdynamicssales/types.ts create mode 100644 packages/api/src/crm/contact/services/microsoftdynamicssales/index.ts create mode 100644 packages/api/src/crm/contact/services/microsoftdynamicssales/mappers.ts create mode 100644 packages/api/src/crm/contact/services/microsoftdynamicssales/types.ts create mode 100644 packages/api/src/crm/deal/services/microsoftdynamicssales/index.ts create mode 100644 packages/api/src/crm/deal/services/microsoftdynamicssales/mappers.ts create mode 100644 packages/api/src/crm/deal/services/microsoftdynamicssales/types.ts create mode 100644 packages/api/src/crm/note/services/microsoftdynamicssales/index.ts create mode 100644 packages/api/src/crm/note/services/microsoftdynamicssales/mappers.ts create mode 100644 packages/api/src/crm/note/services/microsoftdynamicssales/types.ts create mode 100644 packages/api/src/crm/task/services/microsoftdynamicssales/index.ts create mode 100644 packages/api/src/crm/task/services/microsoftdynamicssales/mappers.ts create mode 100644 packages/api/src/crm/task/services/microsoftdynamicssales/types.ts create mode 100644 packages/api/src/crm/user/services/microsoftdynamicssales/index.ts create mode 100644 packages/api/src/crm/user/services/microsoftdynamicssales/mappers.ts create mode 100644 packages/api/src/crm/user/services/microsoftdynamicssales/types.ts diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index 71a8c80ec..4aa51a322 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -549,7 +549,8 @@ CREATE TABLE connector_sets ecom_amazon boolean NULL, ecom_squarespace boolean NULL, ats_ashby boolean NULL, - CONSTRAINT PK_project_connector PRIMARY KEY ( id_connector_set ) + crm_microsoftdynamicssales boolean NULL, +CONSTRAINT PK_project_connector PRIMARY KEY ( id_connector_set ) ); diff --git a/packages/api/scripts/seed.sql b/packages/api/scripts/seed.sql index 233841fa2..9acc8bff4 100644 --- a/packages/api/scripts/seed.sql +++ b/packages/api/scripts/seed.sql @@ -1,10 +1,10 @@ INSERT INTO users (id_user, identification_strategy, email, password_hash, first_name, last_name) VALUES ('0ce39030-2901-4c56-8db0-5e326182ec6b', 'b2c','local@panora.dev', '$2b$10$Y7Q8TWGyGuc5ecdIASbBsuXMo3q/Rs3/cnY.mLZP4tUgfGUOCUBlG', 'local', 'Panora'); -INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github, hris_deel, hris_sage, ats_ashby) VALUES - ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); +INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github, hris_deel, hris_sage, ats_ashby, crm_microsoftdynamicssales) VALUES + ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); INSERT INTO projects (id_project, name, sync_mode, id_user, id_connector_set) VALUES ('1e468c15-aa57-4448-aa2b-7fed640d1e3d', 'Project 1', 'pull', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'), diff --git a/packages/api/src/@core/utils/types/original/original.crm.ts b/packages/api/src/@core/utils/types/original/original.crm.ts index aaeae0263..2e64529d6 100644 --- a/packages/api/src/@core/utils/types/original/original.crm.ts +++ b/packages/api/src/@core/utils/types/original/original.crm.ts @@ -1,3 +1,15 @@ +import { MicrosoftdynamicssalesTaskInput, MicrosoftdynamicssalesTaskOutput } from '@crm/task/services/microsoftdynamicssales/types'; + +import { MicrosoftdynamicssalesNoteInput, MicrosoftdynamicssalesNoteOutput } from '@crm/note/services/microsoftdynamicssales/types'; + +import { MicrosoftdynamicssalesDealInput, MicrosoftdynamicssalesDealOutput } from '@crm/deal/services/microsoftdynamicssales/types'; + +import { MicrosoftdynamicssalesContactInput, MicrosoftdynamicssalesContactOutput } from '@crm/contact/services/microsoftdynamicssales/types'; + +import { MicrosoftdynamicssalesCompanyInput, MicrosoftdynamicssalesCompanyOutput } from '@crm/company/services/microsoftdynamicssales/types'; + +import { MicrosoftdynamicssalesUserInput, MicrosoftdynamicssalesUserOutput } from '@crm/user/services/microsoftdynamicssales/types'; + import { AttioCompanyOutput } from '@crm/company/services/attio/types'; import { HubspotCompanyOutput } from '@crm/company/services/hubspot/types'; import { PipedriveCompanyOutput } from '@crm/company/services/pipedrive/types'; @@ -148,7 +160,7 @@ export type OriginalContactInput = | ZendeskContactInput | PipedriveContactInput | AttioContactInput - | CloseContactInput; + | CloseContactInput | MicrosoftdynamicssalesContactInput; /* deal */ export type OriginalDealInput = @@ -157,7 +169,7 @@ export type OriginalDealInput = | ZendeskDealOutput | PipedriveDealOutput | CloseDealOutput - | AttioDealInput; + | AttioDealInput | MicrosoftdynamicssalesDealInput; /* company */ export type OriginalCompanyInput = @@ -166,7 +178,7 @@ export type OriginalCompanyInput = | ZendeskCompanyOutput | PipedriveCompanyOutput | AttioCompanyOutput - | CloseCompanyOutput; + | CloseCompanyOutput | MicrosoftdynamicssalesCompanyInput; /* engagement */ export type OriginalEngagementInput = @@ -183,7 +195,7 @@ export type OriginalNoteInput = | ZendeskNoteInput | PipedriveNoteInput | CloseNoteInput - | AttioNoteInput; + | AttioNoteInput | MicrosoftdynamicssalesNoteInput; /* task */ export type OriginalTaskInput = @@ -192,7 +204,7 @@ export type OriginalTaskInput = | ZendeskTaskInput | PipedriveTaskInput | CloseTaskInput - | AttioTaskInput; + | AttioTaskInput | MicrosoftdynamicssalesTaskInput; /* stage */ export type OriginalStageInput = @@ -210,7 +222,7 @@ export type OriginalUserInput = | ZohoUserInput | ZendeskUserInput | PipedriveUserInput - | CloseUserOutput; + | CloseUserOutput | MicrosoftdynamicssalesUserInput; export type CrmObjectInput = | OriginalContactInput @@ -230,7 +242,7 @@ export type OriginalContactOutput = | ZendeskContactOutput | PipedriveContactOutput | AttioContactOutput - | CloseContactOutput; + | CloseContactOutput | MicrosoftdynamicssalesContactOutput; /* deal */ export type OriginalDealOutput = @@ -239,7 +251,7 @@ export type OriginalDealOutput = | ZendeskDealOutput | PipedriveDealOutput | CloseDealOutput - | AttioDealOutput; + | AttioDealOutput | MicrosoftdynamicssalesDealOutput; /* company */ export type OriginalCompanyOutput = @@ -248,7 +260,7 @@ export type OriginalCompanyOutput = | ZendeskCompanyOutput | PipedriveCompanyOutput | AttioCompanyOutput - | CloseCompanyOutput; + | CloseCompanyOutput | MicrosoftdynamicssalesCompanyOutput; /* engagement */ export type OriginalEngagementOutput = @@ -265,7 +277,7 @@ export type OriginalNoteOutput = | ZendeskNoteOutput | PipedriveNoteOutput | CloseNoteOutput - | AttioNoteOutput; + | AttioNoteOutput | MicrosoftdynamicssalesNoteOutput; /* task */ export type OriginalTaskOutput = @@ -274,7 +286,7 @@ export type OriginalTaskOutput = | ZendeskTaskOutput | PipedriveTaskOutput | CloseTaskOutput - | AttioTaskOutput; + | AttioTaskOutput | MicrosoftdynamicssalesTaskOutput; /* stage */ export type OriginalStageOutput = @@ -293,7 +305,7 @@ export type OriginalUserOutput = | ZendeskUserOutput | PipedriveUserOutput | CloseUserInput - | AttioUserOutput; + | AttioUserOutput | MicrosoftdynamicssalesUserOutput; export type CrmObjectOutput = | OriginalContactOutput diff --git a/packages/api/src/crm/company/company.module.ts b/packages/api/src/crm/company/company.module.ts index a77852e29..e822f855d 100644 --- a/packages/api/src/crm/company/company.module.ts +++ b/packages/api/src/crm/company/company.module.ts @@ -1,3 +1,5 @@ +import { MicrosoftdynamicssalesCompanyMapper } from './services/microsoftdynamicssales/mappers'; +import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; import { LoggerService } from '@@core/@core-services/logger/logger.service'; import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; @@ -52,6 +54,8 @@ import { SyncService } from './sync/sync.service'; PipedriveCompanyMapper, ZendeskCompanyMapper, ZohoCompanyMapper, + MicrosoftdynamicssalesService, + MicrosoftdynamicssalesCompanyMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/api/src/crm/company/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/company/services/microsoftdynamicssales/index.ts new file mode 100644 index 000000000..b04664a29 --- /dev/null +++ b/packages/api/src/crm/company/services/microsoftdynamicssales/index.ts @@ -0,0 +1,97 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { CrmObject } from '@crm/@lib/@types'; +import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { LoggerService } from '@@core/@core-services/logger/logger.service'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ICompanyService } from '@crm/company/types'; +import { ServiceRegistry } from '../registry.service'; +import { MicrosoftdynamicssalesCompanyInput, MicrosoftdynamicssalesCompanyOutput } from './types'; +import { SyncParam } from '@@core/utils/types/interface'; +import { OriginalCompanyOutput } from '@@core/utils/types/original/original.crm'; + +@Injectable() +export class MicrosoftdynamicssalesService implements ICompanyService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.company.toUpperCase() + ':' + MicrosoftdynamicssalesService.name, + ); + this.registry.registerService('microsoftdynamicssales', this); + } + async addCompany( + companyData: MicrosoftdynamicssalesCompanyInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + const resp = await axios.post( + `${connection.account_url}/api/data/v9.2/accounts`, + JSON.stringify({ + data: companyData, + }), + { + headers: { + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + 'Content-Type': 'application/json', + }, + }, + ); + return { + data: resp.data.value, + message: 'Microsoftdynamicssales company created', + statusCode: 201, + }; + } catch (error) { + throw error; + } + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/accounts`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp.data.value, + message: 'Microsoftdynamicssales companies retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/crm/company/services/microsoftdynamicssales/mappers.ts b/packages/api/src/crm/company/services/microsoftdynamicssales/mappers.ts new file mode 100644 index 000000000..3e4db043a --- /dev/null +++ b/packages/api/src/crm/company/services/microsoftdynamicssales/mappers.ts @@ -0,0 +1,309 @@ +import { MicrosoftdynamicssalesCompanyInput, MicrosoftdynamicssalesCompanyOutput } from './types'; +import { + UnifiedCrmCompanyInput, + UnifiedCrmCompanyOutput, +} from '@crm/company/types/model.unified'; +import { ICompanyMapper } from '@crm/company/types'; +import { Utils } from '@crm/@lib/@utils'; +import { Injectable } from '@nestjs/common'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { getCountryCode, getCountryName } from '@@core/utils/types'; + +@Injectable() +export class MicrosoftdynamicssalesCompanyMapper implements ICompanyMapper { + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'company', 'microsoftdynamicssales', this); + } + async desunify( + source: UnifiedCrmCompanyInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: MicrosoftdynamicssalesCompanyInput = { + name: source.name, + } + + const industryToCodeJSON = { + "accounting": 1, + "agriculture and non-petrol natural resource extraction": 2, + "broadcasting printing and publishing": 3, + "brokers": 4, + "building supply retail": 5, + "business services": 6, + "consulting": 7, + "consumer services": 8, + "design, direction and creative management": 9, + "distributors, dispatchers and processors": 10, + "doctor's offices and clinics": 11, + "durable manufacturing": 12, + "eating and drinking places": 13, + "entertainment retail": 14, + "equipment rental and leasing": 15, + "financial": 16, + "food and tobacco processing": 17, + "inbound capital intensive processing": 18, + "inbound repair and services": 19, + "insurance": 20, + "legal services": 21, + "non-durable merchandise retail": 22, + "outbound consumer service": 23, + "petrochemical extraction and distribution": 24, + "service retail": 25, + "sig affiliations": 26, + "social services": 27, + "special outbound trade contractors": 28, + "specialty realty": 29, + "transportation": 30, + "utility creation and distribution": 31, + "vehicle retail": 32, + "wholesale": 33 + } + + if (source.industry) { + result.industrycode = industryToCodeJSON[source.industry.toLowerCase()]; + } + + if (source.number_of_employees) { + result.numberofemployees = source.number_of_employees; + } + + if (source.email_addresses[0]) { + result.emailaddress1 = source.email_addresses[0]?.email_address; + } + if (source.email_addresses[1]) { + result.emailaddress2 = source.email_addresses[1]?.email_address; + } + if (source.email_addresses[2]) { + result.emailaddress2 = source.email_addresses[2]?.email_address; + } + if (source.addresses[0]) { + result.address1_line1 = source.addresses[0].street_1; + result.address1_city = source.addresses[0].city; + result.address1_country = source.addresses[0].country; + result.address1_stateorprovince = source.addresses[0].state; + result.address1_postalcode = source.addresses[0].postal_code; + } + if (source.addresses[1]) { + result.address2_line1 = source.addresses[1].street_1; + result.address2_city = source.addresses[1].city; + result.address2_country = source.addresses[1].country; + result.address2_stateorprovince = source.addresses[1].state; + result.address2_postalcode = source.addresses[1].postal_code; + } + + if (source.phone_numbers[0]) { + result.telephone1 = source.phone_numbers[0].phone_number; + } + if (source.phone_numbers[1]) { + result.telephone2 = source.phone_numbers[1].phone_number; + } + if (source.phone_numbers[2]) { + result.telephone3 = source.phone_numbers[2].phone_number; + } + + + + + // Cannot set read only property _createdby_value + // if (source.user_id) { + // const owner_id = await this.utils.getRemoteIdFromUserUuid(source.user_id); + // if (owner_id) { + // result._createdby_value = source.user_id; + // } + // } + + // Microsoftdynamicssales company does not have attribute for email address + // Microsoftdynamicssales Company doest not have direct mapping of number of employees + + 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: MicrosoftdynamicssalesCompanyOutput | MicrosoftdynamicssalesCompanyOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleCompanyToUnified( + source, + connectionId, + customFieldMappings, + ); + } + // Handling array of MicrosoftdynamicssalesCompanyOutput + return Promise.all( + source.map((company) => + this.mapSingleCompanyToUnified( + company, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async mapSingleCompanyToUnified( + company: MicrosoftdynamicssalesCompanyOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = company[mapping.remote_id]; + } + } + + let opts: any = { + addresses: [], + email_addresses: [], + phone_numbers: [] + }; + + const industryJSON = { + 1: "Accounting", + 2: "Agriculture and Non-petrol Natural Resource Extraction", + 3: "Broadcasting Printing and Publishing", + 4: "Brokers", + 5: "Building Supply Retail", + 6: "Business Services", + 7: "Consulting", + 8: "Consumer Services", + 9: "Design, Direction and Creative Management", + 10: "Distributors, Dispatchers and Processors", + 11: "Doctor's Offices and Clinics", + 12: "Durable Manufacturing", + 13: "Eating and Drinking Places", + 14: "Entertainment Retail", + 15: "Equipment Rental and Leasing", + 16: "Financial", + 17: "Food and Tobacco Processing", + 18: "Inbound Capital Intensive Processing", + 19: "Inbound Repair and Services", + 20: "Insurance", + 21: "Legal Services", + 22: "Non-Durable Merchandise Retail", + 23: "Outbound Consumer Service", + 24: "Petrochemical Extraction and Distribution", + 25: "Service Retail", + 26: "SIG Affiliations", + 27: "Social Services", + 28: "Special Outbound Trade Contractors", + 29: "Specialty Realty", + 30: "Transportation", + 31: "Utility Creation and Distribution", + 32: "Vehicle Retail", + 33: "Wholesale" + } + + let address: any = {}; + + if (company.address1_line1) { + address = { + street_1: company.address1_line1 ?? '', + city: company.address1_city ?? '', + state: company.address1_stateorprovince ?? '', + postal_code: company.address2_postalcode ?? '', + country: company.address1_country ?? '', + address_type: 'WORK', + owner_type: 'COMPANY', + } + opts.addresses.push(address); + } + if (company.address2_line1) { + address = { + street_1: company.address2_line1 ?? '', + city: company.address2_city ?? '', + state: company.address2_stateorprovince ?? '', + postal_code: company.address2_postalcode ?? '', + country: company.address2_country ?? '', + address_type: 'WORK', + owner_type: 'COMPANY', + } + opts.addresses.push(address); + } + + + if (company.emailaddress1) { + opts.email_addresses.push({ + email_address: company.emailaddress1, + email_address_type: "WORK" + }) + } + if (company.emailaddress2) { + opts.email_addresses.push({ + email_address: company.emailaddress2, + email_address_type: "WORK" + }) + } + if (company.emailaddress2) { + opts.email_addresses.push({ + email_address: company.emailaddress3, + email_address_type: "WORK" + }) + } + if (company.telephone1) { + opts.phone_numbers.push({ + phone_number: company.telephone1, + phone_type: "MOBILE" + }); + } + if (company.telephone2) { + opts.phone_numbers.push({ + phone_number: company.telephone2, + phone_type: "MOBILE" + }); + } + if (company.telephone3) { + opts.phone_numbers.push({ + phone_number: company.telephone3, + phone_type: "MOBILE" + }); + } + + + + if (company._createdby_value) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + company._createdby_value, + connectionId, + ); + if (owner_id) { + opts = { + ...opts, + user_id: owner_id, + }; + } + } + + return { + remote_id: company.accountid, + remote_data: company, + name: company.name, + industry: + company.industrycode ? industryJSON[company.industrycode] : null, + number_of_employees: company.numberofemployees ?? null, + field_mappings, + ...opts, + }; + } +} diff --git a/packages/api/src/crm/company/services/microsoftdynamicssales/types.ts b/packages/api/src/crm/company/services/microsoftdynamicssales/types.ts new file mode 100644 index 000000000..96232c05f --- /dev/null +++ b/packages/api/src/crm/company/services/microsoftdynamicssales/types.ts @@ -0,0 +1,153 @@ +interface MicrosoftdynamicssalesCompany { + accountcategorycode: number; // Edm.Int32 + accountclassificationcode: number; // Edm.Int32 + accountid: string; // Edm.Guid + accountnumber: string; // Edm.String + accountratingcode: number; // Edm.Int32 + address1_addressid: string; // Edm.Guid + address1_addresstypecode: number; // Edm.Int32 + address1_city: string; // Edm.String + address1_composite: string; // Edm.String + address1_country: string; // Edm.String + address1_county: string; // Edm.String + address1_fax: string; // Edm.String + address1_freighttermscode: number; // Edm.Int32 + address1_latitude: number; // Edm.Double + address1_line1: string; // Edm.String + address1_line2: string; // Edm.String + address1_line3: string; // Edm.String + address1_longitude: number; // Edm.Double + address1_name: string; // Edm.String + address1_postalcode: string; // Edm.String + address1_postofficebox: string; // Edm.String + address1_primarycontactname: string; // Edm.String + address1_shippingmethodcode: number; // Edm.Int32 + address1_stateorprovince: string; // Edm.String + address1_telephone1: string; // Edm.String + address1_telephone2: string; // Edm.String + address1_telephone3: string; // Edm.String + address1_upszone: string; // Edm.String + address1_utcoffset: number; // Edm.Int32 + address2_addressid: string; // Edm.Guid + address2_addresstypecode: number; // Edm.Int32 + address2_city: string; // Edm.String + address2_composite: string; // Edm.String + address2_country: string; // Edm.String + address2_county: string; // Edm.String + address2_fax: string; // Edm.String + address2_freighttermscode: number; // Edm.Int32 + address2_latitude: number; // Edm.Double + address2_line1: string; // Edm.String + address2_line2: string; // Edm.String + address2_line3: string; // Edm.String + address2_longitude: number; // Edm.Double + address2_name: string; // Edm.String + address2_postalcode: string; // Edm.String + address2_postofficebox: string; // Edm.String + address2_primarycontactname: string; // Edm.String + address2_shippingmethodcode: number; // Edm.Int32 + address2_stateorprovince: string; // Edm.String + address2_telephone1: string; // Edm.String + address2_telephone2: string; // Edm.String + address2_telephone3: string; // Edm.String + address2_upszone: string; // Edm.String + address2_utcoffset: number; // Edm.Int32 + adx_createdbyipaddress: string; // Edm.String + adx_createdbyusername: string; // Edm.String + adx_modifiedbyipaddress: string; // Edm.String + adx_modifiedbyusername: string; // Edm.String + aging30: number; // Edm.Decimal + aging30_base: number; // Edm.Decimal + aging60: number; // Edm.Decimal + aging60_base: number; // Edm.Decimal + aging90: number; // Edm.Decimal + aging90_base: number; // Edm.Decimal + businesstypecode: number; // Edm.Int32 + createdon: string; // Edm.DateTimeOffset + creditlimit: number; // Edm.Decimal + creditlimit_base: number; // Edm.Decimal + creditonhold: boolean; // Edm.Boolean + customersizecode: number; // Edm.Int32 + customertypecode: number; // Edm.Int32 + description: string; // Edm.String + donotbulkemail: boolean; // Edm.Boolean + donotbulkpostalmail: boolean; // Edm.Boolean + donotemail: boolean; // Edm.Boolean + donotfax: boolean; // Edm.Boolean + donotphone: boolean; // Edm.Boolean + donotpostalmail: boolean; // Edm.Boolean + donotsendmm: boolean; // Edm.Boolean + emailaddress1: string; // Edm.String + emailaddress2: string; // Edm.String + emailaddress3: string; // Edm.String + entityimage: Uint8Array; // Edm.Binary + entityimage_timestamp: number; // Edm.Int64 + entityimage_url: string; // Edm.String + entityimageid: string; // Edm.Guid + exchangerate: number; // Edm.Decimal + fax: string; // Edm.String + followemail: boolean; // Edm.Boolean + ftpsiteurl: string; // Edm.String + importsequencenumber: number; // Edm.Int32 + industrycode: number; // Edm.Int32 + lastonholdtime: string; // Edm.DateTimeOffset + lastusedincampaign: string; // Edm.DateTimeOffset + marketcap: number; // Edm.Decimal + marketcap_base: number; // Edm.Decimal + marketingonly: boolean; // Edm.Boolean + merged: boolean; // Edm.Boolean + modifiedon: string; // Edm.DateTimeOffset + name: string; // Edm.String + numberofemployees: number; // Edm.Int32 + onholdtime: number; // Edm.Int32 + overriddencreatedon: string; // Edm.DateTimeOffset + ownershipcode: number; // Edm.Int32 + participatesinworkflow: boolean; // Edm.Boolean + paymenttermscode: number; // Edm.Int32 + preferredappointmentdaycode: number; // Edm.Int32 + preferredappointmenttimecode: number; // Edm.Int32 + preferredcontactmethodcode: number; // Edm.Int32 + primarysatoriid: string; // Edm.String + primarytwitterid: string; // Edm.String + processid: string; // Edm.Guid + revenue: number; // Edm.Decimal + revenue_base: number; // Edm.Decimal + sharesoutstanding: number; // Edm.Int32 + shippingmethodcode: number; // Edm.Int32 + sic: string; // Edm.String + stageid: string; // Edm.Guid + statecode: number; // Edm.Int32 + statuscode: number; // Edm.Int32 + stockexchange: string; // Edm.String + telephone1: string; // Edm.String + telephone2: string; // Edm.String + telephone3: string; // Edm.String + territorycode: number; // Edm.Int32 + tickersymbol: string; // Edm.String + timezoneruleversionnumber: number; // Edm.Int32 + transactioncurrencyid: string; // Edm.Guid + traversedpath: string; // Edm.String + utcconversiontimezonecode: number; // Edm.Int32 + versionnumber: number; // Edm.Int64 + websiteurl: string; // Edm.String + yominame: string; // Edm.String + _createdby_value: string; + _createdonbehalfby_value: string; + _masterid_value: string; + _modifiedby_value: string; + _modifiedonbehalfby_value: string; + _msa_managingpartnerid_value: string; + _ownerid_value: string; + _owningbusinessunit_value: string; + _owningteam_value: string; + _owninguser_value: string; + _parentaccountid_value: string; + _preferredsystemuserid_value: string; + _primarycontactid_value: string; + _slaid_value: string; + _slainvokedid_value: string; + _transactioncurrencyid_value: string; +} + +export type MicrosoftdynamicssalesCompanyInput = Partial; +export type MicrosoftdynamicssalesCompanyOutput = MicrosoftdynamicssalesCompanyInput; \ No newline at end of file diff --git a/packages/api/src/crm/contact/contact.module.ts b/packages/api/src/crm/contact/contact.module.ts index 9cda6805c..56e51789a 100644 --- a/packages/api/src/crm/contact/contact.module.ts +++ b/packages/api/src/crm/contact/contact.module.ts @@ -1,3 +1,5 @@ +import { MicrosoftdynamicssalesContactMapper } from './services/microsoftdynamicssales/mappers'; +import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; @@ -45,6 +47,8 @@ import { SyncService } from './sync/sync.service'; PipedriveContactMapper, ZendeskContactMapper, ZohoContactMapper, + MicrosoftdynamicssalesService, + MicrosoftdynamicssalesContactMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/api/src/crm/contact/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/contact/services/microsoftdynamicssales/index.ts new file mode 100644 index 000000000..f585e6966 --- /dev/null +++ b/packages/api/src/crm/contact/services/microsoftdynamicssales/index.ts @@ -0,0 +1,99 @@ +import { Injectable } from '@nestjs/common'; +import { IContactService } from '@crm/contact/types'; +import { CrmObject } from '@crm/@lib/@types'; +import axios from 'axios'; +import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { LoggerService } from '@@core/@core-services/logger/logger.service'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { ServiceRegistry } from '../registry.service'; +import { MicrosoftdynamicssalesContactInput, MicrosoftdynamicssalesContactOutput } from './types'; +import { SyncParam } from '@@core/utils/types/interface'; + +@Injectable() +export class MicrosoftdynamicssalesService implements IContactService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.contact.toUpperCase() + ':' + MicrosoftdynamicssalesService.name, + ); + this.registry.registerService('microsoftdynamicssales', this); + } + + async addContact( + contactData: MicrosoftdynamicssalesContactInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + const resp = await axios.post( + `${connection.account_url}/api/data/v9.2/contacts`, + JSON.stringify({ + data: contactData, + }), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp.data.value, + message: 'microsoftdynamicssales contact created', + statusCode: 201, + }; + } catch (error) { + throw error; + } + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/contacts`, + { + headers: { + accept: 'application/json', + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + return { + data: resp.data.value, + message: 'Microsoftdynamicssales contacts retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/crm/contact/services/microsoftdynamicssales/mappers.ts b/packages/api/src/crm/contact/services/microsoftdynamicssales/mappers.ts new file mode 100644 index 000000000..d9c8acdce --- /dev/null +++ b/packages/api/src/crm/contact/services/microsoftdynamicssales/mappers.ts @@ -0,0 +1,235 @@ +import { + UnifiedCrmContactInput, + UnifiedCrmContactOutput, +} from '@crm/contact/types/model.unified'; +import { IContactMapper } from '@crm/contact/types'; +import { MicrosoftdynamicssalesContactInput, MicrosoftdynamicssalesContactOutput } from './types'; +import { Utils } from '@crm/@lib/@utils'; +import { Injectable } from '@nestjs/common'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; + +@Injectable() +export class MicrosoftdynamicssalesContactMapper implements IContactMapper { + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'contact', 'microsoftdynamicssales', this); + } + + async desunify( + source: UnifiedCrmContactInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + + + const result: MicrosoftdynamicssalesContactInput = { + firstname: source.first_name, + lastname: source.last_name, + fullname: `${source.first_name} ${source.last_name}`, + }; + if (source.email_addresses[0]) { + result.emailaddress1 = source.email_addresses[0]?.email_address; + } + if (source.email_addresses[1]) { + result.emailaddress2 = source.email_addresses[1]?.email_address; + } + if (source.email_addresses[2]) { + result.emailaddress2 = source.email_addresses[2]?.email_address; + } + if (source.addresses[0]) { + result.address1_line1 = source.addresses[0].street_1; + result.address1_city = source.addresses[0].city; + result.address1_country = source.addresses[0].country; + result.address1_stateorprovince = source.addresses[0].state; + result.address1_postalcode = source.addresses[0].postal_code; + } + if (source.addresses[1]) { + result.address2_line1 = source.addresses[1].street_1; + result.address2_city = source.addresses[1].city; + result.address2_country = source.addresses[1].country; + result.address2_stateorprovince = source.addresses[1].state; + result.address2_postalcode = source.addresses[1].postal_code; + } + if (source.addresses[2]) { + result.address3_line1 = source.addresses[2].street_1; + result.address3_city = source.addresses[2].city; + result.address3_country = source.addresses[2].country; + result.address3_stateorprovince = source.addresses[2].state; + result.address3_postalcode = source.addresses[2].postal_code; + } + + if (source.phone_numbers[0]) { + result.telephone1 = source.phone_numbers[0].phone_number; + } + if (source.phone_numbers[1]) { + result.telephone2 = source.phone_numbers[1].phone_number; + } + if (source.phone_numbers[2]) { + result.telephone3 = source.phone_numbers[2].phone_number; + } + + + + + 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: MicrosoftdynamicssalesContactOutput | MicrosoftdynamicssalesContactOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return await this.mapSingleContactToUnified( + source, + connectionId, + customFieldMappings, + ); + } + + // Handling array of HubspotContactOutput + return Promise.all( + source.map((contact) => + this.mapSingleContactToUnified( + contact, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async mapSingleContactToUnified( + contact: MicrosoftdynamicssalesContactOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = contact[mapping.remote_id]; + } + } + + let opts: any = { + addresses: [], + email_addresses: [], + phone_numbers: [] + }; + + let address: any = {}; + + if (contact.address1_line1) { + address = { + street_1: contact.address1_line1 ?? '', + city: contact.address1_city ?? '', + state: contact.address1_stateorprovince ?? '', + postal_code: contact.address2_postalcode ?? '', + country: contact.address1_country ?? '', + address_type: 'PERSONAL', + owner_type: 'CONTACT', + } + opts.addresses.push(address); + } + if (contact.address2_line1) { + address = { + street_1: contact.address2_line1 ?? '', + city: contact.address2_city ?? '', + state: contact.address2_stateorprovince ?? '', + postal_code: contact.address2_postalcode ?? '', + country: contact.address2_country ?? '', + address_type: 'PERSONAL', + owner_type: 'CONTACT', + } + opts.addresses.push(address); + } + if (contact.address3_line1) { + address = { + street_1: contact.address3_line1 ?? '', + city: contact.address3_city ?? '', + state: contact.address3_stateorprovince ?? '', + postal_code: contact.address3_postalcode ?? '', + country: contact.address3_country ?? '', + address_type: 'PERSONAL', + owner_type: 'CONTACT', + } + opts.addresses.push(address); + } + + if (contact.emailaddress1) { + opts.email_addresses.push({ + email_address: contact.emailaddress1, + email_address_type: "PERSONAL" + }) + } + if (contact.emailaddress2) { + opts.email_addresses.push({ + email_address: contact.emailaddress2, + email_address_type: "PERSONAL" + }) + } + if (contact.emailaddress2) { + opts.email_addresses.push({ + email_address: contact.emailaddress3, + email_address_type: "PERSONAL" + }) + } + if (contact.telephone1) { + opts.phone_numbers.push({ + phone_number: contact.telephone1, + phone_type: "MOBILE" + }); + } + if (contact.telephone2) { + opts.phone_numbers.push({ + phone_number: contact.telephone2, + phone_type: "MOBILE" + }); + } + if (contact.telephone3) { + opts.phone_numbers.push({ + phone_number: contact.telephone3, + phone_type: "MOBILE" + }); + } + + if (contact._createdby_value) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + contact._createdby_value, + connectionId, + ); + if (owner_id) { + opts = { + ...opts, + user_id: owner_id, + }; + } + } + + return { + remote_id: contact.contactid, + remote_data: contact, + first_name: contact.firstname ?? '', + last_name: contact.lastname ?? '', + field_mappings, + ...opts, + }; + } +} diff --git a/packages/api/src/crm/contact/services/microsoftdynamicssales/types.ts b/packages/api/src/crm/contact/services/microsoftdynamicssales/types.ts new file mode 100644 index 000000000..9bb1a30f9 --- /dev/null +++ b/packages/api/src/crm/contact/services/microsoftdynamicssales/types.ts @@ -0,0 +1,228 @@ +interface MicrosoftdynamicssalesContact { + accountrolecode: number; + address1_addressid: string; + address1_addresstypecode: number; + address1_city: string; + address1_composite: string; + address1_country: string; + address1_county: string; + address1_fax: string; + address1_freighttermscode: number; + address1_latitude: number; + address1_line1: string; + address1_line2: string; + address1_line3: string; + address1_longitude: number; + address1_name: string; + address1_postalcode: string; + address1_postofficebox: string; + address1_primarycontactname: string; + address1_shippingmethodcode: number; + address1_stateorprovince: string; + address1_telephone1: string; + address1_telephone2: string; + address1_telephone3: string; + address1_upszone: string; + address1_utcoffset: number; + address2_addressid: string; + address2_addresstypecode: number; + address2_city: string; + address2_composite: string; + address2_country: string; + address2_county: string; + address2_fax: string; + address2_freighttermscode: number; + address2_latitude: number; + address2_line1: string; + address2_line2: string; + address2_line3: string; + address2_longitude: number; + address2_name: string; + address2_postalcode: string; + address2_postofficebox: string; + address2_primarycontactname: string; + address2_shippingmethodcode: number; + address2_stateorprovince: string; + address2_telephone1: string; + address2_telephone2: string; + address2_telephone3: string; + address2_upszone: string; + address2_utcoffset: number; + address3_addressid: string; + address3_addresstypecode: number; + address3_city: string; + address3_composite: string; + address3_country: string; + address3_county: string; + address3_fax: string; + address3_freighttermscode: number; + address3_latitude: number; + address3_line1: string; + address3_line2: string; + address3_line3: string; + address3_longitude: number; + address3_name: string; + address3_postalcode: string; + address3_postofficebox: string; + address3_primarycontactname: string; + address3_shippingmethodcode: number; + address3_stateorprovince: string; + address3_telephone1: string; + address3_telephone2: string; + address3_telephone3: string; + address3_upszone: string; + address3_utcoffset: number; + adx_confirmremovepassword: boolean; + adx_createdbyipaddress: string; + adx_createdbyusername: string; + adx_identity_accessfailedcount: number; + adx_identity_emailaddress1confirmed: boolean; + adx_identity_lastsuccessfullogin: string; + adx_identity_locallogindisabled: boolean; + adx_identity_lockoutenabled: boolean; + adx_identity_lockoutenddate: string; + adx_identity_logonenabled: boolean; + adx_identity_mobilephoneconfirmed: boolean; + adx_identity_newpassword: string; + adx_identity_passwordhash: string; + adx_identity_securitystamp: string; + adx_identity_twofactorenabled: boolean; + adx_identity_username: string; + adx_modifiedbyipaddress: string; + adx_modifiedbyusername: string; + adx_organizationname: string; + adx_preferredlcid: number; + adx_profilealert: boolean; + adx_profilealertdate: string; + adx_profilealertinstructions: string; + adx_profileisanonymous: boolean; + adx_profilelastactivity: string; + adx_profilemodifiedon: string; + adx_publicprofilecopy: string; + adx_timezone: number; + aging30: number; + aging30_base: number; + aging60: number; + aging60_base: number; + aging90: number; + aging90_base: number; + anniversary: string; + annualincome: number; + annualincome_base: number; + assistantname: string; + assistantphone: string; + birthdate: string; + business2: string; + callback: string; + childrensnames: string; + company: string; + contactid: string; + createdon: string; + creditlimit: number; + creditlimit_base: number; + creditonhold: boolean; + customersizecode: number; + customertypecode: number; + department: string; + description: string; + donotbulkemail: boolean; + donotbulkpostalmail: boolean; + donotemail: boolean; + donotfax: boolean; + donotphone: boolean; + donotpostalmail: boolean; + donotsendmm: boolean; + educationcode: number; + emailaddress1: string; + emailaddress2: string; + emailaddress3: string; + employeeid: string; + entityimage: string; + entityimage_timestamp: number; + entityimage_url: string; + entityimageid: string; + exchangerate: number; + externaluseridentifier: string; + familystatuscode: number; + fax: string; + firstname: string; + followemail: boolean; + ftpsiteurl: string; + fullname: string; + gendercode: number; + governmentid: string; + haschildrencode: number; + home2: string; + importsequencenumber: number; + isbackofficecustomer: boolean; + jobtitle: string; + lastname: string; + lastonholdtime: string; + lastusedincampaign: string; + leadsourcecode: number; + managername: string; + managerphone: string; + marketingonly: boolean; + merged: boolean; + middlename: string; + mobilephone: string; + modifiedon: string; + msdyn_disablewebtracking: boolean; + msdyn_isminor: boolean; + msdyn_isminorwithparentalconsent: boolean; + msdyn_portaltermsagreementdate: string; + mspp_userpreferredlcid: number; + nickname: string; + numberofchildren: number; + onholdtime: number; + overriddencreatedon: string; + pager: string; + participatesinworkflow: boolean; + paymenttermscode: number; + preferredappointmentdaycode: number; + preferredappointmenttimecode: number; + preferredcontactmethodcode: number; + processid: string; + salutation: string; + shippingmethodcode: number; + spousesname: string; + stageid: string; + statecode: number; + statuscode: number; + subscriptionid: string; + suffix: string; + telephone1: string; + telephone2: string; + telephone3: string; + territorycode: number; + timespentbymeonemailandmeetings: string; + timezoneruleversionnumber: number; + traversedpath: string; + utcconversiontimezonecode: number; + versionnumber: number; + websiteurl: string; + yomifirstname: string; + yomifullname: string; + yomilastname: string; + yomimiddlename: string; + _createdby_value: string; + _createdonbehalfby_value: string; + _masterid_value: string; + _modifiedby_value: string; + _modifiedonbehalfby_value: string; + _msa_managingpartnerid_value: string; + _ownerid_value: string; + _owningbusinessunit_value: string; + _owningteam_value: string; + _owninguser_value: string; + _parentcustomerid_value: string; + _preferredsystemuserid_value: string; + _slaid_value: string; + _slainvokedid_value: string; + _transactioncurrencyid_value: string; + + +} + +export type MicrosoftdynamicssalesContactInput = Partial; +export type MicrosoftdynamicssalesContactOutput = MicrosoftdynamicssalesContactInput; \ No newline at end of file diff --git a/packages/api/src/crm/deal/deal.module.ts b/packages/api/src/crm/deal/deal.module.ts index d27a59782..334d7367b 100644 --- a/packages/api/src/crm/deal/deal.module.ts +++ b/packages/api/src/crm/deal/deal.module.ts @@ -1,3 +1,5 @@ +import { MicrosoftdynamicssalesDealMapper } from './services/microsoftdynamicssales/mappers'; +import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; @@ -43,6 +45,8 @@ import { SyncService } from './sync/sync.service'; HubspotDealMapper, AttioDealMapper, CloseDealMapper, + MicrosoftdynamicssalesService, + MicrosoftdynamicssalesDealMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/api/src/crm/deal/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/deal/services/microsoftdynamicssales/index.ts new file mode 100644 index 000000000..12979121b --- /dev/null +++ b/packages/api/src/crm/deal/services/microsoftdynamicssales/index.ts @@ -0,0 +1,96 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { CrmObject } from '@crm/@lib/@types'; +import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { LoggerService } from '@@core/@core-services/logger/logger.service'; +import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; +import { ApiResponse } from '@@core/utils/types'; +import { IDealService } from '@crm/deal/types'; +import { ServiceRegistry } from '../registry.service'; +import { MicrosoftdynamicssalesDealInput, MicrosoftdynamicssalesDealOutput } from './types'; +import { SyncParam } from '@@core/utils/types/interface'; + +@Injectable() +export class MicrosoftdynamicssalesService implements IDealService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.deal.toUpperCase() + ':' + MicrosoftdynamicssalesService.name, + ); + this.registry.registerService('microsoftdynamicssales', this); + } + async addDeal( + dealData: MicrosoftdynamicssalesDealInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + const resp = await axios.post( + `${connection.account_url}/api/data/v9.2/opportunities`, + JSON.stringify({ + data: dealData, + }), + { + headers: { + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + 'Content-Type': 'application/json', + }, + }, + ); + return { + data: resp.data.value, + message: 'Microsoftdynamicssales deal created', + statusCode: 201, + }; + } catch (error) { + throw error; + } + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/opportunities`, + { + headers: { + accept: 'application/json', + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp.data.value, + message: 'Microsoftdynamicssales deals retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/crm/deal/services/microsoftdynamicssales/mappers.ts b/packages/api/src/crm/deal/services/microsoftdynamicssales/mappers.ts new file mode 100644 index 000000000..b8e64523d --- /dev/null +++ b/packages/api/src/crm/deal/services/microsoftdynamicssales/mappers.ts @@ -0,0 +1,130 @@ +import { Injectable } from '@nestjs/common'; +import { + UnifiedCrmDealInput, + UnifiedCrmDealOutput, +} from '@crm/deal/types/model.unified'; +import { IDealMapper } from '@crm/deal/types'; +import { Utils } from '@crm/@lib/@utils'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { MicrosoftdynamicssalesDealInput, MicrosoftdynamicssalesDealOutput } from './types'; + +@Injectable() +export class MicrosoftdynamicssalesDealMapper implements IDealMapper { + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'deal', 'microsoftdynamicssales', this); + } + + async desunify( + source: UnifiedCrmDealInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: MicrosoftdynamicssalesDealInput = { + name: source.name, + description: source.description, + totalamount: source.amount + } + + + + + if (source.company_id) { + const id = await this.utils.getRemoteIdFromCompanyUuid(source.company_id); + result["parentaccountid@odata.bind"] = `/accounts(${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: MicrosoftdynamicssalesDealOutput | MicrosoftdynamicssalesDealOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return await this.mapSingleDealToUnified( + source, + connectionId, + customFieldMappings, + ); + } + // Handling array of MicrosoftdynamicssalesDealOutput + return Promise.all( + source.map((deal) => + this.mapSingleDealToUnified(deal, connectionId, customFieldMappings), + ), + ); + } + + private async mapSingleDealToUnified( + deal: MicrosoftdynamicssalesDealOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = deal[mapping.remote_id]; + } + } + + let opts: any = {}; + + if (deal._ownerid_value) { + const user_id = await this.utils.getUserUuidFromRemoteId( + deal._ownerid_value, + connectionId + ); + if (user_id) { + opts = { + ...opts, + user_id: user_id + } + } + } + + if (deal._accountid_value) { + const company_id = await this.utils.getCompanyUuidFromRemoteId( + deal._accountid_value, + connectionId + ); + if (company_id) { + opts = { + ...opts, + company_id: company_id + } + } + } + + + return { + remote_id: deal.opportunityid, + remote_data: deal, + name: deal.name ?? '', + description: deal.description ?? '', + amount: deal.totalamount ?? 0, + field_mappings, + ...opts, + }; + } +} diff --git a/packages/api/src/crm/deal/services/microsoftdynamicssales/types.ts b/packages/api/src/crm/deal/services/microsoftdynamicssales/types.ts new file mode 100644 index 000000000..363c7ef79 --- /dev/null +++ b/packages/api/src/crm/deal/services/microsoftdynamicssales/types.ts @@ -0,0 +1,125 @@ +interface MicrosoftdynamicssalesDeal { + msdyn_opportunityscore: any, + totalamount_base: number, + pursuitdecision: boolean, + msdyn_scorehistory: any, + _msdyn_predictivescoreid_value: string, + actualvalue_base: any, + totallineitemdiscountamount: number, + totalamount: number, + identifypursuitteam: boolean, + statuscode: number, + msdyn_opportunityscoretrend: any, + totaldiscountamount_base: number, + _createdby_value: string, + name: string, + _owninguser_value: string, + _parentcontactid_value: string, + _msdyn_segmentid_value: any, + totaltax: number, + budgetstatus: any, + _accountid_value: any, + closeprobability: number, + finaldecisiondate: string, + _modifiedby_value: string, + _slainvokedid_value: any, + msdyn_similaropportunities: any, + msdyn_opportunitygrade: any, + quotecomments: any, + timeline: any, + qualificationcomments: any, + customerneed: string, + estimatedclosedate: string, + estimatedvalue: number, + identifycustomercontacts: boolean, + _contactid_value: any, + presentproposal: boolean, + captureproposalfeedback: boolean, + _campaignid_value: any, + estimatedvalue_base: number, + salesstagecode: number, + msdyn_forecastcategory: number, + stageid: any, + lastonholdtime: any, + need: any, + purchaseprocess: number, + sendthankyounote: boolean, + _parentaccountid_value: string, + totalamountlessfreight: number, + _modifiedonbehalfby_value: string, + _ownerid_value: string, + stepid: any, + developproposal: boolean, + freightamount_base: any, + description: string, + processid: any, + skippricecalculation: number, + discountamount_base: any, + onholdtime: any, + schedulefollowup_qualify: any, + importsequencenumber: any, + modifiedon: string, + totaltax_base: number, + opportunityratingcode: number, + utcconversiontimezonecode: any, + totallineitemdiscountamount_base: number, + salesstage: any, + discountamount: any, + traversedpath: any, + _createdonbehalfby_value: any, + _originatingleadid_value: any, + stepname: string, + customerpainpoints: any, + identifycompetitors: boolean, + emailaddress: string, + budgetamount: number, + msdyn_gdproptout: boolean, + totallineitemamount: number, + _owningteam_value: any, + _owningbusinessunit_value: string, + decisionmaker: boolean, + opportunityid: string, + totaldiscountamount: number, + evaluatefit: boolean, + _msdyn_opportunitykpiid_value: string, + proposedsolution: string, + scheduleproposalmeeting: any, + presentfinalproposal: boolean, + statecode: number, + isrevenuesystemcalculated: boolean, + participatesinworkflow: boolean, + exchangerate: number, + timespentbymeonemailandmeetings: any, + purchasetimeframe: number, + filedebrief: boolean, + teamsfollowed: any, + _customerid_value: string, + totalamountlessfreight_base: number, + _pricelevelid_value: string, + timezoneruleversionnumber: number, + overriddencreatedon: any, + prioritycode: number, + initialcommunication: any, + _transactioncurrencyid_value: string, + schedulefollowup_prospect: any, + currentsituation: string, + totallineitemamount_base: number, + msdyn_scorereasons: any, + budgetamount_base: number, + freightamount: any, + actualclosedate: any, + actualvalue: any, + discountpercentage: any, + confirminterest: boolean, + completeinternalreview: boolean, + completefinalproposal: boolean, + _slaid_value: any, + resolvefeedback: boolean, + createdon: string, + pricingerrorcode: number, + versionnumber: number, + [key: string]: any +} + +export type MicrosoftdynamicssalesDealInput = Partial; +export type MicrosoftdynamicssalesDealOutput = MicrosoftdynamicssalesDealInput; \ No newline at end of file diff --git a/packages/api/src/crm/note/note.module.ts b/packages/api/src/crm/note/note.module.ts index 0191db40a..280f0f69e 100644 --- a/packages/api/src/crm/note/note.module.ts +++ b/packages/api/src/crm/note/note.module.ts @@ -1,3 +1,5 @@ +import { MicrosoftdynamicssalesNoteMapper } from './services/microsoftdynamicssales/mappers'; +import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; @@ -42,6 +44,8 @@ import { SyncService } from './sync/sync.service'; AttioNoteMapper, HubspotNoteMapper, CloseNoteMapper, + MicrosoftdynamicssalesService, + MicrosoftdynamicssalesNoteMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/api/src/crm/note/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/note/services/microsoftdynamicssales/index.ts new file mode 100644 index 000000000..8a17ca8d7 --- /dev/null +++ b/packages/api/src/crm/note/services/microsoftdynamicssales/index.ts @@ -0,0 +1,93 @@ +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 { INoteService } from '@crm/note/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { MicrosoftdynamicssalesNoteInput, MicrosoftdynamicssalesNoteOutput } from './types'; + +@Injectable() +export class MicrosoftdynamicssalesService implements INoteService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.note.toUpperCase() + ':' + MicrosoftdynamicssalesService.name, + ); + this.registry.registerService('microsoftdynamicssales', this); + } + + async addNote( + noteData: MicrosoftdynamicssalesNoteInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + const resp = await axios.post( + `${connection.account_url}/api/data/v9.2/annotations`, + JSON.stringify(noteData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp?.data.value, + message: 'Microsoftdynamicssales note created', + statusCode: 201, + }; + } catch (error) { + throw error; + } + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + const baseURL = `${connection.account_url}/api/data/v9.2/annotations`; + + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced microsoftdynamicssales notes !`); + return { + data: resp?.data?.value, + message: 'Microsoftdynamicssales notes retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/crm/note/services/microsoftdynamicssales/mappers.ts b/packages/api/src/crm/note/services/microsoftdynamicssales/mappers.ts new file mode 100644 index 000000000..6052d8f14 --- /dev/null +++ b/packages/api/src/crm/note/services/microsoftdynamicssales/mappers.ts @@ -0,0 +1,151 @@ +import { MicrosoftdynamicssalesNoteInput, MicrosoftdynamicssalesNoteOutput } from './types'; +import { + UnifiedCrmNoteInput, + UnifiedCrmNoteOutput, +} from '@crm/note/types/model.unified'; +import { INoteMapper } from '@crm/note/types'; +import { Utils } from '@crm/@lib/@utils'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class MicrosoftdynamicssalesNoteMapper implements INoteMapper { + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'note', 'microsoftdynamicssales', this); + } + async desunify( + source: UnifiedCrmNoteInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + //get the parent id of the object tied to the note + let opts: any = {}; + + if (source.company_id) { + const id = await this.utils.getRemoteIdFromCompanyUuid(source.company_id); + opts = { + ...opts, + "objectid_account@odata.bind": `/accounts(${id})` + } + } + if (source.contact_id) { + const id = await this.utils.getRemoteIdFromContactUuid(source.contact_id); + opts = { + ...opts, + "objectid_contact@odata.bind": `/contacts(${id})` + } + } + + // Do not think there is option to add opportunity + + + + const result: MicrosoftdynamicssalesNoteInput = { + notetext: source.content, + ...opts + }; + + return result; + } + + async unify( + source: MicrosoftdynamicssalesNoteOutput | MicrosoftdynamicssalesNoteOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return await this.mapSingleNoteToUnified( + source, + connectionId, + customFieldMappings, + ); + } + + return Promise.all( + source.map((note) => + this.mapSingleNoteToUnified(note, connectionId, customFieldMappings), + ), + ); + } + + private async mapSingleNoteToUnified( + note: MicrosoftdynamicssalesNoteOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = note[mapping.remote_id]; + } + } + + let opts: any = {}; + + if (note._ownerid_value) { + const user_id = await this.utils.getUserUuidFromRemoteId( + note._ownerid_value, + connectionId + ); + if (user_id) { + opts = { + ...opts, + user_id: user_id + } + } + } + + // This could only contain either contact, comapany or deal id + if (note._objectid_value) { + const company_id = await this.utils.getCompanyUuidFromRemoteId( + note._objectid_value, + connectionId + ); + if (company_id) { + opts = { + ...opts, + company_id: company_id + } + } + + const contact_id = await this.utils.getContactUuidFromRemoteId( + note._objectid_value, + connectionId + ); + if (contact_id) { + opts = { + ...opts, + contact_id: contact_id + } + } + + const deal_id = await this.utils.getDealUuidFromRemoteId( + note._objectid_value, + connectionId + ); + if (deal_id) { + opts = { + ...opts, + deal_id: deal_id + } + } + + } + + return { + remote_id: note.annotationid, + remote_data: note, + content: note.notetext ?? '', + field_mappings, + ...opts, + }; + } +} diff --git a/packages/api/src/crm/note/services/microsoftdynamicssales/types.ts b/packages/api/src/crm/note/services/microsoftdynamicssales/types.ts new file mode 100644 index 000000000..fcdff526a --- /dev/null +++ b/packages/api/src/crm/note/services/microsoftdynamicssales/types.ts @@ -0,0 +1,32 @@ +interface MicrosoftdynamicssalesNote { + annotationid: string; + createdon: string; + documentbody: string; + documentbody_binary: string; + filename: string; + filesize: number; + importsequencenumber: number; + isdocument: boolean; + langid: string; + mimetype: string; + modifiedon: string; + notetext: string; + objecttypecode: string; + overriddencreatedon: string; + prefix: string; + stepid: string; + subject: string; + versionnumber: number; + _createdby_value: string; + _createdonbehalfby_value: string; + _modifiedby_value: string; + _modifiedonbehalfby_value: string; + _objectid_value: string; + _ownerid_value: string; + _owningbusinessunit_value: string; + _owningteam_value: string; + _owninguser_value: string +} + +export type MicrosoftdynamicssalesNoteInput = Partial; +export type MicrosoftdynamicssalesNoteOutput = MicrosoftdynamicssalesNoteInput; \ No newline at end of file diff --git a/packages/api/src/crm/task/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/task/services/microsoftdynamicssales/index.ts new file mode 100644 index 000000000..b536fc781 --- /dev/null +++ b/packages/api/src/crm/task/services/microsoftdynamicssales/index.ts @@ -0,0 +1,94 @@ +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 { MicrosoftdynamicssalesTaskInput, MicrosoftdynamicssalesTaskOutput } from './types'; + +@Injectable() +export class MicrosoftdynamicssalesService implements ITaskService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.task.toUpperCase() + ':' + MicrosoftdynamicssalesService.name, + ); + this.registry.registerService('microsoftdynamicssales', this); + } + + async addTask( + taskData: MicrosoftdynamicssalesTaskInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + const resp = await axios.post( + `${connection.account_url}/api/data/v9.2/tasks`, + JSON.stringify(taskData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + return { + data: resp?.data?.value, + message: 'Microsoftdynamicssales task created', + statusCode: 201, + }; + } catch (error) { + throw error; + } + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + const baseURL = `${connection.account_url}/api/data/v9.2/tasks`; + + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced microsoftdynamicssales tasks !`); + return { + data: resp?.data?.value, + message: 'Microsoftdynamicssales tasks retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/crm/task/services/microsoftdynamicssales/mappers.ts b/packages/api/src/crm/task/services/microsoftdynamicssales/mappers.ts new file mode 100644 index 000000000..794a2e2fd --- /dev/null +++ b/packages/api/src/crm/task/services/microsoftdynamicssales/mappers.ts @@ -0,0 +1,165 @@ +import { MicrosoftdynamicssalesTaskInput, MicrosoftdynamicssalesTaskOutput } 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 MicrosoftdynamicssalesTaskMapper implements ITaskMapper { + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'task', 'microsoftdynamicssales', this); + } + + async desunify( + source: UnifiedCrmTaskInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: MicrosoftdynamicssalesTaskInput = { + subject: source.subject, + description: source.content + }; + + if (source.status) { + result.statecode = source.status === "COMPLETED" ? 1 : 0; + } + + if (source.due_date) { + result.scheduledend = source.due_date.toUTCString(); + } + + if (source.finished_date) { + result.actualend = source.finished_date.toUTCString(); + } + + // User have to provide either company_id or deal_id which will be associated with current task + if (source.company_id) { + const id = await this.utils.getRemoteIdFromCompanyUuid(source.company_id); + result["regardingobjectid_account@odata.bind"] = `/accounts(${id})`; + } + + else if (source.deal_id) { + const id = await this.utils.getRemoteIdFromDealUuid(source.deal_id); + result["regardingobjectid_opportunity@odata.bind"] = `/opportunities(${id})`; + } + + return result; + } + + async unify( + source: MicrosoftdynamicssalesTaskOutput | MicrosoftdynamicssalesTaskOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + 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: MicrosoftdynamicssalesTaskOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = task[mapping.remote_id]; + } + } + + let opts: any = {}; + + if (task.statecode) { + opts = { + ...opts, + status: task.statecode === 1 ? "COMPLETED" : "PENDING" + } + } + + if (task.scheduledend) { + opts = { + ...opts, + due_date: new Date(task.scheduledend) + } + } + + if (task.actualend) { + opts = { + ...opts, + finished_date: new Date(task.actualend) + } + } + + + // Here the task either associated to deal or company + if (task._regardingobjectid_value) { + const company_id = await this.utils.getCompanyUuidFromRemoteId( + task._regardingobjectid_value, + connectionId, + ); + + if (company_id) { + opts = { + ...opts, + company_id: company_id, + } + } + + const deal_id = await this.utils.getDealUuidFromRemoteId( + task._regardingobjectid_value, + connectionId, + ) + + if (deal_id) { + opts = { + ...opts, + deal_id: deal_id, + } + } + } + + if (task._createdby_value) { + const user_id = await this.utils.getUserUuidFromRemoteId( + task._createdby_value, + connectionId + ) + + opts = { + ...opts, + user_id: user_id + } + } + + return { + remote_id: task.activityid, + subject: task.subject ?? '', + remote_data: task, + content: task.description ?? '', + field_mappings, + ...opts, + }; + } +} diff --git a/packages/api/src/crm/task/services/microsoftdynamicssales/types.ts b/packages/api/src/crm/task/services/microsoftdynamicssales/types.ts new file mode 100644 index 000000000..374c8208e --- /dev/null +++ b/packages/api/src/crm/task/services/microsoftdynamicssales/types.ts @@ -0,0 +1,70 @@ +interface MicrosoftdynamicssalesTask { + activityadditionalparams: string; + activityid: string; + activitytypecode: string; + actualdurationminutes: number; + actualend: string; + actualstart: string; + category: string; + community: number; + createdon: string; + crmtaskassigneduniqueid: string; + deliverylastattemptedon: string; + deliveryprioritycode: number; + description: string; + descriptionblobid: string; + descriptionblobid_name: string; + exchangeitemid: string; + exchangerate: number; + exchangeweblink: string; + formattedscheduledend: string; + formattedscheduledstart: string; + importsequencenumber: number; + instancetypecode: number; + isbilled: boolean; + ismapiprivate: boolean; + isregularactivity: boolean; + isworkflowcreated: boolean; + lastonholdtime: string; + leftvoicemail: boolean; + modifiedon: string; + onholdtime: number; + overriddencreatedon: string; + percentcomplete: number; + postponeactivityprocessinguntil: string; + prioritycode: number; + processid: string; + scheduleddurationminutes: number; + scheduledend: string; + scheduledstart: string; + senton: string; + seriesid: string; + sortdate: string; + stageid: string; + statecode: number; + statuscode: number; + subcategory: string; + subject: string; + subscriptionid: string; + timezoneruleversionnumber: number; + traversedpath: string; + utcconversiontimezonecode: number; + versionnumber: number; + _createdby_value: string; + _createdonbehalfby_value: string; + _modifiedby_value: string; + _modifiedonbehalfby_value: string; + _ownerid_value: string; + _owningbusinessunit_value: string; + _owningteam_value: string; + _owninguser_value: string; + _regardingobjectid_value: string; + _sendermailboxid_value: string; + _slaid_value: string; + _slainvokedid_value: string; + _transactioncurrencyid_value: string; + [key: string]: any; +} + +export type MicrosoftdynamicssalesTaskInput = Partial; +export type MicrosoftdynamicssalesTaskOutput = MicrosoftdynamicssalesTaskInput; \ No newline at end of file diff --git a/packages/api/src/crm/task/task.module.ts b/packages/api/src/crm/task/task.module.ts index c790a2e36..63800e06a 100644 --- a/packages/api/src/crm/task/task.module.ts +++ b/packages/api/src/crm/task/task.module.ts @@ -1,3 +1,5 @@ +import { MicrosoftdynamicssalesTaskMapper } from './services/microsoftdynamicssales/mappers'; +import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; @@ -43,6 +45,8 @@ import { TaskController } from './task.controller'; HubspotTaskMapper, CloseTaskMapper, AttioTaskMapper, + MicrosoftdynamicssalesService, + MicrosoftdynamicssalesTaskMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/api/src/crm/user/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/user/services/microsoftdynamicssales/index.ts new file mode 100644 index 000000000..83b1170b6 --- /dev/null +++ b/packages/api/src/crm/user/services/microsoftdynamicssales/index.ts @@ -0,0 +1,67 @@ +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 { IUserService } from '@crm/user/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { MicrosoftdynamicssalesUserOutput } from './types'; + +@Injectable() +export class MicrosoftdynamicssalesService implements IUserService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.user.toUpperCase() + ':' + MicrosoftdynamicssalesService.name, + ); + this.registry.registerService('microsoftdynamicssales', this); + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + this.logger.log("==========="); + this.logger.log(this.cryptoService.decrypt( + connection.access_token, + )); + this.logger.log("==========="); + + + const baseURL = `${connection.account_url}/api/data/v9.2/systemusers`; + const resp = await axios.get(baseURL, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + + this.logger.log(`Synced microsoftdynamicssales users ! : ${JSON.stringify(resp.data.value)}`); + + return { + data: resp.data.value, + message: 'Microsoftdynamicssales users retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/crm/user/services/microsoftdynamicssales/mappers.ts b/packages/api/src/crm/user/services/microsoftdynamicssales/mappers.ts new file mode 100644 index 000000000..fa88aa44b --- /dev/null +++ b/packages/api/src/crm/user/services/microsoftdynamicssales/mappers.ts @@ -0,0 +1,71 @@ +import { MicrosoftdynamicssalesUserOutput } from './types'; +import { + UnifiedCrmUserInput, + UnifiedCrmUserOutput, +} from '@crm/user/types/model.unified'; +import { IUserMapper } from '@crm/user/types'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { Injectable } from '@nestjs/common'; +import { Utils } from '@crm/@lib/@utils'; + +@Injectable() +export class MicrosoftdynamicssalesUserMapper implements IUserMapper { + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'user', 'microsoftdynamicssales', this); + } + desunify( + source: UnifiedCrmUserInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ) { + return; + } + + unify( + source: MicrosoftdynamicssalesUserOutput | MicrosoftdynamicssalesUserOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return this.mapSingleUserToUnified( + source, + connectionId, + customFieldMappings, + ); + } + // Handling array of MicrosoftdynamicssalesUserOutput + return Promise.all( + source.map((user) => + this.mapSingleUserToUnified(user, connectionId, customFieldMappings), + ), + ); + } + + private async mapSingleUserToUnified( + user: MicrosoftdynamicssalesUserOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = user[mapping.remote_id]; + } + } + return { + remote_id: user.ownerid, + remote_data: user, + name: `${user.firstname} ${user.lastname}`, + email: user.personalemailaddress, + field_mappings, + }; + } +} diff --git a/packages/api/src/crm/user/services/microsoftdynamicssales/types.ts b/packages/api/src/crm/user/services/microsoftdynamicssales/types.ts new file mode 100644 index 000000000..94437e339 --- /dev/null +++ b/packages/api/src/crm/user/services/microsoftdynamicssales/types.ts @@ -0,0 +1,123 @@ +interface MicrosoftdynamicssalesUser { + accessmode: number; // Edm.Int32 + address1_addressid: string; // Edm.Guid + address1_addresstypecode: number; // Edm.Int32 + address1_city: string; // Edm.String + address1_composite: string; // Edm.String + address1_country: string; // Edm.String + address1_county: string; // Edm.String + address1_fax: string; // Edm.String + address1_latitude: number; // Edm.Double + address1_line1: string; // Edm.String + address1_line2: string; // Edm.String + address1_line3: string; // Edm.String + address1_longitude: number; // Edm.Double + address1_name: string; // Edm.String + address1_postalcode: string; // Edm.String + address1_postofficebox: string; // Edm.String + address1_shippingmethodcode: number; // Edm.Int32 + address1_stateorprovince: string; // Edm.String + address1_telephone1: string; // Edm.String + address1_telephone2: string; // Edm.String + address1_telephone3: string; // Edm.String + address1_upszone: string; // Edm.String + address1_utcoffset: number; // Edm.Int32 + address2_addressid: string; // Edm.Guid + address2_addresstypecode: number; // Edm.Int32 + address2_city: string; // Edm.String + address2_composite: string; // Edm.String + address2_country: string; // Edm.String + address2_county: string; // Edm.String + address2_fax: string; // Edm.String + address2_latitude: number; // Edm.Double + address2_line1: string; // Edm.String + address2_line2: string; // Edm.String + address2_line3: string; // Edm.String + address2_longitude: number; // Edm.Double + address2_name: string; // Edm.String + address2_postalcode: string; // Edm.String + address2_postofficebox: string; // Edm.String + address2_shippingmethodcode: number; // Edm.Int32 + address2_stateorprovince: string; // Edm.String + address2_telephone1: string; // Edm.String + address2_telephone2: string; // Edm.String + address2_telephone3: string; // Edm.String + address2_upszone: string; // Edm.String + address2_utcoffset: number; // Edm.Int32 + applicationid: string; // Edm.Guid + applicationiduri: string; // Edm.String + azureactivedirectoryobjectid: string; // Edm.Guid + azuredeletedon: string; // Edm.DateTimeOffset + azurestate: number; // Edm.Int32 + caltype: number; // Edm.Int32 + createdon: string; // Edm.DateTimeOffset + defaultfilterspopulated: boolean; // Edm.Boolean + defaultodbfoldername: string; // Edm.String + deletedstate: number; // Edm.Int32 + disabledreason: string; // Edm.String + displayinserviceviews: boolean; // Edm.Boolean + domainname: string; // Edm.String + emailrouteraccessapproval: number; // Edm.Int32 + employeeid: string; // Edm.String + entityimage: string; // Edm.Binary + entityimage_timestamp: number; // Edm.Int64 + entityimage_url: string; // Edm.String + entityimageid: string; // Edm.Guid + exchangerate: number; // Edm.Decimal + firstname: string; // Edm.String + fullname: string; // Edm.String + governmentid: string; // Edm.String + homephone: string; // Edm.String + identityid: number; // Edm.Int32 + importsequencenumber: number; // Edm.Int32 + incomingemaildeliverymethod: number; // Edm.Int32 + internalemailaddress: string; // Edm.String + invitestatuscode: number; // Edm.Int32 + isdisabled: boolean; // Edm.Boolean + isemailaddressapprovedbyo365admin: boolean; // Edm.Boolean + isintegrationuser: boolean; // Edm.Boolean + islicensed: boolean; // Edm.Boolean + issyncwithdirectory: boolean; // Edm.Boolean + jobtitle: string; // Edm.String + lastname: string; // Edm.String + middlename: string; // Edm.String + mobilealertemail: string; // Edm.String + mobilephone: string; // Edm.String + modifiedon: string; // Edm.DateTimeOffset + nickname: string; // Edm.String + organizationid: string; // Edm.Guid + outgoingemaildeliverymethod: number; // Edm.Int32 + overriddencreatedon: string; // Edm.DateTimeOffset + ownerid: string; // Edm.Guid + passporthi: number; // Edm.Int32 + passportlo: number; // Edm.Int32 + personalemailaddress: string; // Edm.String + photourl: string; // Edm.String + preferredaddresscode: number; // Edm.Int32 + preferredemailcode: number; // Edm.Int32 + preferredphonecode: number; // Edm.Int32 + processid: string; // Edm.Guid + salutation: string; // Edm.String + setupuser: boolean; // Edm.Boolean + sharepointemailaddress: string; // Edm.String + skills: string; // Edm.String + stageid: string; // Edm.Guid + systemuserid: string; // Edm.Guid + timezoneruleversionnumber: number; // Edm.Int32 + title: string; // Edm.String + traversedpath: string; // Edm.String + userlicensetype: number; // Edm.Int32 + userpuid: string; // Edm.String + utcconversiontimezonecode: number; // Edm.Int32 + versionnumber: number; // Edm.Int64 + windowsliveid: string; // Edm.String + yammeremailaddress: string; // Edm.String + yammeruserid: string; // Edm.String + yomifirstname: string; // Edm.String + yomifullname: string; // Edm.String + yomilastname: string; // Edm.String + yomimiddlename: string; // Edm.String +} + +export type MicrosoftdynamicssalesUserInput = Partial; +export type MicrosoftdynamicssalesUserOutput = MicrosoftdynamicssalesUserInput; \ No newline at end of file diff --git a/packages/api/src/crm/user/user.module.ts b/packages/api/src/crm/user/user.module.ts index 0957b622f..9ca93bd02 100644 --- a/packages/api/src/crm/user/user.module.ts +++ b/packages/api/src/crm/user/user.module.ts @@ -1,3 +1,5 @@ +import { MicrosoftdynamicssalesUserMapper } from './services/microsoftdynamicssales/mappers'; +import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; @@ -43,6 +45,8 @@ import { UserController } from './user.controller'; HubspotUserMapper, AttioUserMapper, CloseUserMapper, + MicrosoftdynamicssalesService, + MicrosoftdynamicssalesUserMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/shared/src/authUrl.ts b/packages/shared/src/authUrl.ts index 5a7ae7d8a..04f048dad 100644 --- a/packages/shared/src/authUrl.ts +++ b/packages/shared/src/authUrl.ts @@ -126,13 +126,13 @@ const handleOAuth2Url = async ({ ...dyn }: HandleOAuth2Url) => { const type = providerToType(providerName, vertical, authStrategy); - + const response = await fetch(`${apiUrl}/connection_strategies/getCredentials?projectId=${projectId}&type=${type}`); const data = await response.json() as OAuth2AuthData; const clientId = data.CLIENT_ID; if (!clientId) throw new ReferenceError(`No client id for type ${type}`); - + const scopes = data.SCOPE; const { urls: { authBaseUrl: baseUrl } } = config; if (!baseUrl) throw new ReferenceError(`No authBaseUrl found for type ${type}`); diff --git a/packages/shared/src/connectors/enum.ts b/packages/shared/src/connectors/enum.ts index a66547391..0679963b8 100644 --- a/packages/shared/src/connectors/enum.ts +++ b/packages/shared/src/connectors/enum.ts @@ -4,7 +4,8 @@ export enum CrmConnectors { HUBSPOT = 'hubspot', PIPEDRIVE = 'pipedrive', ATTIO = 'attio', - CLOSE = 'close' + CLOSE = 'close', + MICROSOFTDYNAMICSSALES = 'microsoftdynamicssales' } export enum EcommerceConnectors { diff --git a/packages/shared/src/connectors/index.ts b/packages/shared/src/connectors/index.ts index 105ca557a..c99c194d7 100644 --- a/packages/shared/src/connectors/index.ts +++ b/packages/shared/src/connectors/index.ts @@ -1,4 +1,4 @@ -export const CRM_PROVIDERS = ['zoho', 'zendesk', 'hubspot', 'pipedrive', 'attio', 'close']; +export const CRM_PROVIDERS = ['zoho', 'zendesk', 'hubspot', 'pipedrive', 'attio', 'close', 'microsoftdynamicssales']; export const HRIS_PROVIDERS = []; export const ATS_PROVIDERS = ['ashby']; export const ACCOUNTING_PROVIDERS = []; diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index 873590f9c..3d2251a14 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -3090,4 +3090,4 @@ export const CONNECTORS_METADATA: ProvidersConfig = { } }, } -}; +}; \ No newline at end of file From b32940f1cbe7cba43283595589b89832797f6cc8 Mon Sep 17 00:00:00 2001 From: nael Date: Mon, 26 Aug 2024 22:02:27 +0200 Subject: [PATCH 20/49] :bug: Fix verifyEvent return response type --- .../panora-webhooks/webhook.controller.ts | 5 ++- .../panora-webhooks/webhook.processor.ts | 36 ++++++++++------- .../panora-webhooks/webhook.service.ts | 7 ---- .../ats/services/ashby/ashby.service.ts | 4 +- .../@core/utils/dtos/openapi.respone.dto.ts | 2 +- packages/api/src/crm/@lib/@utils/index.ts | 1 + .../src/crm/company/services/hubspot/index.ts | 2 +- .../src/crm/contact/services/hubspot/index.ts | 2 +- .../api/src/crm/contact/sync/sync.service.ts | 40 +++++++++---------- .../src/crm/deal/services/hubspot/index.ts | 2 +- .../api/src/crm/deal/sync/sync.service.ts | 39 +++++++++--------- .../crm/engagement/services/hubspot/index.ts | 6 +-- .../src/crm/note/services/hubspot/index.ts | 4 +- .../src/crm/task/services/hubspot/index.ts | 19 +++++---- .../src/crm/user/services/hubspot/index.ts | 17 ++++---- packages/api/swagger/swagger-spec.yaml | 4 +- packages/shared/src/authUrl.ts | 2 +- packages/shared/src/connectors/metadata.ts | 2 +- 18 files changed, 97 insertions(+), 97 deletions(-) diff --git a/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.controller.ts b/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.controller.ts index 38b311ade..5cd8b1857 100644 --- a/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.controller.ts +++ b/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.controller.ts @@ -176,10 +176,13 @@ export class WebhookController { @Post('verifyEvent') async verifyPayloadSignature(@Body() data: SignatureVerificationDto) { const { payload, signature, secret } = data; - return await this.webhookService.verifyPayloadSignature( + const resp = await this.webhookService.verifyPayloadSignature( payload, signature, secret, ); + return { + data: resp, + }; } } diff --git a/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.processor.ts b/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.processor.ts index cc43e3f23..741209b02 100644 --- a/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.processor.ts +++ b/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.processor.ts @@ -6,6 +6,7 @@ import { Job } from 'bull'; import { v4 as uuidv4 } from 'uuid'; import { WebhookService } from './webhook.service'; import { Queues } from '@@core/@core-services/queues/types'; +import { response } from 'express'; @Processor(Queues.PANORA_WEBHOOKS_SENDER) export class WebhookProcessor { @@ -60,19 +61,24 @@ export class WebhookProcessor { deliveryAttempt.webhooks_payloads.data, deliveryAttempt.webhook_endpoints.secret, ); - const response = await axios.post( - deliveryAttempt.webhook_endpoints.url, - { - id_event: deliveryAttempt.id_event, - data: deliveryAttempt.webhooks_payloads.data, - type: event.type, - }, - { - headers: { - 'Panora-Signature': signature, + let response; + try { + response = await axios.post( + deliveryAttempt.webhook_endpoints.url, + { + id_event: deliveryAttempt.id_event, + data: deliveryAttempt.webhooks_payloads.data, + type: event.type, }, - }, - ); + { + headers: { + 'Panora-Signature': signature, + }, + }, + ); + } catch (error) { + throw error; + } // Populate the webhooks_responses table await this.prisma.webhooks_reponses.create({ @@ -92,7 +98,7 @@ export class WebhookProcessor { this.logger.log('Webhook delivered !'); } catch (error) { // If the POST request fails, set a next retry time and reinsert the job in the queue - const nextRetry = new Date(); + /*const nextRetry = new Date(); nextRetry.setSeconds(nextRetry.getSeconds() + 60); // Retry after 60 seconds await this.prisma.webhook_delivery_attempts.update({ @@ -104,10 +110,10 @@ export class WebhookProcessor { }); //re-insert the webhook in the queue - await this.webhookService.dispatchFailedWebhook(id_webhook_delivery); + await this.webhookService.dispatchFailedWebhook(id_webhook_delivery);*/ this.logger.log( - 'Webhook delivery failed. Job reinserted in the queue for retry.', + `Webhook delivery failed. Job reinserted in the queue for retry : ${error}`, ); } } else { diff --git a/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.service.ts b/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.service.ts index 323e074d4..e025ef298 100644 --- a/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.service.ts +++ b/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.service.ts @@ -20,7 +20,6 @@ export class WebhookService { generateSignature(payload: any, secret: string): string { try { - this.logger.log('PAYLOAD IS ' + JSON.stringify(payload)); return createHmac('sha256', secret) .update(JSON.stringify(payload)) .digest('hex'); @@ -295,12 +294,6 @@ export class WebhookService { secret: string, ) { try { - this.logger.log( - '(verify) PAYLOAD IS ' + - JSON.stringify(payload) + - ' data from payload is ' + - JSON.stringify(payload.data), - ); const expected = this.generateSignature(payload.data, secret); if (expected !== signature) { throw new WebhooksError({ diff --git a/packages/api/src/@core/connections/ats/services/ashby/ashby.service.ts b/packages/api/src/@core/connections/ats/services/ashby/ashby.service.ts index 77ec8a559..8fa6a0b80 100644 --- a/packages/api/src/@core/connections/ats/services/ashby/ashby.service.ts +++ b/packages/api/src/@core/connections/ats/services/ashby/ashby.service.ts @@ -93,7 +93,7 @@ export class AshbyConnectionService extends AbstractBaseConnectionService { id_connection: isNotUnique.id_connection, }, data: { - access_token: this.cryptoService.encrypt(body.username), + access_token: this.cryptoService.encrypt(body.api_key), account_url: CONNECTORS_METADATA['ats']['ashby'].urls .apiUrl as string, status: 'valid', @@ -110,7 +110,7 @@ export class AshbyConnectionService extends AbstractBaseConnectionService { token_type: 'basic', account_url: CONNECTORS_METADATA['ats']['ashby'].urls .apiUrl as string, - access_token: this.cryptoService.encrypt(body.username), + access_token: this.cryptoService.encrypt(body.api_key), status: 'valid', created_at: new Date(), projects: { diff --git a/packages/api/src/@core/utils/dtos/openapi.respone.dto.ts b/packages/api/src/@core/utils/dtos/openapi.respone.dto.ts index ef4b0103e..49a604708 100644 --- a/packages/api/src/@core/utils/dtos/openapi.respone.dto.ts +++ b/packages/api/src/@core/utils/dtos/openapi.respone.dto.ts @@ -46,7 +46,7 @@ export const ApiPostCustomResponse = >( export const ApiPostGenericJson = (description: string) => { return applyDecorators( - ApiOkResponse({ + ApiCreatedResponse({ schema: { properties: { data: { diff --git a/packages/api/src/crm/@lib/@utils/index.ts b/packages/api/src/crm/@lib/@utils/index.ts index 7f7444c9e..383083625 100644 --- a/packages/api/src/crm/@lib/@utils/index.ts +++ b/packages/api/src/crm/@lib/@utils/index.ts @@ -14,6 +14,7 @@ export class Utils { if (email_addresses) { normalizedEmails = email_addresses.map((email) => ({ ...email, + email_address: email.email_address ?? '', owner_type: email.owner_type ? email.owner_type : '', created_at: new Date(), modified_at: new Date(), diff --git a/packages/api/src/crm/company/services/hubspot/index.ts b/packages/api/src/crm/company/services/hubspot/index.ts index 4ff80ba66..e5feb1919 100644 --- a/packages/api/src/crm/company/services/hubspot/index.ts +++ b/packages/api/src/crm/company/services/hubspot/index.ts @@ -44,7 +44,7 @@ export class HubspotService implements ICompanyService { properties: companyData, }; const resp = await axios.post( - `${connection.account_url}/crm/v3/objects/companies`, + `${connection.account_url}/v3/objects/companies`, JSON.stringify(dataBody), { headers: { diff --git a/packages/api/src/crm/contact/services/hubspot/index.ts b/packages/api/src/crm/contact/services/hubspot/index.ts index a0c32a178..02fdcbbe6 100644 --- a/packages/api/src/crm/contact/services/hubspot/index.ts +++ b/packages/api/src/crm/contact/services/hubspot/index.ts @@ -45,7 +45,7 @@ export class HubspotService implements IContactService { properties: contactData, }; const resp = await axios.post( - `${connection.account_url}/crm/v3/objects/contacts`, + `${connection.account_url}/v3/objects/contacts`, JSON.stringify(dataBody), { headers: { diff --git a/packages/api/src/crm/contact/sync/sync.service.ts b/packages/api/src/crm/contact/sync/sync.service.ts index a64e78ef3..0f388df28 100644 --- a/packages/api/src/crm/contact/sync/sync.service.ts +++ b/packages/api/src/crm/contact/sync/sync.service.ts @@ -1,24 +1,22 @@ -import { FieldMappingService } from '@@core/field-mapping/field-mapping.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 { BullQueueService } from '@@core/@core-services/queues/shared.service'; +import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; +import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; +import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; +import { OriginalContactOutput } from '@@core/utils/types/original/original.crm'; +import { Utils } from '@crm/@lib/@utils'; import { UnifiedCrmContactOutput } from '@crm/contact/types/model.unified'; -import { CrmObject } from '@crm/@lib/@types'; import { Injectable, OnModuleInit } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; -import { v4 as uuidv4 } from 'uuid'; +import { CRM_PROVIDERS } from '@panora/shared'; import { crm_contacts as CrmContact } from '@prisma/client'; -import { IContactService } from '../types'; -import { OriginalContactOutput } from '@@core/utils/types/original/original.crm'; +import { v4 as uuidv4 } from 'uuid'; import { ServiceRegistry } from '../services/registry.service'; -import { CRM_PROVIDERS } from '@panora/shared'; -import { Utils } from '@crm/@lib/@utils'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; -import { BullQueueService } from '@@core/@core-services/queues/shared.service'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { IContactService } from '../types'; @Injectable() export class SyncService implements OnModuleInit, IBaseSync { @@ -58,12 +56,12 @@ export class SyncService implements OnModuleInit, IBaseSync { const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -113,7 +111,9 @@ export class SyncService implements OnModuleInit, IBaseSync { const service: IContactService = this.serviceRegistry.getService(integrationId); if (!service) { - this.logger.log(`No service found in {vertical:crm, commonObject: contact} for integration ID: ${integrationId}`); + this.logger.log( + `No service found in {vertical:crm, commonObject: contact} for integration ID: ${integrationId}`, + ); return; } diff --git a/packages/api/src/crm/deal/services/hubspot/index.ts b/packages/api/src/crm/deal/services/hubspot/index.ts index 5f2ba38ee..237c15b6f 100644 --- a/packages/api/src/crm/deal/services/hubspot/index.ts +++ b/packages/api/src/crm/deal/services/hubspot/index.ts @@ -82,7 +82,7 @@ export class HubspotService implements IDealService { const commonPropertyNames = Object.keys(commonDealHubspotProperties); const allProperties = [...commonPropertyNames, ...custom_properties]; - const baseURL = `${connection.account_url}/crm/v3/objects/deals`; + const baseURL = `${connection.account_url}/v3/objects/deals`; const queryString = allProperties .map((prop) => `properties=${encodeURIComponent(prop)}`) diff --git a/packages/api/src/crm/deal/sync/sync.service.ts b/packages/api/src/crm/deal/sync/sync.service.ts index 24a2c8550..95e80032f 100644 --- a/packages/api/src/crm/deal/sync/sync.service.ts +++ b/packages/api/src/crm/deal/sync/sync.service.ts @@ -1,20 +1,19 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; import { LoggerService } from '@@core/@core-services/logger/logger.service'; import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; +import { BullQueueService } from '@@core/@core-services/queues/shared.service'; +import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; +import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; +import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; +import { OriginalDealOutput } from '@@core/utils/types/original/original.crm'; +import { Injectable, OnModuleInit } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; -import { ApiResponse } from '@@core/utils/types'; +import { CRM_PROVIDERS } from '@panora/shared'; +import { crm_deals as CrmDeal } from '@prisma/client'; import { v4 as uuidv4 } from 'uuid'; -import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; import { ServiceRegistry } from '../services/registry.service'; -import { UnifiedCrmDealOutput } from '../types/model.unified'; import { IDealService } from '../types'; -import { OriginalDealOutput } from '@@core/utils/types/original/original.crm'; -import { crm_deals as CrmDeal } from '@prisma/client'; -import { CRM_PROVIDERS } from '@panora/shared'; -import { CoreSyncRegistry } from '@@core/@core-services/registries/core-sync.registry'; -import { BullQueueService } from '@@core/@core-services/queues/shared.service'; -import { IBaseSync, SyncLinkedUserType } from '@@core/utils/types/interface'; -import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; +import { UnifiedCrmDealOutput } from '../types/model.unified'; @Injectable() export class SyncService implements OnModuleInit, IBaseSync { @@ -48,12 +47,12 @@ export class SyncService implements OnModuleInit, IBaseSync { this.logger.log(`Syncing deals....`); const users = user_id ? [ - await this.prisma.users.findUnique({ - where: { - id_user: user_id, - }, - }), - ] + await this.prisma.users.findUnique({ + where: { + id_user: user_id, + }, + }), + ] : await this.prisma.users.findMany(); if (users && users.length > 0) { for (const user of users) { @@ -103,7 +102,9 @@ export class SyncService implements OnModuleInit, IBaseSync { const service: IDealService = this.serviceRegistry.getService(integrationId); if (!service) { - this.logger.log(`No service found in {vertical:crm, commonObject: deal} for integration ID: ${integrationId}`); + this.logger.log( + `No service found in {vertical:crm, commonObject: deal} for integration ID: ${integrationId}`, + ); return; } @@ -141,7 +142,7 @@ export class SyncService implements OnModuleInit, IBaseSync { const baseData: any = { name: deal.name ?? null, description: deal.description ?? null, - amount: deal.amount ?? null, + amount: deal.amount ?? 0, id_crm_user: deal.user_id ?? null, id_crm_deals_stage: deal.stage_id ?? null, id_crm_company: deal.company_id ?? null, diff --git a/packages/api/src/crm/engagement/services/hubspot/index.ts b/packages/api/src/crm/engagement/services/hubspot/index.ts index 5673e86db..91aa6bff4 100644 --- a/packages/api/src/crm/engagement/services/hubspot/index.ts +++ b/packages/api/src/crm/engagement/services/hubspot/index.ts @@ -81,7 +81,7 @@ export class HubspotService implements IEngagementService { properties: engagementData, }; const resp = await axios.post( - `${connection.account_url}/crm/v3/objects/calls`, + `${connection.account_url}/v3/objects/calls`, JSON.stringify(dataBody), { headers: { @@ -118,7 +118,7 @@ export class HubspotService implements IEngagementService { properties: engagementData, }; const resp = await axios.post( - `${connection.account_url}/crm/v3/objects/meetings`, + `${connection.account_url}/v3/objects/meetings`, JSON.stringify(dataBody), { headers: { @@ -155,7 +155,7 @@ export class HubspotService implements IEngagementService { properties: engagementData, }; const resp = await axios.post( - `${connection.account_url}/crm/v3/objects/emails`, + `${connection.account_url}/v3/objects/emails`, JSON.stringify(dataBody), { headers: { diff --git a/packages/api/src/crm/note/services/hubspot/index.ts b/packages/api/src/crm/note/services/hubspot/index.ts index 71e3e332a..3028e4e2a 100644 --- a/packages/api/src/crm/note/services/hubspot/index.ts +++ b/packages/api/src/crm/note/services/hubspot/index.ts @@ -53,7 +53,7 @@ export class HubspotService implements INoteService { ); const final_resp = await axios.get( - `${connection.account_url}/objects/notes/${resp.data.id}?properties=hs_note_body&associations=deal,contact,company`, + `${connection.account_url}/v3/objects/notes/${resp.data.id}?properties=hs_note_body&associations=deal,contact,company`, { headers: { 'Content-Type': 'application/json', @@ -87,7 +87,7 @@ export class HubspotService implements INoteService { const commonPropertyNames = Object.keys(commonNoteHubspotProperties); const allProperties = [...commonPropertyNames, ...custom_properties]; - const baseURL = `${connection.account_url}/objects/notes`; + const baseURL = `${connection.account_url}/v3/objects/notes`; const queryString = allProperties .map((prop) => `properties=${encodeURIComponent(prop)}`) diff --git a/packages/api/src/crm/task/services/hubspot/index.ts b/packages/api/src/crm/task/services/hubspot/index.ts index 4b8cad070..88b8fcb18 100644 --- a/packages/api/src/crm/task/services/hubspot/index.ts +++ b/packages/api/src/crm/task/services/hubspot/index.ts @@ -1,19 +1,18 @@ -import { Injectable } from '@nestjs/common'; -import { ITaskService } from '@crm/task/types'; +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 { HubspotTaskInput, HubspotTaskOutput, commonTaskHubspotProperties, } from './types'; -import axios from 'axios'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; -import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; -import { ApiResponse } from '@@core/utils/types'; -import { ServiceRegistry } from '../registry.service'; -import { SyncParam } from '@@core/utils/types/interface'; @Injectable() export class HubspotService implements ITaskService { diff --git a/packages/api/src/crm/user/services/hubspot/index.ts b/packages/api/src/crm/user/services/hubspot/index.ts index a86a7ec20..0e35a4f77 100644 --- a/packages/api/src/crm/user/services/hubspot/index.ts +++ b/packages/api/src/crm/user/services/hubspot/index.ts @@ -1,15 +1,14 @@ -import { Injectable } from '@nestjs/common'; -import { IUserService } from '@crm/user/types'; -import { CrmObject } from '@crm/@lib/@types'; -import { HubspotUserOutput, commonUserHubspotProperties } from './types'; -import axios from 'axios'; -import { PrismaService } from '@@core/@core-services/prisma/prisma.service'; -import { LoggerService } from '@@core/@core-services/logger/logger.service'; -import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; 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 { ServiceRegistry } from '../registry.service'; import { SyncParam } from '@@core/utils/types/interface'; +import { CrmObject } from '@crm/@lib/@types'; +import { IUserService } from '@crm/user/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { HubspotUserOutput, commonUserHubspotProperties } from './types'; @Injectable() export class HubspotService implements IUserService { diff --git a/packages/api/swagger/swagger-spec.yaml b/packages/api/swagger/swagger-spec.yaml index 17b806d81..722e6de24 100644 --- a/packages/api/swagger/swagger-spec.yaml +++ b/packages/api/swagger/swagger-spec.yaml @@ -151,7 +151,7 @@ paths: schema: $ref: '#/components/schemas/SignatureVerificationDto' responses: - '200': + '201': description: '' content: application/json: @@ -161,8 +161,6 @@ paths: type: object additionalProperties: true description: Dynamic event payload - '201': - description: '' tags: *ref_0 x-speakeasy-group: webhooks /ticketing/tickets: diff --git a/packages/shared/src/authUrl.ts b/packages/shared/src/authUrl.ts index 5a7ae7d8a..cc719e90c 100644 --- a/packages/shared/src/authUrl.ts +++ b/packages/shared/src/authUrl.ts @@ -48,7 +48,7 @@ export const constructAuthUrl = async ({ let baseRedirectURL = apiUrl; if (config.options?.local_redirect_uri_in_https && redirectUriIngress?.status) { baseRedirectURL = redirectUriIngress.value!; - } + } const encodedRedirectUrl = encodeURIComponent(`${baseRedirectURL}/connections/oauth/callback`); let state = encodeURIComponent(JSON.stringify({ projectId, linkedUserId, providerName, vertical, returnUrl })); diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index 873590f9c..b5baeafac 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -1259,7 +1259,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { primaryColor: '#4a3ead', authStrategy: { strategy: AuthStrategy.basic, - properties: ['username'] + properties: ['api_key'] } }, 'bamboohr': { From d2e3cc3de695c6a27c4a8e2762c328c9db624ed0 Mon Sep 17 00:00:00 2001 From: nael Date: Tue, 27 Aug 2024 01:26:38 +0200 Subject: [PATCH 21/49] :sparkles: Update verifyEvent return response --- .../webhooks/panora-webhooks/webhook.controller.ts | 4 +--- .../api/src/@core/utils/dtos/openapi.respone.dto.ts | 10 +++------- packages/api/swagger/swagger-spec.yaml | 8 +++----- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.controller.ts b/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.controller.ts index 5cd8b1857..1cabce443 100644 --- a/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.controller.ts +++ b/packages/api/src/@core/@core-services/webhooks/panora-webhooks/webhook.controller.ts @@ -181,8 +181,6 @@ export class WebhookController { signature, secret, ); - return { - data: resp, - }; + return resp; } } diff --git a/packages/api/src/@core/utils/dtos/openapi.respone.dto.ts b/packages/api/src/@core/utils/dtos/openapi.respone.dto.ts index 49a604708..bc3151d25 100644 --- a/packages/api/src/@core/utils/dtos/openapi.respone.dto.ts +++ b/packages/api/src/@core/utils/dtos/openapi.respone.dto.ts @@ -48,13 +48,9 @@ export const ApiPostGenericJson = (description: string) => { return applyDecorators( ApiCreatedResponse({ schema: { - properties: { - data: { - type: 'object', - additionalProperties: true, - description: description, - }, - }, + type: 'object', + additionalProperties: true, + description: description, }, }), ); diff --git a/packages/api/swagger/swagger-spec.yaml b/packages/api/swagger/swagger-spec.yaml index 722e6de24..8abc627f7 100644 --- a/packages/api/swagger/swagger-spec.yaml +++ b/packages/api/swagger/swagger-spec.yaml @@ -156,11 +156,9 @@ paths: content: application/json: schema: - properties: - data: - type: object - additionalProperties: true - description: Dynamic event payload + type: object + additionalProperties: true + description: Dynamic event payload tags: *ref_0 x-speakeasy-group: webhooks /ticketing/tickets: From 1b143b0a8b98a53c103a424612efd1910baca487 Mon Sep 17 00:00:00 2001 From: nael Date: Tue, 27 Aug 2024 08:46:10 +0200 Subject: [PATCH 22/49] :bug: Fix limit query type value --- .../api/src/@core/utils/dtos/query.dto.ts | 3 +- .../accounting/account/types/model.unified.ts | 2 +- .../balancesheet/types/model.unified.ts | 2 +- .../cashflowstatement/types/model.unified.ts | 2 +- .../companyinfo/types/model.unified.ts | 2 +- .../accounting/contact/types/model.unified.ts | 2 +- .../creditnote/types/model.unified.ts | 2 +- .../accounting/expense/types/model.unified.ts | 4 +- .../incomestatement/types/model.unified.ts | 2 +- .../accounting/invoice/types/model.unified.ts | 4 +- .../journalentry/types/model.unified.ts | 4 +- .../accounting/payment/types/model.unified.ts | 2 +- .../purchaseorder/types/model.unified.ts | 4 +- .../transaction/types/model.unified.ts | 4 +- .../vendorcredit/types/model.unified.ts | 2 +- packages/api/src/ats/@lib/@types/index.ts | 8 +- .../src/ats/activity/types/model.unified.ts | 13 +- .../src/ats/attachment/types/model.unified.ts | 4 +- .../api/src/ats/eeocs/types/model.unified.ts | 28 +- .../src/ats/interview/types/model.unified.ts | 4 +- .../api/src/ats/job/types/model.unified.ts | 8 +- .../jobinterviewstage/types/model.unified.ts | 2 +- .../api/src/ats/offer/types/model.unified.ts | 8 +- .../src/ats/scorecard/types/model.unified.ts | 4 +- .../api/src/ats/user/types/model.unified.ts | 8 +- packages/api/src/crm/@lib/@types/index.ts | 10 +- .../src/crm/company/types/model.unified.ts | 2 +- .../api/src/crm/contact/contact.controller.ts | 6 +- .../crm/contact/services/hubspot/mappers.ts | 4 +- .../crm/contact/services/zendesk/mappers.ts | 6 +- .../src/crm/contact/services/zoho/mappers.ts | 4 +- .../src/crm/engagement/types/model.unified.ts | 12 +- .../api/src/crm/task/types/model.unified.ts | 6 +- .../api/src/ecommerce/@lib/@types/index.ts | 4 +- .../ecommerce/order/types/model.unified.ts | 22 +- .../ecommerce/product/types/model.unified.ts | 4 +- .../permission/types/model.unified.ts | 6 +- .../src/hris/bankinfo/types/model.unified.ts | 2 +- .../src/hris/dependent/types/model.unified.ts | 4 +- .../src/hris/employee/types/model.unified.ts | 12 +- .../employerbenefit/types/model.unified.ts | 2 +- .../hris/employment/types/model.unified.ts | 14 +- .../api/src/hris/group/types/model.unified.ts | 2 +- .../src/hris/location/types/model.unified.ts | 4 +- .../hris/payrollrun/types/model.unified.ts | 6 +- .../src/hris/timeoff/types/model.unified.ts | 8 +- .../timeoffbalance/types/model.unified.ts | 4 +- .../collection/types/model.unified.ts | 6 +- .../ticketing/comment/types/model.unified.ts | 6 +- .../ticketing/ticket/types/model.unified.ts | 18 +- packages/api/swagger/swagger-spec.yaml | 3320 +---------------- 51 files changed, 295 insertions(+), 3327 deletions(-) diff --git a/packages/api/src/@core/utils/dtos/query.dto.ts b/packages/api/src/@core/utils/dtos/query.dto.ts index 6a0385e97..2f49e0d5e 100644 --- a/packages/api/src/@core/utils/dtos/query.dto.ts +++ b/packages/api/src/@core/utils/dtos/query.dto.ts @@ -30,7 +30,6 @@ export class QueryDto { @Type(() => Number) @IsOptional() @IsNumber() - @Transform((value) => Number(value)) limit: number = DEFAULT_PAGE_SIZE; @ApiProperty({ @@ -41,7 +40,7 @@ export class QueryDto { description: 'Set to get the number of records after this cursor.', }) @IsOptional() - @Transform((p) => Buffer.from(p.value, 'base64').toString()) + //@Transform((p) => Buffer.from(p.value, 'base64').toString()) @IsUUID() cursor: string; } diff --git a/packages/api/src/accounting/account/types/model.unified.ts b/packages/api/src/accounting/account/types/model.unified.ts index 25a221d9d..b7a1317b6 100644 --- a/packages/api/src/accounting/account/types/model.unified.ts +++ b/packages/api/src/accounting/account/types/model.unified.ts @@ -72,7 +72,7 @@ export class UnifiedAccountingAccountInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the account', }) diff --git a/packages/api/src/accounting/balancesheet/types/model.unified.ts b/packages/api/src/accounting/balancesheet/types/model.unified.ts index ba4cf4439..72d99be9d 100644 --- a/packages/api/src/accounting/balancesheet/types/model.unified.ts +++ b/packages/api/src/accounting/balancesheet/types/model.unified.ts @@ -25,7 +25,7 @@ export class UnifiedAccountingBalancesheetInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency used in the balance sheet', }) diff --git a/packages/api/src/accounting/cashflowstatement/types/model.unified.ts b/packages/api/src/accounting/cashflowstatement/types/model.unified.ts index 9bfedd309..c020c1a3b 100644 --- a/packages/api/src/accounting/cashflowstatement/types/model.unified.ts +++ b/packages/api/src/accounting/cashflowstatement/types/model.unified.ts @@ -114,7 +114,7 @@ export class UnifiedAccountingCashflowstatementInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency used in the cash flow statement', }) diff --git a/packages/api/src/accounting/companyinfo/types/model.unified.ts b/packages/api/src/accounting/companyinfo/types/model.unified.ts index 1474454b5..c4e5cc95f 100644 --- a/packages/api/src/accounting/companyinfo/types/model.unified.ts +++ b/packages/api/src/accounting/companyinfo/types/model.unified.ts @@ -70,7 +70,7 @@ export class UnifiedAccountingCompanyinfoInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency used by the company', }) diff --git a/packages/api/src/accounting/contact/types/model.unified.ts b/packages/api/src/accounting/contact/types/model.unified.ts index 6bce88f18..1a1ecd169 100644 --- a/packages/api/src/accounting/contact/types/model.unified.ts +++ b/packages/api/src/accounting/contact/types/model.unified.ts @@ -74,7 +74,7 @@ export class UnifiedAccountingContactInput { type: String, example: 'USD', nullable: true, - enum: CurrencyCode, + // enum: CurrencyCode, description: 'The currency associated with the contact', }) @IsString() diff --git a/packages/api/src/accounting/creditnote/types/model.unified.ts b/packages/api/src/accounting/creditnote/types/model.unified.ts index 12a03f82b..df2cb2f70 100644 --- a/packages/api/src/accounting/creditnote/types/model.unified.ts +++ b/packages/api/src/accounting/creditnote/types/model.unified.ts @@ -105,7 +105,7 @@ export class UnifiedAccountingCreditnoteInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the credit note', }) diff --git a/packages/api/src/accounting/expense/types/model.unified.ts b/packages/api/src/accounting/expense/types/model.unified.ts index 9d1537f3c..09bc6e091 100644 --- a/packages/api/src/accounting/expense/types/model.unified.ts +++ b/packages/api/src/accounting/expense/types/model.unified.ts @@ -23,7 +23,7 @@ export class LineItem { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the line item', }) @@ -123,7 +123,7 @@ export class UnifiedAccountingExpenseInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the expense', }) diff --git a/packages/api/src/accounting/incomestatement/types/model.unified.ts b/packages/api/src/accounting/incomestatement/types/model.unified.ts index 18123d79d..ecb1edbe3 100644 --- a/packages/api/src/accounting/incomestatement/types/model.unified.ts +++ b/packages/api/src/accounting/incomestatement/types/model.unified.ts @@ -22,7 +22,7 @@ export class UnifiedAccountingIncomestatementInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency used in the income statement', }) diff --git a/packages/api/src/accounting/invoice/types/model.unified.ts b/packages/api/src/accounting/invoice/types/model.unified.ts index ce0625651..3fbe810dc 100644 --- a/packages/api/src/accounting/invoice/types/model.unified.ts +++ b/packages/api/src/accounting/invoice/types/model.unified.ts @@ -53,7 +53,7 @@ export class LineItem { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the line item', }) @@ -186,7 +186,7 @@ export class UnifiedAccountingInvoiceInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the invoice', }) diff --git a/packages/api/src/accounting/journalentry/types/model.unified.ts b/packages/api/src/accounting/journalentry/types/model.unified.ts index 36a1bdb06..792b2fe2c 100644 --- a/packages/api/src/accounting/journalentry/types/model.unified.ts +++ b/packages/api/src/accounting/journalentry/types/model.unified.ts @@ -35,7 +35,7 @@ export class LineItem { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the line item', }) @@ -158,7 +158,7 @@ export class UnifiedAccountingJournalentryInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the journal entry', }) diff --git a/packages/api/src/accounting/payment/types/model.unified.ts b/packages/api/src/accounting/payment/types/model.unified.ts index aa85513dd..2b7af2c62 100644 --- a/packages/api/src/accounting/payment/types/model.unified.ts +++ b/packages/api/src/accounting/payment/types/model.unified.ts @@ -123,7 +123,7 @@ export class UnifiedAccountingPaymentInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the payment', }) diff --git a/packages/api/src/accounting/purchaseorder/types/model.unified.ts b/packages/api/src/accounting/purchaseorder/types/model.unified.ts index 8d7de7efd..5e7edcd88 100644 --- a/packages/api/src/accounting/purchaseorder/types/model.unified.ts +++ b/packages/api/src/accounting/purchaseorder/types/model.unified.ts @@ -76,7 +76,7 @@ export class LineItem { type: String, example: 'USD', nullable: true, - enum: CurrencyCode, + // enum: CurrencyCode, description: 'The currency of the line item', }) @IsString() @@ -246,7 +246,7 @@ export class UnifiedAccountingPurchaseorderInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the purchase order', }) diff --git a/packages/api/src/accounting/transaction/types/model.unified.ts b/packages/api/src/accounting/transaction/types/model.unified.ts index 27e5aae97..9246cd35d 100644 --- a/packages/api/src/accounting/transaction/types/model.unified.ts +++ b/packages/api/src/accounting/transaction/types/model.unified.ts @@ -63,7 +63,7 @@ export class LineItem { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the line item', }) @@ -214,7 +214,7 @@ export class UnifiedAccountingTransactionInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the transaction', }) diff --git a/packages/api/src/accounting/vendorcredit/types/model.unified.ts b/packages/api/src/accounting/vendorcredit/types/model.unified.ts index 980918563..270705f67 100644 --- a/packages/api/src/accounting/vendorcredit/types/model.unified.ts +++ b/packages/api/src/accounting/vendorcredit/types/model.unified.ts @@ -154,7 +154,7 @@ export class UnifiedAccountingVendorcreditInput { type: String, example: 'USD', nullable: true, - enum: CurrencyCode, + // enum: CurrencyCode, description: 'The currency of the vendor credit', }) @IsString() diff --git a/packages/api/src/ats/@lib/@types/index.ts b/packages/api/src/ats/@lib/@types/index.ts index 37259f98c..2d2df5f49 100644 --- a/packages/api/src/ats/@lib/@types/index.ts +++ b/packages/api/src/ats/@lib/@types/index.ts @@ -154,12 +154,12 @@ export class Email { @ApiProperty({ type: String, - enum: ['PERSONAL', 'WORK'], + //enum: ['PERSONAL', 'WORK'], nullable: true, description: 'The email address type. Authorized values are either PERSONAL or WORK.', }) - @IsIn(['PERSONAL', 'WORK']) + ////@IsIn(['PERSONAL', 'WORK']) @IsString() email_address_type: string; } @@ -176,11 +176,11 @@ export class Phone { @ApiProperty({ type: String, - enum: ['MOBILE', 'WORK'], + //enum: ['MOBILE', 'WORK'], nullable: true, description: 'The phone type. Authorized values are either MOBILE or WORK', }) - @IsIn(['MOBILE', 'WORK']) + ////@IsIn(['MOBILE', 'WORK']) @IsString() phone_type: string; } diff --git a/packages/api/src/ats/activity/types/model.unified.ts b/packages/api/src/ats/activity/types/model.unified.ts index 6ea43b475..2d21f0bf4 100644 --- a/packages/api/src/ats/activity/types/model.unified.ts +++ b/packages/api/src/ats/activity/types/model.unified.ts @@ -13,12 +13,12 @@ export type ActivityVisibility = 'ADMIN_ONLY' | 'PUBLIC' | 'PRIVATE'; export class UnifiedAtsActivityInput { @ApiPropertyOptional({ type: String, - enum: ['NOTE', 'EMAIL', 'OTHER'], + //// enum: ['NOTE', 'EMAIL', 'OTHER'], example: 'NOTE', nullable: true, - description: 'The type of activity', + description: 'The type of activity. NOTE, EMAIL or OTHER', }) - @IsIn(['NOTE', 'EMAIL', 'OTHER']) + ////@IsIn(['NOTE', 'EMAIL', 'OTHER']) @IsOptional() activity_type?: ActivityType | string; @@ -44,12 +44,13 @@ export class UnifiedAtsActivityInput { @ApiPropertyOptional({ type: String, - enum: ['ADMIN_ONLY', 'PUBLIC', 'PRIVATE'], + // enum: ['ADMIN_ONLY', 'PUBLIC', 'PRIVATE'], example: 'PUBLIC', nullable: true, - description: 'The visibility of the activity', + description: + 'The visibility of the activity. ADMIN_ONLY, PUBLIC or PRIVATE', }) - @IsIn(['ADMIN_ONLY', 'PUBLIC', 'PRIVATE']) + ////@IsIn(['ADMIN_ONLY', 'PUBLIC', 'PRIVATE']) @IsOptional() visibility?: ActivityVisibility | string; diff --git a/packages/api/src/ats/attachment/types/model.unified.ts b/packages/api/src/ats/attachment/types/model.unified.ts index 52bfe9d58..3843665c6 100644 --- a/packages/api/src/ats/attachment/types/model.unified.ts +++ b/packages/api/src/ats/attachment/types/model.unified.ts @@ -36,11 +36,11 @@ export class UnifiedAtsAttachmentInput { @ApiPropertyOptional({ type: String, example: 'RESUME', - enum: ['RESUME', 'COVER_LETTER', 'OFFER_LETTER', 'OTHER'], + //// enum: ['RESUME', 'COVER_LETTER', 'OFFER_LETTER', 'OTHER'], nullable: true, description: 'The type of the file', }) - @IsIn(['RESUME', 'COVER_LETTER', 'OFFER_LETTER', 'OTHER']) + ////@IsIn(['RESUME', 'COVER_LETTER', 'OFFER_LETTER', 'OTHER']) @IsOptional() attachment_type?: AttachmentType | string; diff --git a/packages/api/src/ats/eeocs/types/model.unified.ts b/packages/api/src/ats/eeocs/types/model.unified.ts index 6c3b16a86..1dbb9f15a 100644 --- a/packages/api/src/ats/eeocs/types/model.unified.ts +++ b/packages/api/src/ats/eeocs/types/model.unified.ts @@ -58,7 +58,7 @@ export class UnifiedAtsEeocsInput { @ApiPropertyOptional({ type: String, - enum: [ + /* enum: [ 'AMERICAN_INDIAN_OR_ALASKAN_NATIVE', 'ASIAN', 'BLACK_OR_AFRICAN_AMERICAN', @@ -67,12 +67,12 @@ export class UnifiedAtsEeocsInput { 'NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER', 'TWO_OR_MORE_RACES', 'DECLINE_TO_SELF_IDENTIFY', - ], + ],*/ example: 'AMERICAN_INDIAN_OR_ALASKAN_NATIVE', nullable: true, description: 'The race of the candidate', }) - @IsIn([ + /*@IsIn([ 'AMERICAN_INDIAN_OR_ALASKAN_NATIVE', 'ASIAN', 'BLACK_OR_AFRICAN_AMERICAN', @@ -81,56 +81,56 @@ export class UnifiedAtsEeocsInput { 'NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER', 'TWO_OR_MORE_RACES', 'DECLINE_TO_SELF_IDENTIFY', - ]) + ])*/ @IsOptional() race?: EeocsRace | string; @ApiPropertyOptional({ type: String, example: 'MALE', - enum: ['MALE', 'FEMALE', 'NON_BINARY', 'OTHER', 'DECLINE_TO_SELF_IDENTIFY'], + // enum: ['MALE', 'FEMALE', 'NON_BINARY', 'OTHER', 'DECLINE_TO_SELF_IDENTIFY'], nullable: true, description: 'The gender of the candidate', }) - @IsIn(['MALE', 'FEMALE', 'NON_BINARY', 'OTHER', 'DECLINE_TO_SELF_IDENTIFY']) + //@IsIn(['MALE', 'FEMALE', 'NON_BINARY', 'OTHER', 'DECLINE_TO_SELF_IDENTIFY']) @IsOptional() gender?: EeocsGender | string; @ApiPropertyOptional({ type: String, example: 'I_AM_NOT_A_PROTECTED_VETERAN', - enum: [ + /* enum: [ 'I_AM_NOT_A_PROTECTED_VETERAN', 'I_IDENTIFY_AS_ONE_OR_MORE_OF_THE_CLASSIFICATIONS_OF_A_PROTECTED_VETERAN', 'I_DONT_WISH_TO_ANSWER', - ], + ],*/ nullable: true, description: 'The veteran status of the candidate', }) - @IsIn([ + /*@IsIn([ 'I_AM_NOT_A_PROTECTED_VETERAN', 'I_IDENTIFY_AS_ONE_OR_MORE_OF_THE_CLASSIFICATIONS_OF_A_PROTECTED_VETERAN', 'I_DONT_WISH_TO_ANSWER', - ]) + ])*/ @IsOptional() veteran_status?: EeocsVeteranStatus | string; @ApiPropertyOptional({ type: String, - enum: [ + /* enum: [ 'YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY', 'NO_I_DONT_HAVE_A_DISABILITY', 'I_DONT_WISH_TO_ANSWER', - ], + ],*/ example: 'YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY', nullable: true, description: 'The disability status of the candidate', }) - @IsIn([ + /*@IsIn([ 'YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY', 'NO_I_DONT_HAVE_A_DISABILITY', 'I_DONT_WISH_TO_ANSWER', - ]) + ])*/ @IsOptional() disability_status?: EeocsDisabilityStatus | string; diff --git a/packages/api/src/ats/interview/types/model.unified.ts b/packages/api/src/ats/interview/types/model.unified.ts index 8b4ebe31d..3a1e6e272 100644 --- a/packages/api/src/ats/interview/types/model.unified.ts +++ b/packages/api/src/ats/interview/types/model.unified.ts @@ -13,12 +13,12 @@ export type InterviewStatus = 'SCHEDULED' | 'AWAITING_FEEDBACK' | 'COMPLETED'; export class UnifiedAtsInterviewInput { @ApiPropertyOptional({ type: String, - enum: ['SCHEDULED', 'AWAITING_FEEDBACK', 'COMPLETED'], + // enum: ['SCHEDULED', 'AWAITING_FEEDBACK', 'COMPLETED'], example: 'SCHEDULED', nullable: true, description: 'The status of the interview', }) - @IsIn(['SCHEDULED', 'AWAITING_FEEDBACK', 'COMPLETED']) + //@IsIn(['SCHEDULED', 'AWAITING_FEEDBACK', 'COMPLETED']) @IsOptional() status?: InterviewStatus | string; diff --git a/packages/api/src/ats/job/types/model.unified.ts b/packages/api/src/ats/job/types/model.unified.ts index dde4d6842..6b44bc300 100644 --- a/packages/api/src/ats/job/types/model.unified.ts +++ b/packages/api/src/ats/job/types/model.unified.ts @@ -45,23 +45,23 @@ export class UnifiedAtsJobInput { @ApiPropertyOptional({ type: String, - enum: ['OPEN', 'CLOSED', 'DRAFT', 'ARCHIVED', 'PENDING'], + // enum: ['OPEN', 'CLOSED', 'DRAFT', 'ARCHIVED', 'PENDING'], example: 'OPEN', nullable: true, description: 'The status of the job', }) - @IsIn(['OPEN', 'CLOSED', 'DRAFT', 'ARCHIVED', 'PENDING']) + //@IsIn(['OPEN', 'CLOSED', 'DRAFT', 'ARCHIVED', 'PENDING']) @IsOptional() status?: JobStatus | string; @ApiPropertyOptional({ type: String, example: 'POSTING', - enum: ['POSTING', 'REQUISITION', 'PROFILE'], + // enum: ['POSTING', 'REQUISITION', 'PROFILE'], nullable: true, description: 'The type of the job', }) - @IsIn(['POSTING', 'REQUISITION', 'PROFILE']) + //@IsIn(['POSTING', 'REQUISITION', 'PROFILE']) @IsOptional() type?: JobType | string; diff --git a/packages/api/src/ats/jobinterviewstage/types/model.unified.ts b/packages/api/src/ats/jobinterviewstage/types/model.unified.ts index c963e3a9f..9f3e86188 100644 --- a/packages/api/src/ats/jobinterviewstage/types/model.unified.ts +++ b/packages/api/src/ats/jobinterviewstage/types/model.unified.ts @@ -24,7 +24,7 @@ export class UnifiedAtsJobinterviewstageInput { nullable: true, description: 'The order of the stage', }) - @IsInt() + //@IsInt() @IsOptional() stage_order?: number; diff --git a/packages/api/src/ats/offer/types/model.unified.ts b/packages/api/src/ats/offer/types/model.unified.ts index 9d8636d47..704e84c32 100644 --- a/packages/api/src/ats/offer/types/model.unified.ts +++ b/packages/api/src/ats/offer/types/model.unified.ts @@ -71,7 +71,7 @@ export class UnifiedAtsOfferInput { @ApiPropertyOptional({ type: String, example: 'DRAFT', - enum: [ + /* enum: [ 'DRAFT', 'APPROVAL_SENT', 'APPROVED', @@ -81,11 +81,11 @@ export class UnifiedAtsOfferInput { 'DENIED', 'SIGNED', 'DEPRECATED', - ], + ],*/ description: 'The status of the offer', nullable: true, }) - @IsIn([ + /*@IsIn([ 'DRAFT', 'APPROVAL_SENT', 'APPROVED', @@ -95,7 +95,7 @@ export class UnifiedAtsOfferInput { 'DENIED', 'SIGNED', 'DEPRECATED', - ]) + ])*/ @IsOptional() status?: OfferStatus | string; diff --git a/packages/api/src/ats/scorecard/types/model.unified.ts b/packages/api/src/ats/scorecard/types/model.unified.ts index 226d8e758..e75a05c45 100644 --- a/packages/api/src/ats/scorecard/types/model.unified.ts +++ b/packages/api/src/ats/scorecard/types/model.unified.ts @@ -17,12 +17,12 @@ export type ScoreCardRecommendation = export class UnifiedAtsScorecardInput { @ApiPropertyOptional({ type: String, - enum: ['DEFINITELY_NO', 'NO', 'YES', 'STRONG_YES', 'NO_DECISION'], + // enum: ['DEFINITELY_NO', 'NO', 'YES', 'STRONG_YES', 'NO_DECISION'], example: 'YES', nullable: true, description: 'The overall recommendation', }) - @IsIn(['DEFINITELY_NO', 'NO', 'YES', 'STRONG_YES', 'NO_DECISION']) + //@IsIn(['DEFINITELY_NO', 'NO', 'YES', 'STRONG_YES', 'NO_DECISION']) @IsOptional() overall_recommendation?: ScoreCardRecommendation | string; diff --git a/packages/api/src/ats/user/types/model.unified.ts b/packages/api/src/ats/user/types/model.unified.ts index 7fc8e09fa..6b7f06c0e 100644 --- a/packages/api/src/ats/user/types/model.unified.ts +++ b/packages/api/src/ats/user/types/model.unified.ts @@ -59,23 +59,23 @@ export class UnifiedAtsUserInput { @ApiPropertyOptional({ type: String, example: 'ADMIN', - enum: [ + /* enum: [ 'SUPER_ADMIN', 'ADMIN', 'TEAM_MEMBER', 'LIMITED_TEAM_MEMBER', 'INTERVIEWER', - ], + ],*/ description: 'The access role of the user', nullable: true, }) - @IsIn([ + /*@IsIn([ 'SUPER_ADMIN', 'ADMIN', 'TEAM_MEMBER', 'LIMITED_TEAM_MEMBER', 'INTERVIEWER', - ]) + ])*/ @IsOptional() access_role?: UserAccessRole | string; diff --git a/packages/api/src/crm/@lib/@types/index.ts b/packages/api/src/crm/@lib/@types/index.ts index 5def9dbd5..899868ba3 100644 --- a/packages/api/src/crm/@lib/@types/index.ts +++ b/packages/api/src/crm/@lib/@types/index.ts @@ -314,12 +314,12 @@ export class Email { @ApiProperty({ type: String, - enum: ['PERSONAL', 'WORK'], + //enum: ['PERSONAL', 'WORK'], nullable: true, description: 'The email address type. Authorized values are either PERSONAL or WORK.', }) - @IsIn(['PERSONAL', 'WORK']) + ////@IsIn(['PERSONAL', 'WORK']) @IsString() email_address_type: string; @@ -331,7 +331,7 @@ export class Email { }) @IsString() @IsOptional() - @IsIn(['COMPANY', 'CONTACT']) + //@IsIn(['COMPANY', 'CONTACT']) owner_type?: string; } @@ -347,11 +347,9 @@ export class Phone { @ApiProperty({ type: String, - enum: ['MOBILE', 'WORK'], nullable: true, description: 'The phone type. Authorized values are either MOBILE or WORK', }) - @IsIn(['MOBILE', 'WORK']) @IsString() phone_type: string; @@ -422,13 +420,11 @@ export class Address { @ApiProperty({ type: String, - enum: ['PERSONAL', 'WORK'], nullable: true, example: 'PERSONAL', description: 'The address type. Authorized values are either PERSONAL or WORK.', }) - @IsIn(['PERSONAL', 'WORK']) @IsOptional() @IsString() address_type?: string; diff --git a/packages/api/src/crm/company/types/model.unified.ts b/packages/api/src/crm/company/types/model.unified.ts index 15b767fb2..be755551a 100644 --- a/packages/api/src/crm/company/types/model.unified.ts +++ b/packages/api/src/crm/company/types/model.unified.ts @@ -21,7 +21,7 @@ export class UnifiedCrmCompanyInput { @ApiPropertyOptional({ type: String, example: 'ACCOUNTING', - enum: Industry, + // enum: Industry, description: 'The industry of the company. Authorized values can be found in the Industry enum.', nullable: true, diff --git a/packages/api/src/crm/contact/contact.controller.ts b/packages/api/src/crm/contact/contact.controller.ts index 18fc6e3b8..ecb025767 100644 --- a/packages/api/src/crm/contact/contact.controller.ts +++ b/packages/api/src/crm/contact/contact.controller.ts @@ -62,12 +62,16 @@ export class ContactController { @ApiPaginatedResponse(UnifiedCrmContactOutput) @UseGuards(ApiKeyAuthGuard) @Get() - @UsePipes(new ValidationPipe({ transform: true, disableErrorMessages: true })) + @UsePipes( + new ValidationPipe({ transform: true, disableErrorMessages: false }), + ) async getContacts( @Headers('x-connection-token') connection_token: string, @Query() query: QueryDto, ) { try { + console.log('Received connection_token:', connection_token); + console.log('Received query:', JSON.stringify(query)); const { linkedUserId, remoteSource, connectionId, projectId } = await this.connectionUtils.getConnectionMetadataFromConnectionToken( connection_token, diff --git a/packages/api/src/crm/contact/services/hubspot/mappers.ts b/packages/api/src/crm/contact/services/hubspot/mappers.ts index 95a2649d8..14d75d496 100644 --- a/packages/api/src/crm/contact/services/hubspot/mappers.ts +++ b/packages/api/src/crm/contact/services/hubspot/mappers.ts @@ -143,14 +143,14 @@ export class HubspotContactMapper implements IContactMapper { email_addresses: [ { email_address: contact.properties.email, - email_address_type: 'primary', + email_address_type: 'PERSONAL', owner_type: 'contact', }, ], phone_numbers: [ { phone_number: contact.properties.phone, - phone_type: 'primary', + phone_type: 'PERSONAL', owner_type: 'contact', }, ], diff --git a/packages/api/src/crm/contact/services/zendesk/mappers.ts b/packages/api/src/crm/contact/services/zendesk/mappers.ts index 83fad6e19..832d5d3a5 100644 --- a/packages/api/src/crm/contact/services/zendesk/mappers.ts +++ b/packages/api/src/crm/contact/services/zendesk/mappers.ts @@ -113,17 +113,17 @@ export class ZendeskContactMapper implements IContactMapper { } // Constructing the email and phone details const email_addresses = contact.email - ? [{ email_address: contact.email, email_address_type: 'primary' }] + ? [{ email_address: contact.email, email_address_type: 'PERSONAL' }] : []; const phone_numbers = []; if (contact.phone) { - phone_numbers.push({ phone_number: contact.phone, phone_type: 'work' }); + phone_numbers.push({ phone_number: contact.phone, phone_type: 'WORK' }); } if (contact.mobile) { phone_numbers.push({ phone_number: contact.mobile, - phone_type: 'mobile', + phone_type: 'MOBILE', }); } diff --git a/packages/api/src/crm/contact/services/zoho/mappers.ts b/packages/api/src/crm/contact/services/zoho/mappers.ts index 81ca7ca5b..8f22d0ef5 100644 --- a/packages/api/src/crm/contact/services/zoho/mappers.ts +++ b/packages/api/src/crm/contact/services/zoho/mappers.ts @@ -124,13 +124,13 @@ export class ZohoContactMapper implements IContactMapper { if (contact && contact.Fax) { phone_numbers.push({ phone_number: contact.Fax, - phone_type: 'fax', + phone_type: 'FAX', }); } if (contact && contact.Home_Phone) { phone_numbers.push({ phone_number: contact.Home_Phone, - phone_type: 'home', + phone_type: 'HOME', }); } diff --git a/packages/api/src/crm/engagement/types/model.unified.ts b/packages/api/src/crm/engagement/types/model.unified.ts index 2c1581c88..528e30ec7 100644 --- a/packages/api/src/crm/engagement/types/model.unified.ts +++ b/packages/api/src/crm/engagement/types/model.unified.ts @@ -19,13 +19,13 @@ export class UnifiedCrmEngagementInput { type: String, nullable: true, example: 'INBOUND', - enum: ['INBOUND', 'OUTBOUND'], + // enum: ['INBOUND', 'OUTBOUND'], description: 'The direction of the engagement. Authorized values are INBOUND or OUTBOUND', }) - @IsIn(['INBOUND', 'OUTBOUND'], { + /*@IsIn(['INBOUND', 'OUTBOUND'], { message: 'Direction must be either INBOUND or OUTBOUND', - }) + })*/ @IsOptional() direction?: EngagementDirection | string; @@ -61,13 +61,13 @@ export class UnifiedCrmEngagementInput { type: String, nullable: true, example: 'MEETING', - enum: ['EMAIL', 'CALL', 'MEETING'], + // enum: ['EMAIL', 'CALL', 'MEETING'], description: 'The type of the engagement. Authorized values are EMAIL, CALL or MEETING', }) - @IsIn(['EMAIL', 'CALL', 'MEETING'], { + /*@IsIn(['EMAIL', 'CALL', 'MEETING'], { message: 'Type must be either EMAIL, CALL or MEETING', - }) + })*/ type?: EngagementType | string; @ApiPropertyOptional({ diff --git a/packages/api/src/crm/task/types/model.unified.ts b/packages/api/src/crm/task/types/model.unified.ts index 78f4c280c..57be7f4cf 100644 --- a/packages/api/src/crm/task/types/model.unified.ts +++ b/packages/api/src/crm/task/types/model.unified.ts @@ -24,14 +24,14 @@ export class UnifiedCrmTaskInput { @ApiProperty({ type: String, example: 'PENDING', - enum: ['PENDING', 'COMPLETED'], + // enum: ['PENDING', 'COMPLETED'], description: 'The status of the task. Authorized values are PENDING, COMPLETED.', nullable: true, }) - @IsIn(['PENDING', 'COMPLETED'], { + /*@IsIn(['PENDING', 'COMPLETED'], { message: 'Type must be either PENDING or COMPLETED', - }) + })*/ status: TaskStatus | string; @ApiPropertyOptional({ diff --git a/packages/api/src/ecommerce/@lib/@types/index.ts b/packages/api/src/ecommerce/@lib/@types/index.ts index c0261f61d..ad393e730 100644 --- a/packages/api/src/ecommerce/@lib/@types/index.ts +++ b/packages/api/src/ecommerce/@lib/@types/index.ts @@ -108,13 +108,13 @@ export class Address { @ApiProperty({ type: String, - enum: ['SHIPPING', 'BILLING'], + //enum: ['SHIPPING', 'BILLING'], example: 'SHIPPING', nullable: true, description: 'The address type. Authorized values are either SHIPPING or BILLING.', }) - @IsIn(['SHIPPING', 'BILLING']) + ////@IsIn(['SHIPPING', 'BILLING']) @IsOptional() @IsString() address_type?: 'SHIPPING' | 'BILLING' | string; diff --git a/packages/api/src/ecommerce/order/types/model.unified.ts b/packages/api/src/ecommerce/order/types/model.unified.ts index fa98aef8e..bc9bd95b6 100644 --- a/packages/api/src/ecommerce/order/types/model.unified.ts +++ b/packages/api/src/ecommerce/order/types/model.unified.ts @@ -44,11 +44,11 @@ export class UnifiedEcommerceOrderInput { @ApiPropertyOptional({ type: String, example: 'UNSHIPPED', - enum: ['PENDING', 'UNSHIPPED', 'SHIPPED', 'CANCELED'], + // enum: ['PENDING', 'UNSHIPPED', 'SHIPPED', 'CANCELED'], nullable: true, description: 'The status of the order', }) - @IsIn(['PENDING', 'UNSHIPPED', 'SHIPPED', 'CANCELED']) + //@IsIn(['PENDING', 'UNSHIPPED', 'SHIPPED', 'CANCELED']) @IsOptional() order_status?: OrderStatus | string; @@ -65,11 +65,11 @@ export class UnifiedEcommerceOrderInput { @ApiPropertyOptional({ type: String, example: 'SUCCESS', - enum: ['SUCCESS', 'FAIL'], + // enum: ['SUCCESS', 'FAIL'], nullable: true, description: 'The payment status of the order', }) - @IsIn(['SUCCESS', 'FAIL']) + //@IsIn(['SUCCESS', 'FAIL']) @IsOptional() payment_status?: 'SUCCESS' | 'FAIL' | string; @@ -77,7 +77,7 @@ export class UnifiedEcommerceOrderInput { type: String, nullable: true, example: 'AUD', - enum: CurrencyCode, + // enum: CurrencyCode, description: 'The currency of the order. Authorized value must be of type CurrencyCode (ISO 4217)', }) @@ -91,7 +91,7 @@ export class UnifiedEcommerceOrderInput { nullable: true, description: 'The total price of the order', }) - @IsInt() + //@IsInt() @IsOptional() total_price?: number; @@ -101,7 +101,7 @@ export class UnifiedEcommerceOrderInput { nullable: true, description: 'The total discount on the order', }) - @IsInt() + //@IsInt() @IsOptional() total_discount?: number; @@ -111,7 +111,7 @@ export class UnifiedEcommerceOrderInput { nullable: true, description: 'The total shipping cost of the order', }) - @IsInt() + //@IsInt() @IsOptional() total_shipping?: number; @@ -121,7 +121,7 @@ export class UnifiedEcommerceOrderInput { nullable: true, description: 'The total tax on the order', }) - @IsInt() + //@IsInt() @IsOptional() total_tax?: number; @@ -129,10 +129,10 @@ export class UnifiedEcommerceOrderInput { type: String, nullable: true, example: 'PENDING', - enum: ['PENDING', 'FULFILLED', 'CANCELED'], + // enum: ['PENDING', 'FULFILLED', 'CANCELED'], description: 'The fulfillment status of the order', }) - @IsIn(['PENDING', 'FULFILLED', 'CANCELED']) + //@IsIn(['PENDING', 'FULFILLED', 'CANCELED']) @IsOptional() fulfillment_status?: FulfillmentStatus | string; diff --git a/packages/api/src/ecommerce/product/types/model.unified.ts b/packages/api/src/ecommerce/product/types/model.unified.ts index 3dce5514f..f2e36216a 100644 --- a/packages/api/src/ecommerce/product/types/model.unified.ts +++ b/packages/api/src/ecommerce/product/types/model.unified.ts @@ -41,10 +41,10 @@ export class UnifiedEcommerceProductInput { type: String, example: 'ACTIVE', nullable: true, - enum: ['ARCHIVED', 'ACTIVE', 'DRAFT'], + // enum: ['ARCHIVED', 'ACTIVE', 'DRAFT'], description: 'The status of the product. Either ACTIVE, DRAFT OR ARCHIVED.', }) - @IsIn(['ARCHIVED', 'ACTIVE', 'DRAFT']) + //@IsIn(['ARCHIVED', 'ACTIVE', 'DRAFT']) @IsOptional() product_status?: string; diff --git a/packages/api/src/filestorage/permission/types/model.unified.ts b/packages/api/src/filestorage/permission/types/model.unified.ts index 6cc360379..33dd0741b 100644 --- a/packages/api/src/filestorage/permission/types/model.unified.ts +++ b/packages/api/src/filestorage/permission/types/model.unified.ts @@ -8,7 +8,7 @@ export class UnifiedFilestoragePermissionInput { @ApiProperty({ type: [String], example: ['READ'], - enum: ['READ', 'WRITE', 'OWNER'], + // enum: ['READ', 'WRITE', 'OWNER'], nullable: true, description: 'The roles of the permission', }) @@ -17,12 +17,12 @@ export class UnifiedFilestoragePermissionInput { @ApiProperty({ type: String, - enum: ['USER', 'GROUP', 'COMPANY', 'ANYONE'], + // enum: ['USER', 'GROUP', 'COMPANY', 'ANYONE'], example: 'USER', nullable: true, description: 'The type of the permission', }) - @IsIn(['USER', 'GROUP', 'COMPANY', 'ANYONE']) + //@IsIn(['USER', 'GROUP', 'COMPANY', 'ANYONE']) @IsString() type: PermissionType | string; diff --git a/packages/api/src/hris/bankinfo/types/model.unified.ts b/packages/api/src/hris/bankinfo/types/model.unified.ts index e3a7cbe3b..750acc65a 100644 --- a/packages/api/src/hris/bankinfo/types/model.unified.ts +++ b/packages/api/src/hris/bankinfo/types/model.unified.ts @@ -13,7 +13,7 @@ export class UnifiedHrisBankinfoInput { @ApiPropertyOptional({ type: String, example: 'CHECKING', - enum: ['SAVINGS', 'CHECKING'], + // enum: ['SAVINGS', 'CHECKING'], nullable: true, description: 'The type of the bank account', }) diff --git a/packages/api/src/hris/dependent/types/model.unified.ts b/packages/api/src/hris/dependent/types/model.unified.ts index 70eedd34e..c1e2add3f 100644 --- a/packages/api/src/hris/dependent/types/model.unified.ts +++ b/packages/api/src/hris/dependent/types/model.unified.ts @@ -50,7 +50,7 @@ export class UnifiedHrisDependentInput { @ApiPropertyOptional({ type: String, example: 'CHILD', - enum: ['CHILD', 'SPOUSE', 'DOMESTIC_PARTNER'], + // enum: ['CHILD', 'SPOUSE', 'DOMESTIC_PARTNER'], nullable: true, description: 'The relationship of the dependent to the employee', }) @@ -71,7 +71,7 @@ export class UnifiedHrisDependentInput { @ApiPropertyOptional({ type: String, example: 'MALE', - enum: ['MALE', 'FEMALE', 'NON-BINARY', 'OTHER', 'PREFER_NOT_TO_DISCLOSE'], + // enum: ['MALE', 'FEMALE', 'NON-BINARY', 'OTHER', 'PREFER_NOT_TO_DISCLOSE'], nullable: true, description: 'The gender of the dependent', }) diff --git a/packages/api/src/hris/employee/types/model.unified.ts b/packages/api/src/hris/employee/types/model.unified.ts index a7c6e8239..335e2ba49 100644 --- a/packages/api/src/hris/employee/types/model.unified.ts +++ b/packages/api/src/hris/employee/types/model.unified.ts @@ -184,7 +184,7 @@ export class UnifiedHrisEmployeeInput { @ApiPropertyOptional({ type: String, example: 'MALE', - enum: ['MALE', 'FEMALE', 'NON-BINARY', 'OTHER', 'PREFER_NOT_TO_DISCLOSE'], + // enum: ['MALE', 'FEMALE', 'NON-BINARY', 'OTHER', 'PREFER_NOT_TO_DISCLOSE'], nullable: true, description: 'The gender of the employee', }) @@ -195,7 +195,7 @@ export class UnifiedHrisEmployeeInput { @ApiPropertyOptional({ type: String, example: 'AMERICAN_INDIAN_OR_ALASKA_NATIVE', - enum: [ + /* enum: [ 'AMERICAN_INDIAN_OR_ALASKA_NATIVE', 'ASIAN_OR_INDIAN_SUBCONTINENT', 'BLACK_OR_AFRICAN_AMERICAN', @@ -204,7 +204,7 @@ export class UnifiedHrisEmployeeInput { 'TWO_OR_MORE_RACES', 'WHITE', 'PREFER_NOT_TO_DISCLOSE', - ], + ],*/ nullable: true, description: 'The ethnicity of the employee', }) @@ -215,13 +215,13 @@ export class UnifiedHrisEmployeeInput { @ApiPropertyOptional({ type: String, example: 'Married', - enum: [ + /* enum: [ 'SINGLE', 'MARRIED_FILING_JOINTLY', 'MARRIED_FILING_SEPARATELY', 'HEAD_OF_HOUSEHOLD', 'QUALIFYING_WIDOW_OR_WIDOWER_WITH_DEPENDENT_CHILD', - ], + ],*/ nullable: true, description: 'The marital status of the employee', }) @@ -252,7 +252,7 @@ export class UnifiedHrisEmployeeInput { @ApiPropertyOptional({ type: String, example: 'ACTIVE', - enum: ['ACTIVE', 'PENDING', 'INACTIVE'], + // enum: ['ACTIVE', 'PENDING', 'INACTIVE'], nullable: true, description: 'The employment status of the employee', }) diff --git a/packages/api/src/hris/employerbenefit/types/model.unified.ts b/packages/api/src/hris/employerbenefit/types/model.unified.ts index 3f280edb6..46852f391 100644 --- a/packages/api/src/hris/employerbenefit/types/model.unified.ts +++ b/packages/api/src/hris/employerbenefit/types/model.unified.ts @@ -17,7 +17,7 @@ export class UnifiedHrisEmployerbenefitInput { @ApiPropertyOptional({ type: String, example: 'Health Insurance', - enum: ['MEDICAL', 'HEALTH_SAVINGS', 'INSURANCE', 'RETIREMENT', 'OTHER'], + // enum: ['MEDICAL', 'HEALTH_SAVINGS', 'INSURANCE', 'RETIREMENT', 'OTHER'], nullable: true, description: 'The type of the benefit plan', }) diff --git a/packages/api/src/hris/employment/types/model.unified.ts b/packages/api/src/hris/employment/types/model.unified.ts index aef4d7031..857744110 100644 --- a/packages/api/src/hris/employment/types/model.unified.ts +++ b/packages/api/src/hris/employment/types/model.unified.ts @@ -68,7 +68,7 @@ export class UnifiedHrisEmploymentInput { @ApiPropertyOptional({ type: String, example: 'MONTHLY', - enum: [ + /* enum: [ 'HOUR', 'DAY', 'WEEK', @@ -78,7 +78,7 @@ export class UnifiedHrisEmploymentInput { 'QUARTER', 'EVERY_SIX_MONTHS', 'YEAR', - ], + ],*/ nullable: true, description: 'The pay period of the employment', }) @@ -89,7 +89,7 @@ export class UnifiedHrisEmploymentInput { @ApiPropertyOptional({ type: String, example: 'WEEKLY', - enum: [ + /* enum: [ 'WEEKLY', 'BIWEEKLY', 'MONTHLY', @@ -99,7 +99,7 @@ export class UnifiedHrisEmploymentInput { 'THIRTEEN-MONTHLY', 'PRO_RATA', 'SEMIMONTHLY', - ], + ],*/ nullable: true, description: 'The pay frequency of the employment', }) @@ -110,7 +110,7 @@ export class UnifiedHrisEmploymentInput { @ApiPropertyOptional({ type: String, example: 'USD', - enum: CurrencyCode, + // enum: CurrencyCode, nullable: true, description: 'The currency of the pay', }) @@ -121,7 +121,7 @@ export class UnifiedHrisEmploymentInput { @ApiPropertyOptional({ type: String, example: 'EXEMPT', - enum: ['EXEMPT', 'SALARIED_NONEXEMPT', 'NONEXEMPT', 'OWNER'], + // enum: ['EXEMPT', 'SALARIED_NONEXEMPT', 'NONEXEMPT', 'OWNER'], nullable: true, description: 'The FLSA status of the employment', }) @@ -142,7 +142,7 @@ export class UnifiedHrisEmploymentInput { @ApiPropertyOptional({ type: String, example: 'FULL_TIME', - enum: ['FULL_TIME', 'PART_TIME', 'INTERN', 'CONTRACTOR', 'FREELANCE'], + // enum: ['FULL_TIME', 'PART_TIME', 'INTERN', 'CONTRACTOR', 'FREELANCE'], nullable: true, description: 'The type of employment', }) diff --git a/packages/api/src/hris/group/types/model.unified.ts b/packages/api/src/hris/group/types/model.unified.ts index 6ce238ae6..7ecb4085b 100644 --- a/packages/api/src/hris/group/types/model.unified.ts +++ b/packages/api/src/hris/group/types/model.unified.ts @@ -37,7 +37,7 @@ export class UnifiedHrisGroupInput { @ApiPropertyOptional({ type: String, example: 'DEPARTMENT', - enum: ['TEAM', 'DEPARTMENT', 'COST_CENTER', 'BUSINESS_UNIT', 'GROUP'], + // enum: ['TEAM', 'DEPARTMENT', 'COST_CENTER', 'BUSINESS_UNIT', 'GROUP'], nullable: true, description: 'The type of the group', }) diff --git a/packages/api/src/hris/location/types/model.unified.ts b/packages/api/src/hris/location/types/model.unified.ts index 7c2fa11da..5bf6593cb 100644 --- a/packages/api/src/hris/location/types/model.unified.ts +++ b/packages/api/src/hris/location/types/model.unified.ts @@ -93,12 +93,12 @@ export class UnifiedHrisLocationInput { @ApiPropertyOptional({ type: String, example: 'WORK', - enum: ['WORK', 'HOME'], + // enum: ['WORK', 'HOME'], nullable: true, description: 'The type of the location', }) @IsString() - @IsIn(['WORK', 'HOME']) + //@IsIn(['WORK', 'HOME']) @IsOptional() location_type?: LocationType | string; diff --git a/packages/api/src/hris/payrollrun/types/model.unified.ts b/packages/api/src/hris/payrollrun/types/model.unified.ts index 29a95f9fd..23e77e40f 100644 --- a/packages/api/src/hris/payrollrun/types/model.unified.ts +++ b/packages/api/src/hris/payrollrun/types/model.unified.ts @@ -18,7 +18,7 @@ export class UnifiedHrisPayrollrunInput { @ApiPropertyOptional({ type: String, example: 'PAID', - enum: ['PAID', 'DRAFT', 'APPROVED', 'FAILED', 'CLOSE'], + // enum: ['PAID', 'DRAFT', 'APPROVED', 'FAILED', 'CLOSE'], nullable: true, description: 'The state of the payroll run', }) @@ -29,13 +29,13 @@ export class UnifiedHrisPayrollrunInput { @ApiPropertyOptional({ type: String, example: 'REGULAR', - enum: [ + /* enum: [ 'REGULAR', 'OFF_CYCLE', 'CORRECTION', 'TERMINATION', 'SIGN_ON_BONUS', - ], + ],*/ nullable: true, description: 'The type of the payroll run', }) diff --git a/packages/api/src/hris/timeoff/types/model.unified.ts b/packages/api/src/hris/timeoff/types/model.unified.ts index ab9a25343..8af5a3284 100644 --- a/packages/api/src/hris/timeoff/types/model.unified.ts +++ b/packages/api/src/hris/timeoff/types/model.unified.ts @@ -46,7 +46,7 @@ export class UnifiedHrisTimeoffInput { @ApiPropertyOptional({ type: String, example: 'REQUESTED', - enum: ['REQUESTED', 'APPROVED', 'DECLINED', 'CANCELLED', 'DELETED'], + // enum: ['REQUESTED', 'APPROVED', 'DECLINED', 'CANCELLED', 'DELETED'], nullable: true, description: 'The status of the time off request', }) @@ -67,7 +67,7 @@ export class UnifiedHrisTimeoffInput { @ApiPropertyOptional({ type: String, example: 'DAYS', - enum: ['HOURS', 'DAYS'], + // enum: ['HOURS', 'DAYS'], nullable: true, description: 'The units used for the time off (e.g., Days, Hours)', }) @@ -88,14 +88,14 @@ export class UnifiedHrisTimeoffInput { @ApiPropertyOptional({ type: String, example: 'VACATION', - enum: [ + /* enum: [ 'VACATION', 'SICK', 'PERSONAL', 'JURY_DUTY', 'VOLUNTEER', 'BEREAVEMENT', - ], + ],*/ nullable: true, description: 'The type of time off request', }) diff --git a/packages/api/src/hris/timeoffbalance/types/model.unified.ts b/packages/api/src/hris/timeoffbalance/types/model.unified.ts index ae45a58ae..941825f82 100644 --- a/packages/api/src/hris/timeoffbalance/types/model.unified.ts +++ b/packages/api/src/hris/timeoffbalance/types/model.unified.ts @@ -50,14 +50,14 @@ export class UnifiedHrisTimeoffbalanceInput { @ApiPropertyOptional({ type: String, example: 'VACATION', - enum: [ + /*enum: [ 'VACATION', 'SICK', 'PERSONAL', 'JURY_DUTY', 'VOLUNTEER', 'BEREAVEMENT', - ], + ],*/ nullable: true, description: 'The type of time off policy', }) diff --git a/packages/api/src/ticketing/collection/types/model.unified.ts b/packages/api/src/ticketing/collection/types/model.unified.ts index 41a321f29..9662d414f 100644 --- a/packages/api/src/ticketing/collection/types/model.unified.ts +++ b/packages/api/src/ticketing/collection/types/model.unified.ts @@ -26,14 +26,14 @@ export class UnifiedTicketingCollectionInput { @ApiPropertyOptional({ type: String, example: 'PROJECT', - enum: ['PROJECT', 'LIST'], + // enum: ['PROJECT', 'LIST'], nullable: true, description: 'The type of the collection. Authorized values are either PROJECT or LIST ', }) - @IsIn(['PROJECT', 'LIST'], { + /*@IsIn(['PROJECT', 'LIST'], { message: 'Type must be either PROJECT or LIST', - }) + })*/ @IsOptional() collection_type?: CollectionType | string; } diff --git a/packages/api/src/ticketing/comment/types/model.unified.ts b/packages/api/src/ticketing/comment/types/model.unified.ts index 753c6e1b2..aa7ead01d 100644 --- a/packages/api/src/ticketing/comment/types/model.unified.ts +++ b/packages/api/src/ticketing/comment/types/model.unified.ts @@ -42,13 +42,13 @@ export class UnifiedTicketingCommentInput { type: String, nullable: true, example: 'USER', - enum: ['USER', 'CONTACT'], + // enum: ['USER', 'CONTACT'], description: 'The creator type of the comment. Authorized values are either USER or CONTACT', }) - @IsIn(['USER', 'CONTACT'], { + /*@IsIn(['USER', 'CONTACT'], { message: 'Type must be either USER or CONTACT', - }) + })*/ @IsOptional() creator_type?: CommentCreatorType | string; diff --git a/packages/api/src/ticketing/ticket/types/model.unified.ts b/packages/api/src/ticketing/ticket/types/model.unified.ts index 80da04fa3..8c0de3734 100644 --- a/packages/api/src/ticketing/ticket/types/model.unified.ts +++ b/packages/api/src/ticketing/ticket/types/model.unified.ts @@ -26,14 +26,14 @@ export class UnifiedTicketingTicketInput { @ApiPropertyOptional({ type: String, example: 'OPEN', - enum: ['OPEN', 'CLOSED'], + // enum: ['OPEN', 'CLOSED'], nullable: true, description: 'The status of the ticket. Authorized values are OPEN or CLOSED.', }) - @IsIn(['OPEN', 'CLOSED'], { + /*@IsIn(['OPEN', 'CLOSED'], { message: 'Type must be either OPEN or CLOSED', - }) + })*/ @IsOptional() status?: TicketStatus | string; @@ -58,14 +58,14 @@ export class UnifiedTicketingTicketInput { @ApiPropertyOptional({ type: String, example: 'BUG', - enum: ['BUG', 'SUBTASK', 'TASK', 'TO-DO'], + // enum: ['BUG', 'SUBTASK', 'TASK', 'TO-DO'], nullable: true, description: 'The type of the ticket. Authorized values are PROBLEM, QUESTION, or TASK', }) - @IsIn(['BUG', 'SUBTASK', 'TASK', 'TO-DO'], { + /*@IsIn(['BUG', 'SUBTASK', 'TASK', 'TO-DO'], { message: 'Type must be either BUG, SUBTASK, TASK or TO-DO', - }) + })*/ @IsOptional() type?: TicketType | string; @@ -122,14 +122,14 @@ export class UnifiedTicketingTicketInput { @ApiPropertyOptional({ type: String, example: 'HIGH', - enum: ['HIGH', 'MEDIUM', 'LOW'], + // enum: ['HIGH', 'MEDIUM', 'LOW'], nullable: true, description: 'The priority of the ticket. Authorized values are HIGH, MEDIUM or LOW.', }) - @IsIn(['HIGH', 'MEDIUM', 'LOW'], { + /*@IsIn(['HIGH', 'MEDIUM', 'LOW'], { message: 'Type must be either HIGH, MEDIUM or LOW', - }) + })*/ @IsOptional() priority?: TicketPriority | string; diff --git a/packages/api/swagger/swagger-spec.yaml b/packages/api/swagger/swagger-spec.yaml index 8abc627f7..6a58b8106 100644 --- a/packages/api/swagger/swagger-spec.yaml +++ b/packages/api/swagger/swagger-spec.yaml @@ -9872,9 +9872,6 @@ components: type: string nullable: true example: USER - enum: &ref_121 - - USER - - CONTACT description: >- The creator type of the comment. Authorized values are either USER or CONTACT @@ -9899,12 +9896,12 @@ components: specified) attachments: type: array - items: &ref_122 + items: &ref_114 oneOf: - type: string - $ref: '#/components/schemas/UnifiedTicketingAttachmentOutput' nullable: true - example: &ref_123 + example: &ref_115 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f description: The attachements UUIDs tied to the comment required: @@ -9920,9 +9917,6 @@ components: status: type: string example: OPEN - enum: &ref_92 - - OPEN - - CLOSED nullable: true description: The status of the ticket. Authorized values are OPEN or CLOSED. description: @@ -9939,11 +9933,6 @@ components: type: type: string example: BUG - enum: &ref_93 - - BUG - - SUBTASK - - TASK - - TO-DO nullable: true description: >- The type of the ticket. Authorized values are PROBLEM, QUESTION, or @@ -9955,21 +9944,21 @@ components: description: The UUID of the parent ticket collections: type: array - items: &ref_94 + items: &ref_92 oneOf: - type: string - $ref: '#/components/schemas/UnifiedTicketingCollectionOutput' - example: &ref_95 + example: &ref_93 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: The collection UUIDs the ticket belongs to tags: type: array - items: &ref_96 + items: &ref_94 oneOf: - type: string - $ref: '#/components/schemas/UnifiedTicketingTagOutput' - example: &ref_97 + example: &ref_95 - my_tag - urgent_tag nullable: true @@ -9983,16 +9972,12 @@ components: priority: type: string example: HIGH - enum: &ref_98 - - HIGH - - MEDIUM - - LOW nullable: true description: >- The priority of the ticket. Authorized values are HIGH, MEDIUM or LOW. assigned_to: - example: &ref_99 + example: &ref_96 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: The users UUIDs the ticket is assigned to @@ -10000,7 +9985,7 @@ components: items: type: string comment: - example: &ref_100 + example: &ref_97 content: Assigned the issue ! nullable: true description: The comment of the ticket @@ -10018,17 +10003,17 @@ components: description: The UUID of the contact which the ticket belongs to attachments: type: array - items: &ref_101 + items: &ref_98 oneOf: - type: string - $ref: '#/components/schemas/UnifiedTicketingAttachmentInput' - example: &ref_102 + example: &ref_99 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f description: The attachements UUIDs tied to the ticket nullable: true field_mappings: type: object - example: &ref_103 + example: &ref_100 fav_dish: broccoli fav_color: red nullable: true @@ -10081,7 +10066,6 @@ components: status: type: string example: OPEN - enum: *ref_92 nullable: true description: The status of the ticket. Authorized values are OPEN or CLOSED. description: @@ -10098,7 +10082,6 @@ components: type: type: string example: BUG - enum: *ref_93 nullable: true description: >- The type of the ticket. Authorized values are PROBLEM, QUESTION, or @@ -10110,14 +10093,14 @@ components: description: The UUID of the parent ticket collections: type: array - items: *ref_94 - example: *ref_95 + items: *ref_92 + example: *ref_93 nullable: true description: The collection UUIDs the ticket belongs to tags: type: array - items: *ref_96 - example: *ref_97 + items: *ref_94 + example: *ref_95 nullable: true description: The tags names of the ticket completed_at: @@ -10129,20 +10112,19 @@ components: priority: type: string example: HIGH - enum: *ref_98 nullable: true description: >- The priority of the ticket. Authorized values are HIGH, MEDIUM or LOW. assigned_to: - example: *ref_99 + example: *ref_96 nullable: true description: The users UUIDs the ticket is assigned to type: array items: type: string comment: - example: *ref_100 + example: *ref_97 nullable: true description: The comment of the ticket allOf: @@ -10159,13 +10141,13 @@ components: description: The UUID of the contact which the ticket belongs to attachments: type: array - items: *ref_101 - example: *ref_102 + items: *ref_98 + example: *ref_99 description: The attachements UUIDs tied to the ticket nullable: true field_mappings: type: object - example: *ref_103 + example: *ref_100 nullable: true description: >- The custom field mappings of the ticket between the remote 3rd party @@ -10415,9 +10397,6 @@ components: description: The email address email_address_type: type: string - enum: - - PERSONAL - - WORK nullable: true description: >- The email address type. Authorized values are either PERSONAL or @@ -10467,9 +10446,6 @@ components: description: The country address_type: type: string - enum: - - PERSONAL - - WORK nullable: true example: PERSONAL description: The address type. Authorized values are either PERSONAL or WORK. @@ -10497,9 +10473,6 @@ components: code (e.g +336676778890 for France) phone_type: type: string - enum: - - MOBILE - - WORK nullable: true description: The phone type. Authorized values are either MOBILE or WORK owner_type: @@ -10520,154 +10493,6 @@ components: industry: type: string example: ACCOUNTING - enum: &ref_104 - - ACCOUNTING - - AIRLINES_AVIATION - - ALTERNATIVE_DISPUTE_RESOLUTION - - ALTERNATIVE_MEDICINE - - ANIMATION - - APPAREL_FASHION - - ARCHITECTURE_PLANNING - - ARTS_AND_CRAFTS - - AUTOMOTIVE - - AVIATION_AEROSPACE - - BANKING - - BIOTECHNOLOGY - - BROADCAST_MEDIA - - BUILDING_MATERIALS - - BUSINESS_SUPPLIES_AND_EQUIPMENT - - CAPITAL_MARKETS - - CHEMICALS - - CIVIC_SOCIAL_ORGANIZATION - - CIVIL_ENGINEERING - - COMMERCIAL_REAL_ESTATE - - COMPUTER_NETWORK_SECURITY - - COMPUTER_GAMES - - COMPUTER_HARDWARE - - COMPUTER_NETWORKING - - COMPUTER_SOFTWARE - - INTERNET - - CONSTRUCTION - - CONSUMER_ELECTRONICS - - CONSUMER_GOODS - - CONSUMER_SERVICES - - COSMETICS - - DAIRY - - DEFENSE_SPACE - - DESIGN - - EDUCATION_MANAGEMENT - - E_LEARNING - - ELECTRICAL_ELECTRONIC_MANUFACTURING - - ENTERTAINMENT - - ENVIRONMENTAL_SERVICES - - EVENTS_SERVICES - - EXECUTIVE_OFFICE - - FACILITIES_SERVICES - - FARMING - - FINANCIAL_SERVICES - - FINE_ART - - FISHERY - - FOOD_BEVERAGES - - FOOD_PRODUCTION - - FUND_RAISING - - FURNITURE - - GAMBLING_CASINOS - - GLASS_CERAMICS_CONCRETE - - GOVERNMENT_ADMINISTRATION - - GOVERNMENT_RELATIONS - - GRAPHIC_DESIGN - - HEALTH_WELLNESS_AND_FITNESS - - HIGHER_EDUCATION - - HOSPITAL_HEALTH_CARE - - HOSPITALITY - - HUMAN_RESOURCES - - IMPORT_AND_EXPORT - - INDIVIDUAL_FAMILY_SERVICES - - INDUSTRIAL_AUTOMATION - - INFORMATION_SERVICES - - INFORMATION_TECHNOLOGY_AND_SERVICES - - INSURANCE - - INTERNATIONAL_AFFAIRS - - INTERNATIONAL_TRADE_AND_DEVELOPMENT - - INVESTMENT_BANKING - - INVESTMENT_MANAGEMENT - - JUDICIARY - - LAW_ENFORCEMENT - - LAW_PRACTICE - - LEGAL_SERVICES - - LEGISLATIVE_OFFICE - - LEISURE_TRAVEL_TOURISM - - LIBRARIES - - LOGISTICS_AND_SUPPLY_CHAIN - - LUXURY_GOODS_JEWELRY - - MACHINERY - - MANAGEMENT_CONSULTING - - MARITIME - - MARKET_RESEARCH - - MARKETING_AND_ADVERTISING - - MECHANICAL_OR_INDUSTRIAL_ENGINEERING - - MEDIA_PRODUCTION - - MEDICAL_DEVICES - - MEDICAL_PRACTICE - - MENTAL_HEALTH_CARE - - MILITARY - - MINING_METALS - - MOTION_PICTURES_AND_FILM - - MUSEUMS_AND_INSTITUTIONS - - MUSIC - - NANOTECHNOLOGY - - NEWSPAPERS - - NON_PROFIT_ORGANIZATION_MANAGEMENT - - OIL_ENERGY - - ONLINE_MEDIA - - OUTSOURCING_OFFSHORING - - PACKAGE_FREIGHT_DELIVERY - - PACKAGING_AND_CONTAINERS - - PAPER_FOREST_PRODUCTS - - PERFORMING_ARTS - - PHARMACEUTICALS - - PHILANTHROPY - - PHOTOGRAPHY - - PLASTICS - - POLITICAL_ORGANIZATION - - PRIMARY_SECONDARY_EDUCATION - - PRINTING - - PROFESSIONAL_TRAINING_COACHING - - PROGRAM_DEVELOPMENT - - PUBLIC_POLICY - - PUBLIC_RELATIONS_AND_COMMUNICATIONS - - PUBLIC_SAFETY - - PUBLISHING - - RAILROAD_MANUFACTURE - - RANCHING - - REAL_ESTATE - - RECREATIONAL_FACILITIES_AND_SERVICES - - RELIGIOUS_INSTITUTIONS - - RENEWABLES_ENVIRONMENT - - RESEARCH - - RESTAURANTS - - RETAIL - - SECURITY_AND_INVESTIGATIONS - - SEMICONDUCTORS - - SHIPBUILDING - - SPORTING_GOODS - - SPORTS - - STAFFING_AND_RECRUITING - - SUPERMARKETS - - TELECOMMUNICATIONS - - TEXTILES - - THINK_TANKS - - TOBACCO - - TRANSLATION_AND_LOCALIZATION - - TRANSPORTATION_TRUCKING_RAILROAD - - UTILITIES - - VENTURE_CAPITAL_PRIVATE_EQUITY - - VETERINARY - - WAREHOUSING - - WHOLESALE - - WINE_AND_SPIRITS - - WIRELESS - - WRITING_AND_EDITING description: >- The industry of the company. Authorized values can be found in the Industry enum. @@ -10684,7 +10509,7 @@ components: nullable: true email_addresses: description: The email addresses of the company - example: &ref_105 + example: &ref_101 - email_address: acme@gmail.com email_address_type: WORK nullable: true @@ -10693,7 +10518,7 @@ components: $ref: '#/components/schemas/Email' addresses: description: The addresses of the company - example: &ref_106 + example: &ref_102 - street_1: 5th Avenue city: New York state: NY @@ -10705,7 +10530,7 @@ components: $ref: '#/components/schemas/Address' phone_numbers: description: The phone numbers of the company - example: &ref_107 + example: &ref_103 - phone_number: '+33660606067' phone_type: WORK nullable: true @@ -10714,7 +10539,7 @@ components: $ref: '#/components/schemas/Phone' field_mappings: type: object - example: &ref_108 + example: &ref_104 fav_dish: broccoli fav_color: red description: >- @@ -10763,7 +10588,6 @@ components: industry: type: string example: ACCOUNTING - enum: *ref_104 description: >- The industry of the company. Authorized values can be found in the Industry enum. @@ -10780,28 +10604,28 @@ components: nullable: true email_addresses: description: The email addresses of the company - example: *ref_105 + example: *ref_101 nullable: true type: array items: $ref: '#/components/schemas/Email' addresses: description: The addresses of the company - example: *ref_106 + example: *ref_102 nullable: true type: array items: $ref: '#/components/schemas/Address' phone_numbers: description: The phone numbers of the company - example: *ref_107 + example: *ref_103 nullable: true type: array items: $ref: '#/components/schemas/Phone' field_mappings: type: object - example: *ref_108 + example: *ref_104 description: >- The custom field mappings of the company between the remote 3rd party & Panora @@ -10825,7 +10649,7 @@ components: email_addresses: nullable: true description: The email addresses of the contact - example: &ref_109 + example: &ref_105 - email: john.doe@example.com type: WORK type: array @@ -10834,7 +10658,7 @@ components: phone_numbers: nullable: true description: The phone numbers of the contact - example: &ref_110 + example: &ref_106 - phone: '1234567890' type: WORK type: array @@ -10843,7 +10667,7 @@ components: addresses: nullable: true description: The addresses of the contact - example: &ref_111 + example: &ref_107 - street: 123 Main St city: Anytown state: CA @@ -10860,7 +10684,7 @@ components: example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f field_mappings: type: object - example: &ref_112 + example: &ref_108 fav_dish: broccoli fav_color: red nullable: true @@ -10917,21 +10741,21 @@ components: email_addresses: nullable: true description: The email addresses of the contact - example: *ref_109 + example: *ref_105 type: array items: $ref: '#/components/schemas/Email' phone_numbers: nullable: true description: The phone numbers of the contact - example: *ref_110 + example: *ref_106 type: array items: $ref: '#/components/schemas/Phone' addresses: nullable: true description: The addresses of the contact - example: *ref_111 + example: *ref_107 type: array items: $ref: '#/components/schemas/Address' @@ -10942,7 +10766,7 @@ components: example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f field_mappings: type: object - example: *ref_112 + example: *ref_108 nullable: true description: >- The custom field mappings of the contact between the remote 3rd @@ -10987,7 +10811,7 @@ components: field_mappings: type: object nullable: true - example: &ref_113 + example: &ref_109 fav_dish: broccoli fav_color: red description: >- @@ -11064,7 +10888,7 @@ components: field_mappings: type: object nullable: true - example: *ref_113 + example: *ref_109 description: >- The custom field mappings of the company between the remote 3rd party & Panora @@ -11085,9 +10909,6 @@ components: type: string nullable: true example: INBOUND - enum: &ref_114 - - INBOUND - - OUTBOUND description: >- The direction of the engagement. Authorized values are INBOUND or OUTBOUND @@ -11112,10 +10933,6 @@ components: type: string nullable: true example: MEETING - enum: &ref_115 - - EMAIL - - CALL - - MEETING description: >- The type of the engagement. Authorized values are EMAIL, CALL or MEETING @@ -11131,7 +10948,7 @@ components: description: The UUID of the company tied to the engagement contacts: nullable: true - example: &ref_116 + example: &ref_110 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f description: The UUIDs of contacts tied to the engagement object type: array @@ -11140,7 +10957,7 @@ components: field_mappings: type: object nullable: true - example: &ref_117 + example: &ref_111 fav_dish: broccoli fav_color: red description: >- @@ -11193,7 +11010,6 @@ components: type: string nullable: true example: INBOUND - enum: *ref_114 description: >- The direction of the engagement. Authorized values are INBOUND or OUTBOUND @@ -11218,7 +11034,6 @@ components: type: string nullable: true example: MEETING - enum: *ref_115 description: >- The type of the engagement. Authorized values are EMAIL, CALL or MEETING @@ -11234,7 +11049,7 @@ components: description: The UUID of the company tied to the engagement contacts: nullable: true - example: *ref_116 + example: *ref_110 description: The UUIDs of contacts tied to the engagement object type: array items: @@ -11242,7 +11057,7 @@ components: field_mappings: type: object nullable: true - example: *ref_117 + example: *ref_111 description: >- The custom field mappings of the engagement between the remote 3rd party & Panora @@ -11279,7 +11094,7 @@ components: description: The UUID of the deal tied to the note field_mappings: type: object - example: &ref_118 + example: &ref_112 fav_dish: broccoli fav_color: red nullable: true @@ -11349,7 +11164,7 @@ components: description: The UUID of the deal tied to the note field_mappings: type: object - example: *ref_118 + example: *ref_112 nullable: true description: >- The custom field mappings of the note between the remote 3rd party & @@ -11421,9 +11236,6 @@ components: status: type: string example: PENDING - enum: &ref_119 - - PENDING - - COMPLETED description: The status of the task. Authorized values are PENDING, COMPLETED. nullable: true due_date: @@ -11453,7 +11265,7 @@ components: nullable: true field_mappings: type: object - example: &ref_120 + example: &ref_113 fav_dish: broccoli fav_color: red description: >- @@ -11512,7 +11324,6 @@ components: status: type: string example: PENDING - enum: *ref_119 description: The status of the task. Authorized values are PENDING, COMPLETED. nullable: true due_date: @@ -11542,7 +11353,7 @@ components: nullable: true field_mappings: type: object - example: *ref_120 + example: *ref_113 description: >- The custom field mappings of the task between the remote 3rd party & Panora @@ -11624,9 +11435,6 @@ components: collection_type: type: string example: PROJECT - enum: - - PROJECT - - LIST nullable: true description: >- The type of the collection. Authorized values are either PROJECT or @@ -11685,7 +11493,6 @@ components: type: string nullable: true example: USER - enum: *ref_121 description: >- The creator type of the comment. Authorized values are either USER or CONTACT @@ -11710,9 +11517,9 @@ components: specified) attachments: type: array - items: *ref_122 + items: *ref_114 nullable: true - example: *ref_123 + example: *ref_115 description: The attachements UUIDs tied to the comment id: type: string @@ -12345,9 +12152,6 @@ components: account_type: type: string example: CHECKING - enum: - - SAVINGS - - CHECKING nullable: true description: The type of the bank account bank_name: @@ -12613,10 +12417,6 @@ components: relationship: type: string example: CHILD - enum: - - CHILD - - SPOUSE - - DOMESTIC_PARTNER nullable: true description: The relationship of the dependent to the employee date_of_birth: @@ -12628,12 +12428,6 @@ components: gender: type: string example: MALE - enum: - - MALE - - FEMALE - - NON-BINARY - - OTHER - - PREFER_NOT_TO_DISCLOSE nullable: true description: The gender of the dependent phone_number: @@ -12879,7 +12673,7 @@ components: type: object properties: groups: - example: &ref_124 + example: &ref_116 - Group1 - Group2 nullable: true @@ -12888,7 +12682,7 @@ components: items: type: string locations: - example: &ref_125 + example: &ref_117 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: UUIDs of the of the Location associated with the company @@ -12946,7 +12740,7 @@ components: nullable: true description: The mobile phone number of the employee employments: - example: &ref_126 + example: &ref_118 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true @@ -12962,37 +12756,16 @@ components: gender: type: string example: MALE - enum: &ref_127 - - MALE - - FEMALE - - NON-BINARY - - OTHER - - PREFER_NOT_TO_DISCLOSE nullable: true description: The gender of the employee ethnicity: type: string example: AMERICAN_INDIAN_OR_ALASKA_NATIVE - enum: &ref_128 - - AMERICAN_INDIAN_OR_ALASKA_NATIVE - - ASIAN_OR_INDIAN_SUBCONTINENT - - BLACK_OR_AFRICAN_AMERICAN - - HISPANIC_OR_LATINO - - NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER - - TWO_OR_MORE_RACES - - WHITE - - PREFER_NOT_TO_DISCLOSE nullable: true description: The ethnicity of the employee marital_status: type: string example: Married - enum: &ref_129 - - SINGLE - - MARRIED_FILING_JOINTLY - - MARRIED_FILING_SEPARATELY - - HEAD_OF_HOUSEHOLD - - QUALIFYING_WIDOW_OR_WIDOWER_WITH_DEPENDENT_CHILD nullable: true description: The marital status of the employee date_of_birth: @@ -13010,10 +12783,6 @@ components: employment_status: type: string example: ACTIVE - enum: &ref_130 - - ACTIVE - - PENDING - - INACTIVE nullable: true description: The employment status of the employee termination_date: @@ -13034,7 +12803,7 @@ components: description: UUID of the manager (employee) of the employee field_mappings: type: object - example: &ref_131 + example: &ref_119 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -13085,14 +12854,14 @@ components: type: object properties: groups: - example: *ref_124 + example: *ref_116 nullable: true description: The groups the employee belongs to type: array items: type: string locations: - example: *ref_125 + example: *ref_117 nullable: true description: UUIDs of the of the Location associated with the company type: array @@ -13149,7 +12918,7 @@ components: nullable: true description: The mobile phone number of the employee employments: - example: *ref_126 + example: *ref_118 nullable: true description: The employments of the employee type: array @@ -13163,19 +12932,16 @@ components: gender: type: string example: MALE - enum: *ref_127 nullable: true description: The gender of the employee ethnicity: type: string example: AMERICAN_INDIAN_OR_ALASKA_NATIVE - enum: *ref_128 nullable: true description: The ethnicity of the employee marital_status: type: string example: Married - enum: *ref_129 nullable: true description: The marital status of the employee date_of_birth: @@ -13193,7 +12959,6 @@ components: employment_status: type: string example: ACTIVE - enum: *ref_130 nullable: true description: The employment status of the employee termination_date: @@ -13214,7 +12979,7 @@ components: description: UUID of the manager (employee) of the employee field_mappings: type: object - example: *ref_131 + example: *ref_119 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -13225,12 +12990,6 @@ components: benefit_plan_type: type: string example: Health Insurance - enum: - - MEDICAL - - HEALTH_SAVINGS - - INSURANCE - - RETIREMENT - - OTHER nullable: true description: The type of the benefit plan name: @@ -13319,209 +13078,21 @@ components: pay_period: type: string example: MONTHLY - enum: - - HOUR - - DAY - - WEEK - - EVERY_TWO_WEEKS - - SEMIMONTHLY - - MONTH - - QUARTER - - EVERY_SIX_MONTHS - - YEAR nullable: true description: The pay period of the employment pay_frequency: type: string example: WEEKLY - enum: - - WEEKLY - - BIWEEKLY - - MONTHLY - - QUARTERLY - - SEMIANNUALLY - - ANNUALLY - - THIRTEEN-MONTHLY - - PRO_RATA - - SEMIMONTHLY nullable: true description: The pay frequency of the employment pay_currency: type: string example: USD - enum: - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency of the pay flsa_status: type: string example: EXEMPT - enum: - - EXEMPT - - SALARIED_NONEXEMPT - - NONEXEMPT - - OWNER nullable: true description: The FLSA status of the employment effective_date: @@ -13533,12 +13104,6 @@ components: employment_type: type: string example: FULL_TIME - enum: - - FULL_TIME - - PART_TIME - - INTERN - - CONTRACTOR - - FREELANCE nullable: true description: The type of employment pay_group_id: @@ -13616,12 +13181,6 @@ components: type: type: string example: DEPARTMENT - enum: - - TEAM - - DEPARTMENT - - COST_CENTER - - BUSINESS_UNIT - - GROUP nullable: true description: The type of the group field_mappings: @@ -13719,9 +13278,6 @@ components: location_type: type: string example: WORK - enum: - - WORK - - HOME nullable: true description: The type of the location company_id: @@ -13846,23 +13402,11 @@ components: run_state: type: string example: PAID - enum: - - PAID - - DRAFT - - APPROVED - - FAILED - - CLOSE nullable: true description: The state of the payroll run run_type: type: string example: REGULAR - enum: - - REGULAR - - OFF_CYCLE - - CORRECTION - - TERMINATION - - SIGN_ON_BONUS nullable: true description: The type of the payroll run start_date: @@ -13958,12 +13502,6 @@ components: status: type: string example: REQUESTED - enum: &ref_132 - - REQUESTED - - APPROVED - - DECLINED - - CANCELLED - - DELETED nullable: true description: The status of the time off request employee_note: @@ -13974,9 +13512,6 @@ components: units: type: string example: DAYS - enum: &ref_133 - - HOURS - - DAYS nullable: true description: The units used for the time off (e.g., Days, Hours) amount: @@ -13987,13 +13522,6 @@ components: request_type: type: string example: VACATION - enum: &ref_134 - - VACATION - - SICK - - PERSONAL - - JURY_DUTY - - VOLUNTEER - - BEREAVEMENT nullable: true description: The type of time off request start_time: @@ -14010,7 +13538,7 @@ components: description: The end time of the time off field_mappings: type: object - example: &ref_135 + example: &ref_120 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -14073,7 +13601,6 @@ components: status: type: string example: REQUESTED - enum: *ref_132 nullable: true description: The status of the time off request employee_note: @@ -14084,7 +13611,6 @@ components: units: type: string example: DAYS - enum: *ref_133 nullable: true description: The units used for the time off (e.g., Days, Hours) amount: @@ -14095,7 +13621,6 @@ components: request_type: type: string example: VACATION - enum: *ref_134 nullable: true description: The type of time off request start_time: @@ -14112,7 +13637,7 @@ components: description: The end time of the time off field_mappings: type: object - example: *ref_135 + example: *ref_120 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -14138,13 +13663,6 @@ components: policy_type: type: string example: VACATION - enum: - - VACATION - - SICK - - PERSONAL - - JURY_DUTY - - VOLUNTEER - - BEREAVEMENT nullable: true description: The type of time off policy field_mappings: @@ -14230,7 +13748,7 @@ components: description: Indicates if the timesheet entry was deleted in the remote system field_mappings: type: object - example: &ref_136 + example: &ref_121 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -14303,7 +13821,7 @@ components: description: Indicates if the timesheet entry was deleted in the remote system field_mappings: type: object - example: *ref_136 + example: *ref_121 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -14361,13 +13879,9 @@ components: properties: activity_type: type: string - enum: &ref_137 - - NOTE - - EMAIL - - OTHER example: NOTE nullable: true - description: The type of activity + description: The type of activity. NOTE, EMAIL or OTHER subject: type: string example: Email subject @@ -14380,13 +13894,9 @@ components: description: The body of the activity visibility: type: string - enum: &ref_138 - - ADMIN_ONLY - - PUBLIC - - PRIVATE example: PUBLIC nullable: true - description: The visibility of the activity + description: The visibility of the activity. ADMIN_ONLY, PUBLIC or PRIVATE candidate_id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f @@ -14400,7 +13910,7 @@ components: description: The remote creation date of the activity field_mappings: type: object - example: &ref_139 + example: &ref_122 fav_dish: broccoli fav_color: red additionalProperties: true @@ -14443,10 +13953,9 @@ components: properties: activity_type: type: string - enum: *ref_137 example: NOTE nullable: true - description: The type of activity + description: The type of activity. NOTE, EMAIL or OTHER subject: type: string example: Email subject @@ -14459,10 +13968,9 @@ components: description: The body of the activity visibility: type: string - enum: *ref_138 example: PUBLIC nullable: true - description: The visibility of the activity + description: The visibility of the activity. ADMIN_ONLY, PUBLIC or PRIVATE candidate_id: type: string example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f @@ -14476,7 +13984,7 @@ components: description: The remote creation date of the activity field_mappings: type: object - example: *ref_139 + example: *ref_122 additionalProperties: true nullable: true description: >- @@ -14500,7 +14008,7 @@ components: offers: nullable: true description: The offers UUIDs for the application - example: &ref_140 + example: &ref_123 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - 12345678-1234-1234-1234-123456789012 type: array @@ -14537,7 +14045,7 @@ components: example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f field_mappings: type: object - example: &ref_141 + example: &ref_124 fav_dish: broccoli fav_color: red additionalProperties: true @@ -14603,7 +14111,7 @@ components: offers: nullable: true description: The offers UUIDs for the application - example: *ref_140 + example: *ref_123 type: array items: type: string @@ -14638,7 +14146,7 @@ components: example: 801f9ede-c698-4e66-a7fc-48d19eebaa4f field_mappings: type: object - example: *ref_141 + example: *ref_124 additionalProperties: true nullable: true description: >- @@ -14660,11 +14168,6 @@ components: attachment_type: type: string example: RESUME - enum: &ref_142 - - RESUME - - COVER_LETTER - - OFFER_LETTER - - OTHER nullable: true description: The type of the file remote_created_at: @@ -14686,7 +14189,7 @@ components: description: The UUID of the candidate field_mappings: type: object - example: &ref_143 + example: &ref_125 fav_dish: broccoli fav_color: red additionalProperties: true @@ -14740,7 +14243,6 @@ components: attachment_type: type: string example: RESUME - enum: *ref_142 nullable: true description: The type of the file remote_created_at: @@ -14762,7 +14264,7 @@ components: description: The UUID of the candidate field_mappings: type: object - example: *ref_143 + example: *ref_125 additionalProperties: true nullable: true description: >- @@ -14840,37 +14342,37 @@ components: description: The last interaction date with the candidate attachments: type: array - items: &ref_144 + items: &ref_126 oneOf: - type: string - $ref: '#/components/schemas/UnifiedAtsAttachmentOutput' - example: &ref_145 + example: &ref_127 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: The attachments UUIDs of the candidate applications: type: array - items: &ref_146 + items: &ref_128 oneOf: - type: string - $ref: '#/components/schemas/UnifiedAtsApplicationOutput' - example: &ref_147 + example: &ref_129 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: The applications UUIDs of the candidate tags: type: array - items: &ref_148 + items: &ref_130 oneOf: - type: string - $ref: '#/components/schemas/UnifiedAtsTagOutput' - example: &ref_149 + example: &ref_131 - tag_1 - tag_2 nullable: true description: The tags of the candidate urls: - example: &ref_150 + example: &ref_132 - url: mywebsite.com url_type: WEBSITE nullable: true @@ -14881,7 +14383,7 @@ components: items: $ref: '#/components/schemas/Url' phone_numbers: - example: &ref_151 + example: &ref_133 - phone_number: '+33660688899' phone_type: WORK nullable: true @@ -14890,7 +14392,7 @@ components: items: $ref: '#/components/schemas/Phone' email_addresses: - example: &ref_152 + example: &ref_134 - email_address: joedoe@gmail.com email_address_type: WORK nullable: true @@ -14900,7 +14402,7 @@ components: $ref: '#/components/schemas/Email' field_mappings: type: object - example: &ref_153 + example: &ref_135 fav_dish: broccoli fav_color: red additionalProperties: true @@ -14996,24 +14498,24 @@ components: description: The last interaction date with the candidate attachments: type: array - items: *ref_144 - example: *ref_145 + items: *ref_126 + example: *ref_127 nullable: true description: The attachments UUIDs of the candidate applications: type: array - items: *ref_146 - example: *ref_147 + items: *ref_128 + example: *ref_129 nullable: true description: The applications UUIDs of the candidate tags: type: array - items: *ref_148 - example: *ref_149 + items: *ref_130 + example: *ref_131 nullable: true description: The tags of the candidate urls: - example: *ref_150 + example: *ref_132 nullable: true description: >- The urls of the candidate, possible values for Url type are WEBSITE, @@ -15022,14 +14524,14 @@ components: items: $ref: '#/components/schemas/Url' phone_numbers: - example: *ref_151 + example: *ref_133 nullable: true description: The phone numbers of the candidate type: array items: $ref: '#/components/schemas/Phone' email_addresses: - example: *ref_152 + example: *ref_134 nullable: true description: The email addresses of the candidate type: array @@ -15037,7 +14539,7 @@ components: $ref: '#/components/schemas/Email' field_mappings: type: object - example: *ref_153 + example: *ref_135 additionalProperties: true nullable: true description: >- @@ -15097,10 +14599,6 @@ components: properties: status: type: string - enum: &ref_154 - - SCHEDULED - - AWAITING_FEEDBACK - - COMPLETED example: SCHEDULED nullable: true description: The status of the interview @@ -15120,7 +14618,7 @@ components: nullable: true description: The UUID of the organizer interviewers: - example: &ref_155 + example: &ref_136 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: The UUIDs of the interviewers @@ -15158,7 +14656,7 @@ components: description: The remote modification date of the interview field_mappings: type: object - example: &ref_156 + example: &ref_137 fav_dish: broccoli fav_color: red additionalProperties: true @@ -15201,7 +14699,6 @@ components: properties: status: type: string - enum: *ref_154 example: SCHEDULED nullable: true description: The status of the interview @@ -15221,7 +14718,7 @@ components: nullable: true description: The UUID of the organizer interviewers: - example: *ref_155 + example: *ref_136 nullable: true description: The UUIDs of the interviewers type: array @@ -15258,7 +14755,7 @@ components: description: The remote modification date of the interview field_mappings: type: object - example: *ref_156 + example: *ref_137 additionalProperties: true nullable: true description: >- @@ -15346,22 +14843,12 @@ components: description: The code of the job status: type: string - enum: - - OPEN - - CLOSED - - DRAFT - - ARCHIVED - - PENDING example: OPEN nullable: true description: The status of the job type: type: string example: POSTING - enum: - - POSTING - - REQUISITION - - PROFILE nullable: true description: The type of the job confidential: @@ -15489,16 +14976,6 @@ components: status: type: string example: DRAFT - enum: - - DRAFT - - APPROVAL_SENT - - APPROVED - - SENT - - SENT_MANUALLY - - OPENED - - DENIED - - SIGNED - - DEPRECATED description: The status of the offer nullable: true application_id: @@ -15649,12 +15126,6 @@ components: properties: overall_recommendation: type: string - enum: - - DEFINITELY_NO - - 'NO' - - 'YES' - - STRONG_YES - - NO_DECISION example: 'YES' nullable: true description: The overall recommendation @@ -15798,12 +15269,6 @@ components: access_role: type: string example: ADMIN - enum: - - SUPER_ADMIN - - ADMIN - - TEAM_MEMBER - - LIMITED_TEAM_MEMBER - - INTERVIEWER description: The access role of the user nullable: true remote_created_at: @@ -15874,45 +15339,21 @@ components: description: The submission date of the EEOC race: type: string - enum: - - AMERICAN_INDIAN_OR_ALASKAN_NATIVE - - ASIAN - - BLACK_OR_AFRICAN_AMERICAN - - HISPANIC_OR_LATINO - - WHITE - - NATIVE_HAWAIIAN_OR_OTHER_PACIFIC_ISLANDER - - TWO_OR_MORE_RACES - - DECLINE_TO_SELF_IDENTIFY example: AMERICAN_INDIAN_OR_ALASKAN_NATIVE nullable: true description: The race of the candidate gender: type: string example: MALE - enum: - - MALE - - FEMALE - - NON_BINARY - - OTHER - - DECLINE_TO_SELF_IDENTIFY nullable: true description: The gender of the candidate veteran_status: type: string example: I_AM_NOT_A_PROTECTED_VETERAN - enum: - - I_AM_NOT_A_PROTECTED_VETERAN - - >- - I_IDENTIFY_AS_ONE_OR_MORE_OF_THE_CLASSIFICATIONS_OF_A_PROTECTED_VETERAN - - I_DONT_WISH_TO_ANSWER nullable: true description: The veteran status of the candidate disability_status: type: string - enum: - - YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY - - NO_I_DONT_HAVE_A_DISABILITY - - I_DONT_WISH_TO_ANSWER example: YES_I_HAVE_A_DISABILITY_OR_PREVIOUSLY_HAD_A_DISABILITY nullable: true description: The disability status of the candidate @@ -15992,169 +15433,6 @@ components: currency: type: string example: USD - enum: &ref_157 - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency of the account account_number: @@ -16174,7 +15452,7 @@ components: description: The UUID of the associated company info field_mappings: type: object - example: &ref_158 + example: &ref_138 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -16246,7 +15524,6 @@ components: currency: type: string example: USD - enum: *ref_157 nullable: true description: The currency of the account account_number: @@ -16266,7 +15543,7 @@ components: description: The UUID of the associated company info field_mappings: type: object - example: *ref_158 + example: *ref_138 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -16382,7 +15659,7 @@ components: description: The UUID of the associated account field_mappings: type: object - example: &ref_159 + example: &ref_139 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -16438,7 +15715,7 @@ components: description: The UUID of the associated account field_mappings: type: object - example: *ref_159 + example: *ref_139 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -16503,169 +15780,6 @@ components: currency: type: string example: USD - enum: - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency used in the balance sheet company_info_id: @@ -16772,169 +15886,6 @@ components: currency: type: string example: USD - enum: - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency used in the cash flow statement company_id: @@ -17050,169 +16001,6 @@ components: currency: type: string example: USD - enum: - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency used by the company urls: @@ -17314,169 +16102,6 @@ components: type: string example: USD nullable: true - enum: &ref_160 - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL description: The currency associated with the contact remote_updated_at: type: string @@ -17490,7 +16115,7 @@ components: description: The UUID of the associated company info field_mappings: type: object - example: &ref_161 + example: &ref_140 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -17563,7 +16188,6 @@ components: type: string example: USD nullable: true - enum: *ref_160 description: The currency associated with the contact remote_updated_at: type: string @@ -17577,7 +16201,7 @@ components: description: The UUID of the associated company info field_mappings: type: object - example: *ref_161 + example: *ref_140 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -17637,169 +16261,6 @@ components: currency: type: string example: USD - enum: - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency of the credit note payments: @@ -17902,169 +16363,6 @@ components: currency: type: string example: USD - enum: &ref_162 - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency of the expense exchange_rate: @@ -18093,7 +16391,7 @@ components: nullable: true description: The UUID of the associated company info tracking_categories: - example: &ref_163 + example: &ref_141 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: The UUIDs of the tracking categories associated with the expense @@ -18107,7 +16405,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_164 + example: &ref_142 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -18176,7 +16474,6 @@ components: currency: type: string example: USD - enum: *ref_162 nullable: true description: The currency of the expense exchange_rate: @@ -18205,7 +16502,7 @@ components: nullable: true description: The UUID of the associated company info tracking_categories: - example: *ref_163 + example: *ref_141 nullable: true description: The UUIDs of the tracking categories associated with the expense type: array @@ -18218,7 +16515,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_164 + example: *ref_142 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -18234,169 +16531,6 @@ components: currency: type: string example: USD - enum: - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency used in the income statement start_period: @@ -18507,169 +16641,6 @@ components: currency: type: string example: USD - enum: &ref_165 - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency of the invoice exchange_rate: @@ -18718,7 +16689,7 @@ components: nullable: true description: The UUID of the associated accounting period tracking_categories: - example: &ref_166 + example: &ref_143 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true @@ -18733,7 +16704,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_167 + example: &ref_144 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -18814,7 +16785,6 @@ components: currency: type: string example: USD - enum: *ref_165 nullable: true description: The currency of the invoice exchange_rate: @@ -18863,7 +16833,7 @@ components: nullable: true description: The UUID of the associated accounting period tracking_categories: - example: *ref_166 + example: *ref_143 nullable: true description: The UUIDs of the tracking categories associated with the invoice type: array @@ -18876,7 +16846,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_167 + example: *ref_144 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -18973,7 +16943,7 @@ components: nullable: true description: The date of the transaction payments: - example: &ref_168 + example: &ref_145 - payment1 - payment2 nullable: true @@ -18982,7 +16952,7 @@ components: items: type: string applied_payments: - example: &ref_169 + example: &ref_146 - appliedPayment1 - appliedPayment2 nullable: true @@ -18998,169 +16968,6 @@ components: currency: type: string example: USD - enum: &ref_170 - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency of the journal entry exchange_rate: @@ -19179,7 +16986,7 @@ components: nullable: true description: The journal number tracking_categories: - example: &ref_171 + example: &ref_147 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: >- @@ -19205,7 +17012,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_172 + example: &ref_148 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -19265,14 +17072,14 @@ components: nullable: true description: The date of the transaction payments: - example: *ref_168 + example: *ref_145 nullable: true description: The payments associated with the journal entry type: array items: type: string applied_payments: - example: *ref_169 + example: *ref_146 nullable: true description: The applied payments for the journal entry type: array @@ -19286,7 +17093,6 @@ components: currency: type: string example: USD - enum: *ref_170 nullable: true description: The currency of the journal entry exchange_rate: @@ -19305,7 +17111,7 @@ components: nullable: true description: The journal number tracking_categories: - example: *ref_171 + example: *ref_147 nullable: true description: >- The UUIDs of the tracking categories associated with the journal @@ -19330,7 +17136,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_172 + example: *ref_148 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -19362,169 +17168,6 @@ components: currency: type: string example: USD - enum: &ref_173 - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency of the payment exchange_rate: @@ -19553,7 +17196,7 @@ components: nullable: true description: The UUID of the associated accounting period tracking_categories: - example: &ref_174 + example: &ref_149 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: The UUIDs of the tracking categories associated with the payment @@ -19567,7 +17210,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_175 + example: &ref_150 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -19636,7 +17279,6 @@ components: currency: type: string example: USD - enum: *ref_173 nullable: true description: The currency of the payment exchange_rate: @@ -19665,7 +17307,7 @@ components: nullable: true description: The UUID of the associated accounting period tracking_categories: - example: *ref_174 + example: *ref_149 nullable: true description: The UUIDs of the tracking categories associated with the payment type: array @@ -19678,7 +17320,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_175 + example: *ref_150 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -19802,169 +17444,6 @@ components: currency: type: string example: USD - enum: &ref_176 - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency of the purchase order exchange_rate: @@ -19973,7 +17452,7 @@ components: nullable: true description: The exchange rate applied to the purchase order tracking_categories: - example: &ref_177 + example: &ref_151 - 801f9ede-c698-4e66-a7fc-48d19eebaa4f nullable: true description: >- @@ -19994,7 +17473,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_178 + example: &ref_152 custom_field_1: value1 custom_field_2: value2 nullable: true @@ -20104,7 +17583,6 @@ components: currency: type: string example: USD - enum: *ref_176 nullable: true description: The currency of the purchase order exchange_rate: @@ -20113,7 +17591,7 @@ components: nullable: true description: The exchange rate applied to the purchase order tracking_categories: - example: *ref_177 + example: *ref_151 nullable: true description: >- The UUIDs of the tracking categories associated with the purchase @@ -20133,7 +17611,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_178 + example: *ref_152 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -20296,169 +17774,6 @@ components: currency: type: string example: USD - enum: - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL nullable: true description: The currency of the transaction tracking_categories: @@ -20568,169 +17883,6 @@ components: type: string example: USD nullable: true - enum: - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL description: The currency of the vendor credit exchange_rate: type: string @@ -20910,7 +18062,7 @@ components: nullable: true field_mappings: type: object - example: &ref_179 + example: &ref_153 fav_dish: broccoli fav_color: red description: >- @@ -20996,7 +18148,7 @@ components: nullable: true field_mappings: type: object - example: *ref_179 + example: *ref_153 description: >- The custom field mappings of the object between the remote 3rd party & Panora @@ -21054,7 +18206,7 @@ components: description: The UUID of the permission tied to the folder field_mappings: type: object - example: &ref_180 + example: &ref_154 fav_dish: broccoli fav_color: red additionalProperties: true @@ -21145,7 +18297,7 @@ components: description: The UUID of the permission tied to the folder field_mappings: type: object - example: *ref_180 + example: *ref_154 additionalProperties: true nullable: true description: >- @@ -21310,13 +18462,9 @@ components: type: string example: ACTIVE nullable: true - enum: &ref_181 - - ARCHIVED - - ACTIVE - - DRAFT description: The status of the product. Either ACTIVE, DRAFT OR ARCHIVED. images_urls: - example: &ref_182 + example: &ref_155 - https://myproduct/image nullable: true description: The URLs of the product images @@ -21334,7 +18482,7 @@ components: nullable: true description: The vendor of the product variants: - example: &ref_183 + example: &ref_156 - title: teeshirt price: 20 sku: '3' @@ -21346,7 +18494,7 @@ components: items: $ref: '#/components/schemas/Variant' tags: - example: &ref_184 + example: &ref_157 - tag_1 nullable: true description: The tags associated with the product @@ -21355,7 +18503,7 @@ components: type: string field_mappings: type: object - example: &ref_185 + example: &ref_158 fav_dish: broccoli fav_color: red nullable: true @@ -21406,10 +18554,9 @@ components: type: string example: ACTIVE nullable: true - enum: *ref_181 description: The status of the product. Either ACTIVE, DRAFT OR ARCHIVED. images_urls: - example: *ref_182 + example: *ref_155 nullable: true description: The URLs of the product images type: array @@ -21426,13 +18573,13 @@ components: nullable: true description: The vendor of the product variants: - example: *ref_183 + example: *ref_156 description: The variants of the product type: array items: $ref: '#/components/schemas/Variant' tags: - example: *ref_184 + example: *ref_157 nullable: true description: The tags associated with the product type: array @@ -21440,7 +18587,7 @@ components: type: string field_mappings: type: object - example: *ref_185 + example: *ref_158 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -21451,11 +18598,6 @@ components: order_status: type: string example: UNSHIPPED - enum: &ref_186 - - PENDING - - UNSHIPPED - - SHIPPED - - CANCELED nullable: true description: The status of the order order_number: @@ -21466,178 +18608,12 @@ components: payment_status: type: string example: SUCCESS - enum: &ref_187 - - SUCCESS - - FAIL nullable: true description: The payment status of the order currency: type: string nullable: true example: AUD - enum: &ref_188 - - AED - - AFN - - ALL - - AMD - - ANG - - AOA - - ARS - - AUD - - AWG - - AZN - - BAM - - BBD - - BDT - - BGN - - BHD - - BIF - - BMD - - BND - - BOB - - BRL - - BSD - - BTN - - BWP - - BYN - - BZD - - CAD - - CDF - - CHF - - CLP - - CNY - - COP - - CRC - - CUP - - CVE - - CZK - - DJF - - DKK - - DOP - - DZD - - EGP - - ERN - - ETB - - EUR - - FJD - - FKP - - FOK - - GBP - - GEL - - GGP - - GHS - - GIP - - GMD - - GNF - - GTQ - - GYD - - HKD - - HNL - - HRK - - HTG - - HUF - - IDR - - ILS - - IMP - - INR - - IQD - - IRR - - ISK - - JEP - - JMD - - JOD - - JPY - - KES - - KGS - - KHR - - KID - - KMF - - KRW - - KWD - - KYD - - KZT - - LAK - - LBP - - LKR - - LRD - - LSL - - LYD - - MAD - - MDL - - MGA - - MKD - - MMK - - MNT - - MOP - - MRU - - MUR - - MVR - - MWK - - MXN - - MYR - - MZN - - NAD - - NGN - - NIO - - NOK - - NPR - - NZD - - OMR - - PAB - - PEN - - PGK - - PHP - - PKR - - PLN - - PYG - - QAR - - RON - - RSD - - RUB - - RWF - - SAR - - SBD - - SCR - - SDG - - SEK - - SGD - - SHP - - SLE - - SLL - - SOS - - SRD - - SSP - - STN - - SYP - - SZL - - THB - - TJS - - TMT - - TND - - TOP - - TRY - - TTD - - TVD - - TWD - - TZS - - UAH - - UGX - - USD - - UYU - - UZS - - VES - - VND - - VUV - - WST - - XAF - - XCD - - XDR - - XOF - - XPF - - YER - - ZAR - - ZMW - - ZWL description: >- The currency of the order. Authorized value must be of type CurrencyCode (ISO 4217) @@ -21665,10 +18641,6 @@ components: type: string nullable: true example: PENDING - enum: &ref_189 - - PENDING - - FULFILLED - - CANCELED description: The fulfillment status of the order customer_id: type: string @@ -21677,7 +18649,7 @@ components: description: The UUID of the customer associated with the order items: nullable: true - example: &ref_190 + example: &ref_159 - remote_id: '12345' product_id: prod_001 variant_id: var_001 @@ -21708,7 +18680,7 @@ components: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: &ref_191 + example: &ref_160 fav_dish: broccoli fav_color: red nullable: true @@ -21748,7 +18720,6 @@ components: order_status: type: string example: UNSHIPPED - enum: *ref_186 nullable: true description: The status of the order order_number: @@ -21759,14 +18730,12 @@ components: payment_status: type: string example: SUCCESS - enum: *ref_187 nullable: true description: The payment status of the order currency: type: string nullable: true example: AUD - enum: *ref_188 description: >- The currency of the order. Authorized value must be of type CurrencyCode (ISO 4217) @@ -21794,7 +18763,6 @@ components: type: string nullable: true example: PENDING - enum: *ref_189 description: The fulfillment status of the order customer_id: type: string @@ -21803,14 +18771,14 @@ components: description: The UUID of the customer associated with the order items: nullable: true - example: *ref_190 + example: *ref_159 description: The items in the order type: array items: $ref: '#/components/schemas/LineItem' field_mappings: type: object - example: *ref_191 + example: *ref_160 nullable: true description: >- The custom field mappings of the object between the remote 3rd party @@ -21987,7 +18955,7 @@ components: field_mappings: type: object nullable: true - example: &ref_192 + example: &ref_161 fav_dish: broccoli fav_color: red description: >- @@ -22059,7 +19027,7 @@ components: field_mappings: type: object nullable: true - example: *ref_192 + example: *ref_161 description: >- The custom field mappings of the attachment between the remote 3rd party & Panora From baf95fe27e971432f1816a41b05dfe2cc5bdca60 Mon Sep 17 00:00:00 2001 From: mit-27 Date: Wed, 28 Aug 2024 21:40:17 -0600 Subject: [PATCH 23/49] =?UTF-8?q?=E2=9C=A8=20Tested=20all=20objects=20and?= =?UTF-8?q?=20fix=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/microsoftdynamicssales/index.ts | 24 ++++++++++++---- .../services/microsoftdynamicssales/index.ts | 23 +++++++++++---- .../services/microsoftdynamicssales/index.ts | 28 +++++++++++++++---- .../microsoftdynamicssales/mappers.ts | 6 ++-- .../services/microsoftdynamicssales/index.ts | 20 +++++++++++-- .../services/microsoftdynamicssales/index.ts | 18 ++++++++++-- .../microsoftdynamicssales/mappers.ts | 4 +-- packages/shared/src/connectors/metadata.ts | 2 +- 8 files changed, 100 insertions(+), 25 deletions(-) diff --git a/packages/api/src/crm/company/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/company/services/microsoftdynamicssales/index.ts index b04664a29..4ff25e07d 100644 --- a/packages/api/src/crm/company/services/microsoftdynamicssales/index.ts +++ b/packages/api/src/crm/company/services/microsoftdynamicssales/index.ts @@ -39,11 +39,9 @@ export class MicrosoftdynamicssalesService implements ICompanyService { }, }); - const resp = await axios.post( + const respToPost = await axios.post( `${connection.account_url}/api/data/v9.2/accounts`, - JSON.stringify({ - data: companyData, - }), + JSON.stringify(companyData), { headers: { Authorization: `Bearer ${this.cryptoService.decrypt( @@ -53,8 +51,24 @@ export class MicrosoftdynamicssalesService implements ICompanyService { }, }, ); + + const postCompanyId = respToPost.headers['location'].split("/").pop(); + // console.log(res.headers['location'].split('(')[1].split(')')[0]) + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/${postCompanyId}`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { - data: resp.data.value, + data: resp.data, message: 'Microsoftdynamicssales company created', statusCode: 201, }; diff --git a/packages/api/src/crm/contact/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/contact/services/microsoftdynamicssales/index.ts index f585e6966..caeeb9891 100644 --- a/packages/api/src/crm/contact/services/microsoftdynamicssales/index.ts +++ b/packages/api/src/crm/contact/services/microsoftdynamicssales/index.ts @@ -38,11 +38,9 @@ export class MicrosoftdynamicssalesService implements IContactService { }, }); - const resp = await axios.post( + const respToPost = await axios.post( `${connection.account_url}/api/data/v9.2/contacts`, - JSON.stringify({ - data: contactData, - }), + JSON.stringify(contactData), { headers: { 'Content-Type': 'application/json', @@ -52,8 +50,23 @@ export class MicrosoftdynamicssalesService implements IContactService { }, }, ); + + const postContactId = respToPost.headers['location'].split("/").pop(); + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/${postContactId}`, + { + headers: { + accept: 'application/json', + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); return { - data: resp.data.value, + data: resp.data, message: 'microsoftdynamicssales contact created', statusCode: 201, }; diff --git a/packages/api/src/crm/deal/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/deal/services/microsoftdynamicssales/index.ts index 12979121b..fd93e9d74 100644 --- a/packages/api/src/crm/deal/services/microsoftdynamicssales/index.ts +++ b/packages/api/src/crm/deal/services/microsoftdynamicssales/index.ts @@ -37,11 +37,9 @@ export class MicrosoftdynamicssalesService implements IDealService { }, }); - const resp = await axios.post( + const respToPost = await axios.post( `${connection.account_url}/api/data/v9.2/opportunities`, - JSON.stringify({ - data: dealData, - }), + JSON.stringify(dealData), { headers: { Authorization: `Bearer ${this.cryptoService.decrypt( @@ -51,8 +49,28 @@ export class MicrosoftdynamicssalesService implements IDealService { }, }, ); + + const postDealId = respToPost.headers['location'].split("/").pop(); + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/${postDealId}`, + { + headers: { + accept: 'application/json', + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + + + + return { - data: resp.data.value, + data: resp.data, message: 'Microsoftdynamicssales deal created', statusCode: 201, }; diff --git a/packages/api/src/crm/deal/services/microsoftdynamicssales/mappers.ts b/packages/api/src/crm/deal/services/microsoftdynamicssales/mappers.ts index b8e64523d..a1ca99212 100644 --- a/packages/api/src/crm/deal/services/microsoftdynamicssales/mappers.ts +++ b/packages/api/src/crm/deal/services/microsoftdynamicssales/mappers.ts @@ -24,7 +24,7 @@ export class MicrosoftdynamicssalesDealMapper implements IDealMapper { const result: MicrosoftdynamicssalesDealInput = { name: source.name, description: source.description, - totalamount: source.amount + budgetamount: source.amount } @@ -103,9 +103,9 @@ export class MicrosoftdynamicssalesDealMapper implements IDealMapper { } } - if (deal._accountid_value) { + if (deal._customerid_value) { const company_id = await this.utils.getCompanyUuidFromRemoteId( - deal._accountid_value, + deal._customerid_value, connectionId ); if (company_id) { diff --git a/packages/api/src/crm/note/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/note/services/microsoftdynamicssales/index.ts index 8a17ca8d7..475fba1e5 100644 --- a/packages/api/src/crm/note/services/microsoftdynamicssales/index.ts +++ b/packages/api/src/crm/note/services/microsoftdynamicssales/index.ts @@ -36,7 +36,7 @@ export class MicrosoftdynamicssalesService implements INoteService { vertical: 'crm', }, }); - const resp = await axios.post( + const respToPost = await axios.post( `${connection.account_url}/api/data/v9.2/annotations`, JSON.stringify(noteData), { @@ -48,8 +48,24 @@ export class MicrosoftdynamicssalesService implements INoteService { }, }, ); + + const postNoteId = respToPost.headers['location'].split("/").pop(); + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/${postNoteId}` + , { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + + + return { - data: resp?.data.value, + data: resp?.data, message: 'Microsoftdynamicssales note created', statusCode: 201, }; diff --git a/packages/api/src/crm/task/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/task/services/microsoftdynamicssales/index.ts index b536fc781..3888854b6 100644 --- a/packages/api/src/crm/task/services/microsoftdynamicssales/index.ts +++ b/packages/api/src/crm/task/services/microsoftdynamicssales/index.ts @@ -36,7 +36,7 @@ export class MicrosoftdynamicssalesService implements ITaskService { vertical: 'crm', }, }); - const resp = await axios.post( + const respToPost = await axios.post( `${connection.account_url}/api/data/v9.2/tasks`, JSON.stringify(taskData), { @@ -49,8 +49,22 @@ export class MicrosoftdynamicssalesService implements ITaskService { }, ); + const postTaskId = respToPost.headers['location'].split("/").pop(); + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/${postTaskId}`, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + + + return { - data: resp?.data?.value, + data: resp?.data, message: 'Microsoftdynamicssales task created', statusCode: 201, }; diff --git a/packages/api/src/crm/task/services/microsoftdynamicssales/mappers.ts b/packages/api/src/crm/task/services/microsoftdynamicssales/mappers.ts index 794a2e2fd..28020d540 100644 --- a/packages/api/src/crm/task/services/microsoftdynamicssales/mappers.ts +++ b/packages/api/src/crm/task/services/microsoftdynamicssales/mappers.ts @@ -31,11 +31,11 @@ export class MicrosoftdynamicssalesTaskMapper implements ITaskMapper { } if (source.due_date) { - result.scheduledend = source.due_date.toUTCString(); + result.scheduledend = new Date(source.due_date).toUTCString(); } if (source.finished_date) { - result.actualend = source.finished_date.toUTCString(); + result.actualend = new Date(source.finished_date).toUTCString(); } // User have to provide either company_id or deal_id which will be associated with current task diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index b31075d44..b5baeafac 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -3090,4 +3090,4 @@ export const CONNECTORS_METADATA: ProvidersConfig = { } }, } -}; \ No newline at end of file +}; From ff9e4a6c6231bed73836db862b28fc17a43c66ef Mon Sep 17 00:00:00 2001 From: Irwansyah Date: Fri, 30 Aug 2024 20:23:12 +0700 Subject: [PATCH 24/49] fix: limit query param to use the correct int type instead of string --- packages/api/src/ats/user/user.controller.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/api/src/ats/user/user.controller.ts b/packages/api/src/ats/user/user.controller.ts index fdbcc2d37..9b2b50fcb 100644 --- a/packages/api/src/ats/user/user.controller.ts +++ b/packages/api/src/ats/user/user.controller.ts @@ -8,6 +8,8 @@ import { Param, Headers, UseGuards, + UsePipes, + ValidationPipe, } from '@nestjs/common'; import { LoggerService } from '@@core/@core-services/logger/logger.service'; import { @@ -57,6 +59,12 @@ export class UserController { @ApiPaginatedResponse(UnifiedAtsUserOutput) @UseGuards(ApiKeyAuthGuard) @Get() + @UsePipes( + new ValidationPipe({ + transform: true, + transformOptions: { enableImplicitConversion: true }, + }), + ) async getUsers( @Headers('x-connection-token') connection_token: string, @Query() query: QueryDto, From 87324457e3616f4181ffa62c8f42e18a885774c1 Mon Sep 17 00:00:00 2001 From: mit-27 Date: Fri, 30 Aug 2024 20:21:09 -0600 Subject: [PATCH 25/49] =?UTF-8?q?=E2=9C=A8=20Add=20engagement=20object?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/magic-link/src/lib/ProviderModal.tsx | 2 +- .../utils/types/original/original.crm.ts | 6 +- .../src/crm/engagement/engagement.module.ts | 4 + .../services/microsoftdynamicssales/index.ts | 361 ++++++++++++ .../microsoftdynamicssales/mappers.ts | 518 ++++++++++++++++++ .../services/microsoftdynamicssales/types.ts | 276 ++++++++++ 6 files changed, 1164 insertions(+), 3 deletions(-) create mode 100644 packages/api/src/crm/engagement/services/microsoftdynamicssales/index.ts create mode 100644 packages/api/src/crm/engagement/services/microsoftdynamicssales/mappers.ts create mode 100644 packages/api/src/crm/engagement/services/microsoftdynamicssales/types.ts diff --git a/apps/magic-link/src/lib/ProviderModal.tsx b/apps/magic-link/src/lib/ProviderModal.tsx index e08a2238c..f7bc182c0 100644 --- a/apps/magic-link/src/lib/ProviderModal.tsx +++ b/apps/magic-link/src/lib/ProviderModal.tsx @@ -22,7 +22,7 @@ interface IBasicAuthFormData { } const domainFormats: { [key: string]: string } = { - microsoftdynamicssales: 'YOURORGNAME.api.crm12.dynamics.com', + microsoftdynamicssales: 'YOURORGNAME.api.crm3.dynamics.com', bigcommerce: 'If your api domain is https://api.bigcommerce.com/stores/eubckcvkzg/v3 then store_hash is eubckcvkzg', }; diff --git a/packages/api/src/@core/utils/types/original/original.crm.ts b/packages/api/src/@core/utils/types/original/original.crm.ts index 2e64529d6..61503dad2 100644 --- a/packages/api/src/@core/utils/types/original/original.crm.ts +++ b/packages/api/src/@core/utils/types/original/original.crm.ts @@ -1,3 +1,5 @@ +import { MicrosoftdynamicssalesEngagementInput, MicrosoftdynamicssalesEngagementOutput } from '@crm/engagement/services/microsoftdynamicssales/types'; + import { MicrosoftdynamicssalesTaskInput, MicrosoftdynamicssalesTaskOutput } from '@crm/task/services/microsoftdynamicssales/types'; import { MicrosoftdynamicssalesNoteInput, MicrosoftdynamicssalesNoteOutput } from '@crm/note/services/microsoftdynamicssales/types'; @@ -186,7 +188,7 @@ export type OriginalEngagementInput = | ZohoEngagementInput | ZendeskEngagementInput | PipedriveEngagementInput - | CloseEngagementInput; + | CloseEngagementInput | MicrosoftdynamicssalesEngagementInput; /* note */ export type OriginalNoteInput = @@ -268,7 +270,7 @@ export type OriginalEngagementOutput = | ZohoEngagementOutput | ZendeskEngagementOutput | PipedriveEngagementOutput - | CloseEngagementOutput; + | CloseEngagementOutput | MicrosoftdynamicssalesEngagementOutput; /* note */ export type OriginalNoteOutput = diff --git a/packages/api/src/crm/engagement/engagement.module.ts b/packages/api/src/crm/engagement/engagement.module.ts index 4da6c55b5..bc2c9b92c 100644 --- a/packages/api/src/crm/engagement/engagement.module.ts +++ b/packages/api/src/crm/engagement/engagement.module.ts @@ -1,3 +1,5 @@ +import { MicrosoftdynamicssalesEngagementMapper } from './services/microsoftdynamicssales/mappers'; +import { MicrosoftdynamicssalesService } from './services/microsoftdynamicssales'; import { EncryptionService } from '@@core/@core-services/encryption/encryption.service'; import { LoggerService } from '@@core/@core-services/logger/logger.service'; import { BullQueueModule } from '@@core/@core-services/queues/queue.module'; @@ -48,6 +50,8 @@ import { SyncService } from './sync/sync.service'; PipedriveEngagementMapper, HubspotEngagementMapper, CloseEngagementMapper, + MicrosoftdynamicssalesService, + MicrosoftdynamicssalesEngagementMapper, ], exports: [SyncService, ServiceRegistry, WebhookService], }) diff --git a/packages/api/src/crm/engagement/services/microsoftdynamicssales/index.ts b/packages/api/src/crm/engagement/services/microsoftdynamicssales/index.ts new file mode 100644 index 000000000..27c89a3a6 --- /dev/null +++ b/packages/api/src/crm/engagement/services/microsoftdynamicssales/index.ts @@ -0,0 +1,361 @@ +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 { IEngagementService } from '@crm/engagement/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { + MicrosoftdynamicssalesEngagementAppointmentInput, + MicrosoftdynamicssalesEngagementAppointmentOutput, + MicrosoftdynamicssalesEngagementCallInput, + MicrosoftdynamicssalesEngagementCallOutput, + MicrosoftdynamicssalesEngagementEmailInput, + MicrosoftdynamicssalesEngagementEmailOutput, + MicrosoftdynamicssalesEngagementInput, + MicrosoftdynamicssalesEngagementOutput +} from './types'; + +@Injectable() +export class MicrosoftdynamicssalesService implements IEngagementService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + CrmObject.engagement.toUpperCase() + ':' + MicrosoftdynamicssalesService.name, + ); + this.registry.registerService('microsoftdynamicssales', this); + } + async addEngagement( + engagementData: MicrosoftdynamicssalesEngagementInput, + linkedUserId: string, + engagement_type: string, + ): Promise> { + try { + switch (engagement_type) { + case 'CALL': + return this.addCall( + engagementData as MicrosoftdynamicssalesEngagementCallInput, + linkedUserId, + ); + case 'MEETING': + return this.addMeeting( + engagementData as MicrosoftdynamicssalesEngagementAppointmentInput, + linkedUserId, + ); + case 'EMAIL': + return this.addEmail( + engagementData as MicrosoftdynamicssalesEngagementEmailInput, + linkedUserId, + ); + default: + break; + } + } catch (error) { + throw error; + } + } + + private async addCall( + engagementData: MicrosoftdynamicssalesEngagementCallInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + const respToPost = await axios.post( + `${connection.account_url}/api/data/v9.2/phonecalls`, + JSON.stringify(engagementData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + const postCallId = respToPost.headers['location'].split("/").pop(); + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/${postCallId}`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + + return { + data: resp.data, + message: 'Microsoftdynamicssales call created', + statusCode: 201, + }; + } catch (error) { + throw error; + } + } + + private async addMeeting( + engagementData: MicrosoftdynamicssalesEngagementAppointmentInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + const respToPost = await axios.post( + `${connection.account_url}/api/data/v9.2/appointments`, + JSON.stringify(engagementData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + const postAppointmentId = respToPost.headers['location'].split("/").pop(); + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/${postAppointmentId}`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + + + return { + data: resp.data, + message: 'Microsoftdynamicssales meeting created', + statusCode: 201, + }; + } catch (error) { + throw error; + } + } + + private async addEmail( + engagementData: MicrosoftdynamicssalesEngagementEmailInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + const respToPost = await axios.post( + `${connection.account_url}/api/data/v9.2/emails`, + JSON.stringify(engagementData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + const postEmailId = respToPost.headers['location'].split("/").pop(); + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/${postEmailId}`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + + + return { + data: resp.data, + message: 'Microsoftdynamicssales email created', + statusCode: 201, + }; + } catch (error) { + throw error; + } + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId, custom_properties, engagement_type } = data; + + switch (engagement_type as string) { + case 'CALL': + return this.syncCalls(linkedUserId, custom_properties); + case 'MEETING': + return this.syncMeetings(linkedUserId, custom_properties); + case 'EMAIL': + return this.syncEmails(linkedUserId, custom_properties); + default: + break; + } + } catch (error) { + throw error; + } + } + + private async syncCalls(linkedUserId: string, custom_properties?: string[]) { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + // const commonPropertyNames = Object.keys(commonCallMicrosoftdynamicssaleProperties); + // const allProperties = [...commonPropertyNames, ...custom_properties]; + // const baseURL = 'https://api.hubapi.com/crm/v3/objects/calls'; + + // const queryString = allProperties + // .map((prop) => `properties=${encodeURIComponent(prop)}`) + // .join('&'); + + // const url = `${baseURL}?${queryString}`; + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/phonecalls`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced microsoftdynamicssales engagements calls !`); + + return { + data: resp.data.value, + message: 'Microsoftdynamicssales engagements calls retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } + + private async syncMeetings( + linkedUserId: string, + custom_properties?: string[], + ) { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + // const commonPropertyNames = Object.keys(commonMeetingMicrosoftdynamicssaleProperties); + // const allProperties = [...commonPropertyNames, ...custom_properties]; + // const baseURL = 'https://api.hubapi.com/crm/v3/objects/meetings'; + + // const queryString = allProperties + // .map((prop) => `properties=${encodeURIComponent(prop)}`) + // .join('&'); + + // const url = `${baseURL}?${queryString}`; + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/appointments`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced microsoftdynamicssales engagements meetings !`); + + return { + data: resp.data.value, + message: 'Microsoftdynamicssales engagements meetings retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } + + private async syncEmails(linkedUserId: string, custom_properties?: string[]) { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'microsoftdynamicssales', + vertical: 'crm', + }, + }); + + // const commonPropertyNames = Object.keys(commonEmailMicrosoftdynamicssaleProperties); + // const allProperties = [...commonPropertyNames, ...custom_properties]; + // const baseURL = 'https://api.hubapi.com/crm/v3/objects/emails'; + + // const queryString = allProperties + // .map((prop) => `properties=${encodeURIComponent(prop)}`) + // .join('&'); + + // const url = `${baseURL}?${queryString}`; + + const resp = await axios.get( + `${connection.account_url}/api/data/v9.2/emails`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + this.logger.log(`Synced microsoftdynamicssales engagements emails !`); + + return { + data: resp.data.value, + message: 'Microsoftdynamicssales engagements emails retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/crm/engagement/services/microsoftdynamicssales/mappers.ts b/packages/api/src/crm/engagement/services/microsoftdynamicssales/mappers.ts new file mode 100644 index 000000000..95333a57b --- /dev/null +++ b/packages/api/src/crm/engagement/services/microsoftdynamicssales/mappers.ts @@ -0,0 +1,518 @@ +import { + MicrosoftdynamicssalesEngagementAppointmentInput, + MicrosoftdynamicssalesEngagementAppointmentOutput, + MicrosoftdynamicssalesEngagementCallInput, + MicrosoftdynamicssalesEngagementCallOutput, + MicrosoftdynamicssalesEngagementEmailInput, + MicrosoftdynamicssalesEngagementEmailOutput, + MicrosoftdynamicssalesEngagementInput, + MicrosoftdynamicssalesEngagementOutput +} from './types'; +import { + UnifiedCrmEngagementInput, + UnifiedCrmEngagementOutput, +} from '@crm/engagement/types/model.unified'; +import { IEngagementMapper } from '@crm/engagement/types'; +import { Utils } from '@crm/@lib/@utils'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class MicrosoftdynamicssalesEngagementMapper implements IEngagementMapper { + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('crm', 'engagement', 'microsoftdynamicssales', this); + } + + async desunify( + source: UnifiedCrmEngagementInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const type = source.type; + switch (type) { + case 'CALL': + return await this.desunifyCall(source, customFieldMappings); + case 'MEETING': + return await this.desunifyMeeting(source, customFieldMappings); + case 'EMAIL': + return await this.desunifyEmail(source, customFieldMappings); + default: + break; + } + return; + } + + private async desunifyCall( + source: UnifiedCrmEngagementInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: MicrosoftdynamicssalesEngagementCallInput = { + description: source.content ?? '', + subject: source.subject ?? '', + }; + + if (source.start_at) { + result.actualstart = new Date(source.start_at).toUTCString(); + } + + if (source.end_time) { + result.actualend = new Date(source.end_time).toUTCString(); + } + + if (source.direction) { + result.directioncode = source.direction === 'INBOUND' ? false : true; + + } + + if (source.company_id) { + const id = await this.utils.getRemoteIdFromCompanyUuid(source.company_id); + result["regardingobjectid_account@odata.bind"] = `/accounts(${id})`; + } + + // Only assigning first contact from contacts + if (source.contacts && source.contacts.length > 0) { + const id = await this.utils.getRemoteIdFromContactUuid(source.contacts[0]); + result["regardingobjectid_contact@odata.bind"] = `/contacts(${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; + } + + private async desunifyMeeting( + source: UnifiedCrmEngagementInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: MicrosoftdynamicssalesEngagementAppointmentInput = { + description: source.content ?? '', + subject: source.subject ?? '', + }; + + if (source.start_at) { + result.actualstart = new Date(source.start_at).toUTCString(); + } + + if (source.end_time) { + result.actualend = new Date(source.end_time).toUTCString(); + } + + if (source.company_id) { + const id = await this.utils.getRemoteIdFromCompanyUuid(source.company_id); + result["regardingobjectid_account@odata.bind"] = `/accounts(${id})`; + } + + // Only assigning first contact from contacts + if (source.contacts && source.contacts.length > 0) { + const id = await this.utils.getRemoteIdFromContactUuid(source.contacts[0]); + result["regardingobjectid_contact@odata.bind"] = `/contacts(${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; + } + + private async desunifyEmail( + source: UnifiedCrmEngagementInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: MicrosoftdynamicssalesEngagementEmailInput = { + description: source.content ?? '', + subject: source.subject ?? '', + }; + + if (source.start_at) { + result.actualstart = new Date(source.start_at).toUTCString(); + } + + if (source.end_time) { + result.actualend = new Date(source.end_time).toUTCString(); + } + + if (source.direction) { + result.directioncode = source.direction === 'INBOUND' ? false : true; + + } + + if (source.company_id) { + const id = await this.utils.getRemoteIdFromCompanyUuid(source.company_id); + result["regardingobjectid_account@odata.bind"] = `/accounts(${id})`; + } + + // Only assigning first contact from contacts + if (source.contacts && source.contacts.length > 0) { + const id = await this.utils.getRemoteIdFromContactUuid(source.contacts[0]); + result["regardingobjectid_contact@odata.bind"] = `/contacts(${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: MicrosoftdynamicssalesEngagementOutput | MicrosoftdynamicssalesEngagementOutput[], + engagement_type: string, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + switch (engagement_type) { + case 'CALL': + return await this.unifyCall( + source as MicrosoftdynamicssalesEngagementCallOutput | MicrosoftdynamicssalesEngagementCallOutput[], + connectionId, + customFieldMappings, + ); + case 'MEETING': + return await this.unifyMeeting( + source as + | MicrosoftdynamicssalesEngagementAppointmentOutput + | MicrosoftdynamicssalesEngagementAppointmentOutput[], + connectionId, + customFieldMappings, + ); + case 'EMAIL': + return await this.unifyEmail( + source as + | MicrosoftdynamicssalesEngagementEmailOutput + | MicrosoftdynamicssalesEngagementEmailOutput[], + connectionId, + customFieldMappings, + ); + default: + break; + } + } + + private async unifyCall( + source: MicrosoftdynamicssalesEngagementCallOutput | MicrosoftdynamicssalesEngagementCallOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ) { + if (!Array.isArray(source)) { + return this.mapSingleEngagementCallToUnified( + source, + connectionId, + customFieldMappings, + ); + } + // Handling array of MicrosoftdynamicssalesEngagementOutput + return Promise.all( + source.map((engagement) => + this.mapSingleEngagementCallToUnified( + engagement, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async unifyMeeting( + source: MicrosoftdynamicssalesEngagementAppointmentOutput | MicrosoftdynamicssalesEngagementAppointmentOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ) { + if (!Array.isArray(source)) { + return this.mapSingleEngagementMeetingToUnified( + source, + connectionId, + customFieldMappings, + ); + } + // Handling array of MicrosoftdynamicssalesEngagementOutput + return Promise.all( + source.map((engagement) => + this.mapSingleEngagementMeetingToUnified( + engagement, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async unifyEmail( + source: MicrosoftdynamicssalesEngagementEmailOutput | MicrosoftdynamicssalesEngagementEmailOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ) { + if (!Array.isArray(source)) { + return this.mapSingleEngagementEmailToUnified( + source, + connectionId, + customFieldMappings, + ); + } + // Handling array of MicrosoftdynamicssalesEngagementOutput + return Promise.all( + source.map((engagement) => + this.mapSingleEngagementEmailToUnified( + engagement, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async mapSingleEngagementCallToUnified( + engagement: MicrosoftdynamicssalesEngagementCallOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = engagement[mapping.remote_id]; + } + } + + let opts: any = {}; + if (engagement._ownerid_value) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + engagement._ownerid_value, + connectionId, + ); + if (owner_id) { + opts = { + ...opts, + user_id: owner_id, + }; + } + } + + if (engagement._regardingobjectid_value) { + const company_id = await this.utils.getCompanyUuidFromRemoteId( + engagement._regardingobjectid_value, + connectionId + ); + + if (company_id) { + opts = { + ...opts, + company_id: company_id + } + } + + const contact_id = await this.utils.getContactUuidFromRemoteId( + engagement._regardingobjectid_value, + connectionId + ); + + if (contact_id) { + opts = { + ...opts, + contacts: [contact_id] + } + } + } + + + + return { + remote_data: engagement, + remote_id: engagement.activityid, + content: engagement.description ?? '', + subject: engagement.subject ?? '', + start_at: engagement.actualstart ? new Date(engagement.actualstart) : null, + end_time: engagement.actualend ? new Date(engagement.actualend) : null, + type: 'CALL', + direction: engagement.directioncode ? engagement.directioncode ? "OUTBOUND" : "INBOUND" : null, + field_mappings, + ...opts, + }; + } + + private async mapSingleEngagementMeetingToUnified( + engagement: MicrosoftdynamicssalesEngagementAppointmentOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = engagement[mapping.remote_id]; + } + } + + let opts: any = {}; + if (engagement._ownerid_value) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + engagement._ownerid_value, + connectionId, + ); + if (owner_id) { + opts = { + ...opts, + user_id: owner_id, + }; + } + } + + if (engagement._regardingobjectid_value) { + const company_id = await this.utils.getCompanyUuidFromRemoteId( + engagement._regardingobjectid_value, + connectionId + ); + + if (company_id) { + opts = { + ...opts, + company_id: company_id + } + } + + const contact_id = await this.utils.getContactUuidFromRemoteId( + engagement._regardingobjectid_value, + connectionId + ); + + if (contact_id) { + opts = { + ...opts, + contacts: [contact_id] + } + } + } + + return { + remote_data: engagement, + remote_id: engagement.activityid, + content: engagement.description ?? '', + subject: engagement.subject ?? '', + start_at: engagement.actualstart ? new Date(engagement.actualstart) : null, + end_time: engagement.actualend ? new Date(engagement.actualend) : null, + type: 'MEETING', + field_mappings, + ...opts, + }; + } + + private async mapSingleEngagementEmailToUnified( + engagement: MicrosoftdynamicssalesEngagementEmailOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = engagement[mapping.remote_id]; + } + } + + let opts: any = {}; + if (engagement._ownerid_value) { + const owner_id = await this.utils.getUserUuidFromRemoteId( + engagement._ownerid_value, + connectionId, + ); + if (owner_id) { + opts = { + ...opts, + user_id: owner_id, + }; + } + } + + if (engagement._regardingobjectid_value) { + const company_id = await this.utils.getCompanyUuidFromRemoteId( + engagement._regardingobjectid_value, + connectionId + ); + + if (company_id) { + opts = { + ...opts, + company_id: company_id + } + } + + const contact_id = await this.utils.getContactUuidFromRemoteId( + engagement._regardingobjectid_value, + connectionId + ); + + if (contact_id) { + opts = { + ...opts, + contacts: [contact_id] + } + } + } + + return { + remote_id: engagement.activityid, + remote_data: engagement, + content: engagement.description ?? '', + start_at: engagement.actualstart ? new Date(engagement.actualstart) : null, + end_time: engagement.actualend ? new Date(engagement.actualend) : null, + subject: engagement.subject ?? '', + type: 'EMAIL', + direction: engagement.directioncode ? engagement.directioncode ? "OUTBOUND" : "INBOUND" : null, + field_mappings, + ...opts, + }; + } +} diff --git a/packages/api/src/crm/engagement/services/microsoftdynamicssales/types.ts b/packages/api/src/crm/engagement/services/microsoftdynamicssales/types.ts new file mode 100644 index 000000000..92a7f8f97 --- /dev/null +++ b/packages/api/src/crm/engagement/services/microsoftdynamicssales/types.ts @@ -0,0 +1,276 @@ +interface MicrosoftdynamicssalesEmail { + activityadditionalparams: string; + activityid: string; // Guid + activitytypecode: string; + actualdurationminutes: number; + actualend: string; + actualstart: string; + attachmentcount: number; + attachmentopencount: number; + baseconversationindexhash: number; + category: string; + community: number; + compressed: boolean; + conversationindex: string; + conversationtrackingid: string; // Guid + correlatedsubjectchanged: boolean; + correlationmethod: number; + createdon: string; + delayedemailsendtime: string; + deliveryattempts: number; + deliverylastattemptedon: string; + deliveryprioritycode: number; + deliveryreceiptrequested: boolean; + description: string; + descriptionblobid: string; // Binary + descriptionblobid_name: string; + directioncode: boolean; + emailreminderexpirytime: string; + emailreminderstatus: number; + emailremindertext: string; + emailremindertype: number; + emailtrackingid: string; // Guid + exchangeitemid: string; + exchangerate: number; + exchangeweblink: string; + followemailuserpreference: boolean; + formattedscheduledend: string; + formattedscheduledstart: string; + importsequencenumber: number; + inreplyto: string; + instancetypecode: number; + internetmessageheaders: string; + isbilled: boolean; + isduplicatesenderunresolved: boolean; + isemailfollowed: boolean; + isemailreminderset: boolean; + ismapiprivate: boolean; + isregularactivity: boolean; + isunsafe: number; + isworkflowcreated: boolean; + lastonholdtime: string; + lastopenedtime: string; + leftvoicemail: boolean; + linksclickedcount: number; + messageid: string; + mimetype: string; + modifiedon: string; + notifications: number; + onholdtime: number; + opencount: number; + overriddencreatedon: string; + postponeactivityprocessinguntil: string; + postponeemailprocessinguntil: string; + prioritycode: number; + processid: string; // Guid + readreceiptrequested: boolean; + reminderactioncardid: string; // Guid + replycount: number; + reservedforinternaluse: string; + safedescription: string; + scheduleddurationminutes: number; + scheduledend: string; + scheduledstart: string; + sender: string; + senton: string; + seriesid: string; // Guid + sortdate: string; + stageid: string; // Guid + statecode: number; + statuscode: number; + subcategory: string; + subject: string; + submittedby: string; + timezoneruleversionnumber: number; + torecipients: string; + trackingtoken: string; + traversedpath: string; + utcconversiontimezonecode: number; + versionnumber: number; + _acceptingentityid_value: string; + _createdby_value: string; + _createdonbehalfby_value: string; + _emailsender_value: string; + _modifiedby_value: string; + _modifiedonbehalfby_value: string; + _ownerid_value: string; + _owningbusinessunit_value: string; + _owningteam_value: string; + _owninguser_value: string; + _receivingmailboxid_value: string; + _regardingobjectid_value: string; + _sendermailboxid_value: string; + _sendersaccount_value: string; + _slaid_value: string; + _slainvokedid_value: string; + _templateid_value: string; + _transactioncurrencyid_value: string; + [key: string]: any; +} + +interface MicrosoftdynamicssalesAppointment { + activityadditionalparams: string; + activityid: string; // Guid + activitytypecode: string; + actualdurationminutes: number; + actualend: string; + actualstart: string; + attachmenterrors: number; + category: string; + community: number; + createdon: string; + deliverylastattemptedon: string; + deliveryprioritycode: number; + description: string; + descriptionblobid: string; // Binary + descriptionblobid_name: string; + exchangeitemid: string; + exchangerate: number; + exchangeweblink: string; + formattedscheduledend: string; + formattedscheduledstart: string; + globalobjectid: string; + importsequencenumber: number; + instancetypecode: number; + isalldayevent: boolean; + isbilled: boolean; + isdraft: boolean; + ismapiprivate: boolean; + isonlinemeeting: boolean; + isregularactivity: boolean; + isunsafe: number; + isworkflowcreated: boolean; + lastonholdtime: string; + leftvoicemail: boolean; + location: string; + modifiedfieldsmask: string; + modifiedon: string; + onholdtime: number; + onlinemeetingchatid: string; + onlinemeetingid: string; + onlinemeetingjoinurl: string; + onlinemeetingtype: number; + originalstartdate: string; + outlookownerapptid: number; + overriddencreatedon: string; + postponeactivityprocessinguntil: string; + prioritycode: number; + processid: string; // Guid + scheduleddurationminutes: number; + scheduledend: string; + scheduledstart: string; + senton: string; + seriesid: string; // Guid + sortdate: string; + stageid: string; // Guid + statecode: number; + statuscode: number; + subcategory: string; + subject: string; + subscriptionid: string; // Guid + timezoneruleversionnumber: number; + traversedpath: string; + utcconversiontimezonecode: number; + versionnumber: number; + _createdby_value: string; + _createdonbehalfby_value: string; + _modifiedby_value: string; + _modifiedonbehalfby_value: string; + _ownerid_value: string; + _owningbusinessunit_value: string; + _owningteam_value: string; + _owninguser_value: string; + _regardingobjectid_value: string; + _sendermailboxid_value: string; + _slaid_value: string; + _slainvokedid_value: string; + _transactioncurrencyid_value: string; + [key: string]: any; +} + +interface MicrosoftdynamicssalesPhoneCall { + activityadditionalparams: string; + activityid: string; // Guid + activitytypecode: string; + actualdurationminutes: number; + actualend: string; + actualstart: string; + category: string; + community: number; + createdon: string; + deliverylastattemptedon: string; + deliveryprioritycode: number; + description: string; + descriptionblobid: string; // Binary + descriptionblobid_name: string; + directioncode: boolean; + exchangeitemid: string; + exchangerate: number; + exchangeweblink: string; + formattedscheduledend: string; + formattedscheduledstart: string; + importsequencenumber: number; + instancetypecode: number; + isbilled: boolean; + ismapiprivate: boolean; + isregularactivity: boolean; + isworkflowcreated: boolean; + lastonholdtime: string; + leftvoicemail: boolean; + modifiedon: string; + onholdtime: number; + overriddencreatedon: string; + phonenumber: string; + postponeactivityprocessinguntil: string; + prioritycode: number; + processid: string; // Guid + scheduleddurationminutes: number; + scheduledend: string; + scheduledstart: string; + senton: string; + seriesid: string; // Guid + sortdate: string; + stageid: string; // Guid + statecode: number; + statuscode: number; + subcategory: string; + subject: string; + subscriptionid: string; // Guid + timezoneruleversionnumber: number; + traversedpath: string; + utcconversiontimezonecode: number; + versionnumber: number; + _createdby_value: string; + _createdonbehalfby_value: string; + _modifiedby_value: string; + _modifiedonbehalfby_value: string; + _ownerid_value: string; + _owningbusinessunit_value: string; + _owningteam_value: string; + _owninguser_value: string; + _regardingobjectid_value: string; + _sendermailboxid_value: string; + _slaid_value: string; + _slainvokedid_value: string; + _transactioncurrencyid_value: string; + [key: string]: any; +} + +export type MicrosoftdynamicssalesEngagementCallInput = Partial; +export type MicrosoftdynamicssalesEngagementCallOutput = MicrosoftdynamicssalesEngagementCallInput; + +export type MicrosoftdynamicssalesEngagementEmailInput = Partial; +export type MicrosoftdynamicssalesEngagementEmailOutput = MicrosoftdynamicssalesEngagementEmailInput; + +export type MicrosoftdynamicssalesEngagementAppointmentInput = Partial; +export type MicrosoftdynamicssalesEngagementAppointmentOutput = MicrosoftdynamicssalesEngagementAppointmentInput; + + +export type MicrosoftdynamicssalesEngagementInput = MicrosoftdynamicssalesEngagementCallInput + | MicrosoftdynamicssalesEngagementEmailInput + | MicrosoftdynamicssalesEngagementAppointmentInput; + +export type MicrosoftdynamicssalesEngagementOutput = MicrosoftdynamicssalesEngagementCallOutput + | MicrosoftdynamicssalesEngagementEmailOutput + | MicrosoftdynamicssalesEngagementAppointmentOutput; + From efa7c5ffc462eff509cbaf8f1f2f11615ff25e2e Mon Sep 17 00:00:00 2001 From: mit <1mitccc@gmail.com> Date: Sat, 31 Aug 2024 08:28:04 +0530 Subject: [PATCH 26/49] Add customer object for webflow-ecommerce-connector --- packages/api/scripts/init.sql | 3 +- packages/api/scripts/seed.sql | 8 +- .../types/original/original.ecommerce.ts | 11 ++- .../src/ecommerce/customer/customer.module.ts | 4 + .../customer/services/webflow/index.ts | 60 ++++++++++++ .../customer/services/webflow/mappers.ts | 91 +++++++++++++++++++ .../customer/services/webflow/types.ts | 26 ++++++ packages/shared/src/connectors/enum.ts | 3 +- packages/shared/src/connectors/index.ts | 2 +- packages/shared/src/connectors/metadata.ts | 2 +- 10 files changed, 200 insertions(+), 10 deletions(-) create mode 100644 packages/api/src/ecommerce/customer/services/webflow/index.ts create mode 100644 packages/api/src/ecommerce/customer/services/webflow/mappers.ts create mode 100644 packages/api/src/ecommerce/customer/services/webflow/types.ts diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index 71a8c80ec..d90bf6bbc 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -549,7 +549,8 @@ CREATE TABLE connector_sets ecom_amazon boolean NULL, ecom_squarespace boolean NULL, ats_ashby boolean NULL, - CONSTRAINT PK_project_connector PRIMARY KEY ( id_connector_set ) + ecom_webflow boolean NULL, +CONSTRAINT PK_project_connector PRIMARY KEY ( id_connector_set ) ); diff --git a/packages/api/scripts/seed.sql b/packages/api/scripts/seed.sql index 233841fa2..124e4b592 100644 --- a/packages/api/scripts/seed.sql +++ b/packages/api/scripts/seed.sql @@ -1,10 +1,10 @@ INSERT INTO users (id_user, identification_strategy, email, password_hash, first_name, last_name) VALUES ('0ce39030-2901-4c56-8db0-5e326182ec6b', 'b2c','local@panora.dev', '$2b$10$Y7Q8TWGyGuc5ecdIASbBsuXMo3q/Rs3/cnY.mLZP4tUgfGUOCUBlG', 'local', 'Panora'); -INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github, hris_deel, hris_sage, ats_ashby) VALUES - ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); +INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, fs_box, tcg_github, hris_deel, hris_sage, ats_ashby, ecom_webflow) VALUES + ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); INSERT INTO projects (id_project, name, sync_mode, id_user, id_connector_set) VALUES ('1e468c15-aa57-4448-aa2b-7fed640d1e3d', 'Project 1', 'pull', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'), diff --git a/packages/api/src/@core/utils/types/original/original.ecommerce.ts b/packages/api/src/@core/utils/types/original/original.ecommerce.ts index 95589729e..d7d034a72 100644 --- a/packages/api/src/@core/utils/types/original/original.ecommerce.ts +++ b/packages/api/src/@core/utils/types/original/original.ecommerce.ts @@ -1,3 +1,8 @@ +import { + WebflowCustomerInput, + WebflowCustomerOutput, +} from '@ecommerce/customer/services/webflow/types'; + /* INPUT */ import { AmazonCustomerOutput } from '@ecommerce/customer/services/amazon/types'; @@ -70,7 +75,8 @@ export type OriginalFulfillmentOrdersInput = ShopifyFulfillmentOrdersInput; export type OriginalCustomerInput = | ShopifyCustomerInput | WoocommerceCustomerInput - | SquarespaceCustomerInput; + | SquarespaceCustomerInput + | WebflowCustomerInput; /* fulfillment */ export type OriginalFulfillmentInput = ShopifyFulfillmentInput; @@ -105,7 +111,8 @@ export type OriginalCustomerOutput = | ShopifyCustomerOutput | WoocommerceCustomerOutput | SquarespaceCustomerOutput - | AmazonCustomerOutput; + | AmazonCustomerOutput + | WebflowCustomerOutput; /* fulfillment */ export type OriginalFulfillmentOutput = ShopifyFulfillmentOutput; diff --git a/packages/api/src/ecommerce/customer/customer.module.ts b/packages/api/src/ecommerce/customer/customer.module.ts index a09f9f380..00b95c5f9 100644 --- a/packages/api/src/ecommerce/customer/customer.module.ts +++ b/packages/api/src/ecommerce/customer/customer.module.ts @@ -1,3 +1,5 @@ +import { WebflowCustomerMapper } from './services/webflow/mappers'; +import { WebflowService } from './services/webflow'; import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; @@ -30,6 +32,8 @@ import { AmazonCustomerMapper } from './services/amazon/mappers'; /* PROVIDERS SERVICES */ ShopifyService, WoocommerceService, + WebflowService, + WebflowCustomerMapper, ], exports: [SyncService], }) diff --git a/packages/api/src/ecommerce/customer/services/webflow/index.ts b/packages/api/src/ecommerce/customer/services/webflow/index.ts new file mode 100644 index 000000000..1256429a8 --- /dev/null +++ b/packages/api/src/ecommerce/customer/services/webflow/index.ts @@ -0,0 +1,60 @@ +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 { ICustomerService } from '@ecommerce/customer/types'; +import { Injectable } from '@nestjs/common'; +import { EcommerceObject } from '@panora/shared'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { WebflowCustomerOutput } from './types'; + +@Injectable() +export class WebflowService implements ICustomerService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + EcommerceObject.customer.toUpperCase() + ':' + WebflowService.name, + ); + this.registry.registerService('webflow', this); + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'webflow', + vertical: 'ecommerce', + }, + }); + + const resp = await axios.get(`${connection.account_url}/users`, { + headers: { + 'Content-Type': 'application/json', + authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + const customers: WebflowCustomerOutput[] = resp.data.users; + + this.logger.log(`Synced webflow customers !`); + + return { + data: customers, + message: 'Webflow customers retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/ecommerce/customer/services/webflow/mappers.ts b/packages/api/src/ecommerce/customer/services/webflow/mappers.ts new file mode 100644 index 000000000..7aea5717d --- /dev/null +++ b/packages/api/src/ecommerce/customer/services/webflow/mappers.ts @@ -0,0 +1,91 @@ +import { WebflowCustomerInput, WebflowCustomerOutput } from './types'; +import { + UnifiedEcommerceCustomerInput, + UnifiedEcommerceCustomerOutput, +} from '@ecommerce/customer/types/model.unified'; +import { ICustomerMapper } from '@ecommerce/customer/types'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { Injectable } from '@nestjs/common'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { Utils } from '@ecommerce/@lib/@utils'; + +@Injectable() +export class WebflowCustomerMapper implements ICustomerMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService( + 'ecommerce', + 'customer', + 'webflow', + this, + ); + } + + async desunify( + source: UnifiedEcommerceCustomerInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + return; + } + + async unify( + source: WebflowCustomerOutput | WebflowCustomerOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise< + UnifiedEcommerceCustomerOutput | UnifiedEcommerceCustomerOutput[] + > { + if (!Array.isArray(source)) { + return await this.mapSingleCustomerToUnified( + source, + connectionId, + customFieldMappings, + ); + } + // Handling array of WebflowCustomerOutput + return Promise.all( + source.map((customer) => + this.mapSingleCustomerToUnified( + customer, + connectionId, + customFieldMappings, + ), + ), + ); + } + + private async mapSingleCustomerToUnified( + customer: WebflowCustomerOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result = { + remote_id: customer.id, + remote_data: customer, + email: customer.data.email, + first_name: customer.data.name, + last_name: null, + phone_number: null, + addresses: [], + field_mappings: + customFieldMappings?.reduce((acc, mapping) => { + acc[mapping.slug] = customer[mapping.remote_id]; + return acc; + }, {} as Record) || {}, + }; + + return result; + } +} diff --git a/packages/api/src/ecommerce/customer/services/webflow/types.ts b/packages/api/src/ecommerce/customer/services/webflow/types.ts new file mode 100644 index 000000000..a55c2da4f --- /dev/null +++ b/packages/api/src/ecommerce/customer/services/webflow/types.ts @@ -0,0 +1,26 @@ +export interface WebflowCustomerInput { + id: string; + isEmailVerified: boolean; + lastUpdated: string; + invitedOn: string; + createdOn: string; + lastLogin: string; + status: string; + accessGroups: AccessGroup[]; + data: UserData; +} + +type AccessGroup = { + slug: string; + type: string; +}; + +type UserData = { + name: string; + email: string; + 'accept-privacy': boolean; + 'accept-communications': boolean; + [key: string]: any; +}; + +export type WebflowCustomerOutput = Partial; diff --git a/packages/shared/src/connectors/enum.ts b/packages/shared/src/connectors/enum.ts index a66547391..6cea8afa5 100644 --- a/packages/shared/src/connectors/enum.ts +++ b/packages/shared/src/connectors/enum.ts @@ -11,7 +11,8 @@ export enum EcommerceConnectors { SHOPIFY = 'shopify', WOOCOMMERCE = 'woocommerce', SQUARESPACE = 'squarespace', - AMAZON = 'amazon' + AMAZON = 'amazon', + WEBFLOW = 'webflow' } export enum TicketingConnectors { diff --git a/packages/shared/src/connectors/index.ts b/packages/shared/src/connectors/index.ts index 105ca557a..27c8b40ec 100644 --- a/packages/shared/src/connectors/index.ts +++ b/packages/shared/src/connectors/index.ts @@ -5,4 +5,4 @@ export const ACCOUNTING_PROVIDERS = []; export const TICKETING_PROVIDERS = ['zendesk', 'front', 'jira', 'gitlab', 'github']; export const MARKETINGAUTOMATION_PROVIDERS = []; export const FILESTORAGE_PROVIDERS = ['box']; -export const ECOMMERCE_PROVIDERS = ['shopify', 'woocommerce', 'squarespace', 'amazon']; +export const ECOMMERCE_PROVIDERS = ['shopify', 'woocommerce', 'squarespace', 'amazon', 'webflow']; diff --git a/packages/shared/src/connectors/metadata.ts b/packages/shared/src/connectors/metadata.ts index b5baeafac..3949a4f6c 100644 --- a/packages/shared/src/connectors/metadata.ts +++ b/packages/shared/src/connectors/metadata.ts @@ -2920,7 +2920,7 @@ export const CONNECTORS_METADATA: ProvidersConfig = { }, logoPath: 'https://dailybrand.co.zw/wp-content/uploads/2023/10/webflow-2.png', description: 'Sync & Create orders, fulfillments, fulfillment orders, customers and products', - active: false, + active: true, authStrategy: { strategy: AuthStrategy.oauth2 }, From f880b1a1db08aa315c86948becbca36c852f972c Mon Sep 17 00:00:00 2001 From: nael Date: Sat, 31 Aug 2024 06:38:19 +0200 Subject: [PATCH 27/49] :sparkles: Added posthog telemetry on backend --- packages/api/package.json | 5 ++-- .../environment/environment.service.ts | 12 +++++++++ packages/api/src/@core/auth/auth.service.ts | 25 +++++++++++++++-- pnpm-lock.yaml | 27 +++++++++++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index f9952ae87..23a5c15b2 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -67,6 +67,7 @@ "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "pino-pretty": "^10.2.3", + "posthog-node": "^4.2.0", "qs": "^6.12.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", @@ -75,6 +76,7 @@ "yargs": "^17.7.2" }, "devDependencies": { + "@nestjs-modules/mailer": "^2.0.2", "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", @@ -99,8 +101,7 @@ "ts-loader": "^9.4.3", "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", - "typescript": "^5.1.3", - "@nestjs-modules/mailer": "^2.0.2" + "typescript": "^5.1.3" }, "jest": { "moduleFileExtensions": [ diff --git a/packages/api/src/@core/@core-services/environment/environment.service.ts b/packages/api/src/@core/@core-services/environment/environment.service.ts index 8982198ce..c8f25f3c0 100644 --- a/packages/api/src/@core/@core-services/environment/environment.service.ts +++ b/packages/api/src/@core/@core-services/environment/environment.service.ts @@ -46,6 +46,18 @@ export class EnvironmentService { return this.configService.get('ENCRYPT_CRYPTO_SECRET_KEY'); } + getPosthogHost(): string { + return this.configService.get('POSTHOG_HOST'); + } + + getPosthogKey(): string { + return this.configService.get('POSTHOG_KEY'); + } + + getPhTelemetry(): string { + return this.configService.get('PH_TELEMETRY'); + } + getThrottleConfig(): RateLimit { return { ttl: this.configService.get('THROTTLER_TTL'), diff --git a/packages/api/src/@core/auth/auth.service.ts b/packages/api/src/@core/auth/auth.service.ts index 73baee65d..7be4651ea 100644 --- a/packages/api/src/@core/auth/auth.service.ts +++ b/packages/api/src/@core/auth/auth.service.ts @@ -14,17 +14,27 @@ import { ConflictException } from '@nestjs/common'; import { ResetPasswordDto } from './dto/reset-password.dto'; import { RequestPasswordResetDto } from './dto/request-password-reset.dto'; import * as nodemailer from 'nodemailer'; +import { PostHog } from 'posthog-node' +import { EnvironmentService } from '@@core/@core-services/environment/environment.service'; @Injectable() export class AuthService { + private postHogClient: PostHog; + constructor( private prisma: PrismaService, private projectService: ProjectsService, + private env: EnvironmentService, private jwtService: JwtService, private logger: LoggerService, ) { this.logger.setContext(AuthService.name); + this.postHogClient = new PostHog( + this.env.getPosthogKey(), + { host: this.env.getPosthogHost() } + ); } + async resetPassword(resetPasswordDto: ResetPasswordDto) { const { email, new_password, reset_token } = resetPasswordDto; @@ -179,10 +189,10 @@ export class AuthService { async createUser(user: CreateUserDto, id_user?: string) { try { const hashedPassword = await bcrypt.hash(user.password_hash, 10); + const uuid_user = id_user || uuidv4(); const user_ = await this.prisma.users.create({ data: { - // ...user, - id_user: id_user || uuidv4(), + id_user: uuid_user, password_hash: hashedPassword, identification_strategy: 'b2c', first_name: user.first_name, @@ -195,6 +205,17 @@ export class AuthService { name: 'My Project', id_user: user_.id_user, }); + // send posthog event + if(this.env.getPhTelemetry() == "TRUE"){ + this.postHogClient.capture({ + distinctId: uuid_user, + event: 'user signed up', + properties: { + login_type: 'email', + email: user.email, + }, + }) + } return user_; } catch (error) { throw error; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 630e6c431..bc6da2246 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -553,6 +553,9 @@ importers: pino-pretty: specifier: ^10.2.3 version: 10.3.1 + posthog-node: + specifier: ^4.2.0 + version: 4.2.0 qs: specifier: ^6.12.3 version: 6.12.3 @@ -7179,6 +7182,16 @@ packages: - debug dev: false + /axios@1.7.6: + resolution: {integrity: sha512-Ekur6XDwhnJ5RgOCaxFnXyqlPALI3rVeukZMwOdfghW7/wGz784BYKiQq+QD8NPcr91KRo30KfHOchyijwWw7g==} + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + /axobject-query@3.2.1: resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} dependencies: @@ -13893,6 +13906,16 @@ packages: preact: 10.20.2 dev: false + /posthog-node@4.2.0: + resolution: {integrity: sha512-hgyCYMyzMvuF3qWMw6JvS8gT55v7Mtp5wKWcnDrw+nu39D0Tk9BXD7I0LOBp0lGlHEPaXCEVYUtviNKrhMALGA==} + engines: {node: '>=15.0.0'} + dependencies: + axios: 1.7.6 + rusha: 0.8.14 + transitivePeerDependencies: + - debug + dev: false + /preact@10.20.2: resolution: {integrity: sha512-S1d1ernz3KQ+Y2awUxKakpfOg2CEmJmwOP+6igPx6dgr6pgDvenqYviyokWso2rhHvGtTlWWnJDa7RaPbQerTg==} dev: false @@ -15301,6 +15324,10 @@ packages: dependencies: queue-microtask: 1.2.3 + /rusha@0.8.14: + resolution: {integrity: sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==} + dev: false + /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: From 7811bc65d6c80a92c263ff120964d29afca794d7 Mon Sep 17 00:00:00 2001 From: nael Date: Sat, 31 Aug 2024 07:52:47 +0200 Subject: [PATCH 28/49] :memo: Updated readme --- README.md | 136 ++++++++++++++++--- docs/integrations/ecommerce/amazon/index.mdx | 2 +- docs/integrations/hris/gusto/index.mdx | 2 +- 3 files changed, 120 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 7f793964e..66a80afcc 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@

- # 🕹️ Try - Prerequisite: You should have Git and Docker installed @@ -46,7 +45,6 @@ Panora is now running! Follow our [Quickstart Guide](https://docs.panora.dev/qu See also [our selfhost guide here !](https://docs.panora.dev/open-source/selfhost/self-host-guide) - # ✨ Core Features | | @@ -64,13 +62,14 @@ Panora supports integration with the following objects across multiple platforms ### CRM Unified API -| | Contacts | Deals | Notes | Engagements | Tasks | Users | Companies | -|-----------------------------------------------|:--------:|:-----:|:-----:|:-----------:|:-----:|:-----:|:---------:| -| Hubspot | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | -| Pipedrive | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | -| Zoho CRM | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | -| Zendesk Sell | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | -| Attio | ✔️ | | | | | | ✔️ | +| | Contacts | Deals | Notes | Engagements | Tasks | Users | Companies | Stage | +|-----------------------------------------------|:--------:|:-----:|:-----:|:-----------:|:-----:|:-----:|:---------:|:---------:| +| Hubspot | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | ✔️ | +| Pipedrive | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | |✔️ | +| Zoho CRM | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | | +| Zendesk Sell | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | ✔️| +| Attio | ✔️ | ✔️ | ✔️ | | ✔️ | ✔️ | ✔️ | ✔️ | +| Close | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |✔️ | ### Ticketing Unified API @@ -80,27 +79,128 @@ Panora supports integration with the following objects across multiple platforms | Front | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | Jira | ✔ | ✔ | ✔ | | | ✔ | ✔ | ✔ | | Gitlab | ✔ | ✔ | ✔ | | | | | ✔| - +| Github | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔| | + ### ATS Unified API (New!) -| | Activities | Applications | Candidates | Departments | Interviews | Jobs | Offers | Offices | Scorecard | Users | -|-------------|:----------:|:------------:|:----------:|:-----------:|:----------:|:----:|:------:|:-------:|:---------:|:-----:| -| Ashby | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| | Activities | Applications | Candidates | Departments | Interviews | Jobs | Offers | Offices | Scorecard | Users | Eeocs | Job Interview Stage | Tags | Reject Reasons | +|-------------|:----------:|:------------:|:----------:|:-----------:|:----------:|:----:|:------:|:-------:|:---------:|:-----:|:-------:|:-------:|:-------:|:-------:| +| Ashby | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | ✔ | | ✔| ✔| | + +### HRIS Unified API (New!) +| | Bankinfos | Benefits | Companies | Dependents | Employee | Employee Payroll Runs | Employer Benefits | Employments | Groups | Locations | Paygroups | Payrollrun | Timeoff | Timeoff Balances | Timesheet Entries | +|-------------|:----------:|:------------:|:----------:|:-----------:|:----------:|:----:|:------:|:-------:|:---------:|:-----:|:-----:|:-----:|:-----:|:-----:| :-----:| +| Gusto | | ✔ | ✔ | | ✔ | | ✔ | ✔ | ✔ | ✔ | | | | | | ### File Storage Unified API -| File Storage | Drives | Files | Folders | Groups | Users | Permissions | Shared Links | +| | Drives | Files | Folders | Groups | Users | Permissions | Shared Links | |-----------------------------------------------|:--------:|:-----:|:-----:|:-----------:|:-----:|:-----:|:---------:| -| [Google Drive]() | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | | -| [Box]() | | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | -| [Dropbox]() | | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | -| [OneDrive]() | ✔️ | ✔️ | ✔️| ✔️ | ✔️ | | | +| Google Drive | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | | +| Box | | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | +| Dropbox | | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | +| OneDrive | ✔️ | ✔️ | ✔️| ✔️ | ✔️ | | | +### Ecommerce Unified API +| | Customers | Orders | Fulfillments | Fulfillment Orders | Products | +|-----------------------------------------------|:--------:|:-----:|:-----:|:-----------:|:-----:| +| Amazon | ✔️ | ✔️ | | | | +| Shopify | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| Squarespace | ✔️ | ✔️ | | | ✔️ | +| Woocommerce | ✔️ | ✔️ | | | ✔️ | Your favourite software is missing? [Ask the community to build a connector!](https://github.com/panoratech/Panora/issues/new) +# 🚢 Roadmap + +## 🧠 Retrieval Engine for RAG + +- [ ] Access and manage data from any source, including documents, chunk & vectors +- [ ] Semantic, keyword and hybrid search against a vector database + +## 🪄 Integrations Coming Soon + +#### CRM + +- [x] Microsoft Dynamics 365 +- [x] Linear +- [x] Redtail CRM +- [x] Wealthbox +- [x] Leadsquared +- [ ] Salesforce +- [ ] Affinity CRM +- [ ] Odoo +- [ ] Intelliflow +- [ ] Xplan +- [ ] Plannr +- [ ] ACT! +- [ ] Jungo +- [ ] Surefire +- [ ] Velocity + +#### Ticketing + +- [ ] Service Now +- [ ] Wrike +- [ ] Dixa +- [ ] Service Now +- [ ] Asana +- [ ] Aha +- [ ] Clickup + +#### Accounting + +- [ ] Wave Financial +- [ ] Xero +- [ ] Quickbooks + +#### File Storage + +- [ ] Google Drive +- [ ] Dropbox +- [ ] Sharepoint + +#### Productivity + +- [ ] Slack +- [ ] Notion + +#### HRIS + +- [ ] Workday +- [ ] ADP Workforce +- [x] Sage +- [x] Deel +- [ ] BambooHR +- [ ] Rippling + +#### Ecommerce + +- [ ] Ebay +- [ ] Faire +- [ ] Webflow +- [ ] Mercado Libre +- [ ] Prestashop +- [ ] Magento +- [ ] BigCommerce + +#### ATS + +- [ ] Greenhouse +- [ ] Lever +- [ ] Avature + +#### Legacy Softwares + +- [ ] Netsuite +- [ ] SAP (ERP) +- [ ] Ariba +- [ ] Concur +- [ ] Magaya (TMS) +- [ ] Cargowise (TMS) + # 👾 Join the community - [Join the Discord](https://discord.com/invite/PH5k7gGubt) diff --git a/docs/integrations/ecommerce/amazon/index.mdx b/docs/integrations/ecommerce/amazon/index.mdx index c4cbdfb6b..8c0d728d4 100644 --- a/docs/integrations/ecommerce/amazon/index.mdx +++ b/docs/integrations/ecommerce/amazon/index.mdx @@ -2,7 +2,7 @@ title: "Amazon" description: "" --- - + # Common Objects diff --git a/docs/integrations/hris/gusto/index.mdx b/docs/integrations/hris/gusto/index.mdx index aecd42b50..9cc3e37ac 100644 --- a/docs/integrations/hris/gusto/index.mdx +++ b/docs/integrations/hris/gusto/index.mdx @@ -4,7 +4,7 @@ description: "" --- # Common Objects - + | Unified Model | Supported | Provider Endpoints | | ---------------- | --------- | ------------------ | | Bankinfo | No 🚫 | | From 64e53547b7a004b229ef3ecceece4a761601fc29 Mon Sep 17 00:00:00 2001 From: nael Date: Sat, 31 Aug 2024 07:57:01 +0200 Subject: [PATCH 29/49] :memo: Updated readme --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 66a80afcc..a96df5438 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,7 @@ Your favourite software is missing? [Ask the community to build a connector!](ht - [ ] Google Drive - [ ] Dropbox - [ ] Sharepoint +- [ ] One Drive #### Productivity @@ -192,9 +193,20 @@ Your favourite software is missing? [Ask the community to build a connector!](ht - [ ] Lever - [ ] Avature +#### Cybersecurity + +- [ ] Snyk +- [ ] Qualys +- [ ] Crowdstrike +- [ ] Semgrep +- [ ] Rapids7InsightVm +- [ ] Tenable +- [ ] SentinelOne +- [ ] Microsoft Defender + #### Legacy Softwares -- [ ] Netsuite +- [ ] Netsuite (Accounting) - [ ] SAP (ERP) - [ ] Ariba - [ ] Concur From 000e22ef28a84bd16829e0aef353d986b9ad7f20 Mon Sep 17 00:00:00 2001 From: nael Date: Sat, 31 Aug 2024 09:48:55 +0200 Subject: [PATCH 30/49] :bug: Added dockerfile env vars --- apps/webapp/Dockerfile | 2 ++ docker-compose.source.yml | 43 ++++++++++++++++++++------------------- docker-compose.yml | 14 ++++++++----- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/apps/webapp/Dockerfile b/apps/webapp/Dockerfile index 8a3ba64e4..38519ee0c 100644 --- a/apps/webapp/Dockerfile +++ b/apps/webapp/Dockerfile @@ -34,12 +34,14 @@ ARG NEXT_PUBLIC_DISTRIBUTION ARG NEXT_PUBLIC_BACKEND_DOMAIN ARG NEXT_PUBLIC_MAGIC_LINK_DOMAIN ARG NEXT_PUBLIC_WEBAPP_DOMAIN +ARG NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS ENV NEXT_PUBLIC_DISTRIBUTION="$NEXT_PUBLIC_DISTRIBUTION" ENV NEXT_PUBLIC_BACKEND_DOMAIN="${NEXT_PUBLIC_BACKEND_DOMAIN}" ENV NEXT_PUBLIC_MAGIC_LINK_DOMAIN="${NEXT_PUBLIC_MAGIC_LINK_DOMAIN}" ENV NEXT_PUBLIC_WEBAPP_DOMAIN="${NEXT_PUBLIC_WEBAPP_DOMAIN}" +ENV NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS="${NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS}" RUN corepack enable diff --git a/docker-compose.source.yml b/docker-compose.source.yml index d81637773..4ddc42544 100644 --- a/docker-compose.source.yml +++ b/docker-compose.source.yml @@ -188,7 +188,10 @@ services: SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET} CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID} CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET} - + POSTHOG_HOST: "https://us.i.posthog.com" + POSTHOG_KEY: "phc_WhWJfNPOHAuWVdyTacGxrPa9JW54scnofA9KVEjFcFw" + PH_TELEMETRY: "TRUE" + restart: unless-stopped ports: - 3000:3000 @@ -199,12 +202,12 @@ services: condition: service_healthy networks: - backend - healthcheck: - test: ["CMD-SHELL", "curl", "-f", "http://localhost:3000/health"] - start_period: 60s - interval: 10s - timeout: 1s - retries: 50 + #healthcheck: + #test: ["CMD-SHELL", "curl", "-f", "http://localhost:3000/health"] + #start_period: 60s + #interval: 10s + #timeout: 1s + #retries: 50 redis: image: valkey/valkey:7.2-alpine @@ -230,15 +233,15 @@ services: args: NEXT_PUBLIC_DISTRIBUTION: ${DISTRIBUTION} NEXT_PUBLIC_BACKEND_DOMAIN: ${NEXT_PUBLIC_BACKEND_DOMAIN} - NEXT_PUBLIC_FRONTEND_DOMAIN: ${NEXT_PUBLIC_FRONTEND_DOMAIN} + NEXT_PUBLIC_MAGIC_LINK_DOMAIN: ${NEXT_PUBLIC_MAGIC_LINK_DOMAIN} NEXT_PUBLIC_WEBAPP_DOMAIN: ${NEXT_PUBLIC_WEBAPP_DOMAIN} - NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS: ${REDIRECT_TUNNEL_INGRESS} + NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS: ${REDIRECT_TUNNEL_INGRESS} restart: unless-stopped ports: - 80:8090 - depends_on: - api: - condition: service_healthy + #depends_on: + #api: + #condition: service_healthy networks: - backend - frontend @@ -247,14 +250,12 @@ services: build: dockerfile: ./apps/magic-link/Dockerfile context: ./ - args: - VITE_BACKEND_DOMAIN: ${NEXT_PUBLIC_MAGIC_LINK_DOMAIN} - restart: always + restart: always ports: - 81:80 - depends_on: - api: - condition: service_healthy + #depends_on: + #api: + #condition: service_healthy networks: - backend - frontend @@ -271,9 +272,9 @@ services: - ./ngrok.yml:/etc/ngrok.yml ports: - 4040:4040 - depends_on: - api: - condition: service_healthy + #depends_on: + #api: + #condition: service_healthy network_mode: "host" networks: diff --git a/docker-compose.yml b/docker-compose.yml index c8603707e..d5db1b698 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,7 +30,7 @@ services: DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/${POSTGRES_DB}?ssl=false DISTRIBUTION: ${DISTRIBUTION} WEBHOOK_INGRESS: ${WEBHOOK_INGRESS} - REDIRECT_TUNNEL_INGRESS: ${REDIRECT_TUNNEL_INGRESS} + REDIRECT_TUNNEL_INGRESS: "https://panora-contributors-redirect.rachid-7e5.workers.dev/http://localhost:3000" JWT_SECRET: ${JWT_SECRET} SENTRY_ENABLED: ${SENTRY_ENABLED} SENTRY_DSN: ${SENTRY_DSN} @@ -41,8 +41,8 @@ services: REDIS_DB: ${REDIS_DB} WEBAPP_URL: ${NEXT_PUBLIC_WEBAPP_DOMAIN} ENCRYPT_CRYPTO_SECRET_KEY: ${ENCRYPT_CRYPTO_SECRET_KEY} - HUBSPOT_CRM_CLOUD_CLIENT_ID: ${HUBSPOT_CRM_CLOUD_CLIENT_ID} - HUBSPOT_CRM_CLOUD_CLIENT_SECRET: ${HUBSPOT_CRM_CLOUD_CLIENT_SECRET} + HUBSPOT_CRM_CLOUD_CLIENT_ID: "ca8ef3b9-201d-4472-84c3-1246f83d0b03" + HUBSPOT_CRM_CLOUD_CLIENT_SECRET: "16b75410-8884-4c4a-a6b7-ca50b61eb289" ATTIO_CRM_CLOUD_CLIENT_ID: ${ATTIO_CRM_CLOUD_CLIENT_ID} ATTIO_CRM_CLOUD_CLIENT_SECRET: ${ATTIO_CRM_CLOUD_CLIENT_SECRET} ZOHO_CRM_CLOUD_CLIENT_ID: ${ZOHO_CRM_CLOUD_CLIENT_ID} @@ -182,7 +182,9 @@ services: SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET} CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID} CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET} - + POSTHOG_HOST: "https://us.i.posthog.com" + POSTHOG_KEY: "phc_WhWJfNPOHAuWVdyTacGxrPa9JW54scnofA9KVEjFcFw" + PH_TELEMETRY: "TRUE" restart: unless-stopped ports: - 3000:3000 @@ -231,10 +233,12 @@ services: webapp-next: image: panora.docker.scarf.sh/panoradotdev/frontend-webapp:selfhosted environment: - NEXT_PUBLIC_DISTRIBUTION: ${DISTRIBUTION} + NEXT_PUBLIC_DISTRIBUTION: "selfhost" NEXT_PUBLIC_BACKEND_DOMAIN: ${NEXT_PUBLIC_BACKEND_DOMAIN} NEXT_PUBLIC_MAGIC_LINK_DOMAIN: ${NEXT_PUBLIC_MAGIC_LINK_DOMAIN} NEXT_PUBLIC_WEBAPP_DOMAIN: ${NEXT_PUBLIC_WEBAPP_DOMAIN} + NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS: "https://panora-contributors-redirect.rachid-7e5.workers.dev/http://localhost:3000" + restart: unless-stopped ports: - 80:8090 From 59a781aaf364d3fa897006dc2bb066265d040deb Mon Sep 17 00:00:00 2001 From: Rachid Flih Date: Sat, 31 Aug 2024 01:04:56 -0700 Subject: [PATCH 31/49] =?UTF-8?q?=F0=9F=93=9D=20Update=20to=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/mint.json | 2 +- docs/quick-start.mdx | 142 +++++++++++-------------------------------- 2 files changed, 37 insertions(+), 107 deletions(-) diff --git a/docs/mint.json b/docs/mint.json index 5456ae6ec..9076d1888 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -1,5 +1,6 @@ { "$schema": "https://mintlify.com/schema.json", + "layout": "topnav", "analytics": { "hotjar": { "hjid": "5154921", @@ -53,7 +54,6 @@ { "group": "Documentation", "pages": [ - "welcome", "quick-start", "catalog" ] diff --git a/docs/quick-start.mdx b/docs/quick-start.mdx index ef6cd12e2..a7074e894 100644 --- a/docs/quick-start.mdx +++ b/docs/quick-start.mdx @@ -8,29 +8,19 @@ icon: "star" Before you begin, make sure you have the following: -- A cloud [account](https://tally.so/r/w5ZoaN?source=docs_welcome) (or see our guide [here](/open-source/self-host-guide) on getting started with the selfhost version) -- An API key, see our guide [here](/core-concepts/auth) -- Node JS v18.17.1 or newer installed on your computer - -## **Step 1: Get access to your users data** - -You either have the option to use a [**no-code magic link**](/core-concepts/magic-links) or the [**embedded frontend snippet**](/recipes/embed-catalog) to get access to your user's data. -Choose the one that fits your needs ! - -[We recommend listening to webhooks to get notified once a user connects and catch the connection token used for requests. -](/recipes/catch-connection-token) - - - - Let's send our first magic link so you can ask your customers to grant you access to their tools, without writing code. - - - - Embed Panora into your product with our native component to collect access to use user's data. - - You can find the component on NPM - [here](https://www.npmjs.com/package/@panora/embedded-card-react) - - - - - ```shell React - npm i @panora/embedded-card-react - ``` - - - - - - ```javascript React - import "@panora/embedded-card-react/dist/index.css"; - import PanoraProviderCard from "@panora/embedded-card-react"; - import PanoraDynamicCatalog from "@panora/embedded-card-react"; - ``` - - - - - You can either import a single connector card or a catalog of all connectors that you select in the UI dashboard configuration (see [here](/recipes/embed-catalog) for more details). - In order to fill the `linkedUserId` prop, use your own remote id that exist in your system **OR** check this [recipe](/recipes/import-existing-users) ! - - ```javascript Single Connector Card - import "@panora/embedded-card-react/dist/index.css"; - import { PanoraProviderCard } from "@panora/embedded-card-react"; - import { ConnectorCategory } from '@panora/shared' - - - ``` - ```javascript Whole Connector Catalog - import "@panora/embedded-card-react/dist/index.css"; - import { PanoraDynamicCatalogCard } from "@panora/embedded-card-react"; - import { ConnectorCategory } from '@panora/shared' - - - ``` - - You should see a card or catalog being rendered client-side ! - - __insert photo__ - - +Once the user successfully completes the granting auth flow, the connection will have a `status` value of `valid`. - - +## Step 2: Grab the connection token -Once the user successfully completes the granting auth flow, the connection will have a `status` value of `valid`. +Connection tokens are the way Panora represents a third party's account, whatever the underlying identification mechanism is. +There are two ways to get these tokens: catchting them with a webhook, or just finding them in your dashboard. For now, we'll stick to the dashboard,. -## **Step 2: Send your first unified API requests** +Let's find the `connection token` in the dashboard. + +ADD SCREEN HERE + +## **Step 3: Make your first API requests** + +Let's Create a contact in our Hubspot acccount, using Panora's API. + +This will require you to have: +- The `connection token` from the previous step +- An API key (guide [here](/core-concepts/auth#learn-how-to-generate-your-api-keys)) + +The API key identifies _YOU_ as a user of Panora, while the connection token identifies which user's account you behalf you want to. In the future, you will have one connection token per user account connected on the Panora platform, while keeping one API key. -### _Create a contact in a CRM_ - - We assume for this tutorial that you have a valid Panora API Key, and a - `connection_token`. Find help [here](/core-concepts/auth). - - - You can find the Typescript SDK on NPM - [here](https://www.npmjs.com/package/@panora/sdk) - In this example, we will create a contact in a CRM. Visit other sections of the [documentation](/api-reference/introduction) to find category-specific examples. ```javascript TypeScript @@ -225,16 +158,13 @@ Let’s break down what’s happening here: - We pass the connection token catched during step 1 ([More info here](/core-concepts/auth#catch-connection-tokens)) - Finally, we print the response. -## Next Steps - -Congratulations, you’ve successfully sent your first unified API request with Panora! Here are some next steps to continue your journey. - -### Listen to events using a webhook +## Suggested Next Steps -Visit our [webhooks section](/webhooks/overview). +Amazing! You’ve successfully sent your first unified API request with Panora! Here are some next steps to continue your learning journey: -### API resources +- Setup your first [webhooks](/webhooks/overview) -Drop into our [API documentation](/api-reference/introduction) for more details on the available endpoints and parameters. +API resources: +Check our [API documentation](/api-reference/introduction) for more details on the available endpoints and parameters. -Check out our [client SDKs](/backend-sdk/typescript) for a set of tools to make it easier for you to build with and integrate Panora into your applications. \ No newline at end of file +Check our SDKs: [Typescript](/backend-sdk/typescript) for a set of tools to make it easier for you to build with and integrate Panora into your applications. \ No newline at end of file From 74aaf489f1d147272b8fcb982fd0cf3de21c2b3b Mon Sep 17 00:00:00 2001 From: Rachid Flih Date: Sat, 31 Aug 2024 02:36:16 -0700 Subject: [PATCH 32/49] =?UTF-8?q?=F0=9F=93=9D=20Update=20docs=20quickstart?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/images/connections_screen.png | Bin 0 -> 68117 bytes docs/quick-start.mdx | 111 +++++++++-------------------- 2 files changed, 32 insertions(+), 79 deletions(-) create mode 100644 docs/images/connections_screen.png diff --git a/docs/images/connections_screen.png b/docs/images/connections_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..ce306336b5040c89cff925cc11289beda1708440 GIT binary patch literal 68117 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

Bi#bSMGaDYKTIush2%j37jPq6d~+A&qpq+4VRU% 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 A1ONa4 literal 0 HcmV?d00001 diff --git a/docs/quick-start.mdx b/docs/quick-start.mdx index a7074e894..3bb1222ff 100644 --- a/docs/quick-start.mdx +++ b/docs/quick-start.mdx @@ -36,66 +36,55 @@ Let's now create our first magic link! it. Click `Generate` - - -Once the user successfully completes the granting auth flow, the connection will have a `status` value of `valid`. + Now, for the purpose of this tutorial, open the magic link you just created and login using the Hubspot account you previously created + ## Step 2: Grab the connection token Connection tokens are the way Panora represents a third party's account, whatever the underlying identification mechanism is. There are two ways to get these tokens: catchting them with a webhook, or just finding them in your dashboard. For now, we'll stick to the dashboard,. -Let's find the `connection token` in the dashboard. - -ADD SCREEN HERE +To find the `connection token` in the dashboard, visit the `connections` section. Copy the connection token from its column and save it. +![title](/images/connections_screen.png) ## **Step 3: Make your first API requests** -Let's Create a contact in our Hubspot acccount, using Panora's API. +Let's list the contacts that are in the Hubspot acccount, using Panora's API. This will require you to have: - The `connection token` from the previous step - An API key (guide [here](/core-concepts/auth#learn-how-to-generate-your-api-keys)) +- You don't need to create contacts, since Hubspot accounts come with mockup contacts included. The API key identifies _YOU_ as a user of Panora, while the connection token identifies which user's account you behalf you want to. In the future, you will have one connection token per user account connected on the Panora platform, while keeping one API key. +You can copy past the Curl example below, and copy-paste it in your terminal. Don't forget to replace the API key and the connection token. - In this example, we will create a contact in a CRM. Visit other sections of the [documentation](/api-reference/introduction) to find category-specific examples. - ```javascript TypeScript + ```shell curl + curl --location 'http://localhost:3000/crm/contacts' \ +--header 'x-connection-token: 7e6b9454-f616-477a-a71a-0f23eb74a72c' \ +--header 'x-api-key: sk_dev_30a78556-6a83-425x-b461-a87f007a09e1' + ``` + + ```javascript TypeScript SDK (alpha) import { Panora } from "@panora/sdk"; const panora = new Panora({ apiKey: process.env.API_KEY, }); - const input = { - first_name: 'tom', - last_name: 'jedusor', - email_addresses: [ - { - 'email_address': 'tom@jedusor.com', - 'email_address_type': 'PERSONAL' - } - ], - phone_numbers: [ - { - 'phone_number': '+33650438278', - 'phone_type': 'MOBILE' - } - ], - }; - - const result = await panora.crm.contacts.create({ - xConnectionToken: "YOUR_USER_CONNECTION_TOKEN", - unifiedCrmContactInput: input, + const result = await panora.crm.contacts.list({ + xConnectionToken: "YOUR_USER_CONNECTION_TOKEN" }); - console.log(result); + for await (const page of result) { + // handle page + } ``` - ```python Python + ```python Python SDK (alpha) import os from panora_sdk import Panora @@ -103,60 +92,24 @@ The API key identifies _YOU_ as a user of Panora, while the connection token ide api_key=os.getenv("API_KEY", ""), ) - body = { - 'first_name': 'tom', - 'last_name': 'jedusor', - 'email_addresses': [ - { - 'email_address': 'tom@jedusor.com', - 'email_address_type': 'PERSONAL' - } - ], - 'phone_numbers': [ - { - 'phone_number': '+33650438278', - 'phone_type': 'MOBILE' - } - ] - } + res = panora.crm.contacts.list(x_connection_token="YOUR_USER_CONNECTION_TOKEN") - res = panora.crm.contacts.create(x_connection_token="YOUR_USER_CONNECTION_TOKEN", unified_crm_contact_input=body) + if res is not None: + while True: + # handle items - print(res) - ``` - - ```shell curl - curl --request POST \ - --url https://api.panora.dev/crm/contacts \ - --header 'Content-Type: application/json' \ - --header 'x-api-key: ' \ - --header 'x-connection-token: ' \ - --data '{ - "first_name": "tom", - "last_name": "jedusor", - "email_addresses": [ - { - "email_address": "tom@jedusor.com", - "email_address_type": "PERSONAL" - } - ], - "phone_numbers": [ - { - "phone_number": "+33650438278", - "phone_type": "MOBILE" - } - ] - }' + res = res.Next() + if res is None: + break ``` -Let’s break down what’s happening here: +Let’s break down what we did in this tutorial: + -- We import the `Panora` sdk, which provides a convenient way to interact with the Panora Unified API. -- We create an instance of the `Panora` client, passing in our API key. -- We call the `panora.crm.contacts.create` method to add a contact inside a 3rd party. We specify the input we want to use (see [here](/crm/contacts/overview) for reference). -- We pass the connection token catched during step 1 ([More info here](/core-concepts/auth#catch-connection-tokens)) -- Finally, we print the response. +- We created a Magic Link to let a user grant us access to its account +- After he connected its account, Panora synced data with Hubspot +- We used a `connection token` and our Panora API key to make our first request to the Panora API ## Suggested Next Steps From b7d8255711afc0a7b81e82d05cac186d20dfbac4 Mon Sep 17 00:00:00 2001 From: Rachid F <109089247+rflihxyz@users.noreply.github.com> Date: Sat, 31 Aug 2024 02:41:28 -0700 Subject: [PATCH 33/49] Delete docs/welcome.mdx --- docs/welcome.mdx | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 docs/welcome.mdx diff --git a/docs/welcome.mdx b/docs/welcome.mdx deleted file mode 100644 index ff28bdec1..000000000 --- a/docs/welcome.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Welcome to Panora -description: "Panora is an open-source, unified API for shipping customer-facing integrations" -icon: t-rex ---- - -{" "} - - - Learn Panora's main concepts and start building integrations - - -### FAQ - - - - Panora reduces the amount of APIs you will need to interact with. Using a single endpoint, you will be able to read and write data from various CRMs, ticketing platforms. No need to go through endless cycles of documentation exploration, testing and maintaining. - - -{" "} - - - No! Panora is extensible. We are open-source, and written in TypeScript. - Building new connectors on top of Panora is fun and faster than building them - from scratch.

[Read our guide here](https://github.com/panoratech/Panora/blob/main/INTEGRATIONS.md). -
- -
- -### 🔮 Core Features - -| | -| ------------------------------------------------------------------------------------------------------------------------------------ | -| **[Magic Links:](/core-concepts/magic-links)** Let your users grant you access to their data. Without writing code. | -| **[Custom Fields:](/core-concepts/custom-fields)** Reflect in Panora the specific data points that matter to your users | -| **[Passthrough Requests:](/core-concepts/passthrough-requests)** Interact with other software platforms in their native format. | -| **[Flexible Sync Settings:](/core-concepts/sync-strategies)** Businesses that need real-time update can configure Panora accordingly | -| **[Webhooks:](/webhooks/overview)** Listen to one webhook to receive normalized data from various software platforms | From e2fb610176c58a00226d12f27db40da85733e32c Mon Sep 17 00:00:00 2001 From: Rachid F <109089247+rflihxyz@users.noreply.github.com> Date: Sat, 31 Aug 2024 02:59:07 -0700 Subject: [PATCH 34/49] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a96df5438..848e5604e 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ 3. Start ``` - docker compose up + docker compose -f docker-compose.source.yml up ``` Panora is now running! Follow our [Quickstart Guide](https://docs.panora.dev/quick-start) to start adding integrations to your product ! From a896247bc29d7ca0e027aadc186c317b5fe6a845 Mon Sep 17 00:00:00 2001 From: Rachid F <109089247+rflihxyz@users.noreply.github.com> Date: Sat, 31 Aug 2024 02:59:52 -0700 Subject: [PATCH 35/49] Update guide.mdx --- docs/open-source/self_hosting/guide.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/open-source/self_hosting/guide.mdx b/docs/open-source/self_hosting/guide.mdx index d65cde004..4b5dfb791 100644 --- a/docs/open-source/self_hosting/guide.mdx +++ b/docs/open-source/self_hosting/guide.mdx @@ -21,7 +21,7 @@ git clone https://github.com/panoratech/Panora.git ```bash - docker compose up -d + docker compose -f docker-compose.source.yml up -d ``` From 6d94b36e3f4624eb5bc240c934ee3f3e4cac40ab Mon Sep 17 00:00:00 2001 From: mit <1mitccc@gmail.com> Date: Sat, 31 Aug 2024 17:13:14 +0530 Subject: [PATCH 36/49] Add types.ts for product object of webflow-ecommerce service --- .../product/services/webflow/index.ts | 0 .../product/services/webflow/mappers.ts | 0 .../product/services/webflow/types.ts | 83 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 packages/api/src/ecommerce/product/services/webflow/index.ts create mode 100644 packages/api/src/ecommerce/product/services/webflow/mappers.ts create mode 100644 packages/api/src/ecommerce/product/services/webflow/types.ts diff --git a/packages/api/src/ecommerce/product/services/webflow/index.ts b/packages/api/src/ecommerce/product/services/webflow/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/api/src/ecommerce/product/services/webflow/mappers.ts b/packages/api/src/ecommerce/product/services/webflow/mappers.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/api/src/ecommerce/product/services/webflow/types.ts b/packages/api/src/ecommerce/product/services/webflow/types.ts new file mode 100644 index 000000000..46c6976a2 --- /dev/null +++ b/packages/api/src/ecommerce/product/services/webflow/types.ts @@ -0,0 +1,83 @@ +// reference: https://docs.developers.webflow.com/data/reference/list-products + +export interface WebflowProductInput { + product: ProductData; + skus: SkuData[]; + publishStatus: 'staging' | 'live'; +} + +export interface ProductData { + id: string; + cmsLocaleId: string; + lastPublished: string; + lastUpdated: string; + createdOn: string; + isArchived: boolean; + isDraft: boolean; + fieldData: ProductFieldData; +} + +export interface ProductFieldData { + name: string; // required + slug: string; // required + description?: string; + shippable: boolean; + skuProperties?: SkuProperty[]; + categories?: string[]; + taxCategory?: string; + defaultSku: string; + ecProductType?: string; + [key: string]: any; // for custom fields +} + +export interface SkuProperty { + id: string; // required + name: string; // required + enum: VariantOption[]; +} + +export interface VariantOption { + id: string; // required + name: string; // required + slug: string; // required +} + +export interface SkuData { + id: string; + cmsLocaleId: string; + lastPublished: string; + lastUpdated: string; + createdOn: string; + fieldData: SkuFieldData; +} + +export interface SkuFieldData { + skuValues: { [key: string]: string }; // maps SKU property ID to SKU value ID + name: string; // required + slug: string; // required + price: Price; // required + compareAtPrice?: Price; + ecSkuBillingMethod?: 'one-time' | 'subscription'; + ecSkuSubscriptionPlan?: SubscriptionPlan; + trackInventory?: boolean; // Defaults to false + quantity?: number; + [key: string]: any; // for custom +} + +export interface Price { + value: number; // required + unit: string; // required +} + +export interface SubscriptionPlan { + interval: 'day' | 'week' | 'month' | 'year'; // required + frequency: number; // required + trial?: number; + plans?: { + id: string; + platform: string; + status: string; + }[]; +} + +export type WebflowProductOutput = Partial; From f91d79dd2a17e45707c7c235300f009973416a90 Mon Sep 17 00:00:00 2001 From: mit <1mitccc@gmail.com> Date: Sat, 31 Aug 2024 18:38:32 +0530 Subject: [PATCH 37/49] Add mappers.ts for product object of webflow-ecommerce service --- .../product/services/webflow/mappers.ts | 119 ++++++++++++++++++ .../product/services/webflow/types.ts | 49 +++++--- 2 files changed, 154 insertions(+), 14 deletions(-) diff --git a/packages/api/src/ecommerce/product/services/webflow/mappers.ts b/packages/api/src/ecommerce/product/services/webflow/mappers.ts index e69de29bb..be7e90b6d 100644 --- a/packages/api/src/ecommerce/product/services/webflow/mappers.ts +++ b/packages/api/src/ecommerce/product/services/webflow/mappers.ts @@ -0,0 +1,119 @@ +import { + UnifiedEcommerceProductInput, + UnifiedEcommerceProductOutput, +} from '@ecommerce/product/types/model.unified'; +import { IProductMapper } from '@ecommerce/product/types'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { Injectable } from '@nestjs/common'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { Utils } from '@ecommerce/@lib/@utils'; +import { WebflowProductInput, WebflowProductOutput } from './types'; + +@Injectable() +export class WebflowProductMapper implements IProductMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService( + 'ecommerce', + 'product', + 'webflow', + this, + ); + } + + async desunify( + source: UnifiedEcommerceProductInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise> { + const res: Partial = { + product: { + fieldData: { + name: source.variants?.[0]?.title, + slug: source.product_url?.split('/').pop() || '', + description: source.description, + shippable: true, + skuProperties: [], + }, + }, + skus: source.variants?.map((item) => ({ + fieldData: { + name: item.title, + slug: item.sku, + sku: item.sku, + price: { + value: item.price, + unit: 'USD', + }, + }, + })), + }; + + customFieldMappings?.forEach((mapping) => { + if (mapping.slug === 'publishStatus') { + res.publishStatus = source[mapping.remote_id]; + return; + } + + res.product.fieldData[mapping.slug] = source[mapping.remote_id]; + }); + + return res; + } + + async unify( + source: WebflowProductOutput | WebflowProductOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return await this.mapSingleProductToUnified( + source, + connectionId, + customFieldMappings, + ); + } + // Handling array of WebflowProductOutput + return Promise.all( + source.map((data) => + this.mapSingleProductToUnified(data, connectionId, customFieldMappings), + ), + ); + } + + private async mapSingleProductToUnified( + data: WebflowProductOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + return { + remote_id: data.product.id, + remote_data: data, + images_urls: + data.skus?.map((sku) => sku?.fieldData?.mainImage?.url) || [], + description: data.product.fieldData?.description, + tags: data.product.fieldData?.categories, + created_at: data.product?.createdOn, + modified_at: data.product?.lastUpdated, + variants: data.skus?.map((sku) => ({ + title: sku?.fieldData?.name, + price: sku?.fieldData?.price?.value, + sku: sku?.fieldData?.sku, + inventory_quantity: sku?.fieldData?.quantity, + weight: sku?.fieldData?.weight, + options: null, + })), + }; + } +} diff --git a/packages/api/src/ecommerce/product/services/webflow/types.ts b/packages/api/src/ecommerce/product/services/webflow/types.ts index 46c6976a2..e6d9ada21 100644 --- a/packages/api/src/ecommerce/product/services/webflow/types.ts +++ b/packages/api/src/ecommerce/product/services/webflow/types.ts @@ -7,13 +7,13 @@ export interface WebflowProductInput { } export interface ProductData { - id: string; - cmsLocaleId: string; - lastPublished: string; - lastUpdated: string; - createdOn: string; - isArchived: boolean; - isDraft: boolean; + id?: string; + cmsLocaleId?: string; + lastPublished?: string; + lastUpdated?: string; + createdOn?: string; + isArchived?: boolean; + isDraft?: boolean; fieldData: ProductFieldData; } @@ -25,7 +25,7 @@ export interface ProductFieldData { skuProperties?: SkuProperty[]; categories?: string[]; taxCategory?: string; - defaultSku: string; + defaultSku?: string; ecProductType?: string; [key: string]: any; // for custom fields } @@ -43,19 +43,28 @@ export interface VariantOption { } export interface SkuData { - id: string; - cmsLocaleId: string; - lastPublished: string; - lastUpdated: string; - createdOn: string; + id?: string; + cmsLocaleId?: string; + lastPublished?: string; + lastUpdated?: string; + createdOn?: string; fieldData: SkuFieldData; } export interface SkuFieldData { - skuValues: { [key: string]: string }; // maps SKU property ID to SKU value ID + skuValues?: { [key: string]: string }; // maps SKU property ID to SKU value ID name: string; // required slug: string; // required price: Price; // required + product?: string; + width?: number; + length?: number; + height?: number; + weight?: number; + sku: string; + mainImage?: Image | null; + moreImages?: Image[]; + downloadFiles?: DownloadFile[]; compareAtPrice?: Price; ecSkuBillingMethod?: 'one-time' | 'subscription'; ecSkuSubscriptionPlan?: SubscriptionPlan; @@ -80,4 +89,16 @@ export interface SubscriptionPlan { }[]; } +export interface Image { + fileId: string; + url: string; + alt?: string | null; +} + +export interface DownloadFile { + name: string; + url: string; + id: string; +} + export type WebflowProductOutput = Partial; From 07a88df5f13f167f58ba73f92c760c4d18dd3248 Mon Sep 17 00:00:00 2001 From: mit <1mitccc@gmail.com> Date: Sat, 31 Aug 2024 19:49:51 +0530 Subject: [PATCH 38/49] Add index.ts for product object of webflow-ecommerce service --- .../types/original/original.ecommerce.ts | 6 +- .../src/ecommerce/product/product.module.ts | 4 + .../product/services/webflow/index.ts | 99 +++++++++++++++++++ .../product/services/webflow/mappers.ts | 17 ++-- .../product/services/webflow/types.ts | 9 +- 5 files changed, 122 insertions(+), 13 deletions(-) diff --git a/packages/api/src/@core/utils/types/original/original.ecommerce.ts b/packages/api/src/@core/utils/types/original/original.ecommerce.ts index d7d034a72..4c262cba5 100644 --- a/packages/api/src/@core/utils/types/original/original.ecommerce.ts +++ b/packages/api/src/@core/utils/types/original/original.ecommerce.ts @@ -1,3 +1,5 @@ +import { WebflowProductInput, WebflowProductOutput } from '@ecommerce/product/services/webflow/types'; + import { WebflowCustomerInput, WebflowCustomerOutput, @@ -59,7 +61,7 @@ import { export type OriginalProductInput = | ShopifyProductInput | WoocommerceProductInput - | SquarespaceProductInput; + | SquarespaceProductInput | WebflowProductInput; /* order */ export type OriginalOrderInput = @@ -94,7 +96,7 @@ export type EcommerceObjectInput = export type OriginalProductOutput = | ShopifyProductOutput | WoocommerceProductOutput - | SquarespaceProductOutput; + | SquarespaceProductOutput | WebflowProductOutput; /* order */ export type OriginalOrderOutput = diff --git a/packages/api/src/ecommerce/product/product.module.ts b/packages/api/src/ecommerce/product/product.module.ts index feb69f6d7..ac43271e3 100644 --- a/packages/api/src/ecommerce/product/product.module.ts +++ b/packages/api/src/ecommerce/product/product.module.ts @@ -1,3 +1,5 @@ +import { WebflowProductMapper } from './services/webflow/mappers'; +import { WebflowService } from './services/webflow'; import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; @@ -31,6 +33,8 @@ import { SyncService } from './sync/sync.service'; ShopifyService, WoocommerceService, SquarespaceService, + WebflowService, + WebflowProductMapper, ], exports: [SyncService], }) diff --git a/packages/api/src/ecommerce/product/services/webflow/index.ts b/packages/api/src/ecommerce/product/services/webflow/index.ts index e69de29bb..5636377e7 100644 --- a/packages/api/src/ecommerce/product/services/webflow/index.ts +++ b/packages/api/src/ecommerce/product/services/webflow/index.ts @@ -0,0 +1,99 @@ +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 { IProductService } from '@ecommerce/product/types'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { EcommerceObject } from '@panora/shared'; +import { WebflowProductInput, WebflowProductOutput } from './types'; +import { DesunifyReturnType } from '@@core/utils/types/desunify.input'; +import { OriginalProductOutput } from '@@core/utils/types/original/original.ecommerce'; + +@Injectable() +export class WebflowService implements IProductService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + EcommerceObject.product.toUpperCase() + ':' + WebflowService.name, + ); + this.registry.registerService('webflow', this); + } + + async addProduct( + productData: WebflowProductInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'webflow', + vertical: 'ecommerce', + }, + }); + const resp = await axios.post( + // https://api.webflow.com/v2/sites/{site_id}/products + `${connection.account_url}/products`, + productData, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + return { + data: resp.data, + message: 'Webflow product created', + statusCode: 201, + }; + } catch (error) { + throw error; + } + } + + async sync(data: SyncParam): Promise> { + try { + const { linkedUserId } = data; + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'webflow', + vertical: 'ecommerce', + }, + }); + const resp = await axios.get( + // https://api.webflow.com/v2/sites/{site_id}/products + `${connection.account_url}/products`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + const products: WebflowProductOutput[] = resp.data.items; + this.logger.log(`Synced webflow products !`); + + return { + data: products, + message: 'Webflow products retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/ecommerce/product/services/webflow/mappers.ts b/packages/api/src/ecommerce/product/services/webflow/mappers.ts index be7e90b6d..1a3bc479c 100644 --- a/packages/api/src/ecommerce/product/services/webflow/mappers.ts +++ b/packages/api/src/ecommerce/product/services/webflow/mappers.ts @@ -38,20 +38,19 @@ export class WebflowProductMapper implements IProductMapper { slug: source.product_url?.split('/').pop() || '', description: source.description, shippable: true, - skuProperties: [], }, }, - skus: source.variants?.map((item) => ({ + sku: { fieldData: { - name: item.title, - slug: item.sku, - sku: item.sku, + name: source.variants?.[0]?.title, + slug: source.variants?.[0]?.sku, + sku: source.variants?.[0]?.sku, price: { - value: item.price, + value: parseInt(source.variants?.[0]?.price.toString(), 10), unit: 'USD', }, }, - })), + }, }; customFieldMappings?.forEach((mapping) => { @@ -101,7 +100,9 @@ export class WebflowProductMapper implements IProductMapper { remote_id: data.product.id, remote_data: data, images_urls: - data.skus?.map((sku) => sku?.fieldData?.mainImage?.url) || [], + data.skus + ?.map((sku) => sku?.fieldData?.mainImage?.url) + .filter((url) => Boolean(url)) || [], description: data.product.fieldData?.description, tags: data.product.fieldData?.categories, created_at: data.product?.createdOn, diff --git a/packages/api/src/ecommerce/product/services/webflow/types.ts b/packages/api/src/ecommerce/product/services/webflow/types.ts index e6d9ada21..773d7ac87 100644 --- a/packages/api/src/ecommerce/product/services/webflow/types.ts +++ b/packages/api/src/ecommerce/product/services/webflow/types.ts @@ -1,9 +1,8 @@ // reference: https://docs.developers.webflow.com/data/reference/list-products -export interface WebflowProductInput { +export interface WebflowProductOutput { product: ProductData; skus: SkuData[]; - publishStatus: 'staging' | 'live'; } export interface ProductData { @@ -101,4 +100,8 @@ export interface DownloadFile { id: string; } -export type WebflowProductOutput = Partial; +export interface WebflowProductInput { + product: ProductData; + sku: SkuData; + publishStatus: 'staging' | 'live'; +} From 011d3bec1d67b25ae58c909290dd317634cb9638 Mon Sep 17 00:00:00 2001 From: nael Date: Sat, 31 Aug 2024 21:42:00 +0200 Subject: [PATCH 39/49] :bug: Add posthog to dev docker compose --- docker-compose.dev.yml | 4 ++++ docker-compose.source.yml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index e82cf3769..a2efd6fa1 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -188,6 +188,10 @@ services: SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${SNYK_CYBERSECURITY_CLOUD_CLIENT_SECRET} CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_ID} CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET: ${CROWDSTRIKE_CYBERSECURITY_CLOUD_CLIENT_SECRET} + POSTHOG_HOST: ${POSTHOG_HOST} + POSTHOG_KEY: ${POSTHOG_KEY} + PH_TELEMETRY: ${PH_TELEMETRY} + restart: unless-stopped ports: - 3000:3000 diff --git a/docker-compose.source.yml b/docker-compose.source.yml index 4ddc42544..b7661ab81 100644 --- a/docker-compose.source.yml +++ b/docker-compose.source.yml @@ -11,7 +11,7 @@ services: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} - ports: + ports: - '5432:5432' healthcheck: test: ['CMD-SHELL', 'pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}'] From 957900ea9af9c2f128cd9ebc2c86a9a0f6f7bad2 Mon Sep 17 00:00:00 2001 From: Rachid Flih Date: Sat, 31 Aug 2024 13:35:17 -0700 Subject: [PATCH 40/49] =?UTF-8?q?=F0=9F=92=9A=20Added=20missing=20variable?= =?UTF-8?q?s=20to=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker.check-build.dashboard.selfhosted.yml | 4 +++- .../workflows/docker.export.frontend-dashboard.selfhosted.yml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.check-build.dashboard.selfhosted.yml b/.github/workflows/docker.check-build.dashboard.selfhosted.yml index 0ca62a5d8..72a152c4c 100644 --- a/.github/workflows/docker.check-build.dashboard.selfhosted.yml +++ b/.github/workflows/docker.check-build.dashboard.selfhosted.yml @@ -29,4 +29,6 @@ jobs: build-args: | NEXT_PUBLIC_BACKEND_DOMAIN=${{ secrets.NEXT_PUBLIC_BACKEND_DOMAIN }} NEXT_PUBLIC_MAGIC_LINK_DOMAIN=${{ secrets.NEXT_PUBLIC_MAGIC_LINK_DOMAIN }} - NEXT_PUBLIC_DISTRIBUTION=${{ env.DISTRIBUTION }} \ No newline at end of file + NEXT_PUBLIC_DISTRIBUTION=${{ env.DISTRIBUTION }} + NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS=${{ secrets.NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS }} + NEXT_PUBLIC_WEBAPP_DOMAIN= ${{env.NEXT_PUBLIC_WEBAPP_DOMAIN}} \ No newline at end of file diff --git a/.github/workflows/docker.export.frontend-dashboard.selfhosted.yml b/.github/workflows/docker.export.frontend-dashboard.selfhosted.yml index 05472ff17..76d0785fb 100644 --- a/.github/workflows/docker.export.frontend-dashboard.selfhosted.yml +++ b/.github/workflows/docker.export.frontend-dashboard.selfhosted.yml @@ -35,4 +35,6 @@ jobs: build-args: | NEXT_PUBLIC_BACKEND_DOMAIN=${{ secrets.NEXT_PUBLIC_BACKEND_DOMAIN }} NEXT_PUBLIC_MAGIC_LINK_DOMAIN=${{ secrets.NEXT_PUBLIC_MAGIC_LINK_DOMAIN }} - NEXT_PUBLIC_DISTRIBUTION=${{ env.DISTRIBUTION }} \ No newline at end of file + NEXT_PUBLIC_DISTRIBUTION=${{ env.DISTRIBUTION }} + NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS=${{ secrets.NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS }} + NEXT_PUBLIC_WEBAPP_DOMAIN= ${{env.NEXT_PUBLIC_WEBAPP_DOMAIN}} \ No newline at end of file From 77eed137555942a3cd4c5ba7308b0b552c510c78 Mon Sep 17 00:00:00 2001 From: nael Date: Sun, 1 Sep 2024 00:14:45 +0200 Subject: [PATCH 41/49] :bug: Updated .env.example --- .env.example | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.env.example b/.env.example index 9d10efb8c..54aa00bb3 100644 --- a/.env.example +++ b/.env.example @@ -11,6 +11,9 @@ ENCRYPT_CRYPTO_SECRET_KEY="0123456789abcdef0123456789abcdef" SENTRY_DSN= SENTRY_ENABLED=FALSE +POSTHOG_HOST= +POSTHOG_KEY= +PH_TELEMETRY= #FALSE or TRUE # ================================================ # REDIS From 220931dddca667be8e2ab838eec58a20b2bdd5e1 Mon Sep 17 00:00:00 2001 From: mit <1mitccc@gmail.com> Date: Sun, 1 Sep 2024 10:38:21 +0530 Subject: [PATCH 42/49] Add types.ts for order object of webflow connector --- .../api/src/ecommerce/order/webflow/types.ts | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 packages/api/src/ecommerce/order/webflow/types.ts diff --git a/packages/api/src/ecommerce/order/webflow/types.ts b/packages/api/src/ecommerce/order/webflow/types.ts new file mode 100644 index 000000000..5b3f5bacc --- /dev/null +++ b/packages/api/src/ecommerce/order/webflow/types.ts @@ -0,0 +1,173 @@ +export interface WebflowOrderOutput { + orderId: string; + status: + | 'pending' + | 'unfulfilled' + | 'fulfilled' + | 'disputed' + | 'dispute-lost' + | 'refunded'; + comment?: string | null; // A comment string for this Order, which is editable by API user (not used by Webflow). + orderComment?: string | null; // A comment that the customer left when making their Order + acceptedOn: string | null; + fulfilledOn: string | null; + refundedOn: string | null; + disputedOn: string | null; + disputeUpdatedOn: string | null; + disputeLastStatus: DisputeStatus | null; + customerPaid: Money; + netAmount: Money; + applicationFee: Money; + allAddresses: Address[]; + shippingAddress: Address; + shippingProvider?: string | null; + shippingTracking?: string | null; + shippingTrackingURL: string | null; + customerInfo: { + fullName: string; + email: string; + }; + purchasedItems: PurchasedItem[]; + purchasedItemsCount: number; + stripeDetails: StripeDetails | null; + stripeCard: StripeCard | null; + paypalDetails: PaypalDetails | null; + paymentProcessor?: string; + customData: Array>; // Array of objects + metadata: { + isBuyNow: boolean; + hasDownloads?: boolean; + paymentProcessor?: string; + }; + billingAddress: Address; + totals?: Totals; + isCustomerDeleted?: boolean; + isShippingRequired: boolean; + hasDownloads?: boolean; + downloadFiles?: { + id: string; + name: string; + url: string; + }[]; +} + +interface Money { + unit: string; // The three-letter ISO currency code + value: number; + string: string; +} + +interface Address { + type: 'billing' | 'shipping'; + japanType?: 'kana' | 'kanji' | null; // Represents a Japan-only address format. This field will only appear on orders placed from Japan. + addressee: string; + line1: string; + line2?: string; + city: string; + state: string; + country: string; + postalCode: string; +} + +interface FileObjectVariant { + url: string; + originalFileName: string; + size: number; + width: number; // in pixels + height: number; // in pixels +} + +interface FileObject { + size: number; + originalFileName: string; + createdOn: string; + contentType: string; // The MIME type of the image + width: number; // The image width in pixels + height: number; // The image height in pixels + variants: FileObjectVariant[]; +} + +interface PurchasedItem { + count: number; + rowTotal: Money; + productId: string; + productName: string; + productSlug: string; + variantId: string; + variantName: string; + variantSlug: string; + variantSKU: string; + variantImage: { + url: string; + file: FileObject | null; + }; + variantPrice: Money; + weight: number | null; + height: number | null; + width: number | null; + length: number | null; +} + +interface StripeDetails { + customerId: string | null; + paymentMethod: string | null; + chargeId: string | null; + disputeId: string | null; + paymentIntentId: string | null; + subscriptionId: string | null; + refundId: string | null; + refundReason: string | null; +} + +interface PaypalDetails { + orderId: string; + payerId: string; + captureId: string; + refundId: string; + refundReason: string; + disputeId: string; +} + +interface StripeCard { + last4: string; + brand: StripeCardBrands; + ownerName: string; + expires: { + month: number; + year: number; + }; +} + +interface Totals { + subtotal: Money; + extras: { + type: 'tax' | 'shipping' | 'discount' | 'discount-shipping'; + name: string; + description?: string; + price: Money; + }[]; + total: Money; +} + +enum DisputeStatus { + WARNING_NEEDS_RESPONSE = 'warning_needs_response', + WARNING_UNDER_REVIEW = 'warning_under_review', + WARNING_CLOSED = 'warning_closed', + NEEDS_RESPONSE = 'needs_response', + UNDER_REVIEW = 'under_review', + CHARGE_REFUNDED = 'charge_refunded', + WON = 'won', + LOST = 'lost', +} + +enum StripeCardBrands { + VISA = 'Visa', + AMEX = 'American Express', + MASTERCARD = 'MasterCard', + DISCOVER = 'Discover', + JCB = 'JCB', + DINERS_CLUB = 'Diners Club', + UNKNOWN = 'Unknown', +} + +export type WebflowOrderInput = Partial; From ec3aa328f77b9f1a3c2d770b04e0bdffa2a1fa93 Mon Sep 17 00:00:00 2001 From: mit <1mitccc@gmail.com> Date: Sun, 1 Sep 2024 10:42:37 +0530 Subject: [PATCH 43/49] Fix dir path --- packages/api/src/ecommerce/order/services/webflow/index.ts | 0 packages/api/src/ecommerce/order/services/webflow/mappers.ts | 0 packages/api/src/ecommerce/order/{ => services}/webflow/types.ts | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/api/src/ecommerce/order/services/webflow/index.ts create mode 100644 packages/api/src/ecommerce/order/services/webflow/mappers.ts rename packages/api/src/ecommerce/order/{ => services}/webflow/types.ts (100%) diff --git a/packages/api/src/ecommerce/order/services/webflow/index.ts b/packages/api/src/ecommerce/order/services/webflow/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/api/src/ecommerce/order/services/webflow/mappers.ts b/packages/api/src/ecommerce/order/services/webflow/mappers.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/api/src/ecommerce/order/webflow/types.ts b/packages/api/src/ecommerce/order/services/webflow/types.ts similarity index 100% rename from packages/api/src/ecommerce/order/webflow/types.ts rename to packages/api/src/ecommerce/order/services/webflow/types.ts From d9bee09a16e4131fda201eab85801b6fc866a001 Mon Sep 17 00:00:00 2001 From: mit <1mitccc@gmail.com> Date: Sun, 1 Sep 2024 11:52:42 +0530 Subject: [PATCH 44/49] Add mappers.ts for order object of webflow connector --- .../order/services/webflow/mappers.ts | 126 ++++++++++++++++++ .../ecommerce/order/services/webflow/types.ts | 6 +- 2 files changed, 130 insertions(+), 2 deletions(-) diff --git a/packages/api/src/ecommerce/order/services/webflow/mappers.ts b/packages/api/src/ecommerce/order/services/webflow/mappers.ts index e69de29bb..40400168a 100644 --- a/packages/api/src/ecommerce/order/services/webflow/mappers.ts +++ b/packages/api/src/ecommerce/order/services/webflow/mappers.ts @@ -0,0 +1,126 @@ +import { + UnifiedEcommerceOrderInput, + UnifiedEcommerceOrderOutput, +} from '@ecommerce/order/types/model.unified'; +import { IOrderMapper } from '@ecommerce/order/types'; +import { MappersRegistry } from '@@core/@core-services/registries/mappers.registry'; +import { Injectable } from '@nestjs/common'; +import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; +import { Utils } from '@ecommerce/@lib/@utils'; +import { WebflowOrderInput, WebflowOrderOutput } from './types'; + +@Injectable() +export class WebflowOrderMapper implements IOrderMapper { + constructor( + private mappersRegistry: MappersRegistry, + private utils: Utils, + private coreUnificationService: CoreUnification, + ) { + this.mappersRegistry.registerService('ecommerce', 'order', 'webflow', this); + } + + async desunify( + source: UnifiedEcommerceOrderInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + return; + } + + async unify( + source: WebflowOrderOutput | WebflowOrderOutput[], + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + if (!Array.isArray(source)) { + return await this.mapSingleOrderToUnified( + source, + connectionId, + customFieldMappings, + ); + } + return Promise.all( + source.map((order) => + this.mapSingleOrderToUnified(order, connectionId, customFieldMappings), + ), + ); + } + + private async mapSingleOrderToUnified( + source: WebflowOrderOutput, + connectionId: string, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const result: Partial = { + remote_id: source.orderId, + remote_data: source, + created_at: source.acceptedOn, + order_status: this.mapWebflowStatusToUnified(source.status), + currency: source.customerPaid.unit, + total_price: source.totals.total.value, + fulfillment_status: this.mapWebflowStatusToUnified(source.status), + items: + source.purchasedItems?.map((item) => ({ + product_id: item.productId, + variant_id: item.variantId, + sku: item.variantSKU, + title: item.productName, + quantity: item.count, + price: item.variantPrice.value.toString(), + total: item.rowTotal.value.toString(), + variant_title: item.variantName, + weight: item.weight, + properties: [ + { + name: 'image_url', + value: item.variantImage.url, + }, + ], + })) || [], + field_mappings: {}, + }; + + result.total_discount = source.totals.extras + .filter((extra) => ['discount', 'discount-shipping'].includes(extra.type)) + .reduce((acc, curr) => acc + curr.price.value, 0); + + result.total_shipping = source.totals.extras + .filter((extra) => extra.type === 'shipping') + .reduce((acc, curr) => acc + curr.price.value, 0); + + result.total_tax = source.totals.extras + .filter((extra) => extra.type === 'tax') + .reduce((acc, curr) => acc + curr.price.value, 0); + + if (customFieldMappings && source.customData) { + for (const [key, value] of Object.entries(source.customData)) { + if (customFieldMappings.find((m) => m.slug === key)) { + result.field_mappings[key] = value; + } + } + } + + return result; + } + + private mapWebflowStatusToUnified( + status?: WebflowOrderInput['status'], + ): string { + switch (status) { + case 'pending': + return 'PENDING'; + case 'fulfilled': + return 'FULLFILLED'; + default: + return status; + } + } +} diff --git a/packages/api/src/ecommerce/order/services/webflow/types.ts b/packages/api/src/ecommerce/order/services/webflow/types.ts index 5b3f5bacc..f6deb88ab 100644 --- a/packages/api/src/ecommerce/order/services/webflow/types.ts +++ b/packages/api/src/ecommerce/order/services/webflow/types.ts @@ -1,3 +1,5 @@ +import { CurrencyCode } from '@@core/utils/types'; + export interface WebflowOrderOutput { orderId: string; status: @@ -52,9 +54,9 @@ export interface WebflowOrderOutput { } interface Money { - unit: string; // The three-letter ISO currency code + unit: CurrencyCode; // The three-letter ISO currency code value: number; - string: string; + string: string; // example: "$ 109.05 USD" } interface Address { From a3501d1a1a47827fecd50b804e5350ffae998a48 Mon Sep 17 00:00:00 2001 From: mit <1mitccc@gmail.com> Date: Sun, 1 Sep 2024 12:08:51 +0530 Subject: [PATCH 45/49] Add index.ts for order object of webflow connector --- .../ecommerce/order/services/webflow/index.ts | 58 +++++++++++++++++++ .../ecommerce/order/services/webflow/types.ts | 6 +- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/packages/api/src/ecommerce/order/services/webflow/index.ts b/packages/api/src/ecommerce/order/services/webflow/index.ts index e69de29bb..964fe2bc2 100644 --- a/packages/api/src/ecommerce/order/services/webflow/index.ts +++ b/packages/api/src/ecommerce/order/services/webflow/index.ts @@ -0,0 +1,58 @@ +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 { IOrderService } from '@ecommerce/order/types'; +import { Injectable } from '@nestjs/common'; +import { EcommerceObject } from '@panora/shared'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { WebflowOrderInput, WebflowOrderOutput } from './types'; + +@Injectable() +export class WebflowService implements IOrderService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + EcommerceObject.order.toUpperCase() + ':' + WebflowService.name, + ); + this.registry.registerService('webflow', this); + } + + async sync(data: SyncParam): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: data.linkedUserId, + provider_slug: 'webflow', + vertical: 'ecommerce', + }, + }); + + // ref: https://docs.developers.webflow.com/data/reference/list-orders + const resp = await axios.get(`${connection.account_url}/orders`, { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }); + const orders: WebflowOrderOutput[] = resp.data.orders; + this.logger.log(`Synced webflow orders !`); + + return { + data: orders, + message: 'Webflow orders retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + } + } +} diff --git a/packages/api/src/ecommerce/order/services/webflow/types.ts b/packages/api/src/ecommerce/order/services/webflow/types.ts index f6deb88ab..80bde1b47 100644 --- a/packages/api/src/ecommerce/order/services/webflow/types.ts +++ b/packages/api/src/ecommerce/order/services/webflow/types.ts @@ -1,7 +1,7 @@ import { CurrencyCode } from '@@core/utils/types'; -export interface WebflowOrderOutput { - orderId: string; +export interface WebflowOrderInput { + readonly orderId: string; status: | 'pending' | 'unfulfilled' @@ -172,4 +172,4 @@ enum StripeCardBrands { UNKNOWN = 'Unknown', } -export type WebflowOrderInput = Partial; +export type WebflowOrderOutput = Partial; From 4cc21a023f17da9f9cfa35f954c6541eae5ae836 Mon Sep 17 00:00:00 2001 From: mit <1mitccc@gmail.com> Date: Sun, 1 Sep 2024 12:51:23 +0530 Subject: [PATCH 46/49] Cleanup code-loyout in some files --- .../types/original/original.ecommerce.ts | 31 ++++++++++++------- .../src/ecommerce/customer/customer.module.ts | 4 +-- .../api/src/ecommerce/order/order.module.ts | 4 +++ .../src/ecommerce/product/product.module.ts | 4 +-- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/packages/api/src/@core/utils/types/original/original.ecommerce.ts b/packages/api/src/@core/utils/types/original/original.ecommerce.ts index 4c262cba5..308185fc9 100644 --- a/packages/api/src/@core/utils/types/original/original.ecommerce.ts +++ b/packages/api/src/@core/utils/types/original/original.ecommerce.ts @@ -1,10 +1,3 @@ -import { WebflowProductInput, WebflowProductOutput } from '@ecommerce/product/services/webflow/types'; - -import { - WebflowCustomerInput, - WebflowCustomerOutput, -} from '@ecommerce/customer/services/webflow/types'; - /* INPUT */ import { AmazonCustomerOutput } from '@ecommerce/customer/services/amazon/types'; @@ -56,19 +49,33 @@ import { WoocommerceProductInput, WoocommerceProductOutput, } from '@ecommerce/product/services/woocommerce/types'; +import { + WebflowOrderInput, + WebflowOrderOutput, +} from '@ecommerce/order/services/webflow/types'; +import { + WebflowProductInput, + WebflowProductOutput, +} from '@ecommerce/product/services/webflow/types'; +import { + WebflowCustomerInput, + WebflowCustomerOutput, +} from '@ecommerce/customer/services/webflow/types'; /* product */ export type OriginalProductInput = | ShopifyProductInput | WoocommerceProductInput - | SquarespaceProductInput | WebflowProductInput; + | SquarespaceProductInput + | WebflowProductInput; /* order */ export type OriginalOrderInput = | ShopifyOrderInput | WoocommerceOrderInput | SquarespaceOrderInput - | AmazonOrderInput; + | AmazonOrderInput + | WebflowOrderInput; /* fulfillmentorders */ export type OriginalFulfillmentOrdersInput = ShopifyFulfillmentOrdersInput; @@ -96,14 +103,16 @@ export type EcommerceObjectInput = export type OriginalProductOutput = | ShopifyProductOutput | WoocommerceProductOutput - | SquarespaceProductOutput | WebflowProductOutput; + | SquarespaceProductOutput + | WebflowProductOutput; /* order */ export type OriginalOrderOutput = | ShopifyOrderOutput | WoocommerceOrderOutput | SquarespaceOrderOutput - | AmazonOrderOutput; + | AmazonOrderOutput + | WebflowOrderOutput; /* fulfillmentorders */ export type OriginalFulfillmentOrdersOutput = ShopifyFulfillmentOrdersOutput; diff --git a/packages/api/src/ecommerce/customer/customer.module.ts b/packages/api/src/ecommerce/customer/customer.module.ts index 00b95c5f9..ceead802d 100644 --- a/packages/api/src/ecommerce/customer/customer.module.ts +++ b/packages/api/src/ecommerce/customer/customer.module.ts @@ -1,5 +1,3 @@ -import { WebflowCustomerMapper } from './services/webflow/mappers'; -import { WebflowService } from './services/webflow'; import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; @@ -15,6 +13,8 @@ import { WoocommerceCustomerMapper } from './services/woocommerce/mappers'; import { SyncService } from './sync/sync.service'; import { SquarespaceCustomerMapper } from './services/squarespace/mappers'; import { AmazonCustomerMapper } from './services/amazon/mappers'; +import { WebflowService } from './services/webflow'; +import { WebflowCustomerMapper } from './services/webflow/mappers'; @Module({ controllers: [CustomerController], providers: [ diff --git a/packages/api/src/ecommerce/order/order.module.ts b/packages/api/src/ecommerce/order/order.module.ts index f74634825..568f01b47 100644 --- a/packages/api/src/ecommerce/order/order.module.ts +++ b/packages/api/src/ecommerce/order/order.module.ts @@ -10,6 +10,8 @@ import { ShopifyService } from './services/shopify'; import { ShopifyOrderMapper } from './services/shopify/mappers'; import { WoocommerceService } from './services/woocommerce'; import { WoocommerceOrderMapper } from './services/woocommerce/mappers'; +import { WebflowService } from './services/webflow'; +import { WebflowOrderMapper } from './services/webflow/mappers'; import { SyncService } from './sync/sync.service'; import { SquarespaceService } from './services/squarespace'; import { SquarespaceOrderMapper } from './services/squarespace/mappers'; @@ -35,6 +37,8 @@ import { AmazonService } from './services/amazon'; WoocommerceService, SquarespaceService, AmazonService, + WebflowService, + WebflowOrderMapper, ], exports: [SyncService], }) diff --git a/packages/api/src/ecommerce/product/product.module.ts b/packages/api/src/ecommerce/product/product.module.ts index ac43271e3..ef8633954 100644 --- a/packages/api/src/ecommerce/product/product.module.ts +++ b/packages/api/src/ecommerce/product/product.module.ts @@ -1,5 +1,3 @@ -import { WebflowProductMapper } from './services/webflow/mappers'; -import { WebflowService } from './services/webflow'; import { CoreUnification } from '@@core/@core-services/unification/core-unification.service'; import { IngestDataService } from '@@core/@core-services/unification/ingest-data.service'; import { WebhookService } from '@@core/@core-services/webhooks/panora-webhooks/webhook.service'; @@ -14,6 +12,8 @@ import { SquarespaceService } from './services/squarespace'; import { SquarespaceProductMapper } from './services/squarespace/mappers'; import { WoocommerceService } from './services/woocommerce'; import { WoocommerceProductMapper } from './services/woocommerce/mappers'; +import { WebflowService } from './services/webflow'; +import { WebflowProductMapper } from './services/webflow/mappers'; import { SyncService } from './sync/sync.service'; @Module({ From 42302d9b7f772d5f5858f76d5a99f45c13104d36 Mon Sep 17 00:00:00 2001 From: mit <1mitccc@gmail.com> Date: Sun, 1 Sep 2024 14:11:59 +0530 Subject: [PATCH 47/49] Update customer types.ts for ecommerce > customer > webflow --- .../ecommerce/customer/services/webflow/types.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/api/src/ecommerce/customer/services/webflow/types.ts b/packages/api/src/ecommerce/customer/services/webflow/types.ts index a55c2da4f..6ac8c32a0 100644 --- a/packages/api/src/ecommerce/customer/services/webflow/types.ts +++ b/packages/api/src/ecommerce/customer/services/webflow/types.ts @@ -1,3 +1,5 @@ +// ref: https://docs.developers.webflow.com/data/reference/list-users + export interface WebflowCustomerInput { id: string; isEmailVerified: boolean; @@ -5,21 +7,26 @@ export interface WebflowCustomerInput { invitedOn: string; createdOn: string; lastLogin: string; - status: string; + status: 'invited' | 'verified' | 'unverified'; accessGroups: AccessGroup[]; data: UserData; } type AccessGroup = { slug: string; - type: string; + type: AccessGroupType; }; +enum AccessGroupType { + ADMIN = 'admin', // Assigned to the user via API or in the designer + ECOMMERCE = 'ecommerce', // Assigned to the user via an ecommerce purchase +} + type UserData = { name: string; email: string; - 'accept-privacy': boolean; - 'accept-communications': boolean; + acceptPrivacy: boolean; + acceptCommunications: boolean; [key: string]: any; }; From 465558325d8d75d24c1f01b0c00b7b8e764f2c6b Mon Sep 17 00:00:00 2001 From: mit <1mitccc@gmail.com> Date: Sun, 1 Sep 2024 14:59:08 +0530 Subject: [PATCH 48/49] Refactor some code --- .../src/ecommerce/customer/services/webflow/index.ts | 2 +- .../src/ecommerce/order/services/webflow/index.ts | 2 +- .../src/ecommerce/order/services/webflow/mappers.ts | 2 +- .../src/ecommerce/product/services/webflow/index.ts | 3 ++- .../ecommerce/product/services/webflow/mappers.ts | 7 +++---- .../src/ecommerce/product/services/webflow/types.ts | 12 ++++++------ 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/api/src/ecommerce/customer/services/webflow/index.ts b/packages/api/src/ecommerce/customer/services/webflow/index.ts index 1256429a8..54c747341 100644 --- a/packages/api/src/ecommerce/customer/services/webflow/index.ts +++ b/packages/api/src/ecommerce/customer/services/webflow/index.ts @@ -19,7 +19,7 @@ export class WebflowService implements ICustomerService { private registry: ServiceRegistry, ) { this.logger.setContext( - EcommerceObject.customer.toUpperCase() + ':' + WebflowService.name, + `${EcommerceObject.customer.toUpperCase()}:${WebflowService.name}`, ); this.registry.registerService('webflow', this); } diff --git a/packages/api/src/ecommerce/order/services/webflow/index.ts b/packages/api/src/ecommerce/order/services/webflow/index.ts index 964fe2bc2..b3d1c5ca3 100644 --- a/packages/api/src/ecommerce/order/services/webflow/index.ts +++ b/packages/api/src/ecommerce/order/services/webflow/index.ts @@ -19,7 +19,7 @@ export class WebflowService implements IOrderService { private registry: ServiceRegistry, ) { this.logger.setContext( - EcommerceObject.order.toUpperCase() + ':' + WebflowService.name, + `${EcommerceObject.order.toUpperCase()}:${WebflowService.name}`, ); this.registry.registerService('webflow', this); } diff --git a/packages/api/src/ecommerce/order/services/webflow/mappers.ts b/packages/api/src/ecommerce/order/services/webflow/mappers.ts index 40400168a..d3e4c3e35 100644 --- a/packages/api/src/ecommerce/order/services/webflow/mappers.ts +++ b/packages/api/src/ecommerce/order/services/webflow/mappers.ts @@ -59,7 +59,7 @@ export class WebflowOrderMapper implements IOrderMapper { remote_id: string; }[], ): Promise { - const result: Partial = { + const result: UnifiedEcommerceOrderOutput = { remote_id: source.orderId, remote_data: source, created_at: source.acceptedOn, diff --git a/packages/api/src/ecommerce/product/services/webflow/index.ts b/packages/api/src/ecommerce/product/services/webflow/index.ts index 5636377e7..92ebdb1b4 100644 --- a/packages/api/src/ecommerce/product/services/webflow/index.ts +++ b/packages/api/src/ecommerce/product/services/webflow/index.ts @@ -21,7 +21,7 @@ export class WebflowService implements IProductService { private registry: ServiceRegistry, ) { this.logger.setContext( - EcommerceObject.product.toUpperCase() + ':' + WebflowService.name, + `${EcommerceObject.product.toUpperCase()}:${WebflowService.name}`, ); this.registry.registerService('webflow', this); } @@ -51,6 +51,7 @@ export class WebflowService implements IProductService { }, }, ); + return { data: resp.data, message: 'Webflow product created', diff --git a/packages/api/src/ecommerce/product/services/webflow/mappers.ts b/packages/api/src/ecommerce/product/services/webflow/mappers.ts index 1a3bc479c..7f9c68d0e 100644 --- a/packages/api/src/ecommerce/product/services/webflow/mappers.ts +++ b/packages/api/src/ecommerce/product/services/webflow/mappers.ts @@ -99,10 +99,9 @@ export class WebflowProductMapper implements IProductMapper { return { remote_id: data.product.id, remote_data: data, - images_urls: - data.skus - ?.map((sku) => sku?.fieldData?.mainImage?.url) - .filter((url) => Boolean(url)) || [], + images_urls: (data.skus || []) + .map(({ fieldData: { mainImage } }) => mainImage?.url) + .filter(Boolean), description: data.product.fieldData?.description, tags: data.product.fieldData?.categories, created_at: data.product?.createdOn, diff --git a/packages/api/src/ecommerce/product/services/webflow/types.ts b/packages/api/src/ecommerce/product/services/webflow/types.ts index 773d7ac87..f6337ac07 100644 --- a/packages/api/src/ecommerce/product/services/webflow/types.ts +++ b/packages/api/src/ecommerce/product/services/webflow/types.ts @@ -5,6 +5,12 @@ export interface WebflowProductOutput { skus: SkuData[]; } +export interface WebflowProductInput { + product: ProductData; + sku: SkuData; + publishStatus?: 'staging' | 'live'; +} + export interface ProductData { id?: string; cmsLocaleId?: string; @@ -99,9 +105,3 @@ export interface DownloadFile { url: string; id: string; } - -export interface WebflowProductInput { - product: ProductData; - sku: SkuData; - publishStatus: 'staging' | 'live'; -} From c1cb62ad5a2c9d2ea56beab7999e94d078737f25 Mon Sep 17 00:00:00 2001 From: nael <39710677+naelob@users.noreply.github.com> Date: Mon, 2 Sep 2024 01:17:39 +0200 Subject: [PATCH 49/49] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 848e5604e..60f91bc15 100644 --- a/README.md +++ b/README.md @@ -181,7 +181,7 @@ Your favourite software is missing? [Ask the community to build a connector!](ht - [ ] Ebay - [ ] Faire -- [ ] Webflow +- [x] Webflow - [ ] Mercado Libre - [ ] Prestashop - [ ] Magento