---
# === IDENTITY ===
id: business/erp-integration/field-mapping-data-type-comparison/2026
canonical_question: "How do ERPs handle dates, currencies, multi-language fields, and custom fields differently?"
aliases:
  - "ERP data type comparison dates currencies custom fields"
  - "How do Salesforce SAP NetSuite handle date formats differently in API?"
  - "ERP field mapping data type gotchas cross-system integration"
  - "Currency decimal handling across ERP APIs Salesforce SAP Oracle NetSuite Dynamics 365"
entity_type: erp_integration
domain: business > erp-integration > field-mapping-data-type-comparison
region: global
jurisdiction: global
temporal_scope: 2025-2026

# === SYSTEM PROFILE ===
systems:
  - name: "Salesforce"
    vendor: "Salesforce"
    version: "API v62.0 (Spring '26)"
    edition: "Enterprise, Unlimited, Developer"
    deployment: cloud
    api_surface: "REST, SOAP, Bulk API 2.0"
  - name: "SAP S/4HANA Cloud"
    vendor: "SAP"
    version: "2408 / 2502"
    edition: "Public Cloud, Private Cloud"
    deployment: cloud
    api_surface: "OData V2/V4, SOAP, RFC/BAPI"
  - name: "Oracle ERP Cloud (Fusion)"
    vendor: "Oracle"
    version: "24D / 25A"
    edition: "Standard, Enterprise"
    deployment: cloud
    api_surface: "REST, SOAP, FBDI"
  - name: "Oracle NetSuite"
    vendor: "Oracle"
    version: "2025.1"
    edition: "Standard, Premium, Enterprise, Ultimate"
    deployment: cloud
    api_surface: "REST, SuiteTalk SOAP, SuiteQL, RESTlets"
  - name: "Microsoft Dynamics 365 Finance & Operations"
    vendor: "Microsoft"
    version: "10.0.40+"
    edition: "Finance, Supply Chain Management"
    deployment: cloud
    api_surface: "OData V4, Custom Services, DMF"
  - name: "Workday"
    vendor: "Workday"
    version: "2025R1"
    edition: "HCM, Financials"
    deployment: cloud
    api_surface: "REST, SOAP (WWS), RaaS"
  - name: "Acumatica"
    vendor: "Acumatica"
    version: "2025 R1"
    edition: "General Business, Distribution, Manufacturing"
    deployment: cloud
    api_surface: "REST, OData, SOAP (Screen-Based)"
  - name: "Microsoft Dynamics 365 Business Central"
    vendor: "Microsoft"
    version: "2024 Wave 2 (v25)"
    edition: "Essentials, Premium"
    deployment: cloud
    api_surface: "REST (API pages), OData V4"

# === VERIFICATION ===
last_verified: 2026-03-03
confidence: 0.85
version: 1.0
first_published: 2026-03-03

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: volatile
  last_breaking_change: "SAP 2502 OData V4 currency decimal behavior; D365 10.0.40 extensible enum changes"
  next_review: 2026-08-30
  change_sensitivity: high

# === CONSTRAINTS ===
constraints:
  - "SAP CURR fields store amounts in smallest currency unit (e.g., cents) — divide by 10^TCURX decimals before mapping to other systems"
  - "Salesforce picklist values are strings; D365 enums are integers — bidirectional sync requires a lookup table"
  - "NetSuite datetime fields depend on user timezone preference (defaults to PST/PDT) — always specify timezone explicitly"
  - "Workday does not expose custom fields through standard REST API — requires custom reports (RaaS) or SOAP WWS"
  - "D365 F&O NoYes enum maps to integer 0/1 internally but exposes as 'No'/'Yes' strings via OData — writes may require either form depending on entity"
  - "Oracle ERP Cloud flexfields (DFF/KFF) require segment-level configuration and are not discoverable via REST metadata"
  - "Acumatica custom fields use naming convention 'UsrFieldName' — the 'Usr' prefix is mandatory and case-sensitive"
  - "Business Central API pages only expose fields explicitly added to the page definition — custom fields require publisher extension"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User needs rate limits and API surface comparison, not data type details"
    use_instead: "business/erp-integration/erp-rest-api-comparison/2026"
  - condition: "User needs a single-system API reference"
    use_instead: "business/erp-integration/salesforce-rest-api/2026 (or equivalent single-system card)"
  - condition: "User needs authentication flow comparison"
    use_instead: "business/erp-integration/erp-rest-api-comparison/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: data_type_concern
    question: "Which data type is causing integration issues?"
    type: choice
    options:
      - "dates/datetimes/timezones"
      - "currency amounts and decimal precision"
      - "multi-language/locale fields"
      - "custom fields and extensibility"
      - "picklists/enums/lookup values"
      - "null/empty value handling"
  - key: source_system
    question: "Which ERP is the source system?"
    type: choice
    options:
      - "Salesforce"
      - "SAP S/4HANA"
      - "Oracle ERP Cloud"
      - "NetSuite"
      - "Dynamics 365 F&O"
      - "Workday"
      - "Acumatica"
      - "Business Central"
  - key: target_system
    question: "Which ERP is the target system?"
    type: choice
    options:
      - "Salesforce"
      - "SAP S/4HANA"
      - "Oracle ERP Cloud"
      - "NetSuite"
      - "Dynamics 365 F&O"
      - "Workday"
      - "Acumatica"
      - "Business Central"

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/business/erp-integration/field-mapping-data-type-comparison/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-03-03)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "business/erp-integration/erp-rest-api-comparison/2026"
      label: "ERP REST API comparison (rate limits, auth, bulk patterns)"
  solves:
    - id: "business/erp-integration/salesforce-netsuite-integration/2026"
      label: "Salesforce-NetSuite integration playbook (uses field mapping from this card)"
  alternative_to: []
  often_confused_with:
    - id: "business/erp-integration/erp-rest-api-comparison/2026"
      label: "REST API comparison focuses on rate limits/auth, not data types"

# === SOURCES ===
sources:
  - id: src1
    title: "Valid Date and DateTime Formats — REST API Developer Guide"
    author: Salesforce
    url: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_valid_date_formats.htm
    type: official_docs
    published: 2025-10-15
    reliability: authoritative
  - id: src2
    title: "Currency Fields — ABAP Keyword Documentation"
    author: SAP
    url: https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abenddic_currency_field.htm
    type: official_docs
    published: 2025-01-01
    reliability: authoritative
  - id: src3
    title: "Conversions in SAP Gateway Foundation — Part 3 (Currency Amounts)"
    author: SAP Community
    url: https://blogs.sap.com/2019/08/26/conversions-in-sap-gateway-foundation-part-3-currency-amounts/
    type: technical_blog
    published: 2019-08-26
    reliability: moderate_high
  - id: src4
    title: "NetSuite Applications Suite — Datetime Fields"
    author: Oracle
    url: https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_1521451348.html
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src5
    title: "Service Protection API Limits — Dynamics 365 Finance & Operations"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/service-protection-api-limits
    type: official_docs
    published: 2026-01-20
    reliability: authoritative
  - id: src6
    title: "Write Extensible Enums — Dynamics 365 Finance & Operations"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/extensibility/extensible-enums
    type: official_docs
    published: 2025-08-01
    reliability: authoritative
  - id: src7
    title: "Create Solutions That Support Multiple Languages — Dynamics 365"
    author: Microsoft
    url: https://learn.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/create-solutions-support-multiple-languages
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src8
    title: "NetSuite Applications Suite — Currency Decimal Precision"
    author: Oracle
    url: https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_N1397831.html
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
---

# Field Mapping & Data Type Comparison: How ERPs Handle Dates, Currencies, Multi-Language Fields, and Custom Fields Differently

## TL;DR

- **Bottom line**: Every ERP has different date formats, currency representations, null semantics, and custom field APIs. The #1 cause of silent integration data corruption is assuming two systems use the same conventions when they do not.
- **Key limit**: SAP stores currency amounts in the smallest currency unit (cents) while most others use decimal notation — mapping without conversion creates 100x overcharges or underpayments.
- **Watch out for**: NetSuite datetimes default to PST/PDT (not UTC), Salesforce picklists are strings but D365 enums are integers, and Oracle flexfields are not discoverable via REST metadata.
- **Best for**: Any cross-ERP integration project where you need to build field transformation logic between two or more of these eight platforms.
- **Authentication**: N/A for this card — see the ERP REST API Comparison card for auth flow details.

## System Profile

This card compares how eight major ERP/CRM platforms represent and handle fundamental data types in their APIs. It covers cloud deployments only. The comparison spans: Salesforce, SAP S/4HANA Cloud, Oracle ERP Cloud (Fusion), Oracle NetSuite, Microsoft Dynamics 365 Finance & Operations, Workday, Acumatica, and Microsoft Dynamics 365 Business Central. On-premise variants may differ significantly in their data type handling.

| System | Role | API Surface | Direction Covered |
|---|---|---|---|
| Salesforce (v62.0) | CRM + ERP-adjacent | REST, SOAP, Bulk 2.0 | Read + Write |
| SAP S/4HANA Cloud (2502) | ERP — financials + SCM | OData V2/V4 | Read + Write |
| Oracle ERP Cloud (25A) | ERP — financials + procurement | REST, FBDI | Read + Write |
| Oracle NetSuite (2025.1) | ERP — mid-market | REST, SuiteQL, SOAP | Read + Write |
| D365 F&O (10.0.40+) | ERP — financials + SCM | OData V4, DMF | Read + Write |
| Workday (2025R1) | HCM + Financials | REST, SOAP (WWS) | Read + Write |
| Acumatica (2025 R1) | ERP — mid-market | REST, OData | Read + Write |
| Business Central (v25) | ERP — SMB | REST API pages, OData V4 | Read + Write |

## Quick Reference

### Date/DateTime Format Comparison

| System | Date Format | DateTime Format | Timezone Storage | Timezone in API Response | Timezone on Write |
|---|---|---|---|---|---|
| Salesforce | `YYYY-MM-DD` | `YYYY-MM-DDTHH:mm:ss.SSSZ` | UTC always | UTC (Z suffix) | Must send UTC; system converts | [src1]
| SAP S/4HANA | `YYYY-MM-DD` | `YYYY-MM-DDTHH:mm:ss` | UTC (OData V4) | UTC (no offset in V4, V2 varies) | UTC recommended; some V2 APIs accept offsets | [src2]
| Oracle ERP Cloud | `YYYY-MM-DD` | `YYYY-MM-DDTHH:mm:ss+00:00` | UTC | UTC with explicit offset | UTC or offset-aware | [src4]
| NetSuite | `MM/DD/YYYY` (SOAP) / `YYYY-MM-DD` (REST) | `YYYY-MM-DDTHH:mm:ss.SSS-08:00` | User timezone (PST default) | User's preferred timezone offset | Must specify timezone; defaults to PST/PDT | [src4]
| D365 F&O | `YYYY-MM-DD` | `YYYY-MM-DDTHH:mm:ssZ` | UTC | UTC | UTC | [src5]
| Workday | `YYYY-MM-DD` | `YYYY-MM-DDTHH:mm:ss.SSS-07:00` | Tenant timezone | Tenant timezone offset | Must specify or uses tenant default |
| Acumatica | `YYYY-MM-DD` | `YYYY-MM-DDTHH:mm:ss+00:00` | UTC (REST) | UTC with offset | UTC recommended |
| Business Central | `YYYY-MM-DD` | `YYYY-MM-DDTHH:mm:ssZ` | UTC | UTC | UTC |

### Currency & Amount Handling

| System | Storage Model | Decimal Precision | Currency Identifier | Zero-Decimal Currencies (JPY, KRW) | Multi-Currency |
|---|---|---|---|---|---|
| Salesforce | Decimal (e.g., 100.50) | Up to 18 digits, scale configurable per field | `CurrencyIsoCode` field on record | Stored as decimal (100.00 = 100 JPY) | Multi-currency org setting required | [src1]
| SAP S/4HANA | Smallest unit via CURR type (e.g., 10050 for $100.50) | Defined by TCURX table; default 2 decimals | `TransactionCurrency` / `DocumentCurrency` | TCURX entry with 0 decimals — stored as integer | Parallel currencies (up to 10 ledger currencies) | [src2, src3]
| Oracle ERP Cloud | Decimal (e.g., 100.50) | Configurable precision + extended precision per currency | `InvoiceCurrencyCode` | Precision = 0 in currency setup | Multi-currency per ledger |
| NetSuite | Decimal (e.g., 100.50) | 0 or 2 decimal places per currency | `currency.id` (internalId) or `currency.refName` | Currency Precision = 0 on Currency record | Multi-subsidiary, multi-currency | [src8]
| D365 F&O | Decimal (e.g., 100.50) | Default 2; configurable per currency | `CurrencyCode` (ISO) | Rounding rule = 1 (no decimals) | Multi-company, multi-currency |
| Workday | Decimal (e.g., 100.50) | 2-4 decimals depending on currency | `Currency_Reference` (WID or ISO) | Handled via currency definition | Multi-currency per company |
| Acumatica | Decimal (e.g., 100.50) | Configurable per currency (default 2) | `CuryID` (ISO code) | Decimal places = 0 in currency setup | Multi-currency per company |
| Business Central | Decimal (e.g., 100.50) | Configurable (Amount Rounding Precision) | `currencyCode` (ISO) | Amount Rounding Precision = 1 | Multi-currency per company |

### Custom Field / Extensibility Comparison

| System | Custom Field Mechanism | API Access | Naming Convention | Discovery via API | Max Custom Fields |
|---|---|---|---|---|---|
| Salesforce | Custom Fields on standard/custom objects | Full REST/SOAP access; `__c` suffix | `MyField__c` (custom), `MyObject__c` (custom object) | `describe` endpoint returns all fields | ~800 per object (varies by edition) | [src1]
| SAP S/4HANA | Key User Extensibility (custom fields) | Exposed on OData if published to API | `YY1_FieldName` prefix | `$metadata` endpoint | Varies by object; limited by SAP extensibility |
| Oracle ERP Cloud | Descriptive Flexfields (DFF) + Extensible Flexfields (EFF) | REST access via segment names | `Attribute1`..`AttributeN` (DFF), configurable (EFF) | Not discoverable via REST metadata — requires setup config | Up to 30 DFF segments per context |
| NetSuite | Custom Fields (entity, transaction, item, CRM, etc.) | Full REST/SOAP/SuiteQL access | `custbody_name` (transaction), `custentity_name` (entity) | `GET /metadata-catalog` + field-level metadata | Thousands (no hard limit, performance degrades) | [src4]
| D365 F&O | Extended data types + extensible enums | Exposed on OData data entities if entity includes them | Standard naming; no forced prefix | `$metadata` endpoint | Limited by entity design and ISV extensions | [src6]
| Workday | Calculated Fields + Custom Objects | Custom Reports (RaaS) for reads; limited write via EIB | Custom object naming | Not discoverable via standard REST | Limited by Workday plan |
| Acumatica | User-Defined Fields (customization project) | REST access with `Usr` prefix | `UsrFieldName` (Usr prefix mandatory, case-sensitive) | `$metadata` or `?$expand=CustomFields` | Limited by screen customization |
| Business Central | Table Extensions + Page Extensions (AL) | Only fields added to API page definition | Standard AL naming | `$metadata` on API page | No hard limit (publisher extension required) |

### Picklist / Enum / Lookup Value Handling

| System | Mechanism | API Representation | Restricted Values? | Null/Blank Handling | Multi-Select |
|---|---|---|---|---|---|
| Salesforce | Picklist (standard + custom) | String value (label or API name) | Restricted or unrestricted picklist option | Blank = NULL in database; `--None--` in UI | Multi-Select Picklist: semicolon-delimited string | [src1]
| SAP S/4HANA | Domain Fixed Values / Check Tables | String (domain value) | Hard: check table enforced; Soft: validation optional | Empty string or initial value (type-dependent) | Not natively supported — use multi-row child entity |
| Oracle ERP Cloud | Lookups (Standard + Extensible) | String (lookup code) | Enforced against lookup type | NULL allowed on non-required fields | Not standard — use child table pattern |
| NetSuite | List/Record fields + Custom Lists | String (value) or internalId (numeric) | Enforced against list definition | NULL or empty string (behavior varies by field) | Multi-Select Custom Field: pipe-delimited in SOAP, array in REST |
| D365 F&O | Enum (base + extensible) | Integer internally; String label via OData | Enforced (must match defined enum values) | 0 = first enum value (often "None"); no true NULL for enums | Not natively supported — use bitmap pattern or child table | [src6]
| Workday | Reference ID / Integration ID | WID (Workday ID) or Reference_ID | Enforced against reference type | NULL not accepted for required references | Not standard |
| Acumatica | Selector / Combo Box | String value | Enforced against selector definition | Empty string allowed | Multi-Select: comma-separated string |
| Business Central | Enum (AL) | Integer or String via OData | Enforced | Default value (typically 0 or empty) | Not natively supported |

### Null / Empty Value Handling

| System | Null Semantics | Empty String Behavior | Default Value Logic | Clearing a Field via API |
|---|---|---|---|---|
| Salesforce | Fields can be NULL; API returns `null` | Empty string treated as NULL for most field types | Default values applied on create if field not specified | Send `null` in JSON or `fieldsToNull` in SOAP |
| SAP S/4HANA | Depends on data element; many fields have initial values | Empty string = initial value for CHAR types; 0 for numeric | Initial values set by data dictionary | Send initial value or omit from PATCH payload |
| Oracle ERP Cloud | NULL supported on optional fields | Empty string and NULL treated differently in some contexts | Defaults from setup configuration | Send `null` explicitly in JSON payload |
| NetSuite | NULL supported; API returns `null` | Empty string and NULL are distinct in SuiteScript | Defaults from field configuration | Send `null` or omit field in REST; use `platformCore:nullFieldList` in SOAP |
| D365 F&O | NULL for nullable types; 0 for enums and numerics | Empty string != NULL for string fields | Enum defaults to 0 (first value); numerics to 0 | Send `null` in OData JSON; some fields reject null |
| Workday | NULL not accepted for required fields; optional fields can be absent | Not applicable — Workday uses structured references, not free text | Defaults from business process configuration | Omit field from payload to leave unchanged; no explicit null |
| Acumatica | NULL supported on optional fields | Empty string accepted; some fields treat as NULL | Defaults from GI or automation | Send `null` in REST JSON |
| Business Central | NULL supported on optional fields | Empty string accepted for text fields | Defaults from table definition | Send `null` in JSON; some fields require empty string instead |

### Multi-Language / Locale Field Handling

| System | Translation Mechanism | API Access to Translations | Storage Model | Fallback Behavior |
|---|---|---|---|---|
| Salesforce | Translation Workbench (labels, picklist values) | Describe API returns labels per locale; no per-record multi-language | Single-value fields; translated labels are metadata, not data | Falls back to default org language |
| SAP S/4HANA | Text tables (e.g., `T001T` for company code texts) | OData: `?$expand=to_Text` or language-specific navigation | Separate text table per entity, keyed by language code | Falls back to logon language, then EN |
| Oracle ERP Cloud | Multi-Language Support (MLS) on seeded fields | REST: `?fields=...&lang=xx` or `Accept-Language` header | Separate translation table per MLS-enabled entity | Falls back to base language |
| NetSuite | Multi-Language feature (field-level per supported languages) | SOAP: `language` header; REST: not fully supported | Language-specific columns on record | Falls back to default subsidiary language | [src4]
| D365 F&O | Label translations (metadata); TranslationHelper for data | OData: `Accept-Language` header changes enum labels | Labels: metadata store; Data: custom translation tables | Falls back to system language | [src7]
| Workday | Built-in multi-language for standard fields | REST: `Accept-Language` header | Workday-managed translation store | Falls back to tenant language |
| Acumatica | Locale-specific attributes (limited) | REST: locale setting on authentication | Limited — primarily UI labels, not data fields | Falls back to system locale |
| Business Central | Translation files (.xlf) for labels; data not multi-language | OData: UI labels respond to `Accept-Language` | Labels: app resource files; data fields: single language | Falls back to system language |

### Character Encoding & String Limits

| System | Default Encoding | Max String Field Length | Max Long Text | Unicode Support | Special Character Gotchas |
|---|---|---|---|---|---|
| Salesforce | UTF-8 | 255 (Text), 131,072 (Long Text Area) | 131,072 chars (Long Text Area) | Full Unicode (incl. 4-byte emoji) | SOQL: escape `'` with `\'`; bulk CSV: BOM causes issues |
| SAP S/4HANA | UTF-8 (OData) / various codepages (legacy) | CHAR: 1-1333 bytes; SSTRING: 1-1333 chars | STRING: up to 2GB | Full Unicode (OData); legacy depends on codepage | ABAP string literals: `'` doubled; OData: URL-encode `$filter` values |
| Oracle ERP Cloud | UTF-8 | Varies by field (typically 240 for flexfields) | CLOB-backed fields (no practical limit) | Full Unicode | JSON payload: escape backslashes and quotes |
| NetSuite | UTF-8 | Free-Form Text: 300; Text Area: 4,000 | Long Text: 1,000,000 chars | Full Unicode | SuiteQL: escape `'` with `''`; CSV import: UTF-8 BOM recommended |
| D365 F&O | UTF-16 (internal); UTF-8 (OData) | EDT-defined (typically 20-100) | Memo: 1,073,741,823 chars (theoretical) | Full Unicode | OData `$filter`: single quotes must be doubled `''` |
| Workday | UTF-8 | Varies by field | Rich text fields supported | Full Unicode | XML (SOAP): escape `<>&"'`; REST: standard JSON escaping |
| Acumatica | UTF-8 | Configurable per DAC field (default 256) | Note field: large text support | Full Unicode | REST JSON: standard escaping |
| Business Central | UTF-8 | Text: 250; Code: 20 | BigText (internal): very large | Full Unicode | API JSON: standard escaping; AL filter syntax differs |

## Data Mapping

### Cross-System Date/Time Transformation Rules

| Source System | Target System | Transformation Required | Code Pattern |
|---|---|---|---|
| Salesforce (UTC) | NetSuite (PST default) | Convert UTC to target user's timezone or specify offset explicitly | `new Date(sf_datetime).toLocaleString('en-US', {timeZone: 'America/Los_Angeles'})` |
| SAP (smallest unit) | Salesforce (decimal) | Divide amount by 10^(TCURX decimals); typically 10^2 = 100 | `salesforce_amount = sap_amount / Math.pow(10, tcurx_decimals)` |
| Salesforce (UTC) | D365 F&O (UTC) | No conversion needed — both use UTC | Direct mapping |
| NetSuite (PST) | SAP (UTC) | Convert from PST/PDT offset to UTC | Parse offset from NetSuite datetime string; convert to UTC |
| D365 (integer enum) | Salesforce (string picklist) | Map integer to string label using enum definition | Lookup table: `{0: 'No', 1: 'Yes'}` |
| Salesforce (18-char ID) | NetSuite (externalId) | Store SF 18-char ID as NetSuite externalId for idempotent upsert | Direct string mapping to `externalId` field |
| SAP (text table) | Salesforce (single value) | Select text for target locale from text table, map to single field | Filter `to_Text` expansion by `Language eq 'EN'` |
| Oracle (DFF segment) | D365 (custom field) | Map `Attribute1..N` to named D365 field; requires mapping document | Static mapping per DFF context |

### Data Type Gotchas

- **SAP amounts in smallest currency unit**: SAP CURR data type stores amounts internally in the smallest currency unit (e.g., 10050 for $100.50 USD). The TCURX table defines how many decimal places each currency uses (default 2). OData V4 with SAP Gateway Foundation uses `Edm.Decimal` with variable scale but may still deliver internal representation. Always check the `sap:unit` annotation on amount fields and apply TCURX-based conversion. Forgetting this creates 100x overcharges. [src2, src3]
- **NetSuite timezone trap**: NetSuite stores datetimes in the user's preferred timezone, defaulting to PST (UTC-8) / PDT (UTC-7). API responses include the offset, but if your integration user's timezone preference is wrong, all datetimes are systematically shifted. Set the integration user's timezone to UTC and always parse the offset explicitly. [src4]
- **Salesforce 18-char vs 15-char IDs**: The API always returns 18-character case-insensitive IDs, but formulas, reports, and some SOAP endpoints may reference 15-character case-sensitive IDs. Always store and compare 18-character versions. [src1]
- **D365 enum integer vs string**: D365 F&O uses integer enums internally (`NoYes::Yes = 1`) but exposes them as string labels through OData (`'Yes'`). When writing back, some entities accept the string label, others require the integer. Test each entity's write behavior individually. [src6]
- **Salesforce multi-select picklist delimiter**: Multi-select picklist values are semicolon-delimited in the API (`Value1;Value2;Value3`) but display comma-separated in the UI. Parsing the wrong delimiter silently corrupts data. [src1]
- **Oracle flexfield segment order**: Oracle DFF segments (`Attribute1` through `Attribute30`) have meaning determined entirely by the flexfield configuration in the target instance. `Attribute1` in a dev environment may map to a completely different business field than `Attribute1` in production. Always validate segment mapping per environment.
- **NetSuite currency precision**: NetSuite only supports 0 or 2 decimal places per currency. Currencies requiring 3+ decimal places (e.g., Bahraini Dinar: 3 decimals, or Bitcoin: 8 decimals) cannot be represented natively and require workaround fields. [src8]
- **Workday reference IDs vs WIDs**: Workday objects are identified by both a WID (Workday ID, a UUID) and one or more Reference_IDs (human-readable). The WID is system-generated and immutable; Reference_IDs can be changed by admins. Integrations should store WIDs as the stable identifier but may use Reference_IDs for initial matching.

## Error Handling & Failure Points

### Common Data Type Errors

| Error Scenario | Salesforce | SAP S/4HANA | Oracle ERP Cloud | NetSuite | D365 F&O |
|---|---|---|---|---|---|
| Invalid date format | `MALFORMED_ID` or `INVALID_TYPE_ON_FIELD_IN_RECORD` | OData parse error (400) | `JBO-27024: Bad date format` | `INVALID_DATE_FORMAT` | OData 400 validation error |
| Currency mismatch | `FIELD_INTEGRITY_EXCEPTION` | `CX_BAPI_ERROR` (currency not in TCURC) | `INVALID_CURRENCY` | `INVALID_CURRENCY` | Validation error on CurrencyCode |
| Picklist/enum invalid value | `INVALID_OR_NULL_FOR_RESTRICTED_PICKLIST` | Check table violation (400) | Lookup validation failure | `SSS_INVALID_TYPE_ARG` | `Enum value out of range` |
| Null on required field | `REQUIRED_FIELD_MISSING` | OData `mandatory field missing` | `JBO-27014: Attribute required` | `SSS_MISSING_REQD_ARGUMENT` | OData required field error |
| String too long | `STRING_TOO_LONG` | OData payload error (truncation in some cases) | ORA-12899 (DB-level) | `FIELD_VALIDATION` | `Value exceeds maximum length` |
| Character encoding | Bulk CSV: `INVALID_CONTENT_TYPE` if BOM present | Codepage conversion error (legacy) | Unicode decode error on REST | SuiteQL parse error on unescaped quotes | OData parse error |

### Failure Points in Production

- **SAP currency conversion drift**: An integration maps SAP amounts directly to Salesforce without TCURX conversion. Works fine for USD/EUR (2 decimals) but JPY amounts are 100x too large because JPY has 0 decimals in TCURX. Fix: `always read TCURX for the transaction currency; never assume 2 decimal places`. [src2, src3]
- **NetSuite timezone shift on DST transition**: Integration user is set to PST. During March DST transition, datetimes shift by 1 hour. Scheduled integrations running near 2:00 AM PST may process duplicate or missing records. Fix: `set integration user timezone to UTC; parse all datetime fields with explicit offset handling`. [src4]
- **D365 enum write failures on extensible enums**: An integration writes a valid string label for an extensible enum, but the entity rejects it because the underlying X++ code expects the integer value. Fix: `test both integer and string forms for each enum field; prefer the integer form for writes to extensible enums`. [src6]
- **Oracle DFF segment mismatch across environments**: DFF segments (Attribute1-30) are mapped differently in dev vs production. Integration tested in dev passes validation but writes data to the wrong business fields in prod. Fix: `export DFF segment mapping from both environments and diff; include segment mapping in deployment checklist`.
- **Salesforce multi-select picklist corruption**: Integration writes `Value1,Value2` (comma-delimited) instead of `Value1;Value2` (semicolon-delimited). Salesforce stores the entire comma-delimited string as a single value. No error is thrown. Fix: `always use semicolon as delimiter for multi-select picklist API writes; validate against describe metadata before insert`. [src1]

## Anti-Patterns

### Wrong: Assuming all ERPs use UTC for datetime fields

```javascript
// BAD — Treating NetSuite datetime as UTC
const order_date = new Date(netsuite_record.tranDate); // "2026-03-15T00:00:00.000-08:00"
const utc_date = order_date.toISOString(); // Wrong: treats PST offset as UTC
// Sends to Salesforce as 2026-03-15T08:00:00Z — 8 hours off!
```

### Correct: Parse timezone offset from each system's format

```javascript
// GOOD — Respect the timezone offset in NetSuite's response
const ns_datetime = "2026-03-15T00:00:00.000-08:00";
const parsed = new Date(ns_datetime); // JavaScript Date correctly handles offset
const utc_iso = parsed.toISOString(); // "2026-03-15T08:00:00.000Z" — correct UTC
// For Salesforce, send the UTC value directly
```

### Wrong: Mapping SAP amounts directly without TCURX conversion

```python
# BAD — Direct mapping of SAP amount to Salesforce
salesforce_amount = sap_record['NetAmount']  # 10050 (SAP smallest unit for USD)
# Salesforce receives 10050.00 instead of 100.50 — 100x overcharge!
```

### Correct: Apply TCURX-based currency conversion

```python
# GOOD — Convert SAP smallest-unit amount using TCURX decimal places
TCURX_DECIMALS = {'USD': 2, 'EUR': 2, 'JPY': 0, 'KWD': 3, 'BHD': 3}
currency = sap_record['TransactionCurrency']
decimals = TCURX_DECIMALS.get(currency, 2)  # Default 2 if not in lookup
salesforce_amount = sap_record['NetAmount'] / (10 ** decimals)
# USD: 10050 / 100 = 100.50 (correct)
# JPY: 10050 / 1 = 10050 (correct — no decimals for yen)
```

### Wrong: Writing D365 enums as integers without checking entity behavior

```javascript
// BAD — Assuming all D365 entities accept integer enum values
const payload = { "IsActive": 1 }; // NoYes.Yes = 1
// Some OData entities reject this and require the string form
```

### Correct: Use the OData string label format for D365 enum writes

```javascript
// GOOD — Use the Microsoft.Dynamics.DataEntities namespace for enum values
const payload = { "IsActive": "Yes" }; // OData exposes as string
// For entities that require the full qualified form:
// "IsActive": "Microsoft.Dynamics.DataEntities.NoYes'Yes'"
// Test each entity individually — behavior is not consistent
```

## Common Pitfalls

- **Salesforce picklist API name vs label**: The API returns the picklist value's API name, not the display label. If integration logic compares against the human-readable label (e.g., "Closed - Won" vs API name "Closed_Won"), matching silently fails. Fix: `use describe metadata to build a label-to-API-name map; always compare using API names`. [src1]
- **SAP OData V2 vs V4 date format difference**: OData V2 represents dates as `datetime'2026-03-03T00:00:00'` (Edm.DateTime with quotes), while V4 uses `2026-03-03` (Edm.Date) or `2026-03-03T00:00:00Z` (Edm.DateTimeOffset). Mixing formats between V2 and V4 endpoints causes parse errors. Fix: `check each API endpoint's OData version on api.sap.com; use the correct format per version`. [src2, src3]
- **NetSuite internalId vs externalId in lookups**: Setting a currency field by name (`{"currency": "USD"}`) fails — NetSuite requires the internalId or a reference object (`{"currency": {"id": "1"}}`). Fix: `always use internalId or externalId references for lookup fields; cache ID mappings to avoid extra API calls`. [src4]
- **D365 F&O date-effective entities**: Some D365 entities use date-effective patterns where records have `ValidFrom` and `ValidTo` fields. Updating these entities requires understanding the effective dating model — simple PATCH may create a new effective record instead of updating the current one. Fix: `check entity metadata for date-effective fields; use the proper update pattern for temporal entities`. [src5]
- **Oracle ERP Cloud flexfield context sensitivity**: Oracle DFF (Descriptive Flexfield) segments are context-sensitive — `Attribute1` maps to different business fields depending on the context value. Writing `Attribute1` without setting the correct context first puts data in the wrong segment. Fix: `always set the context segment before populating attribute segments; validate context values against the setup`.
- **Workday Reference_ID instability**: Workday Reference_IDs can be changed by administrators, breaking integrations that use them as stable keys. Fix: `store the WID (Workday ID) as the immutable key; use Reference_ID only for initial matching or human-readable display`.
- **Acumatica Usr prefix case sensitivity**: Acumatica custom fields must use the `Usr` prefix exactly (capital U, lowercase sr). `USR`, `usr`, or `User` prefixes are rejected silently or cause field-not-found errors. Fix: `always use exactly 'Usr' prefix for custom field names in API payloads`.

## Cross-System Comparison

| Capability | Salesforce | SAP S/4HANA | Oracle ERP Cloud | NetSuite | D365 F&O | Workday | Acumatica | Business Central |
|---|---|---|---|---|---|---|---|---|
| **Date format** | ISO 8601 UTC | ISO 8601 UTC (V4) | ISO 8601 w/ offset | ISO 8601 w/ PST offset | ISO 8601 UTC | ISO 8601 w/ offset | ISO 8601 UTC | ISO 8601 UTC |
| **Currency storage** | Decimal | Smallest unit (CURR) | Decimal | Decimal | Decimal | Decimal | Decimal | Decimal |
| **Currency precision** | Configurable | TCURX table | Configurable | 0 or 2 only | Configurable | Per currency | Configurable | Configurable |
| **Custom field access** | Full (describe API) | If published to OData | Limited (DFF segments) | Full (metadata API) | Via data entity | Limited (RaaS) | Full (REST) | Publisher ext required |
| **Custom field naming** | `__c` suffix | `YY1_` prefix | `Attribute1..30` | `cust*_name` pattern | No forced prefix | N/A | `Usr` prefix | No forced prefix |
| **Enum/picklist type** | String | String (domain) | String (lookup) | String or internalId | Integer (internal) | Reference ID | String | Integer (enum) |
| **Multi-select** | Semicolon-delimited | Child entity rows | Child table | Pipe-delimited (SOAP) | Bitmap or child | Not standard | Comma-separated | Not standard |
| **Multi-language data** | Translation Workbench (metadata only) | Text tables per entity | MLS (Accept-Language) | Field-level per language | Custom translation tables | Built-in | Limited | Label files only |
| **Null handling** | NULL + fieldsToNull | Initial value per type | NULL explicit | NULL + nullFieldList | NULL or 0 for enums | Omit = unchanged | NULL explicit | NULL or empty string |
| **Unicode support** | Full (UTF-8) | Full (UTF-8 via OData) | Full (UTF-8) | Full (UTF-8) | Full (UTF-16 internal) | Full (UTF-8) | Full (UTF-8) | Full (UTF-8) |
| **ID format** | 18-char alphanum | 10-digit numeric (BP) | Numeric (PartyId) | Numeric (internalId) | Alphanumeric (varies) | UUID (WID) | Integer | UUID (systemId) |
| **Boolean type** | true/false | true/false (OData) | true/false | true/false or T/F | NoYes enum (0/1) | true/false | true/false | true/false |

## Integration Pattern Decision Tree

```
START — Data type mismatch between Source ERP and Target ERP
├── Date/DateTime issue?
│   ├── Source and target both UTC?
│   │   ├── YES → Direct mapping (no conversion needed)
│   │   └── NO (one system uses local timezone) ↓
│   ├── Is NetSuite involved?
│   │   ├── YES → Convert from user-timezone offset to UTC first
│   │   └── NO → Parse offset from source; convert to target format
│   └── Date-only field?
│       ├── YES → Use YYYY-MM-DD; strip time component
│       └── NO → Preserve full datetime with timezone conversion
├── Currency amount issue?
│   ├── Is SAP the source?
│   │   ├── YES → Lookup TCURX decimals; divide by 10^decimals
│   │   └── NO → Direct decimal mapping (most systems use decimal)
│   ├── Zero-decimal currency (JPY, KRW)?
│   │   ├── YES → Verify both systems handle 0-decimal correctly
│   │   └── NO → Standard decimal mapping
│   └── Multi-currency?
│       └── YES → Map currency code alongside amount; validate code exists in target
├── Picklist/Enum mismatch?
│   ├── Source is string, target is integer (D365)?
│   │   ├── YES → Build lookup table: string label → integer value
│   │   └── NO → Direct string mapping (validate values exist)
│   └── Multi-select?
│       └── YES → Check delimiter: SF=semicolon, NS=pipe, Acumatica=comma
├── Custom field issue?
│   ├── Target is Oracle ERP Cloud?
│   │   ├── YES → Map to DFF segments; set context first
│   │   └── NO ↓
│   ├── Target is Acumatica?
│   │   ├── YES → Use 'Usr' prefix exactly
│   │   └── NO ↓
│   └── Target is Business Central?
│       ├── YES → Verify field is on API page definition
│       └── NO → Use standard custom field API (SF __c, NS cust*, SAP YY1_)
└── Null handling issue?
    ├── Source sends NULL, target doesn't accept NULL?
    │   ├── YES → Map to default value (0 for numeric, "" for string, first enum for D365)
    │   └── NO → Pass NULL through
    └── Need to clear a field in target?
        ├── Salesforce → Send null in JSON or use fieldsToNull in SOAP
        ├── NetSuite → Send null (REST) or use nullFieldList (SOAP)
        ├── D365 → Send null (check if field is nullable first)
        └── Others → Send null explicitly in payload
```

## Diagnostic Commands

```bash
# === SALESFORCE: Inspect field metadata (types, picklist values, lengths) ===
curl "https://{instance}.my.salesforce.com/services/data/v62.0/sobjects/Account/describe" \
  -H "Authorization: Bearer ${SF_TOKEN}" | jq '.fields[] | {name, type, length, precision, scale, picklistValues}'

# === SAP S/4HANA: Check OData $metadata for field types and annotations ===
curl "https://{tenant}.s4hana.cloud.sap/sap/opu/odata4/sap/api_business_partner/srvd_a2x/sap/businesspartner/0001/\$metadata" \
  -H "Authorization: Bearer ${SAP_TOKEN}"
# Look for: Edm.Decimal (amounts), Edm.DateTimeOffset (datetimes), sap:unit annotation (currency ref)

# === ORACLE ERP CLOUD: Check available fields on an entity ===
curl "https://{instance}.fa.{dc}.oraclecloud.com/fscmRestApi/resources/latest/invoices/describe" \
  -H "Authorization: Bearer ${ORACLE_TOKEN}" | jq '.Resources[].attributes[] | {name, type}'

# === NETSUITE: Inspect record metadata (field types, custom fields) ===
curl "https://{account_id}.suitetalk.api.netsuite.com/services/rest/record/v1/metadata-catalog/customer" \
  -H "Authorization: OAuth ..." | jq '.properties | to_entries[] | {name: .key, type: .value.type}'

# === DYNAMICS 365 F&O: Check entity metadata for enums and field types ===
curl "https://{env}.operations.dynamics.com/data/\$metadata" \
  -H "Authorization: Bearer ${D365_TOKEN}" -o metadata.xml
# Parse XML for EnumType definitions and Property types

# === CHECK CURRENCY PRECISION: Validate decimal handling before go-live ===
# Test with JPY (0 decimals), USD (2 decimals), BHD (3 decimals)
# Create a test record with amount 12345 in each currency
# Verify the stored value matches expectations in both source and target
```

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Building field-level transformation logic between two or more ERPs | You need rate limits, auth flows, or API surface comparison | business/erp-integration/erp-rest-api-comparison/2026 |
| Debugging data type mismatch errors in an existing integration | You need a complete integration playbook for a specific pair | business/erp-integration/salesforce-netsuite-integration/2026 |
| Designing a canonical data model for a multi-ERP integration hub | You need middleware/iPaaS selection guidance | business/erp-integration/ipaas-comparison/2026 |
| Validating currency handling before go-live on a new integration | You need a single-system deep-dive | Single-system API capability cards |
| Training integration developers on cross-platform data type differences | You need ERP selection guidance | business/erp-selection/* |

## Important Caveats

- **Version-dependent behavior**: Data type handling can change between releases. SAP OData V2 and V4 handle dates and currency amounts differently. Always verify against your specific system version.
- **Edition-specific custom field limits**: Salesforce Professional Edition cannot create certain custom field types. NetSuite Standard tier has fewer custom field options. Verify field type availability for your specific edition.
- **Sandbox configuration drift**: Custom fields, flexfield segments, enum extensions, and translation configurations may differ between sandbox and production environments. Always validate field mapping against the target production environment before go-live.
- **This card covers cloud deployments only**: On-premise variants (SAP ECC, Oracle E-Business Suite, Dynamics AX/GP) may have different data type representations, character encodings, and custom field mechanisms.
- **Currency handling is the highest-risk area**: SAP's smallest-unit currency storage is the single most common source of financial data corruption in cross-ERP integrations. Always implement and test TCURX conversion for any SAP integration.

## Related Units

- [ERP REST API Comparison (rate limits, auth, bulk patterns)](/business/erp-integration/erp-rest-api-comparison/2026)
- [Salesforce-NetSuite Integration Playbook](/business/erp-integration/salesforce-netsuite-integration/2026)
