@accounter/server 0.0.8-alpha-20251105181339-4ce756e457f7145a4035565e526bc02285ab2675 → 0.0.8-alpha-20251105183654-b3147b83bfda6ce62d09073ce81342ceadf5160c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +25 -5
  2. package/dist/server/src/__generated__/types.d.ts +39 -10
  3. package/dist/server/src/__generated__/types.js.map +1 -1
  4. package/dist/server/src/modules/financial-entities/__generated__/clients.types.d.ts +11 -4
  5. package/dist/server/src/modules/financial-entities/__generated__/types.d.ts +19 -7
  6. package/dist/server/src/modules/financial-entities/__generated__/types.js.map +1 -1
  7. package/dist/server/src/modules/financial-entities/helpers/clients.helper.d.ts +11 -0
  8. package/dist/server/src/modules/financial-entities/helpers/clients.helper.js +26 -0
  9. package/dist/server/src/modules/financial-entities/helpers/clients.helper.js.map +1 -0
  10. package/dist/server/src/modules/financial-entities/providers/businesses.provider.js +1 -1
  11. package/dist/server/src/modules/financial-entities/providers/clients.provider.js +18 -16
  12. package/dist/server/src/modules/financial-entities/providers/clients.provider.js.map +1 -1
  13. package/dist/server/src/modules/financial-entities/resolvers/clients.resolvers.js +26 -24
  14. package/dist/server/src/modules/financial-entities/resolvers/clients.resolvers.js.map +1 -1
  15. package/dist/server/src/modules/financial-entities/typeDefs/clients.graphql.js +24 -8
  16. package/dist/server/src/modules/financial-entities/typeDefs/clients.graphql.js.map +1 -1
  17. package/dist/server/src/modules/green-invoice/__generated__/types.d.ts +8 -0
  18. package/dist/server/src/modules/green-invoice/__generated__/types.js.map +1 -1
  19. package/dist/server/src/modules/green-invoice/helpers/contract-to-draft.helper.d.ts +3 -1
  20. package/dist/server/src/modules/green-invoice/helpers/contract-to-draft.helper.js +5 -12
  21. package/dist/server/src/modules/green-invoice/helpers/contract-to-draft.helper.js.map +1 -1
  22. package/dist/server/src/modules/green-invoice/helpers/green-invoice-clients.helper.d.ts +1 -2
  23. package/dist/server/src/modules/green-invoice/helpers/green-invoice-clients.helper.js +17 -31
  24. package/dist/server/src/modules/green-invoice/helpers/green-invoice-clients.helper.js.map +1 -1
  25. package/dist/server/src/modules/green-invoice/helpers/green-invoice.helper.d.ts +2 -2
  26. package/dist/server/src/modules/green-invoice/helpers/green-invoice.helper.js +16 -14
  27. package/dist/server/src/modules/green-invoice/helpers/green-invoice.helper.js.map +1 -1
  28. package/dist/server/src/modules/green-invoice/resolvers/green-invoice.resolvers.js +91 -29
  29. package/dist/server/src/modules/green-invoice/resolvers/green-invoice.resolvers.js.map +1 -1
  30. package/dist/server/src/modules/green-invoice/typeDefs/green-invoice.graphql.js +4 -0
  31. package/dist/server/src/modules/green-invoice/typeDefs/green-invoice.graphql.js.map +1 -1
  32. package/package.json +3 -4
  33. package/src/__generated__/types.ts +42 -10
  34. package/src/modules/financial-entities/__generated__/clients.types.ts +10 -4
  35. package/src/modules/financial-entities/__generated__/types.ts +19 -7
  36. package/src/modules/financial-entities/helpers/clients.helper.ts +28 -0
  37. package/src/modules/financial-entities/providers/businesses.provider.ts +1 -1
  38. package/src/modules/financial-entities/providers/clients.provider.ts +19 -16
  39. package/src/modules/financial-entities/resolvers/clients.resolvers.ts +30 -27
  40. package/src/modules/financial-entities/typeDefs/clients.graphql.ts +24 -8
  41. package/src/modules/green-invoice/__generated__/types.ts +8 -0
  42. package/src/modules/green-invoice/helpers/contract-to-draft.helper.ts +7 -13
  43. package/src/modules/green-invoice/helpers/green-invoice-clients.helper.ts +19 -40
  44. package/src/modules/green-invoice/helpers/green-invoice.helper.ts +17 -18
  45. package/src/modules/green-invoice/resolvers/green-invoice.resolvers.ts +101 -35
  46. package/src/modules/green-invoice/typeDefs/green-invoice.graphql.ts +4 -0
@@ -281,7 +281,7 @@ const replaceBusinesses = sql<IReplaceBusinessesQuery>`
281
281
  UPDATE accounter_schema.clients
282
282
  SET business_id = $targetBusinessId
283
283
  WHERE business_id = $businessIdToReplace
284
- RETURNING green_invoice_id
284
+ RETURNING (integrations->>'greenInvoiceId')::uuid
285
285
  )
286
286
  UPDATE accounter_schema.transactions
287
287
  SET business_id = $targetBusinessId
@@ -3,6 +3,7 @@ import { Injectable, Scope } from 'graphql-modules';
3
3
  import { DBProvider } from '@modules/app-providers/db.provider.js';
4
4
  import { sql } from '@pgtyped/runtime';
5
5
  import { getCacheInstance } from '@shared/helpers';
6
+ import { validateClientIntegrations } from '../helpers/clients.helper.js';
6
7
  import type {
7
8
  IDeleteClientQuery,
8
9
  IGetAllClientsQuery,
@@ -27,24 +28,14 @@ const getClientsByIds = sql<IGetClientsByIdsQuery>`
27
28
  `;
28
29
 
29
30
  const getClientsByGreenInvoiceIds = sql<IGetClientsByGreenInvoiceIdsQuery>`
30
- SELECT *
31
+ SELECT *, (integrations->>'greenInvoiceId')::uuid as green_invoice_business_id
31
32
  FROM accounter_schema.clients
32
- WHERE green_invoice_id IN $$greenInvoiceBusinessIds;
33
+ WHERE (integrations->>'greenInvoiceId')::uuid in $$greenInvoiceBusinessIds;
33
34
  `;
34
35
 
35
36
  const updateClient = sql<IUpdateClientQuery>`
36
37
  UPDATE accounter_schema.clients
37
38
  SET
38
- green_invoice_id = COALESCE(
39
- $greenInvoiceId,
40
- green_invoice_id,
41
- NULL
42
- ),
43
- hive_id = COALESCE(
44
- $hiveId,
45
- hive_id,
46
- NULL
47
- ),
48
39
  emails = COALESCE(
49
40
  $emails,
50
41
  emails,
@@ -54,6 +45,11 @@ const updateClient = sql<IUpdateClientQuery>`
54
45
  $newBusinessId,
55
46
  business_id,
56
47
  NULL
48
+ ),
49
+ integrations = COALESCE(
50
+ $integrations,
51
+ integrations,
52
+ NULL
57
53
  )
58
54
  WHERE
59
55
  business_id = $businessId
@@ -67,8 +63,8 @@ const deleteClient = sql<IDeleteClientQuery>`
67
63
  `;
68
64
 
69
65
  const insertClient = sql<IInsertClientQuery>`
70
- INSERT INTO accounter_schema.clients (business_id, green_invoice_id, hive_id, emails)
71
- VALUES ($businessId, $greenInvoiceId, $hiveId, $emails)
66
+ INSERT INTO accounter_schema.clients (business_id, emails, integrations)
67
+ VALUES ($businessId, $emails, $integrations)
72
68
  RETURNING *;`;
73
69
 
74
70
  @Injectable({
@@ -91,7 +87,12 @@ export class ClientsProvider {
91
87
  this.cache.set('all-clients', data);
92
88
  data.map(client => {
93
89
  this.cache.set(`client-id-${client.business_id}`, client);
94
- this.cache.set(`client-green-invoice-id-${client.green_invoice_id}`, client);
90
+ try {
91
+ const { greenInvoiceId } = validateClientIntegrations(client.integrations ?? {});
92
+ this.cache.set(`client-green-invoice-id-${greenInvoiceId}`, client);
93
+ } catch {
94
+ // swallow errors
95
+ }
95
96
  });
96
97
  return data;
97
98
  });
@@ -123,7 +124,9 @@ export class ClientsProvider {
123
124
  this.dbProvider,
124
125
  );
125
126
 
126
- return greenInvoiceIds.map(id => matches.find(match => match.green_invoice_id === id));
127
+ return greenInvoiceIds.map(id =>
128
+ matches.find(match => match.green_invoice_business_id === id),
129
+ );
127
130
  } catch (e) {
128
131
  console.error(e);
129
132
  return greenInvoiceIds.map(() => undefined);
@@ -1,11 +1,11 @@
1
1
  import { GraphQLError } from 'graphql';
2
- import { GreenInvoiceClientProvider } from '@modules/app-providers/green-invoice-client.js';
3
2
  import { BusinessesProvider } from '@modules/financial-entities/providers/businesses.provider.js';
4
3
  import {
5
4
  addGreenInvoiceClient,
6
5
  updateGreenInvoiceClient,
7
6
  } from '@modules/green-invoice/helpers/green-invoice-clients.helper.js';
8
- import { Resolvers } from '@shared/gql-types';
7
+ import { ClientIntegrationsInput, Resolvers } from '@shared/gql-types';
8
+ import { validateClientIntegrations } from '../helpers/clients.helper.js';
9
9
  import { ClientsProvider } from '../providers/clients.provider.js';
10
10
  import type {
11
11
  FinancialEntitiesModule,
@@ -45,12 +45,26 @@ export const clientsResolvers: FinancialEntitiesModule.Resolvers &
45
45
  },
46
46
  Mutation: {
47
47
  updateClient: async (_, { businessId, fields }, { injector }) => {
48
+ let updatedIntegrations: ClientIntegrationsInput | undefined =
49
+ fields.integrations ?? undefined;
50
+ if (updatedIntegrations) {
51
+ const currentClient = await injector
52
+ .get(ClientsProvider)
53
+ .getClientByIdLoader.load(businessId);
54
+ if (!currentClient) {
55
+ throw new GraphQLError(`Client with ID="${businessId}" not found`);
56
+ }
57
+ const currentIntegrations = validateClientIntegrations(currentClient.integrations);
58
+ updatedIntegrations = {
59
+ ...currentIntegrations,
60
+ ...updatedIntegrations,
61
+ };
62
+ }
48
63
  const adjustedFields: IUpdateClientParams = {
49
64
  businessId,
50
65
  emails: fields.emails ? [...fields.emails] : undefined,
51
- greenInvoiceId: fields.greenInvoiceId,
52
- hiveId: fields.hiveId,
53
66
  newBusinessId: fields.newBusinessId,
67
+ integrations: updatedIntegrations,
54
68
  };
55
69
  try {
56
70
  const [updatedClient] = await injector
@@ -81,8 +95,7 @@ export const clientsResolvers: FinancialEntitiesModule.Resolvers &
81
95
  const newClient: IInsertClientParams = {
82
96
  businessId: fields.businessId,
83
97
  emails: fields.emails ? [...fields.emails] : [],
84
- greenInvoiceId: fields.greenInvoiceId,
85
- hiveId: fields.hiveId,
98
+ integrations: fields.integrations,
86
99
  };
87
100
  const [insertClient] = await injector.get(ClientsProvider).insertClient(newClient);
88
101
 
@@ -116,28 +129,18 @@ export const clientsResolvers: FinancialEntitiesModule.Resolvers &
116
129
 
117
130
  return businessMatch;
118
131
  },
119
- greenInvoiceId: business => business.green_invoice_id,
120
- hiveId: business => business.hive_id,
121
132
  emails: business => business.emails ?? [],
122
- greenInvoiceInfo: async (business, _, { injector }) => {
123
- if (!business.green_invoice_id) {
124
- return null;
125
- }
126
- const client = await injector
127
- .get(GreenInvoiceClientProvider)
128
- .clientLoader.load(business.green_invoice_id);
129
- if (!client) {
130
- throw new GraphQLError(
131
- `Green Invoice client with ID "${business.green_invoice_id}" not found`,
132
- );
133
- }
134
- const emails = client.emails ? (client.emails.filter(Boolean) as string[]) : [];
135
- return {
136
- ...client,
137
- emails,
138
- id: business.green_invoice_id,
139
- };
140
- },
133
+ integrations: business => business,
134
+ },
135
+ ClientIntegrations: {
136
+ id: business => `${business.business_id}-integrations`,
137
+ hiveId: business => validateClientIntegrations(business.integrations).hiveId ?? null,
138
+ linearId: business => validateClientIntegrations(business.integrations).linearId ?? null,
139
+ slackChannelKey: business =>
140
+ validateClientIntegrations(business.integrations).slackChannelKey ?? null,
141
+ notionId: business => validateClientIntegrations(business.integrations).notionId ?? null,
142
+ workflowyUrl: business =>
143
+ validateClientIntegrations(business.integrations).workflowyUrl ?? null,
141
144
  },
142
145
  LtdFinancialEntity: {
143
146
  clientInfo: async (business, _, { injector }) => {
@@ -16,29 +16,45 @@ export default gql`
16
16
  type Client {
17
17
  id: UUID!
18
18
  originalBusiness: LtdFinancialEntity!
19
- greenInvoiceId: UUID
20
- hiveId: String
21
19
  emails: [String!]!
22
20
  generatedDocumentType: DocumentType!
23
- greenInvoiceInfo: GreenInvoiceClient
21
+ integrations: ClientIntegrations!
22
+ }
23
+
24
+ " integrations associated with a client "
25
+ type ClientIntegrations {
26
+ id: ID!
27
+ hiveId: String
28
+ linearId: String
29
+ slackChannelKey: String
30
+ notionId: String
31
+ workflowyUrl: String
24
32
  }
25
33
 
26
34
  " fields for inserting a new client "
27
35
  input ClientInsertInput {
28
36
  businessId: UUID!
29
- greenInvoiceId: UUID
30
- hiveId: String
31
37
  emails: [String!]
32
38
  generatedDocumentType: DocumentType!
39
+ integrations: ClientIntegrationsInput
33
40
  }
34
41
 
35
42
  " fields for updating an existing client "
36
43
  input ClientUpdateInput {
37
- greenInvoiceId: UUID
38
- hiveId: String
44
+ newBusinessId: UUID
39
45
  emails: [String!]
40
46
  generatedDocumentType: DocumentType
41
- newBusinessId: UUID
47
+ integrations: ClientIntegrationsInput
48
+ }
49
+
50
+ " integrations input for client insert/update "
51
+ input ClientIntegrationsInput {
52
+ greenInvoiceId: UUID
53
+ hiveId: String
54
+ linearId: String
55
+ slackChannelKey: String
56
+ notionId: String
57
+ workflowyUrl: String
42
58
  }
43
59
 
44
60
  " result type for updateClient "
@@ -11,6 +11,7 @@ export namespace GreenInvoiceModule {
11
11
  GreenInvoiceIncome: 'currency' | 'currencyRate' | 'description' | 'itemId' | 'price' | 'quantity' | 'vatRate' | 'vatType';
12
12
  GreenInvoicePayment: 'currency' | 'currencyRate' | 'date' | 'price' | 'type' | 'subType' | 'bankName' | 'bankBranch' | 'bankAccount' | 'chequeNum' | 'accountId' | 'transactionId' | 'appType' | 'cardType' | 'cardNum' | 'dealType' | 'numPayments' | 'firstPayment';
13
13
  IssuedDocumentInfo: 'originalDocument';
14
+ ClientIntegrations: 'greenInvoiceInfo';
14
15
  };
15
16
 
16
17
  interface DefinedEnumValues {
@@ -46,6 +47,7 @@ export namespace GreenInvoiceModule {
46
47
  export type FileScalar = Types.FileScalar;
47
48
  export type Charge = Types.Charge;
48
49
  export type IssuedDocumentInfo = Types.IssuedDocumentInfo;
50
+ export type ClientIntegrations = Types.ClientIntegrations;
49
51
  export type DocumentType = Types.DocumentType;
50
52
  export type GreenInvoiceDocumentLang = DefinedEnumValues['GreenInvoiceDocumentLang'];
51
53
  export type Currency = Types.Currency;
@@ -75,6 +77,7 @@ export namespace GreenInvoiceModule {
75
77
  export type GreenInvoiceIncomeResolvers = Pick<Types.GreenInvoiceIncomeResolvers, DefinedFields['GreenInvoiceIncome']>;
76
78
  export type GreenInvoicePaymentResolvers = Pick<Types.GreenInvoicePaymentResolvers, DefinedFields['GreenInvoicePayment']>;
77
79
  export type IssuedDocumentInfoResolvers = Pick<Types.IssuedDocumentInfoResolvers, DefinedFields['IssuedDocumentInfo']>;
80
+ export type ClientIntegrationsResolvers = Pick<Types.ClientIntegrationsResolvers, DefinedFields['ClientIntegrations']>;
78
81
 
79
82
  export interface Resolvers {
80
83
  Query?: QueryResolvers;
@@ -86,6 +89,7 @@ export namespace GreenInvoiceModule {
86
89
  GreenInvoiceIncome?: GreenInvoiceIncomeResolvers;
87
90
  GreenInvoicePayment?: GreenInvoicePaymentResolvers;
88
91
  IssuedDocumentInfo?: IssuedDocumentInfoResolvers;
92
+ ClientIntegrations?: ClientIntegrationsResolvers;
89
93
  };
90
94
 
91
95
  export interface MiddlewareMap {
@@ -112,6 +116,10 @@ export namespace GreenInvoiceModule {
112
116
  '*'?: gm.Middleware[];
113
117
  originalDocument?: gm.Middleware[];
114
118
  };
119
+ ClientIntegrations?: {
120
+ '*'?: gm.Middleware[];
121
+ greenInvoiceInfo?: gm.Middleware[];
122
+ };
115
123
  GenerateMonthlyClientDocumentsResult?: {
116
124
  '*'?: gm.Middleware[];
117
125
  success?: gm.Middleware[];
@@ -3,13 +3,13 @@ import { GraphQLError } from 'graphql';
3
3
  import { Injector } from 'graphql-modules';
4
4
  import type { IGetContractsByIdsResult } from '@modules/contracts/types.js';
5
5
  import { normalizeDocumentType } from '@modules/documents/resolvers/common.js';
6
+ import { validateClientIntegrations } from '@modules/financial-entities/helpers/clients.helper.js';
6
7
  import { BusinessesProvider } from '@modules/financial-entities/providers/businesses.provider.js';
7
8
  import { ClientsProvider } from '@modules/financial-entities/providers/clients.provider.js';
8
9
  import { Currency } from '@shared/enums';
9
10
  import { NewDocumentInfo } from '@shared/gql-types';
10
11
  import { dateToTimelessDateString } from '@shared/helpers';
11
12
  import { TimelessDateString } from '@shared/types';
12
- import { getClientFromGreenInvoiceClient } from './green-invoice-clients.helper.js';
13
13
 
14
14
  export const convertContractToDraft = async (
15
15
  contract: IGetContractsByIdsResult,
@@ -20,12 +20,7 @@ export const convertContractToDraft = async (
20
20
  .get(BusinessesProvider)
21
21
  .getBusinessByIdLoader.load(contract.client_id);
22
22
  const clientPromise = injector.get(ClientsProvider).getClientByIdLoader.load(contract.client_id);
23
- const greenInvoiceClientPromise = getClientFromGreenInvoiceClient(injector, contract.client_id);
24
- const [business, client, greenInvoiceClient] = await Promise.all([
25
- businessPromise,
26
- clientPromise,
27
- greenInvoiceClientPromise,
28
- ]);
23
+ const [business, client] = await Promise.all([businessPromise, clientPromise]);
29
24
 
30
25
  if (!business) {
31
26
  throw new GraphQLError(`Business ID="${contract.client_id}" not found`);
@@ -35,7 +30,9 @@ export const convertContractToDraft = async (
35
30
  throw new GraphQLError(`Client not found for business ID="${contract.client_id}"`);
36
31
  }
37
32
 
38
- if (!greenInvoiceClient) {
33
+ const greenInvoiceId = validateClientIntegrations(client.integrations)?.greenInvoiceId;
34
+
35
+ if (!greenInvoiceId) {
39
36
  throw new GraphQLError(`Green invoice match not found for business ID="${contract.client_id}"`);
40
37
  }
41
38
 
@@ -45,7 +42,7 @@ export const convertContractToDraft = async (
45
42
  const year = today.getFullYear() + (today.getMonth() === 0 ? -1 : 0);
46
43
  const month = format(subMonths(today, 1), 'MMMM');
47
44
 
48
- const documentInput: NewDocumentInfo = {
45
+ const documentInput: Omit<NewDocumentInfo, 'client'> & { client: string | undefined } = {
49
46
  remarks: `${contract.purchase_orders[0] ? `PO: ${contract.purchase_orders[0]}${contract.remarks ? ', ' : ''}` : ''}${contract.remarks ?? ''}`,
50
47
  description: `GraphQL Hive Enterprise License - ${month} ${year}`,
51
48
  type: normalizeDocumentType(contract.document_type),
@@ -56,10 +53,7 @@ export const convertContractToDraft = async (
56
53
  vatType: 'EXEMPT',
57
54
  rounding: false,
58
55
  signed: true,
59
- client: {
60
- ...greenInvoiceClient,
61
- emails: [...((client.emails?.filter(Boolean) as string[]) ?? [])],
62
- },
56
+ client: greenInvoiceId,
63
57
  income: [
64
58
  {
65
59
  description: `GraphQL Hive Enterprise License - ${month} ${year}`,
@@ -5,51 +5,16 @@ import {
5
5
  } from '@accounter/green-invoice-graphql';
6
6
  import { GreenInvoiceClientProvider } from '@modules/app-providers/green-invoice-client.js';
7
7
  import { CountryCode } from '@modules/countries/types.js';
8
+ import { validateClientIntegrations } from '@modules/financial-entities/helpers/clients.helper.js';
8
9
  import { BusinessesProvider } from '@modules/financial-entities/providers/businesses.provider.js';
9
10
  import { ClientsProvider } from '@modules/financial-entities/providers/clients.provider.js';
10
11
  import {
11
12
  IGetBusinessesByIdsResult,
12
13
  IGetClientsByIdsResult,
13
14
  } from '@modules/financial-entities/types.js';
14
- import { ClientUpdateInput, GreenInvoiceClient, UpdateBusinessInput } from '@shared/gql-types';
15
+ import { ClientUpdateInput, UpdateBusinessInput } from '@shared/gql-types';
15
16
  import { countryCodeToGreenInvoiceCountry } from './green-invoice.helper.js';
16
17
 
17
- export async function getClientFromGreenInvoiceClient(
18
- injector: Injector,
19
- businessId: string,
20
- useGreenInvoiceId = false,
21
- ): Promise<GreenInvoiceClient | undefined> {
22
- const client = await injector.get(ClientsProvider).getClientByIdLoader.load(businessId);
23
- if (!client?.green_invoice_id) {
24
- return useGreenInvoiceId ? undefined : { id: businessId };
25
- }
26
-
27
- const greenInvoiceClient = await injector
28
- .get(GreenInvoiceClientProvider)
29
- .clientLoader.load(client.green_invoice_id);
30
-
31
- if (!greenInvoiceClient) {
32
- return useGreenInvoiceId ? undefined : { id: businessId };
33
- }
34
-
35
- return {
36
- id: useGreenInvoiceId && greenInvoiceClient.id ? greenInvoiceClient.id : businessId,
37
- country: greenInvoiceClient.country,
38
- emails: [
39
- ...((greenInvoiceClient.emails?.filter(Boolean) as string[]) ?? []),
40
- 'ap@the-guild.dev',
41
- ],
42
- name: greenInvoiceClient.name,
43
- phone: greenInvoiceClient.phone,
44
- taxId: greenInvoiceClient.taxId,
45
- address: greenInvoiceClient.address,
46
- city: greenInvoiceClient.city,
47
- zip: greenInvoiceClient.zip,
48
- fax: greenInvoiceClient.fax,
49
- mobile: greenInvoiceClient.mobile,
50
- };
51
- }
52
-
53
18
  function convertLocalClientToGreenInvoiceCreateClientInput(
54
19
  localClient: IGetClientsByIdsResult,
55
20
  localBusiness: IGetBusinessesByIdsResult,
@@ -134,10 +99,15 @@ export async function addGreenInvoiceClient(clientId: string, injector: Injector
134
99
  throw new Error('Failed to create Green Invoice client');
135
100
  }
136
101
 
102
+ const integrations = validateClientIntegrations(localClient.integrations);
103
+
137
104
  // add green invoice id to local client
138
105
  await injector.get(ClientsProvider).updateClient({
139
106
  businessId: clientId,
140
- greenInvoiceId: greenInvoiceClient.id,
107
+ integrations: {
108
+ ...integrations,
109
+ greenInvoiceId: greenInvoiceClient.id,
110
+ },
141
111
  });
142
112
  } catch (error) {
143
113
  const message = 'Error adding Green Invoice client';
@@ -216,7 +186,16 @@ export async function updateGreenInvoiceClient(
216
186
  localBusinessPromise,
217
187
  localClientPromise,
218
188
  ]);
219
- if (!localBusiness?.name || !localClient?.green_invoice_id) {
189
+
190
+ let greenInvoiceId: string | undefined = undefined;
191
+ try {
192
+ greenInvoiceId =
193
+ validateClientIntegrations(localClient?.integrations ?? {}).greenInvoiceId ?? undefined;
194
+ } catch {
195
+ // swallow errors
196
+ return;
197
+ }
198
+ if (!localBusiness?.name || !greenInvoiceId) {
220
199
  // We cannot update a client in Green Invoice without its ID.
221
200
  console.warn(
222
201
  `Cannot update Green Invoice client: missing local business name or client ID for business${clientId}`,
@@ -233,7 +212,7 @@ export async function updateGreenInvoiceClient(
233
212
  }
234
213
 
235
214
  const greenInvoiceClient = await injector.get(GreenInvoiceClientProvider).updateClient({
236
- id: localClient.green_invoice_id,
215
+ id: greenInvoiceId,
237
216
  input: fieldsToUpdate,
238
217
  });
239
218
 
@@ -25,14 +25,15 @@ import { CountryCode } from '@modules/countries/types.js';
25
25
  import { DocumentsProvider } from '@modules/documents/providers/documents.provider.js';
26
26
  import { IssuedDocumentsProvider } from '@modules/documents/providers/issued-documents.provider.js';
27
27
  import type { document_status, IInsertDocumentsParams } from '@modules/documents/types';
28
+ import { validateClientIntegrations } from '@modules/financial-entities/helpers/clients.helper.js';
28
29
  import { ClientsProvider } from '@modules/financial-entities/providers/clients.provider.js';
29
30
  import {
30
31
  Currency,
31
32
  DocumentType,
32
33
  GreenInvoiceCountry,
33
34
  GreenInvoicePaymentType,
34
- NewDocumentInfo,
35
35
  NewDocumentInput,
36
+ ResolversTypes,
36
37
  type GreenInvoiceDiscountType,
37
38
  type GreenInvoiceDocumentLang,
38
39
  type GreenInvoiceLinkType,
@@ -1247,18 +1248,24 @@ export async function convertDocumentInputIntoGreenInvoiceInput(
1247
1248
  if (!clientInfo) {
1248
1249
  throw new GraphQLError(`Client with ID ${initialInput.client.id} not found`);
1249
1250
  }
1250
- if (!clientInfo.green_invoice_id) {
1251
+ let greenInvoiceId: string | null = null;
1252
+ try {
1253
+ greenInvoiceId =
1254
+ validateClientIntegrations(clientInfo.integrations ?? {}).greenInvoiceId ?? null;
1255
+ } catch (error) {
1256
+ console.error('Failed to validate client integrations', error);
1257
+ throw new GraphQLError(`Client with ID ${initialInput.client.id} has invalid integrations`);
1258
+ }
1259
+ if (!greenInvoiceId) {
1251
1260
  throw new GraphQLError(`Client with ID ${initialInput.client.id} not found in Green Invoice`);
1252
1261
  }
1253
1262
  const greenInvoiceClient = await injector
1254
1263
  .get(GreenInvoiceClientProvider)
1255
- .clientLoader.load(clientInfo.green_invoice_id);
1264
+ .clientLoader.load(greenInvoiceId);
1256
1265
  if (!greenInvoiceClient) {
1257
- throw new GraphQLError(
1258
- `Green Invoice client with ID ${clientInfo.green_invoice_id} not found`,
1259
- );
1266
+ throw new GraphQLError(`Green Invoice client with ID ${greenInvoiceId} not found`);
1260
1267
  }
1261
- const emails: (string | null)[] = ['ap@the-guild.dev'];
1268
+ const emails: (string | null)[] = ['ap@the-guild.dev']; // TODO: remove hardcoded email
1262
1269
  const inputEmails = initialInput.client?.emails?.filter(Boolean) ?? [];
1263
1270
  if (inputEmails.length) {
1264
1271
  emails.push(...inputEmails);
@@ -1266,7 +1273,7 @@ export async function convertDocumentInputIntoGreenInvoiceInput(
1266
1273
  emails.push(...(greenInvoiceClient.emails ?? []));
1267
1274
  }
1268
1275
  client = {
1269
- id: clientInfo.green_invoice_id,
1276
+ id: greenInvoiceClient.id,
1270
1277
  country: greenInvoiceClient.country,
1271
1278
  name: greenInvoiceClient.name,
1272
1279
  phone: greenInvoiceClient.phone,
@@ -1323,18 +1330,10 @@ export async function convertDocumentInputIntoGreenInvoiceInput(
1323
1330
 
1324
1331
  export function convertGreenInvoiceDocumentToLocalDocumentInfo(
1325
1332
  greenInvoiceDocument: _DOLLAR_defs_Document,
1326
- ): NewDocumentInfo {
1333
+ ): ResolversTypes['NewDocumentInfo'] {
1327
1334
  return {
1328
1335
  ...greenInvoiceDocument,
1329
- client: greenInvoiceDocument.client?.id
1330
- ? {
1331
- ...greenInvoiceDocument.client,
1332
- id: greenInvoiceDocument.client.id,
1333
- emails: greenInvoiceDocument.client.emails
1334
- ? (greenInvoiceDocument.client.emails.filter(Boolean) as string[])
1335
- : [],
1336
- }
1337
- : undefined,
1336
+ client: greenInvoiceDocument.client?.id,
1338
1337
  currency: greenInvoiceDocument.currency as Currency,
1339
1338
  income: greenInvoiceDocument.income?.filter(Boolean).map(income => ({
1340
1339
  ...income!,