diff --git a/CHANGELOG.md b/CHANGELOG.md index 23a89eca..f1ba1ef8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + +- Added custom fields for organizations and cost centers + ## [0.28.0] - 2023-02-08 ### Added diff --git a/graphql/schema.graphql b/graphql/schema.graphql index 7eb5ba24..f85c84f7 100644 --- a/graphql/schema.graphql +++ b/graphql/schema.graphql @@ -118,6 +118,7 @@ type Mutation { collections: [CollectionInput] paymentTerms: [PaymentTermInput] priceTables: [String] + customFields: [CustomFieldInput] salesChannel: String sellers: [SellerInput] notifyUsers: Boolean @@ -274,6 +275,7 @@ type DefaultCostCenter { address: Address phoneNumber: String businessDocument: String + customFields: [CustomField] stateRegistration: String } @@ -285,6 +287,7 @@ type Organization { paymentTerms: [PaymentTerm] priceTables: [String] sellers: [Seller] + customFields: [CustomField] salesChannel: String costCenters: [ID] status: String @@ -309,6 +312,7 @@ type CostCenter { paymentTerms: [PaymentTerm] phoneNumber: String businessDocument: String + customFields: [CustomField] stateRegistration: String } @@ -399,6 +403,8 @@ type B2BSettings { autoApprove: Boolean defaultPaymentTerms: [PaymentTerm] defaultPriceTables: [String] + organizationCustomFields: [SettingsCustomField] + costCenterCustomFields: [SettingsCustomField] uiSettings: UISettings } @@ -419,6 +425,7 @@ input OrganizationInput { priceTables: [String] salesChannel: String sellers: [SellerInput] + customFields: [CustomFieldInput] } input B2BUserInput { @@ -432,6 +439,7 @@ input DefaultCostCenterInput { address: AddressInput phoneNumber: String businessDocument: String + customFields: [CustomFieldInput] stateRegistration: String } @@ -441,6 +449,7 @@ input CostCenterInput { paymentTerms: [PaymentTermInput] phoneNumber: String businessDocument: String + customFields: [CustomFieldInput] stateRegistration: String } @@ -476,6 +485,35 @@ input PaymentTermInput { name: String } +enum CustomFieldType { + text + dropdown +} + +type CustomField { + name: String + type: CustomFieldType + value: String + dropdownValues: [DropdownValue] + useOnRegistration: Boolean +} + +type SettingsCustomField { + name: String + type: CustomFieldType + dropdownValues: [DropdownValue] + useOnRegistration: Boolean +} + +input CustomFieldInput { + name: String! + type: CustomFieldType! + value: String + dropdownValues: [DropdownValueInput] + useOnRegistration: Boolean +} + + input SalesChannelsInput { id: String name: String @@ -491,4 +529,16 @@ input B2BSettingsInput { defaultPaymentTerms: [PaymentTermInput] defaultPriceTables: [String] uiSettings: UISettingsInput + organizationCustomFields: [CustomFieldInput] + costCenterCustomFields: [CustomFieldInput] +} + +type DropdownValue { + value: String + label: String +} + +input DropdownValueInput { + value: String + label: String } diff --git a/node/mdSchema.ts b/node/mdSchema.ts index 6dc291fc..b3605b73 100644 --- a/node/mdSchema.ts +++ b/node/mdSchema.ts @@ -29,6 +29,7 @@ export const ORGANIZATION_FIELDS = [ 'sellers', 'status', 'created', + 'customFields', ] export const ORGANIZATION_SCHEMA_VERSION = 'v0.0.8' @@ -41,6 +42,7 @@ export const COST_CENTER_FIELDS = [ 'organization', 'phoneNumber', 'businessDocument', + 'customFields', 'stateRegistration', ] export const COST_CENTER_SCHEMA_VERSION = 'v0.0.7' @@ -155,6 +157,10 @@ export const schemas = [ title: 'Created', format: 'date-time', }, + customFields: { + type: 'array', + title: 'Custom Fields', + }, }, 'v-indexed': ['name', 'status', 'created'], 'v-immediate-indexing': true, @@ -194,6 +200,10 @@ export const schemas = [ type: ['string', 'null'], title: 'Phone Number', }, + customFields: { + type: ['array', 'null'], + title: 'Custom Fields', + }, }, 'v-indexed': [ 'name', diff --git a/node/resolvers/Mutations/CostCenters.ts b/node/resolvers/Mutations/CostCenters.ts index 72f43d2b..70d6fdef 100644 --- a/node/resolvers/Mutations/CostCenters.ts +++ b/node/resolvers/Mutations/CostCenters.ts @@ -17,6 +17,7 @@ const CostCenters = { phoneNumber, businessDocument, stateRegistration, + customFields, }, }: { organizationId: string; input: CostCenterInput }, ctx: Context @@ -53,6 +54,7 @@ const CostCenters = { ...(phoneNumber && { phoneNumber }), ...(businessDocument && { businessDocument }), ...(stateRegistration && { stateRegistration }), + ...(customFields && { customFields }), } const createCostCenterResult = await masterdata.createDocument({ @@ -162,6 +164,7 @@ const CostCenters = { phoneNumber, businessDocument, stateRegistration, + customFields, }, }: { id: string; input: CostCenterInput }, ctx: Context @@ -188,6 +191,9 @@ const CostCenters = { ...((businessDocument || businessDocument === '') && { businessDocument, }), + ...((customFields || customFields === '') && { + customFields, + }), }, id, }) diff --git a/node/resolvers/Mutations/Organizations.ts b/node/resolvers/Mutations/Organizations.ts index da00bf7a..a0cce20b 100644 --- a/node/resolvers/Mutations/Organizations.ts +++ b/node/resolvers/Mutations/Organizations.ts @@ -26,6 +26,7 @@ const Organizations = { tradeName, defaultCostCenter, costCenters, + customFields, paymentTerms, priceTables, salesChannel, @@ -55,6 +56,7 @@ const Organizations = { ...(paymentTerms?.length && { paymentTerms }), ...(priceTables?.length && { priceTables }), ...(salesChannel && { salesChannel }), + customFields: customFields ?? [], status: ORGANIZATION_STATUSES.ACTIVE, } @@ -78,6 +80,9 @@ const Organizations = { ...(data?.businessDocument && { businessDocument: data?.businessDocument, }), + ...(defaultCostCenter?.customFields && { + customFields: defaultCostCenter.customFields, + }), ...(data?.stateRegistration && { stateRegistration: data?.stateRegistration, }), @@ -129,6 +134,7 @@ const Organizations = { b2bCustomerAdmin, costCenters, defaultCostCenter, + customFields, name, tradeName, priceTables, @@ -174,6 +180,7 @@ const Organizations = { b2bCustomerAdmin, costCenters, created: now, + customFields, defaultCostCenter: defaultCostCenter ?? (costCenters?.length && costCenters[0]), notes: '', @@ -244,6 +251,7 @@ const Organizations = { collections, paymentTerms, priceTables, + customFields, salesChannel, sellers, notifyUsers = true, @@ -255,6 +263,7 @@ const Organizations = { collections: any[] paymentTerms: any[] priceTables: any[] + customFields: any[] salesChannel?: string sellers?: any[] notifyUsers?: boolean @@ -299,6 +308,7 @@ const Organizations = { collections, paymentTerms, priceTables, + customFields, ...(salesChannel && { salesChannel }), ...(sellers && { sellers }), status, @@ -402,6 +412,9 @@ const Organizations = { ...(defaultCostCenter.businessDocument && { businessDocument: defaultCostCenter.businessDocument, }), + ...(defaultCostCenter.customFields && { + customFields: defaultCostCenter.customFields, + }), }, name, paymentTerms, diff --git a/node/resolvers/Mutations/Settings.ts b/node/resolvers/Mutations/Settings.ts index bcd4932b..791ec050 100644 --- a/node/resolvers/Mutations/Settings.ts +++ b/node/resolvers/Mutations/Settings.ts @@ -35,6 +35,8 @@ const Settings = { defaultPaymentTerms, defaultPriceTables, uiSettings, + organizationCustomFields, + costCenterCustomFields, }, }: { input: B2BSettingsInput @@ -53,11 +55,45 @@ const Settings = { const B2B_SETTINGS_DATA_ENTITY = 'b2b_settings' + // Get current settings to save them depending on where the saveB2BSettings is coming from. Custom fields come without autoApprove settings and vice versa. + + let currentB2BSettings: B2BSettingsInput | undefined + + try { + const getSettingsObj: B2BSettingsInput = await vbase.getJSON( + B2B_SETTINGS_DATA_ENTITY, + 'settings' + ) + + currentB2BSettings = getSettingsObj + } catch (e) { + if (e.response?.status === 404) { + // when run for the first time on the project - returns 404. ignore in that case + } else { + logger.error({ + message: 'saveB2BSettings-currentB2BSettings-error', + error: e, + }) + if (e.message) { + throw new GraphQLError(e.message) + } else if (e.response?.data?.message) { + throw new GraphQLError(e.response.data.message) + } else { + throw new GraphQLError(e) + } + } + } + try { const b2bSettings = { autoApprove, + costCenterCustomFields: + costCenterCustomFields ?? currentB2BSettings?.costCenterCustomFields, defaultPaymentTerms, defaultPriceTables, + organizationCustomFields: + organizationCustomFields ?? + currentB2BSettings?.organizationCustomFields, uiSettings, } diff --git a/node/resolvers/Queries/Settings.ts b/node/resolvers/Queries/Settings.ts index 9e301f9b..019d56f1 100644 --- a/node/resolvers/Queries/Settings.ts +++ b/node/resolvers/Queries/Settings.ts @@ -12,7 +12,7 @@ const B2BSettings = { // create schema if it doesn't exist await checkConfig(ctx) - let settings = null + let settings: Partial | null = null try { settings = await vbase.getJSON( @@ -20,6 +20,13 @@ const B2BSettings = { 'settings', true ) + + settings = { + ...settings, + // if custom fields are null, set to an empty array + costCenterCustomFields: settings?.costCenterCustomFields ?? [], + organizationCustomFields: settings?.organizationCustomFields ?? [], + } } catch (e) { if (e.message) { throw new GraphQLError(e.message) diff --git a/node/typings.d.ts b/node/typings.d.ts index 06b50826..c1a3042b 100644 --- a/node/typings.d.ts +++ b/node/typings.d.ts @@ -48,6 +48,7 @@ interface OrganizationInput { b2bCustomerAdmin: B2BCustomerInput defaultCostCenter?: DefaultCostCenterInput costCenters?: DefaultCostCenterInput[] + customFields?: CustomField[] paymentTerms?: PaymentTerm[] priceTables?: Price[] salesChannel?: string @@ -65,6 +66,7 @@ interface DefaultCostCenterInput { address: AddressInput phoneNumber?: string businessDocument?: string + customFields?: CustomField[] stateRegistration?: string } @@ -74,6 +76,7 @@ interface CostCenterInput { paymentTerms?: PaymentTerm[] phoneNumber?: string businessDocument?: string + customFields?: CustomField[] stateRegistration?: string } @@ -114,6 +117,7 @@ interface Organization { paymentTerms: PaymentTerm[] status: string created: string + customFields?: CustomField[] } interface CostCenter { @@ -126,6 +130,7 @@ interface CostCenter { priceTables: Price[] phoneNumber?: string businessDocument?: string + customFields: CustomField[] } interface Address { @@ -146,10 +151,12 @@ interface Address { checked?: boolean } -interface B2BSetting { +interface B2BSettings { autoApprove: boolean defaultPaymentTerms: PaymentTerm[] defaultPriceTables: [string] + organizationCustomFields: SettingsCustomField[] + costCenterCustomFields: SettingsCustomField[] } interface UserArgs { @@ -179,9 +186,31 @@ interface UISettings { clearCart: boolean } +interface CustomField { + name: string + type: 'text' | 'dropdown' + value: string + dropdownValues?: Array<{ label: string; value: string }> | null + useOnRegistration?: boolean +} + +interface SettingsCustomField { + name: string + type: 'text' | 'dropdown' + dropdownValues?: Array<{ label: string; value: string }> | null + useOnRegistration?: boolean +} + +interface CustomFieldSetting { + name: string + type: 'text' | 'dropdown' +} + interface B2BSettingsInput { autoApprove: boolean defaultPaymentTerms: PaymentTerm[] defaultPriceTables: Price[] uiSettings: UISettings + organizationCustomFields: CustomFieldSetting[] + costCenterCustomFields: CustomFieldSetting[] }