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
MappedLanguagesContextto standardize language codes.Mapped Countries: Retrieved from the App4Sales system's
MappedCountriesContextfor country code normalization.Custom Extra Fields: Retrieved from
CustomExtraFieldsContextto define how dynamic free fields should be processed.Existing Customer Data: Fetched from the
CustomersContextto retrieve existingCustomerGuidvalues for reactivation and to preserveItemFiltersettings.Price Lists: Retrieved from
PriceListsContextfor 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 asCsvCustomerobjects.
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 |
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 |
|
ActionPriceList | Customer.ActionPriceList |
|
PaymentConditionCode | Customer.PaymentConditionCode | Empty strings are converted to |
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 |
InternalCode | Customer.InternalCode | If updating from the App4Sales app and the input is empty, the existing |
ExtraData | Customer.ExtraData | If updating from the App4Sales app and the input is empty, the existing |
VatCode | Customer.VatCode | Set to |
VatLiable | Customer.VatLiable | Set to |
FreeFields | Customer.FreeFieldList / FTP/CSV (UnknownElements) |
|
ItemFilter | FTP/CSV (UnknownElements) |
|
Addresses
App4Sales Field | Source Field (API/Excel/DB) | Logic/Notes |
Addresses (General) | Customer.Addresses |
|
Address.Email | Address.Email / Customer.Email / ContactPerson.Email |
|
Address.Iso2 | Address.Iso2 / Address.Country |
|
Address.Country | Address.Country / Address.Iso2 | If empty, defaults to |
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 |
Address.AddressLine (object) | Address.AddressLine1 | If |
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 |
ContactPerson.FullName | ContactPerson.FirstName, ContactPerson.MiddleName, ContactPerson.LastName | If |
ContactPerson.Initials | ContactPerson.FirstName | Set to the first character of |
ContactPerson.ContactId | Derived | If in 'Updater' mode and |
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 |
ContactPerson.IsMainContactPerson | ContactPerson.IsMainContactPerson / Customer.MainContactPerson |
|
Extra Data & Free Fields
App4Sales Field | Source Field (API/Excel/DB) | Logic/Notes |
Customer (various fields) |
| Properties of the |
FreeFieldList (CustomerFreeField objects) |
| Elements in the |
ItemFilter |
| If the |
Discounts & Dashboards
Item Class Value Discounts
App4Sales Field | Source Field (API/Excel/DB) | Logic/Notes |
ItemClassValueDiscount.CustomerGuid | Customer.CustomerGuid | The |
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 |
Dashboards
App4Sales Field | Source Field (API/Excel/DB) | Logic/Notes |
Dashboard.CustomerCode | Customer.CustomerCode | The |
Dashboard.DashboardData | Customer.CustomerDashboard |
|
Special Logic & Filters
Invalid Customer Filtering: Customers with empty or null
CustomerNameorCustomerCodeare 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,
UsesPriceFieldandActionPriceListIDs may be migrated to deduplicated versions. Missing or invalid IDs fall back to default values.Payment Condition Sanitization: Empty
PaymentConditionCodestrings are converted tonullto 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
ContactIdif 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,
VatCodeis set tonullandVatLiabletofalse.Error Handling:
try-catchblocks 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.EnablePriceDeduplicationor 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 tonull/falsefor customers.Updater Mode (
UpdaterHelper.IsUpdater): This internal flag (indicating a bulk update process) influences several behaviors:Resets
PasswordWebshopfor customers and contact persons.Enables reactivation logic for inactive customers by reusing their GUIDs.
Enables generation of
ContactIdfor contact persons if empty.Modifies fallback logic for
InternalCode,ExtraData, andLanguageCodewhen 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:
PasswordWebshopfields for customers and contact persons are explicitly set tonullwhen 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.CustomerNotewill 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
ProcessExtraDataForCustomerexplicitly skips properties that are genericDictionarytypes. Any dictionary-based extra data inCsvCustomerwill not be mapped to correspondingCustomerproperties through this mechanism.Item Filter Setting Dependency: Processing of item filters from external extra data (e.g., FTP/CSV
ItemFilter_elements) is conditional on theSyncSettings.CustomerItemFilterbeing enabled. If this setting is disabled, such item filter data will be ignored.Hardcoded Customer Note Values: The
salesrep("BackOffice") andsubject("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.