Skip to main content

Visma - Customer sync

Visma AccountView - CustomerUpdate The CustomerUpdate function in the Visma AccountView connector synchronizes customer-related data fro...

Updated over a week ago

Visma AccountView - CustomerUpdate

The CustomerUpdate function in the Visma AccountView connector synchronizes customer-related data from the AccountView ERP system into the App4Sales platform. This process involves retrieving customer records, including their core information, associated addresses, contact persons, item class value discounts, and dashboards. The synchronization runs periodically, pulling data from AccountView's REST API and updating the corresponding entities within the App4Sales database. It also handles the creation and updating of custom extra fields for search codes and relation groups.

3. Data Source Configuration

The connector retrieves customer data from the Visma AccountView REST API. Data is pulled from the API using HTTP GET requests. Authentication is performed using an OAuth 2.0 Bearer token (Settings.AccessToken) and a company ID (Settings.CompanyId) in the request headers.

API Endpoints and Business Objects:

  • Customer Data: /accountviewdata (GET) using business object ar1.

  • Payment Conditions: /accountviewdata (GET) using business object pt1.

  • Prospect Data: /accountviewdata (GET) using business object CM1.

  • Customer/Contact Update: /accountviewdata (POST/PUT).

Key Query Parameters:

  • pagesize: Specifies the number of records per page (e.g., 500 for customers).

  • fields: A comma-separated list of fields to retrieve from AccountView (e.g., contact.sub_nr, contact.acct_name, ...).

  • filtercontrolsourceX, filteroperatorX, filtervaluetypeX, filtervalueX: Used for filtering records (e.g., filtering out blocked customers, or specific account links for prospects).

  • sortfields, sortorder: For ordering the retrieved data.

  • lastkey: Used for pagination to retrieve subsequent pages of data.

Filtering Logic:

  • Customers marked as 'Blocked' in AccountView are not synchronized.

  • If Settings.CustomFieldForCustomerSync is configured, customers are filtered based on the boolean value of this custom field.

  • Prospects are identified by filtering on contact.acct_link = 0.

4. Data Mapping Table

Customer Core Fields

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

CustomerCode

contact.sub_nr

Directly mapped. For new customers created within App4Sales, a code is generated using Settings.AccountViewCustomerCodeStartingNumber.

CustomerName

contact.acct_name

Directly mapped. For prospects (CM1 business object), [P] is prepended.

Discount

contact.disc_pct

Mapped as an integer percentage.

LanguageCode

contact.lng_code

Mapped via MappedLanguagesContext. If not found, the original code is used. Can be inherited from existing customer data if empty in source.

VatCode

contact.vat_code

Directly mapped. Set to null if Administration.IsUSA is true.

VatLiable

Derived

Based on Settings.CustomerVatLiableCodes. If Administration.IsUSA is true, set to false.

CreatedDate

contact.inp_date

Timestamp of creation. Truncated to seconds. If not available, defaults to DateTime.Now.

Sysmodified

contact.cng_date

Timestamp of last modification. Truncated to seconds. If not available, defaults to DateTime.Now.

PaymentConditionCode

contact.disc_code

Directly mapped. Empty string values are converted to null to prevent foreign key issues.

ChamberOfCommerceCode

contact.COC_CODE

Directly mapped.

Email

contact.mail_bus

Trimmed for whitespace. Can be inherited by addresses if empty.

Telephone

contact.tel_bus

Directly mapped.

Mobile

contact.TEL_MOB

Directly mapped.

Fax

CONTACT.FAX_BUS

Directly mapped.

ExtraData

contact.COMMENT1

Mapped from contact.COMMENT1 in AccountView. This value is also used to generate CustomerNotes.

SearchCode

CONTACT.SRC_CODE

Directly mapped. If empty, derived from the first 4 characters of CustomerName and converted to uppercase.

RelationGroup

CONTACT.RPL_GRP

Directly mapped. Populated as a dropdown extra field.

UsesPriceField

contact.APX_LIST or contact.pct_list

Maps to a numeric ID representing the price list used by the customer. Subject to price deduplication migration logic.

ActionPriceList

Settings.DefaultActionPriceListCode

If not specified by AccountView, defaults to the ID of the price list specified in Settings.DefaultActionPriceListCode. Subject to price deduplication migration logic.

EmployeeNumber

contact.EMP_NR

If Settings.UseUsernameAsEmployeeForNewCustomer is enabled, maps the App4Sales username to this field for new customers.

CollectionAccountCode

Settings.CollectionAccountCodeForNewCustomer

If Settings.CollectionAccountCodeForNewCustomer is set, it maps this setting to the AccountView field acct_nr.

CustomerGuid

Derived

Retrieved from local cache, Portal API, or generated as a new GUID if not found. Reuses GUIDs for reactivated customers.

CustomerDashboard

Base64 string from AccountView

Base64-encoded dashboard layout. Decoded and stored separately. Original field is set to null after processing.

FreeFieldList

UnknownElements from CSV/API or DynamicFreeFields XML

Processed from FreeField_ prefixed fields in UnknownElements or deserialized from DynamicFreeFields XML, then re-serialized into XML for storage.

ItemFilter

ItemFilter_ prefixed fields in UnknownElements from CSV/API

Processed if SyncSettings.CustomerItemFilter is enabled. Creates a query string based on item class filters.

Addresses

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

AddressType

Derived

Defaults to 'Visit' if not explicitly defined. Ensures both 'Visit' and 'Delivery' addresses exist, duplicating if only one is provided.

AddressLine1

contact.ADDRESS1

Combined street, house number, and addition. Parsed from AccountView ADDRESS1 if AddressLine is null, otherwise formatted from AddressLine components.

Street

Derived from contact.ADDRESS1

Extracted from AddressLine1 during parsing.

HouseNumber

Derived from contact.ADDRESS1

Extracted from AddressLine1 during parsing.

Addition

Derived from contact.ADDRESS1

Extracted from AddressLine1 during parsing.

PostCode

contact.POST_CODE

Directly mapped.

City

contact.City

Directly mapped.

Country

contact.COUNTRY or Iso2

If contact.COUNTRY is empty, falls back to Iso2.

Iso2

contact.cnt_code or Derived

Mapped via MappedCountriesContext. If empty, derived from Country using CultureHelper.GetIso2FromCountry.

Email

Inherited from Customer or other Addresses/Contacts

Trimmed. If the address is the main address and its email is empty, it inherits the email from the customer or other available addresses/contacts.

CustomerGuid

Derived

Assigned from the parent customer's GUID.

IsMainAddress

Derived

Automatically set to true for the first 'Visit' and 'Delivery' address if no main address is explicitly defined.

ExternalId

AccountView source

Used to indicate if an address is valid in the external system; prevents filtering out empty addresses.

Contact Persons

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

ContactId

Derived from contact.main_pp or contact.admin_pp

If UpdaterHelper.IsUpdater and ContactId is empty, a new ContactId is generated from the hash code of the contact. For new customers, generated via Settings.AccountViewContactCodeStartingNumber.

FirstName

Derived from contact.main_cont (FullName)

Parsed from FullName if FirstName, LastName, and MiddleName are empty.

LastName

Derived from contact.main_cont (FullName)

Parsed from FullName if FirstName, LastName, and MiddleName are empty.

MiddleName

Derived from contact.main_cont (FullName)

Parsed from FullName if FirstName, LastName, and MiddleName are empty.

FullName

contact.main_cont or Derived

If empty, constructed from FirstName, MiddleName, and LastName.

Initials

Derived from FirstName

First letter of FirstName.

Email

contact.mail_bus

Trimmed for whitespace.

Phonenumber

contact.tel_bus

Directly mapped.

MobileNumber

contact.TEL_MOB

Directly mapped.

CustomerGuid

Derived

Assigned from the parent customer's GUID.

IsMainContactPerson

Derived

Set to true if the contact is designated as the main contact for the customer, either explicitly from the source or if it's the first contact in the list.

DynamicFreeFields

XML payload

Deserialized from XML, converted to extra fields using contactPersonExtraFields, and then re-serialized into XML. Invalid XML characters are removed.

PasswordWebshop

N/A

Set to null if UpdaterHelper.IsUpdater is true.

Item Class Value Discounts

App4Sales Field

Source Field (API/Excel/DB)

Logic/Notes

CustomerGuid

Derived

Assigned from the parent customer's GUID.

ItemClass

ItemFilter_ prefixed fields in UnknownElements from CSV/API

The name of the item class.

ItemClassId

Derived

Internal ID of the item class, resolved from ItemClass.

discount

CSV/API UnknownElements or calculated

The percentage discount. Only discounts greater than 0 are processed.

5. Special Logic & Filters

  • Invalid Customer Filtering: Customers with missing or empty CustomerName or CustomerCode are considered invalid, logged as warnings, and excluded from the synchronization process.

  • Customer Reactivation: The system attempts to reuse existing CustomerGuid values for customers that were previously marked as inactive or deleted, preserving historical data like notes and order links.

  • Password Handling: If the connector is running in 'Updater' mode (UpdaterHelper.IsUpdater is true), any webshop passwords (PasswordWebshop) associated with customers or contact persons are cleared (set to null) to ensure secure handling and prevent accidental synchronization of sensitive credentials.

  • Dashboard Processing: Customer dashboards, received as Base64-encoded strings, are decoded and stored separately within the App4Sales database via _customerDataManager.SaveDashboards. The original CustomerDashboard field on the customer object is then nulled out to optimize memory usage and avoid storing large Base64 strings directly in the main customer record.

  • Default Action Price List: If a DefaultActionPriceListCode is configured in the connector's base settings and a customer does not have an explicit action price list assigned, the system automatically assigns the price list corresponding to the default code.

  • Payment Condition Sanitization: Empty string values for PaymentConditionCode are converted to null before being saved to prevent potential foreign key constraint issues in the database.

  • Email Trimming: All email addresses associated with customers and contact persons are trimmed of leading and trailing whitespace to ensure data consistency.

  • Address Processing Logic:

    • Empty Address Filtering: Addresses that are entirely empty are filtered out unless they have an ExternalId, indicating they are valid in the external system.

    • Dummy Address Creation: If a customer has no valid addresses after filtering, but an ISO2 country code is available, a dummy 'Visit' address is created to preserve the country information.

    • Visit/Delivery Address Duplication: To ensure both 'Visit' and 'Delivery' address types exist for a customer, if only one is initially present, the existing address is duplicated to create the missing type. The duplicated address's Modified status is set to false, and AddressId and ExternalId are nulled to prevent conflicts.

    • Last Resort Address Duplication: If no 'Visit' or 'Delivery' addresses are available but other address types exist, the primary existing address (either the main address or the first one found) is duplicated to create both 'Visit' and 'Delivery' address types.

    • Email Inheritance for Addresses: If an address is marked as the main address and its email field is empty, it attempts to inherit an email address from the customer's main email, another address on the same customer, or from a contact person associated with the customer.

    • ISO/Country Code Normalization: ISO2 country codes are mapped via MappedCountriesContext. If an ISO2 code is missing, it attempts to derive it from the full country name. If the country name is missing, it defaults to the ISO2 code.

    • Address Line Parsing: If a structured AddressLine (containing Street, House Number, Addition) is not present, the system attempts to parse AddressLine1 into these components. Conversely, if AddressLine components exist, they are formatted back into AddressLine1.

    • Main Address Designation: The system ensures that at least one 'Visit' and one 'Delivery' address are designated as the main address if multiple addresses of these types exist.

  • Dynamic Free Fields Conversion: For both customers and contact persons, dynamic free fields, initially stored as XML payloads (DynamicFreeFields), are deserialized into a structured format, converted into App4Sales' internal extra field representation using predefined custom extra field contexts, and then re-serialized into XML for storage. Invalid XML characters are removed during this process.

  • Contact Person ContactId Generation: In 'Updater' mode, if a contact person's ContactId is empty, a unique ID is generated based on the contact's hash code. For newly created customers, contact IDs are generated sequentially using Settings.AccountViewContactCodeStartingNumber.

  • Main Contact Person Logic: The system identifies and designates a main contact person for each customer. If a MainContactPerson is explicitly provided, it is ensured to be part of the customer's contact list and marked as the main contact. If not, and contacts exist, the first available contact is designated as the main contact.

  • Customer Notes Handling: Any existing 'Backoffice note' for a customer, identified by the BackofficeUser, is deleted from the CustomerNotes table before new notes, sourced from customer.CustomerNote or customer.ExtraValues (AccountView's COMMENT1), are inserted.

  • ERP Leading Data (Non-Updater): When not operating in 'Updater' mode, the system prioritizes existing App4Sales data for certain fields (InternalCode, ExtraData, LanguageCode) if the incoming AccountView data provides empty values, ensuring that crucial information is not inadvertently overwritten.

  • USA VAT Handling: For administrations configured for the USA (Administration.IsUSA is true), VAT-related fields (VatCode and VatLiable) on the customer object are explicitly cleared (set to null and false respectively), as VAT handling differs in the USA.

  • Processing Extra Data from CSV/FTP: The ProcessExtraDataForCustomer method is invoked if extraCustomerData is available (e.g., from CSV or FTP sources). This method overlays additional data onto the customer object, potentially overwriting standard customer properties if they exist in the extra data. It also extracts and processes:

    • Free Fields: Any fields in the extra data's UnknownElements that are prefixed with FreeField_ are extracted and added to the customer's FreeFieldList.

    • Fixed Item Filters: If SyncSettings.CustomerItemFilter is enabled, fields prefixed with ItemFilter_ in UnknownElements are processed to construct and apply item class filters for the customer.

  • Price Deduplication Migration: Price list IDs (UsesPriceField and ActionPriceList) on customer objects are adjusted based on a stored migration lookup (KeySettings.GenericPriceListMigrations). This ensures that customers are linked to deduplicated price lists when EnablePriceDeduplication is active. If a migrated price list is not found, UsesPriceField defaults to 1 and ActionPriceList is nulled.

  • Batch Processing: Customer updates are processed in batches of 100 to optimize database operations and reduce resource consumption.

  • JINT Scripts: The connector executes custom JINT scripts at specific points in the update process:

    • BeforeUpdateCustomers: Executed before any customer processing within the `Update` method.

    • UpdateCustomers: Executed after each batch of customers has been processed, and again at the very end of the entire update cycle.

  • Database Batch Updates: For efficiency, changes to customers, addresses, and item class value discounts are committed to the database using batch updates via stored procedures (e.g., OPTA4SUpdateCustomersBatch, OPTA4SUpdateAddressesBatch, OPTA4SUpdateItemClassValueDiscountsBatch).

  • Error Handling: Comprehensive error logging is implemented at various stages, including during individual customer processing and batch database updates. In case of errors during customer processing, data update intervals for 'CustomerUpdate' are reset.

6. Domain Specifics / Extension Section

Customer Core Fields

The core customer record is updated with information such as customer code, name, discount percentage, language, VAT details, and payment conditions. Creation and modification timestamps are also synchronized. Special handling is in place for new customer codes and search codes. Customer GUIDs are managed to ensure data integrity and facilitate reactivation of previously inactive customers. For administrations in the USA, VAT-related fields are cleared to align with local regulations. Prospect customers have their names prefixed with '[P]'.

Addresses

The connector ensures that each customer has both 'Visit' and 'Delivery' address types. If only one is provided by AccountView, the other is automatically generated by duplicating the existing one. Address lines are parsed to extract street, house number, and addition components, and country codes (ISO2) are normalized. Email addresses for main addresses can be inherited from the customer or other contacts if not explicitly provided. Addresses marked with an ExternalId are considered valid even if otherwise empty.

Contacts

Contact persons are linked to their respective customers. The system manages contact IDs, generating new ones if necessary in 'Updater' mode. Full names are composed from or decomposed into first, middle, and last names. A main contact person is always designated for each customer. Email addresses are trimmed. Webshop passwords for contacts are cleared during updates in 'Updater' mode.

Extra Data & Free Fields

Extra customer data, potentially sourced from CSV or other file-based imports, can be used to overwrite standard customer fields. Additionally, any fields from the external data source prefixed with 'FreeField_' are converted into generic free fields for the customer. If SyncSettings.CustomerItemFilter is enabled, fields prefixed with 'ItemFilter_' are processed to define customer-specific item filters, which control what items a customer can see or order.

Discounts & Dashboards

Customer-specific item class value discounts (percentage discounts based on item classifications) are synchronized and stored. Only discounts greater than 0% are processed. Customer dashboards, represented as Base64-encoded layouts from AccountView, are extracted, decoded, and stored in a dedicated location in the App4Sales database. The Base64 string is then removed from the customer object to optimize performance and storage.

7. Related Settings & Prerequisites

This section lists the connector settings and prerequisites that influence the customer synchronization process:

  • CustomFieldForCustomerSync: (Connector Setting) Specifies a custom field in AccountView used to filter which customers are synchronized. Only customers where this field evaluates to true will be processed.

  • CustomerVatLiableCodes: (Connector Setting) A comma-separated list of VAT codes that indicate a customer is VAT liable. Used in conjunction with contact.vat_code from AccountView.

  • SyncProspects: (Connector Setting) If enabled, prospects are also synchronized from AccountView (using business object CM1) and are prefixed with [P] in their customer name within App4Sales.

  • AccountViewCustomerCodeStartingNumber: (Connector Setting) Defines the starting number for generating new customer codes when customers are created from within App4Sales.

  • AccountViewContactCodeStartingNumber: (Connector Setting) Defines the starting number for generating new contact IDs for contact persons created from within App4Sales.

  • SendContactAsAdministrationContact: (Connector Setting) If enabled, contact persons are sent to AccountView as 'Administration Contact'.

  • SendContactAsMainContact: (Connector Setting) If enabled, contact persons are sent to AccountView as 'Main Contact'.

  • UseUsernameAsEmployeeForNewCustomer: (Connector Setting) If enabled, the App4Sales username is mapped to the EmployeeNumber field (contact.EMP_NR) in AccountView for new customers.

  • CollectionAccountCodeForNewCustomer: (Connector Setting) If set, this code is mapped to the acct_nr field in AccountView for new customers.

  • SwapDeliveryAndVisitAddress: (Connector Setting) If enabled, the logic for determining visit and delivery addresses is swapped.

  • CustomerItemFilter: (Sync Setting) If enabled, the connector processes ItemFilter_ prefixed fields from extra customer data to apply item class filters for customers.

  • EnablePriceDeduplication: (Connector Base Setting / Sync Setting) If enabled, the system performs a migration of price list IDs to ensure that customers are linked to deduplicated price lists.

  • DefaultActionPriceListCode: (Connector Base Setting) Defines a default action price list to be assigned to customers if they do not have one explicitly specified.

  • Administration.IsUSA: (Administration Setting) This internal setting determines if the administration is based in the USA, influencing the clearing of VAT-related fields for customers.

  • Custom JINT Scripts:

    • BeforeUpdateCustomers: Allows execution of custom logic before customers are processed.

    • UpdateCustomers: Allows execution of custom logic after customer batches are processed.

8. Known Limitations

  • AccountView API Idiosyncrasies: The connector includes workarounds for specific behaviors observed in the AccountView API, such as inconsistencies in how filters (e.g., 'StartsWith' vs. 'Equal') and search operations on item descriptions function.

  • Implicit API Ordering for Pagination: The use of lastkey for API pagination implies an assumption that the AccountView API maintains a consistent and predictable ordering of records across paginated requests. Deviations in this ordering could lead to missed or duplicated records.

  • Potential Customer Code/Contact ID Conflicts: When generating new customer codes or contact IDs for new entities created in App4Sales, the system defaults to a starting number of 500000 if the corresponding connector settings (AccountViewCustomerCodeStartingNumber, AccountViewContactCodeStartingNumber) are not explicitly configured or are set to zero. If existing customer or contact codes in AccountView fall within this auto-generated range, potential conflicts could arise.

Did this answer your question?