The structure itself is fine, if you need to use it, but make sure you've commented it sufficiently. Sometimes the only way to get the performance you need is to build relatively massive structures.
However, your code is overly complex, and would greatly benefit from a few changes:
Don't Use Large Getters
This is terribly hard to read and will likely trip up developers of any caliber, even someone like me that's been writing code since computers were measured in kilobytes. Instead, use a static initialization block to initialize the list when you need it.
Don't Nest If-Else If Avoidable
This code is incredibly hard to read, and furthermore, you're duplicating logic repeatedly. Instead, initialize each part of the map at a time instead of jumping around.
Use Static Initializers
Getters/setters are great for enforcing validation logic, etc, but you really shouldn't use getters like this. Instead, use a static initializer, which guarantees that your data will be available before your code attempts to access it. Also, your branching logic will only be called once, which can save a ton of processing time if called many times.
Use Inline Queries and Sub-Queries
You'll save memory, and internally it produces better query performance, so long as the tables are small enough to justify it.
I took the liberty of translating your code, and it looks correct. Take a look at how much easier it is to read:
static {
initializeList();
}
static void initializeList() {
pb2c2p2PBE = new Map<Id, Map<String, Map<Id, PricebookEntry>>>();
for (PricebookEntry pbEntry : [SELECT Id, CurrencyIsoCode, Pricebook2Id, Product2Id FROM PricebookEntry
WHERE Product2Id in (SELECT Subscription_Part__c from Legacy_Mapping__c) and
IsActive = true]) {
if (!pb2c2p2PBE.containsKey(pbEntry.Pricebook2Id)) {
pb2c2p2PBE.put(pbEntry.Pricebook2Id, new Map<String, Map<Id, PricebookEntry>>());
}
if(!pb2c2p2PBE.get(pbEntry.Pricebook2Id).containsKey(pbEntry.CurrencyIsoCode)) {
pb2c2p2PBE.get(pbEntry.Pricebook2Id).put(pbEntry.CurrencyIsoCode, new Map<Id, PricebookEntry>());
}
pb2c2p2PBE.get(pbEntry.Pricebook2Id).get(pbEntry.CurrencyIsoCode).put(pbEntry.Product2Id,pbEntry);
}
}
public static Map<Id, Map<String, Map<Id, PricebookEntry>>> pb2c2p2PBE { get; private set; }
You will want to add comments, etc, but hopefully this will show you how to properly initialize a nested list from data.
Edit: Removed the "array" part of the PricebookEntry; the platform guarantees that for any given currency, pricebook, and product, there will be exactly one entry. You could consider this a composite key.
Edit (2): Removed a variable that you're only using for just a simple loop afterwards. This also saves memory/heap because the query isn't held in memory the entire function, and is furthermore paged using queryMore, an internal efficiency.
Edit (3): Removed the entire set, because really what you want is a sub-query inside the main query. This gets to the heart of the matter. After that, we can move the entire query inside the loop for another boost on memory.
I believe the issue is (and it caught me for some time before) is that with SeeAllData=false
, the Standard Pricebook doesn't actually exist but its ID
does.
You need to change your query to:
List<PricebookEntry> pbes = [SELECT ID FROM PricebookEntry
WHERE Product2id IN :new List<ID> {p1.id,p2.id} AND
Pricebook2Id = :Test.getStandardPricebookId() AND
CurrencyIsoCode = 'USD' AND isActive = TRUE];
Of course, since you are using SeeAllData=false
, simply doing:
System.assertEquals(2,[select COUNT() from PricebookEntry]);
will verify that you successfully inserted your PBE
Best Answer
Ok, I am to lazy to wait for answer ;/
It will work, but before adding product to custom PriceBook it must have Standard Price. In other words it must be added to Standard PriceBook.