DbFact Connector - CustomerUpdate
The CustomerUpdate pipeline for the DbFact connector synchronizes customer-related data from the external DbFact ERP system into the App4Sales platform. This process involves retrieving core customer information, associated addresses, contact persons, item class discounts, and customer dashboards. The data is retrieved via various DbFact API endpoints and transformed before being stored in the App4Sales internal database.
When does this sync occur?
This synchronization occurs when the DbFact connector's CustomerUpdate function is executed.
What entities does it touch?
Customers (core account records)
Addresses (Visit, Delivery, Invoice, or custom types)
ContactPersons (individuals linked to the customer)
Customer Dashboards (Base64-encoded dashboard layouts)
ItemClassValueDiscounts (per-item-class percentage discounts)
Customer Notes
Mapped Languages
Data Mapping Table (Customer Core Fields)
App4Sales Field | Source Field (API/Excel/DB) | Logic/Notes |
CustomerCode |
| Directly mapped from DbFact. Used for identifying customers. |
CustomerGuid |
| Attempted to be retrieved from an existing inactive customer (if |
CustomerName |
| Converted to uppercase ( |
Created |
| If the source |
Sysmodified | Source value | Truncated to seconds to avoid SQL timeout issues. |
PasswordWebshop |
| Always set to |
CustomerDashboard |
| If |
LanguageCode |
| Mapped from the source |
ActionPriceList |
| If |
UsesPriceField |
| If price lists are downloaded, |
PaymentConditionCode |
| If the source |
Source email | Whitespace is trimmed from the email address. | |
DynamicFreeFields | Source XML payload for dynamic free fields | If the source |
VatCode | Source VAT Code | Set to |
VatLiable | Source VAT Liable status | Set to |
InternalCode | Existing Customer's | If the source |
ExtraData | Existing Customer's | If the source |
FreeFieldList | Derived from | Populated if |
FreeFields |
| If |
ItemFilter | Derived from | If |
Data Mapping Table (Addresses)
App4Sales Field | Source Field (API/Excel/DB) | Logic/Notes |
CustomerGuid | Parent Customer's | Addresses are linked to the parent customer using its GUID. |
ExternalId |
| Addresses are only considered valid if they have an |
AddressType |
| If no valid addresses exist but an |
Iso2 |
| If |
| Unicode unit separators (
Whitespace is trimmed from the final email address. | |
AddressId |
| If |
Country |
| If |
Street |
| If |
HouseNumber |
| If |
Addition |
| If |
AddressLine1 | Formatted from | If |
IsMainAddress | Derived or Defaulted | If no |
Data Mapping Table (Contact Persons)
App4Sales Field | Source Field (API/Excel/DB) | Logic/Notes |
CustomerGuid | Parent Customer's | Contact persons are linked to the parent customer using its GUID. |
Source email | Whitespace is trimmed from the email address if not empty. | |
DynamicFreeFields | Source XML payload for dynamic free fields | If the source |
FullName | Derived from | If |
FirstName | Derived from | Populated from the source |
LastName | Derived from | Populated from the source |
MiddleName | Derived from | Populated from the source |
Initials | Derived from | Set to the first character of the |
ContactId | Source | If |
IsMainContactPerson | Source value or Derived | If the customer has a |
PasswordWebshop |
| Always set to |
Data Mapping Table (ItemClassValueDiscount)
App4Sales Field | Source Field (API/Excel/DB) | Logic/Notes |
CustomerGuid | Parent Customer's | Item class value discounts are linked to the parent customer using its GUID. |
discount |
| Only discounts with a value greater than 0 and not null are processed. |
Special Logic & Filters
Customer Validation & Filtering
Customers with empty or whitespace
CustomerNameor emptyCustomerCodeare considered invalid, logged as warnings, and skipped from the update process.SyncCustomerOnType(relation.CustomerType): Customers can be filtered based on theirCustomerTypeif the connector settingCustomerTypesis configured.
Identifiers & Reactivation
Customer GUID Retrieval: The system prioritizes retrieving an existing
CustomerGuid.It first checks for reactivation links (
_customerCodeGuidReactivationLinks) for previously inactivated customers.If not found, it checks a local cache (
_customerCodeLinks) byCustomerCode.If still not found, it queries the
PortalServerProviderfor an existing GUID usingCustomerCodeandCustomerName.If no GUID is found, a new
Guid.NewGuid()is generated. New GUIDs for updaters are added to the local cache.
Reactivation: Inactivated customers (those with a
CustomerCodestarting with '~') can be reactivated by reusing their originalCustomerGuid.
Price Deduplication
If
AdministrationSession.CurrentSession.ConnectorBaseSettings.EnablePriceDeduplicationor the connector's ownEnablePriceDeduplicationsetting is true, price lists are migrated.Existing price list IDs (
UsesPriceFieldandActionPriceList) are checked against apriceMigrationLookup(deserialized fromKeySettings.GenericPriceListMigrations).If a migration entry is found, the price list ID is updated to the
MigratedId.If a price list ID is not found in existing price lists after migration,
UsesPriceFielddefaults to 1, andActionPriceListis set tonull.
Address Processing (`ProcessAddresses` method)
Address Filtering: Only addresses that are not empty or have an
ExternalIdset are processed.Dummy Address Creation: If no valid addresses exist but an
iso2Backupis available, a dummyVisitaddress is created to store the ISO2 code.Visit/Delivery Address Pair Assurance:
If only one of
VisitorDeliveryaddress types is present, the missing type is created as a shallow copy of the existing one. TheAddressIdandExternalIdare reset to avoid duplication.If neither
VisitnorDeliveryexists but other address types are present, bothVisitandDeliveryaddresses are created as shallow copies from an existing address (preferring the main address). TheAddressIdandExternalIdare reset.
Address Normalization (`NormalizeAddresses` method):
Removes Unicode unit separators from email addresses.
Normalizes
Iso2codes usingMappedCountryCodesif available; otherwise, trims the existingIso2.Generates a default
AddressIdif empty.Populates empty email addresses for main addresses from customer or contact person emails.
Derives
Iso2fromCountry, or setsCountrytoIso2if empty.Defaults
AddressTypetoVisitif empty.Parses
AddressLine1intoStreet,HouseNumber, andAdditionifAddressLineis null.Sets the first
VisitandDeliveryaddresses asIsMainAddress = trueif no other main address is specified.
Contact Person Processing
Name Filling (`FillContactName` method): Handles parsing and constructing full names from various combinations of first, middle, and last names.
Contact ID Generation: If
UpdaterHelper.IsUpdateris true and a contact has noContactId, a hash code-based ID is generated.
Extra Data & Free Fields (`ProcessExtraDataForCustomer` method)
Property Overwriting: If
extraCustomerData(from e.g., FTP/CSV) is present, it can overwrite standard customer fields if property names match.FreeField Extraction: Elements from
extraCustomerData.UnknownElementsprefixed withFreeField_are extracted and added tocustomer.FreeFieldList.Item Filter Processing: If
SyncSettings.CustomerItemFilteris enabled, elements fromextraCustomerData.UnknownElementsprefixed withItemFilter_are processed to create customer-specific item filters.
Dashboards & Notes
Dashboard Handling: Base64-encoded customer dashboard data is decoded and saved via
_customerDataManager.SaveDashboards. After saving, thecustomer.CustomerDashboardfield is cleared for memory optimization.Customer Notes: If
customer.CustomerNoteis provided, any existing notes for the customer from 'BackOffice' are deleted, and a new note is inserted into theCustomerNotestable.
Post-processing Scripts
A Jint script named
UpdateCustomerscan be executed after a batch of customer data has been processed and committed to the database.
Payment Conditions
Empty string payment conditions are set to
nullto prevent foreign key errors in the database.
VAT Handling
If
AdministrationSession.CurrentSession.Administration.IsUSAis true,VatCodeis set tonullandVatLiableis set tofalse.
Domain Specifics / Extension Section
Customer Core Fields
Refer to the Data Mapping Table (Customer Core Fields) for detailed mappings of identity, price list selection, VAT flags, and payment terms. Invalid customer records (missing name or code) are skipped, and existing inactive customer GUIDs are reused to maintain historical data. Language codes are mapped, and default action price lists can be applied if configured.
Addresses
Refer to the Data Mapping Table (Addresses) for detailed mappings and normalization rules. The system ensures that both
VisitandDeliveryaddresses exist for a customer, creating dummy addresses or copying existing ones if necessary. Email addresses within addresses are cleaned and populated from customer or contact emails if empty.Contacts
Refer to the Data Mapping Table (Contact Persons) for detailed mappings and logic. Contact names are parsed and constructed. If a
ContactIdis missing for an updater, a hash-based ID is generated. The main contact person is determined based on source data or by defaulting to the first available contact.Extra Data & Free Fields
The connector supports the processing of extra customer data, typically from FTP/CSV sources (
CsvCustomer).Property Overwriting: Properties in the extra data that match existing customer properties (by name) will overwrite the customer's corresponding field.
Free Fields (
FreeField_prefix): Any unknown elements in the extra data with keys starting withFreeField_are converted intoCustomerFreeFieldobjects. The part of the key after the prefix becomes theCaption, and its value becomes theContent. These are then serialized into the customer'sFreeFieldsXML payload.Dynamic Free Fields: Existing XML payloads for dynamic free fields are deserialized, converted to a standardized format, and re-serialized, ensuring compatibility with App4Sales' custom extra fields definitions.
Item Filters (
ItemFilter_prefix): If theCustomerItemFiltersetting is enabled, elements fromextraCustomerData.UnknownElementswith keys prefixedItemFilter_are used to construct customer-specific item filters. The filter values are split by '~' and matched against predefinedItemClassValuesto create a query string.
Discounts & Dashboards
Item Class Discounts: Discounts per item class value are processed if their discount value is greater than 0. These are linked to the customer's GUID and stored.
Customer Dashboards: Base64-encoded dashboard data from DbFact is downloaded, decoded, and saved via the
_customerDataManager. The raw dashboard string on the customer object is then cleared.Customer Notes: Notes provided in
customer.CustomerNoteare handled by deleting any existing 'BackOffice' notes for the customer and then inserting the new note into theCustomerNotestable.
Customers are processed in batches of 25,000 at a time if the CustomerBatch setting is enabled.
Pre-processing Scripts
A Jint script named BeforeUpdateCustomers can be executed before the customer update process begins, allowing for custom pre-processing of customer data.
Data Source Configuration
The DbFact connector retrieves customer-related data through several API calls to the DbFact system. The data is pulled from DbFact and then processed before being updated in App4Sales.
Customer Relations: Core customer data is retrieved using the
Client.RelExportDossierAPI call. This call can be batched, retrieving customer records based on numerical ranges (e.g.,between(relatie.r_nummer,{begin},{end})).Delivery Addresses: Delivery-specific addresses are fetched via the
Client.LadresExportDossierAPI call, which returns a list ofCrsLadresobjects.Contact Persons: Contact person information associated with a customer is retrieved using the
Client.ContPersExportDossierAPI, providing details based on the customer code.Customer Dashboards: If enabled, customer dashboard layouts are downloaded using
Client.DownloadCustomerDashboard, which returns Base64-encoded dashboard data for a given customer code.Price Lists: Available price lists are obtained through
Client.DownloadPriceLists. These are used to map price list IDs from DbFact to App4Sales' internal representation.
Related Settings & Prerequisites
The following settings and prerequisites influence the behavior of the Customer Update pipeline:
CustomerBatch: (Connector Setting) A boolean setting that, if enabled, processes customers in batches of 25,000 to optimize performance during large synchronizations.CustomerTypes: (Connector Setting) A semi-colon separated string that specifies which customer types from DbFact should be synchronized. If empty, all customer types are synchronized.EnablePriceDeduplication: (Connector Base Setting or Connector Sync Setting) If true, enables the migration of price lists to deduplicated versions based on mappings inKeySettings.GenericPriceListMigrations.DefaultActionPriceListCode: (Connector Base Setting) If set, this price list code will be used as theActionPriceListfor customers if no action price list is provided by the source.EnableCustomerDashboards: (Connector Setting) If true, the connector will attempt to download and process customer dashboards from DbFact.CustomerItemFilter: (Sync Setting) If true, enables the processing of customer-specific item filters from extra data (e.g., from CSV/FTP sources). If false, any item filter information in extra data will be ignored.FtpFolderForCustomerNotes: (Connector Setting) If not empty, indicates that customer notes should be uploaded via FTP rather than using the default database insertion mechanism. This implies that theClient.UploadCustomerNotemethod is used.Mapped Languages: (System Configuration) Language codes from DbFact are mapped to App4Sales internal language codes via the configured
MappedLanguagesin the system. If no mapping is found, the original code is used.Mapped Countries: (System Configuration) ISO2 country codes from DbFact are mapped to App4Sales internal country codes via the configured
MappedCountriesin the system. If no mapping is found, the original code is used.Administration.IsUSA: (System Configuration) If the administration is configured for the USA, VAT related fields (
VatCode,VatLiable) are cleared/set to default values.Scripts:
BeforeUpdateCustomers: A Jint script that can be executed before any customer data is processed, allowing for custom transformations or validations on the incoming customer list.UpdateCustomers: A Jint script that can be executed after customer data batches have been processed and updated in the database.
Known Limitations
The parsing of full names into first, middle, and last names for contact persons assumes a space-separated format and might not accurately handle all naming conventions.
The connector does not store webshop passwords for customers or contact persons when running in Updater mode.
Extra data processing (
ProcessExtraDataForCustomer) only overwrites existing properties if the property names match exactly (case-insensitive for comparison). Enumerable fields likeUnknownElementsandFreeCsvFieldsfrom the source extra data are not directly mapped as properties.The
TempCustomerCodePrefixand related logic for handling temporary customer codes are marked with a TODO in the code, indicating a potential future refactoring. Admins should be aware that temporary customer codes are managed viaKeySettings.The current implementation of
UpdateCustomerToDatabasealways usesnew CustomerUpdateHandler().Update(customers);in a loop. While functional, it could potentially be optimized to prevent redundant object instantiations if the handler is stateless across batch calls.