Multi-currency test data generation fails the moment a generator treats CurrencyIsoCode as just another picklist. It isn't. It drives field-level formatting, roll-up summary math, and report totals across every currency-enabled object in the org. Get it wrong and you end up with Opportunity amounts that look fine in isolation but produce nonsense when a forecast rolls up across USD, EUR, and JPY records in the same pipeline.
Most test data tools stop at "insert a valid ISO code." That clears validation but doesn't test anything real. A QA team running regression on a multi-currency org needs amounts that respect each currency's decimal precision, exchange rates that match the corporate or dated rate table, and enough currency variety in the sample set to actually exercise conversion logic. Anything less is a green checkmark hiding a blind spot.
Why generic generators produce currency-blind data
A field-mapping tool that reads the schema sees CurrencyIsoCode as a text or picklist field with a set of valid values. It picks one at random, or worse, hardcodes the org's default currency for every record. That satisfies the field's validation rule but ignores the business logic sitting on top of it.
Here's the practical failure: a sandbox loaded with 50,000 Opportunities, all tagged USD, gives you zero coverage on currency conversion, zero coverage on multi-currency roll-up summaries, and zero coverage on report filters that segment by ISO code. The load succeeds. The test plan doesn't.
SproutEzee reads the org's active currency configuration through the schema and Tooling API before generating a single record. It knows which currencies are active, which one is the corporate default, and whether the org runs Advanced Currency Management with dated exchange rates. That context shapes every amount it writes, not just the code on the record.
CurrencyIsoCode isn't a free-text field, so stop treating it like one
CurrencyIsoCode only accepts values the org admin has activated in Setup under Company Information. An org with USD, GBP, and INR active will reject a record tagged CAD, even though CAD is a perfectly normal currency in the real world. Generic Bulk API v2 loads that don't check this table first will throw INVALID_CURRENCY_ISO_CODE errors mid-batch, and those errors compound quickly at volume.
SproutEzee pulls the active currency list before generation starts and distributes records across it in a ratio the admin controls. Want 60% USD, 25% EUR, 15% JPY to mirror production's regional split? That's a generation parameter, not a manual cleanup task after the fact.
Distribution matters more than most teams assume. A sales rep-assignment rule that routes deals by region, or a validation rule that only fires on non-USD Opportunities, needs a realistic mix to get exercised at all. Ten thousand records that are 99% USD and 1% everything else won't trip either scenario reliably.
Decimal precision and rounding rules differ by currency
JPY doesn't use decimal places. USD uses two. Some currencies configured with custom decimal places in Salesforce's currency setup can use three or more. A generator that writes 1500.50 to a JPY-tagged Amount field isn't wrong in a way Salesforce will catch on insert, but it's wrong in a way that will confuse every downstream report and every finance stakeholder reviewing the sandbox.
This is where synthetic data generation earns its keep or falls flat. SproutEzee reads the DecimalPlaces setting from the currency type record and rounds generated Currency field values accordingly, per record, per currency. A JPY Opportunity gets a whole-number amount. A three-decimal currency gets three decimals. It's a small detail, and it's exactly the kind of small detail that turns a demo-quality sandbox into a QA-quality one.
| Currency | Typical Decimal Places | Generation Behavior |
|---|---|---|
| USD | 2 | Standard rounding to cents |
| JPY | 0 | Whole-number amounts only |
| KWD | 3 | Three-decimal precision preserved |
| EUR | 2 | Standard rounding, locale-aware formatting |
Dated exchange rates and Advanced Currency Management
Orgs running Advanced Currency Management (ACM) don't use a single static conversion rate. They use dated exchange rate ranges tied to Opportunity CloseDate or other date fields. A generator that ignores this writes a CloseDate of March 2024 with an exchange rate that was only valid starting June 2024, and the converted amount shown in reports won't match what a hand calculation produces.
This mismatch is subtle. It won't throw an error. It will just quietly produce wrong numbers that someone in finance QA eventually notices and then spends a half-day chasing. I'd rather spend the setup time upfront than get that Slack message later.
SproutEzee cross-references DatedConversionRate records against the generated CloseDate (or equivalent date field) before writing the Amount value, so the converted figure lines up with what ACM would actually calculate. For orgs without ACM, it falls back to the standard CurrencyType conversion rate table. Either way, the amount a report shows in the corporate currency is arithmetically consistent with the rate that was active on that record's date.
Bulk API v2 batching across multiple currencies
Bulk API v2 loads don't care what currency a record is tagged with, but the volume math still matters when you're generating a realistic multi-currency spread. A 200,000-record Opportunity load split across six currencies means six different rounding rules, six different rate lookups, and one job that still needs to land inside Salesforce's governor limits without duplicate-key or lock contention errors.
SproutEzee batches by currency internally before submitting to Bulk API v2, which keeps rate-lookup logic isolated per batch and simplifies error handling if one currency's dated rate table has a gap. If a batch fails, you know immediately which currency segment needs attention instead of combing through a mixed-currency error CSV line by line.
This also matters for load testing scenarios where multi-currency orgs need to simulate realistic transaction volume per region. A batch-per-currency approach lets teams throttle or prioritize specific currency segments without re-architecting the whole data generation job.
Testing roll-up summaries and currency conversion logic directly
The real payoff of currency-aware test data shows up in roll-up summary fields and any custom conversion formula built on top of them. An Account with child Opportunities in three different currencies needs a roll-up that correctly converts each child amount to the corporate currency before summing, and that's exactly the calculation most teams never get to test because their sandbox data is single-currency by default.
Once SproutEzee has generated a proper multi-currency spread with correct dated rates, that roll-up either matches expected math or it doesn't. There's no ambiguity introduced by the test data itself. Any discrepancy found at that point is a real bug in the formula or roll-up configuration, not an artifact of sloppy sample data, and that distinction saves debugging time nobody has to spare.
Reports segmented by CurrencyIsoCode, dashboards filtered by region, and forecast categories that blend currencies all get the same benefit. Test them against data that actually varies by currency, and the results tell you something. Test them against 10,000 identical USD records, and you've validated nothing you didn't already know.
Frequently Asked Questions
Does SproutEzee support Advanced Currency Management dated exchange rates?
Yes. SproutEzee reads DatedConversionRate records from the org and matches them against the date field driving conversion, typically CloseDate on Opportunities, before writing generated Amount values. This keeps converted totals arithmetically consistent with what ACM would calculate in production. Orgs without ACM enabled fall back to the standard CurrencyType conversion table automatically.
Can I control the distribution of currencies in generated test data?
Yes. Admins set a percentage distribution across the org's active currencies, and SproutEzee generates records matching that ratio. This lets teams mirror production's regional currency split rather than defaulting every record to the org's corporate currency. It also ensures currency-dependent rules and validation logic actually get exercised in testing.
What happens if I try to generate data for a currency that isn't active in the org?
SproutEzee reads the org's active currency list from Company Information before generation starts and only assigns ISO codes from that list. This prevents INVALID_CURRENCY_ISO_CODE errors during Bulk API v2 loads, which would otherwise interrupt batches and require manual cleanup. Inactive currencies are simply excluded from the generation pool.
Does decimal precision differ between currencies in generated records?
Yes, and this is one of the more overlooked details in currency-aware testing. SproutEzee reads the DecimalPlaces setting on each active CurrencyType record and rounds generated amounts accordingly, so JPY records get whole numbers while a three-decimal currency like KWD retains that precision. This keeps generated data consistent with how Salesforce actually formats and calculates each currency.
How does multi-currency test data affect roll-up summary testing?
Roll-up summaries on parent records with multi-currency children require correct currency conversion before summing, and that logic can't be validated with single-currency sample data. SproutEzee generates properly distributed, correctly rated multi-currency records so any discrepancy in the roll-up total reflects a real configuration issue rather than a data artifact. This turns roll-up testing into a reliable diagnostic instead of a guessing exercise.