Skip to main content

Profit - Create customer

Profit Connector - Create Customer Overview This document details the process by which new customer records or updates to existing custom...

Updated over a week ago

Profit Connector - Create Customer

Overview

This document details the process by which new customer records or updates to existing customer records are synchronized from App4Sales to Profit via the Profit Connector. This function handles the incoming customer payload, performs validation, maps data to Profit's organizational, address, and contact person structures, and manages responses and error handling from the ERP.

Trigger & Permissions

The customer creation/update flow is triggered in App4Sales, typically when a user submits a new customer record or modifies an existing one. To execute this function, the connector must have the Core.Constants.ConnectorInfoInternalIds.EditCustomer permission enabled.

Data Source Configuration

The connector pulls customer data directly from the App4Sales internal database objects (Customer, Address, ContactPerson) and sends it to the Profit ERP system using update connectors. It also retrieves reference data (countries, book accounts) from Profit.

  • App4Sales Customer Object: Contains the primary customer data, contact persons, and addresses.

  • App4Sales Sales Rep Data: Used to identify the sales representative associated with the customer, which can influence default settings like price lists and customer/user managers in Profit.

  • Profit Get Connectors:

    • Routes.Get.Country: Used to retrieve country-specific information based on ISO-2 code.

    • Routes.Get.Account: Used to retrieve book account details, typically filtered by "description" to find "debiteuren".

    • Routes.Get.InsertedPerson: Used to check for existing contact persons by App4Sales Person GUID.

    • Routes.Get.CustomerCode: Used to retrieve the Profit customer code (debtor number) using the App4Sales Customer GUID.

    • Routes.Get.Contacts: Used to retrieve contact persons for a given customer code.

    • Routes.Get.CustomerAddresses: Used to retrieve customer addresses from Profit.

  • Profit Update Connectors:

    • Routes.Update.Organisation: The primary update connector used to create or update the main customer (organization) record in Profit.

    • Routes.Update.Person: Used to create or update individual person records (contact persons).

    • Routes.Update.Contact: Used to link or update contact person information within an organization.

Payload Mapping

The App4Sales customer data is transformed into an XML structure for the Profit update connectors. The primary structure is AfasNewCustomer, which encapsulates various Profit-specific fields and sub-objects for the organization, addresses, and contact persons.

Customer (Debtor) Fields (

)

KnSalesRelationOrg -> Element -> Fields

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

Action

-

insert for new customers (customer.CustomerCode is empty), otherwise update.

IsDebtor

-

Hardcoded to "1".

VatCode

customer.VatCode

Directly mapped.

PaymentCondition

customer.PaymentConditionCode, user.DefaultPaymentConditionForNewCustomer

If customer.PaymentConditionCode is provided, it is used. Otherwise, the sales representative's default payment condition (user.DefaultPaymentConditionForNewCustomer) is used. If neither is available and the user is null, an error is thrown.

ValutaCode

-

Defaulted to "EUR". Can be overridden if settings.PriceListBasedOnCurrencyCode is enabled and a pricelist with currency is derived.

VatLiable

customer.VatLiable, visitAddress.Iso2

Derived from customer.VatLiable:

  • If true: uses settings.VatLiableCode (default "1").

  • If false and country is in EU: uses settings.VatLiableCodeForEUCountries.

  • Otherwise: uses settings.NotVatLiableCode (default "0").

PriceList

customer.UsesPriceField, user.UseDefaultPricelistForCreateCustomer

  • If settings.UseSalesRepDefaultPriceListForCreateCustomer is true and sales rep has a default price list: uses sales rep's default price list code.

  • If settings.PriceListBasedOnCurrencyCode is enabled, currency is appended to the price list code (e.g., "PRICELIST_EUR").

  • If customer.UsesPriceField is 1: price list is null.

  • Otherwise: uses price list derived from customer.UsesPriceField.

AccountNumber

bookAccount.AccountId

Mapped from the retrieved Profit book account ID.

SaveMethod

-

Hardcoded to "E".

IsConsumer

customer.UnknownElements (specifically DynamicField.IsConsumer)

Set to settings.IsConsumerValue if customer.UnknownElements contains a DynamicField.IsConsumer entry that evaluates to true, otherwise null.

Discount

customer.Discount

Directly mapped (defaults to 0 if null).

CustomerManager

user.ResIdString

Mapped from the sales representative's internal ID (ResIdString) if settings.SendManagerToProfitAsManager is true.

UserManager

user.ResIdString

Mapped from the sales representative's internal ID (ResIdString) if settings.SendManagerToProfitAsUser is true.

Language

customer.LanguageCode

Mapped if settings.SendLanguageCodeForNewCustomer is true, otherwise null.

Dynamic Fields

customer.UnknownElements

Any other dynamic fields present in customer.UnknownElements are mapped.

Organization Fields (

)

KnSalesRelationOrg -> Element -> Objects -> KnOrganisation -> Element -> Fields

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

AfasRowAction

-

insert for new customers, otherwise update.

PadAdr

-

Hardcoded to "0".

CreateNumber

-

Hardcoded to "1".

OrganisationType

-

"6" for new customers (always add new), "9" for updates (use existing entity).

TypeCrm

-

Hardcoded to "0".

CustomerName

customer.CustomerName

Directly mapped.

EmailWork

customer.Email, contact.Email, visitAddress.Email

Prioritizes customer.Email, then main contact person's email, then visit address email.

Fax

customer.Fax

Directly mapped.

Telephone

customer.Phone

Directly mapped.

ChamberOfCommerce

customer.ChamberOfCommerceCode

Directly mapped.

Statutair

customer.CustomerName

Directly mapped to customer name.

Description

customer.CustomerGuid

Mapped from the App4Sales Customer GUID.

Address Fields (Visit Address:

; Delivery Address:

)

KnSalesRelationOrg -> Element -> Objects -> KnOrganisation -> Element -> Objects -> KnBasicAddressAdr -> Element -> Fields -> AfasNewAddress
KnSalesRelationOrg -> Element -> Objects -> KnOrganisation -> Element -> Objects -> KnBasicAddressPad -> Element -> Fields -> AfasNewAddress

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

AfasRowAction

-

insert for new customers, otherwise update.

AddressType

-

Hardcoded to "0".

Street

deliveryAddress.AddressLine1 (parsed), visitAddress.AddressLine1 (parsed)

The street and house number are parsed from AddressLine1 using SetStreetAndHouseNumber.

HouseNumber

deliveryAddress.AddressLine1 (parsed), visitAddress.AddressLine1 (parsed)

The house number is parsed from AddressLine1 using SetStreetAndHouseNumber.

PostalCode

deliveryAddress.PostCode, visitAddress.PostCode

Directly mapped.

IsoCode

country.CountryCode

Mapped from the retrieved Profit country code.

City

deliveryAddress.City, visitAddress.City

Directly mapped.

BeginDate

Current Date

Set to the current date in "YYYY-MM-DD" format.

ResZip

deliveryAddress.PostCode, visitAddress.PostCode, settings.DoNotSearchCityAtPostalCode

"0" if postal code is empty or settings.DoNotSearchCityAtPostalCode is true, otherwise "1".

AdAd (Address Addition)

deliveryAddress.HouseNumberAddition, visitAddress.HouseNumberAddition

Only serialized if not empty.

Special Logic: If delivery address is empty, it defaults to the visit address (controlled by afasVisitAddress.CheckAddress(afasDeliveryAddress, settings)).

Special Logic: If delivery address is empty, it defaults to the visit address (controlled by afasVisitAddress.CheckAddress(afasDeliveryAddress, settings)).

Special Logic: If delivery address is empty, it defaults to the visit address (controlled by afasVisitAddress.CheckAddress(afasDeliveryAddress, settings)).

Main Contact Person Fields (

)

KnSalesRelationOrg -> Element -> Objects -> KnOrganisation -> Element -> Objects -> KnContact -> Element -> Objects -> KnPerson -> Element -> Fields

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

AfasRowAction

-

insert for new customers, otherwise update.

AddToPortal

-

Hardcoded to "0".

AutoNum

-

Boolean value, true for new customers, false for updates.

MatchPer

-

"0" (AddAlwaysANewPerson) for new customers, "1" (SearchingOnPersonNumber) for updates.

BcId

-

Hardcoded to "1".

CallName

contact.FullName

Directly mapped.

Corr

-

Hardcoded to "0".

FirstName

contact.FirstName

Directly mapped.

Initials

contact.Initials

Directly mapped.

LastName

contact.LastName

Directly mapped.

SpNm

-

Hardcoded to "0".

ViGe

-

Hardcoded to "O".

TtId

-

Hardcoded to "ONB".

SearchName

contact.FullName

First 10 characters of contact.FullName, or empty if null.

PersonId

contactPersonId (App4Sales Contact Person GUID)

Mapped from the App4Sales Contact Person GUID.

BcCo (Profit Person Code)

contact.ContactId

For updates, this is mapped from the App4Sales contact.ContactId. For inserts, it is null.

Validation & Defaults

  • Required Connectors: Before processing, the system verifies the existence of necessary Get and Update connectors in Profit (e.g., Country, Account, Organisation, Person, Contact). If any are missing, the operation fails with a MissingConnectorException.

  • Payment Condition: If customer.PaymentConditionCode is not provided, the sales representative's default payment condition is used. If neither is available and no sales representative is linked, the process will halt and throw an exception.

  • VAT Liable Status: Defaults to "1" if customer.VatLiable is true, and "0" if false (unless specific settings for EU countries or a custom "not liable" code are configured).

  • Price List: Defaults are applied based on sales representative settings, customer price field, or fallbacks to a null price list.

  • Addresses: If a delivery address is not explicitly provided, the visit address is used as the delivery address.

  • Customer Code: If customer.CustomerCode is empty for an insert operation, Profit is expected to generate a new customer code. The system then retrieves this generated code from Profit using the App4Sales CustomerGuid and updates the internal App4Sales customer record.

  • Contact Person Duplication: When inserting a new contact person, the connector defaults to "Add Always a New Person" to avoid unintended merging with existing Profit records. For updates, it searches on the "Person Number".

Response & Error Handling

  • Profit Update Response: The Update method returns a ProfitUpdateResponse object. If its ErrorMessage property starts with Core.Constants.Error.ERROR, the operation is considered failed, and the error message is returned.

  • Organization Code: Upon successful creation or update of the organization in Profit, the OrganisationResult.OrganisationNumber (Profit's internal organization code) is stored in customer.ExtraData as OrganizationCode.

  • Customer Code Retrieval: After the initial organization update, the system queries Profit for the newly created customer's CustomerCode (debtor number) using the App4Sales CustomerGuid (stored as an "Opmerking" in Profit). If no customer code is found, an error is returned.

  • Context Updates: The retrieved Profit CustomerCode and ContactId are used to update the App4Sales internal contexts (CustomersContext and ContactPersonsContext) to maintain synchronization. Address IDs are also updated internally.

  • Exception Handling: A general try-catch block surrounds the main customer creation logic. Any exceptions are logged and result in a generic error message returned to the user, indicating that the customer could not be added or updated.

Domain Specifics / Extension Section

  • Dynamic Fields: The connector supports dynamic fields by checking customer.UnknownElements for additional data to send to Profit.

  • Client-Specific Logic: The SetCustomerDynamicFields method uses a ClientFactory, allowing for client-specific implementations to handle dynamic fields, indicating that certain clients may have unique requirements for customer data.

  • Customer GUID as Description: The App4Sales CustomerGuid is sent as the "Description" for the organization in Profit. This is crucial for retrieving the Profit CustomerCode later using the GUID.

Related Settings & Prerequisites

The following connector settings significantly influence the customer creation process:

  • Edit Customer (Permission): Must be enabled for the connector to allow customer creation/updates.

  • Send manager to Profit as manager: If enabled, the sales representative's internal ID is sent as the customer manager in Profit.

  • Send manager to Profit as user: If enabled, the sales representative's internal ID is sent as the user manager in Profit.

  • Send Language Code For New Customer: Determines if the customer's language code from App4Sales is sent to Profit.

  • Use Sales Rep Default Price List For Create Customer: If enabled, and the sales rep has a default price list configured, it will be used for the new customer.

  • Price List Based On Currency Code: If enabled, the currency code will be appended to the price list code when sending to Profit.

  • Do Not Search City At Postal Code: If enabled, Profit will not attempt to auto-fill the city based on the postal code for addresses.

  • Vat Liable Code: Custom VAT code to use when a customer is VAT liable.

  • Vat Liable Code For EU Countries: Custom VAT code for EU countries when a customer is not VAT liable.

  • Not Vat Liable Code: Custom VAT code to use when a customer is not VAT liable.

  • Is Consumer Value: The specific string value in Profit that indicates a customer is a consumer.

Did this answer your question?