Skip to main content

VismaNET - Customer sync

VismaNET Connector - Customer Update The CustomerUpdateHandler processes a list of Customer objects received from the VismaNET connector....

Updated over a week ago

VismaNET Connector - Customer Update

The CustomerUpdateHandler processes a list of Customer objects received from the VismaNET connector. It is responsible for synchronizing customer data, including associated addresses, contact persons, item class value discounts, and dashboards, into the App4Sales internal data structure. Invalid customer records (missing customer name or code) are identified and logged, then excluded from further processing. The update process supports batching to handle large datasets efficiently.

Data Source Configuration

The primary input for the Customer Update function is a List<Customer> object, which contains customer records retrieved from the VismaNET ERP system by the connector.

Additional data sources influencing the update process include:

  • Mapped Languages: Retrieved from the App4Sales system's MappedLanguagesContext to standardize language codes.

  • Mapped Countries: Retrieved from the App4Sales system's MappedCountriesContext for country code normalization.

  • Custom Extra Fields: Retrieved from CustomExtraFieldsContext to define how dynamic free fields should be processed.

  • Existing Customer Data: Fetched from the CustomersContext to retrieve existing CustomerGuid values for reactivation and to preserve ItemFilter settings.

  • Price Lists: Retrieved from PriceListsContext for price list deduplication and default action price list assignment.

  • Extra Customer Data (CSV): Optionally provided via _databaseManager.ExtraCustomerData, typically from FTP/CSV sources, which can override standard customer fields. This data is represented as CsvCustomer objects.

Domain Specifics

Customer Core Fields

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

CustomerName

Customer.CustomerName

Required field. Customer records with empty or whitespace names are logged as warnings and skipped.

CustomerCode

Customer.CustomerCode

Required field. Customer records with empty customer codes are logged as warnings and skipped. Used for identification and linking.

Created

Customer.Created

If the provided 'Created' date is its default minimum value, it is set to the current date and time. The value is truncated to the second, discarding milliseconds.

Sysmodified

Customer.Sysmodified

The value is truncated to the second, discarding milliseconds.

PasswordWebshop

Customer.PasswordWebshop

Set to null if the update is running in 'Updater' mode to prevent overwriting existing webshop passwords.

CustomerDashboard

Customer.CustomerDashboard

Expected to be a Base64-encoded string. If valid, the Base64 string is decoded and stored as a dashboard artifact. The field is then cleared from the customer object. Invalid Base64 strings are ignored.

LanguageCode

Customer.LanguageCode

Mapped to a standardized App4Sales language code using configured language mappings. If in App4Sales app update mode and input is empty, falls back to existing database value.

CustomerGuid

Derived

  • If 'Updater' mode is active, attempts to reuse GUID from previously inactive customers with the same CustomerCode.

  • If not found, searches a local cache for existing GUIDs based on CustomerCode.

  • If still not found, queries the Portal Server Provider using CustomerCode and CustomerName.

  • If no GUID is retrieved, a new GUID is generated, indicating a new customer.

ActionPriceList

Customer.ActionPriceList

  • If price deduplication is enabled, the Price List ID may be migrated to a deduplicated version.

  • If no action price list is specified, and a default action price list code is configured in settings, it is assigned.

PaymentConditionCode

Customer.PaymentConditionCode

Empty strings are converted to null to prevent foreign key constraint issues.

Email

Customer.Email

Whitespace is trimmed from the email address.

DynamicFreeFields

Customer.DynamicFreeFields

XML payload containing extra ERP data. If valid and not boilerplate, deserialized, converted to App4Sales extra field XML format, and sanitized.

CustomerNote

Customer.CustomerNote

If present, any existing 'BackOffice' notes for the customer are deleted, and a new note is inserted into the CustomerNotes table.

InternalCode

Customer.InternalCode

If updating from the App4Sales app and the input is empty, the existing InternalCode from the database is retained.

ExtraData

Customer.ExtraData

If updating from the App4Sales app and the input is empty, the existing ExtraData from the database is retained.

VatCode

Customer.VatCode

Set to null if the App4Sales administration is configured for USA.

VatLiable

Customer.VatLiable

Set to false if the App4Sales administration is configured for USA.

FreeFields

Customer.FreeFieldList / FTP/CSV (UnknownElements)

  • If FreeFieldList is populated, it's serialized to XML and stored in FreeFields.

  • Extra data from FTP/CSV (UnknownElements starting with 'FreeField_') are parsed and added to FreeFieldList.

ItemFilter

FTP/CSV (UnknownElements)

  • If CustomerItemFilter setting is enabled, existing database ItemFilter is preserved.

  • Extra data from FTP/CSV (UnknownElements starting with 'ItemFilter_') are parsed, validated against item classes, and formatted as a query string.

Addresses

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

Addresses (General)

Customer.Addresses

  • Only non-empty addresses or those with an ExternalId are processed.

  • Ensures both a 'Visit' and a 'Delivery' address exist; if one is missing, it is created by copying an existing address.

  • If no 'Visit' or 'Delivery' addresses exist, but other address types are present, a 'Visit' and 'Delivery' address are created from the most relevant existing address (prioritizing IsMainAddress).

  • If all addresses are empty but an ISO2 backup exists, a dummy 'Visit' address is created to store the ISO2.

Address.Email

Address.Email / Customer.Email / ContactPerson.Email

  • Cleans email by removing Unicode unit separator characters (U+001F).

  • If address email is empty and it's the main address, it attempts to inherit email from customer's email, then from other customer addresses, then from contact persons.

Address.Iso2

Address.Iso2 / Address.Country

  • Mapped to a standardized country code using configured country mappings.

  • If empty, attempts to derive from Address.Country.

  • Whitespace is trimmed.

Address.Country

Address.Country / Address.Iso2

If empty, defaults to Address.Iso2.

Address.CustomerGuid

Customer.CustomerGuid

Assigned from the parent customer's GUID.

Address.AddressId

Derived

If empty, a default ID is generated.

Address.AddressType

Address.AddressType

If empty, defaults to 'Visit'.

Address.AddressLine1

Address.AddressLine.Street, Address.AddressLine.Number, Address.AddressLine.Addition

If AddressLine object is present, it formats AddressLine1 using Street, House Number, and Addition, considering USA specific formatting.

Address.AddressLine (object)

Address.AddressLine1

If AddressLine object is null, it attempts to parse AddressLine1 into Street, House Number, and Addition.

Address.IsMainAddress

Derived

For 'Visit' and 'Delivery' addresses, if no address is explicitly marked as main, the first one found is set as main.

Contacts

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

ContactPerson.CustomerGuid

Customer.CustomerGuid

Inherited from the parent customer to link the contact to the correct customer.

ContactPerson.FirstName, ContactPerson.MiddleName, ContactPerson.LastName

ContactPerson.FullName

If individual name fields are empty but FullName is present, FullName is parsed to populate FirstName (first word), LastName (last word), and MiddleName (remaining middle words).

ContactPerson.FullName

ContactPerson.FirstName, ContactPerson.MiddleName, ContactPerson.LastName

If FullName is empty but individual name fields are present, FullName is constructed by concatenating FirstName, MiddleName, and LastName.

ContactPerson.Initials

ContactPerson.FirstName

Set to the first character of FirstName if FirstName is not empty.

ContactPerson.ContactId

Derived

If in 'Updater' mode and ContactId is empty, a unique ID is generated using the contact's hash code. This applies to updates, not new contact creation in the App.

ContactPerson.DynamicFreeFields

ContactPerson.DynamicFreeFields

XML payload. If not empty, it is deserialized into an object and converted to App4Sales extra field XML format, similar to customer dynamic free fields.

ContactPerson.PasswordWebshop

ContactPerson.PasswordWebshop

Set to null if the update is running in 'Updater' mode to prevent overwriting existing webshop passwords.

ContactPerson.IsMainContactPerson

ContactPerson.IsMainContactPerson / Customer.MainContactPerson

  • If Customer.MainContactPerson is provided, the corresponding contact is marked as main.

  • If no contact is explicitly marked as main, the first contact in the list is designated as the main contact.

Extra Data & Free Fields

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

Customer (various fields)

CsvCustomer properties

Properties of the CsvCustomer object (from external data like FTP/CSV) with non-null values will overwrite corresponding fields in the Customer object if their property names match (e.g., CsvCustomer.Email overwrites Customer.Email). Note: Generic dictionary types are skipped.

FreeFieldList (CustomerFreeField objects)

CsvCustomer.UnknownElements (keys starting with "FreeField_")

Elements in the CsvCustomer.UnknownElements dictionary whose keys begin with "FreeField_" (e.g., FreeField_CustomData) are parsed. The key (without the prefix) becomes the Caption, and the value becomes the Content of a new CustomerFreeField. These are added to the customer's FreeFieldList.

ItemFilter

CsvCustomer.UnknownElements (keys starting with "ItemFilter_")

If the CustomerItemFilter setting is enabled, elements in CsvCustomer.UnknownElements whose keys begin with "ItemFilter_" are processed. The key (without prefix) identifies the item class, and the value (e.g., "VALUE1~VALUE2") specifies the item class values. These are validated against the system's item class map and converted into a formatted query string for customer.ItemFilter. This allows for customer-specific item filtering.

Discounts & Dashboards

Item Class Value Discounts

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

ItemClassValueDiscount.CustomerGuid

Customer.CustomerGuid

The CustomerGuid of the parent customer is assigned to each item class value discount, linking it to the correct customer.

ItemClassValueDiscount.discount

ItemClassValueDiscount.discount

Only discounts with a value greater than 0 and not null are processed and stored.

Other fields of ItemClassValueDiscount

ItemClassValueDiscount (from source)

All other fields of the ItemClassValueDiscount object (e.g., ItemClassId, ItemClassValue, etc.) are taken directly from the source customer.DiscountsPerItemCLassValue. These are batched and updated in the database via the OPTA4SUpdateItemClassValueDiscountsBatch stored procedure.

Dashboards

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

Dashboard.CustomerCode

Customer.CustomerCode

The CustomerCode from the Customer object is used to link the dashboard to the correct customer.

Dashboard.DashboardData

Customer.CustomerDashboard

  • Expected to contain Base64 encoded dashboard data.

  • If null, empty, or whitespace, it is ignored.

  • Attempts to decode Base64 string into a byte array. Errors during decoding result in the field being ignored.

  • After processing, the Customer.CustomerDashboard field on the customer object is set to null to free up memory.

  • Persisted separately via _customerDataManager.SaveDashboards.

Special Logic & Filters

  • Invalid Customer Filtering: Customers with empty or null CustomerName or CustomerCode are filtered out and logged as warnings, then skipped from processing.

  • Batch Processing: Customers are processed in batches of 100 for optimized memory usage and database operations.

  • Script Hooks:

    • BeforeUpdateCustomers: Executed before any customer processing, allowing custom logic to modify or filter the incoming customer list.

    • UpdateCustomers (during batch): Executed after each customer batch processing but before database update, allowing for further modifications to the batch.

    • UpdateCustomers (final): Executed at the very end after all batches and database updates are complete, with all processed customers.

  • Customer GUID Reactivation and Generation:

    • In 'Updater' mode, attempts to reuse GUIDs of previously inactive customers to preserve historical data.

    • Looks up existing GUIDs from a local cache or the Portal Server based on CustomerCode and CustomerName.

    • Generates a new GUID if no existing one is found, indicating a new customer.

  • Price Deduplication Migration: If enabled, UsesPriceField and ActionPriceList IDs may be migrated to deduplicated versions. Missing or invalid IDs fall back to default values.

  • Payment Condition Sanitization: Empty PaymentConditionCode strings are converted to null to prevent foreign key issues.

  • Address Processing: Filters empty addresses, ensures 'Visit' and 'Delivery' address pairs, normalizes address data (email cleaning, country code mapping, default values, address line parsing/formatting), and sets default main addresses.

  • Contact Person Name Filling: Parses or constructs full names from individual name components and generates initials.

  • Contact Person ID Generation: In 'Updater' mode, generates a hash-based ContactId if it's empty.

  • XML Character Removal: Invalid XML characters are removed from Customer and ContactPerson fields to prevent parsing errors.

  • VAT Handling for USA: If the App4Sales administration is configured for USA, VatCode is set to null and VatLiable to false.

  • Error Handling: try-catch blocks log errors and reset data update intervals on failure.

Related Settings & Prerequisites

  • Customer Item Filter (SyncSettings.CustomerItemFilter): If enabled, allows for customer-specific item filters to be processed from extra data and preserves existing filters during updates from the App4Sales app.

  • Enable Price Deduplication (ConnectorBaseSettings.EnablePriceDeduplication or connector-specific setting): When active, price list IDs are migrated to a deduplicated format using a predefined mapping (KeySettings.GenericPriceListMigrations).

  • Default Action Price List Code (ConnectorBaseSettings.DefaultActionPriceListCode): Specifies a price list code to be used as the default action price list for customers if none is provided in the source data.

  • USA Administration (Administration.IsUSA): If the App4Sales administration is configured for the USA, VAT-related fields (VatCode, VatLiable) are reset to null/false for customers.

  • Updater Mode (UpdaterHelper.IsUpdater): This internal flag (indicating a bulk update process) influences several behaviors:

    • Resets PasswordWebshop for customers and contact persons.

    • Enables reactivation logic for inactive customers by reusing their GUIDs.

    • Enables generation of ContactId for contact persons if empty.

    • Modifies fallback logic for InternalCode, ExtraData, and LanguageCode when updates originate from the App4Sales app.

  • Script Hooks (`BeforeUpdateCustomers`, `UpdateCustomers`): Custom JavaScript hooks allow for extending or modifying the customer update pipeline at predefined points, enabling custom logic, data manipulation, or validation.

Known Limitations

  • Webshop Password Sync: PasswordWebshop fields for customers and contact persons are explicitly set to null when the system is in 'Updater' mode. This means webshop passwords cannot be synchronized via the bulk customer update pipeline.

  • Customer Note Replacement: Providing a new Customer.CustomerNote will completely replace any existing 'BackOffice' customer notes. There is no functionality for appending or merging notes.

  • Dynamic Free Fields for Dictionaries: The generic property overwrite mechanism in ProcessExtraDataForCustomer explicitly skips properties that are generic Dictionary types. Any dictionary-based extra data in CsvCustomer will not be mapped to corresponding Customer properties through this mechanism.

  • Item Filter Setting Dependency: Processing of item filters from external extra data (e.g., FTP/CSV ItemFilter_ elements) is conditional on the SyncSettings.CustomerItemFilter being enabled. If this setting is disabled, such item filter data will be ignored.

  • Hardcoded Customer Note Values: The salesrep ("BackOffice") and subject ("Backoffice note") for customer notes are hardcoded within the handler and cannot be dynamically influenced by incoming customer data.

  • No Barcode Sync: There is no explicit logic or mapping for synchronizing customer-specific barcodes.

  • Single Warehouse Assumption: The code does not appear to contain logic for handling customer associations with multiple warehouses.

Did this answer your question?