Like other have said, it's a bad idea to have business logic in a trigger, so your trigger should be something simpler like below:
trigger OpportunityTrigger on Opportunity (before update) {
if (trigger.isBefore) {
if (trigger.isUpdate) {
OpportunityTriggerHandler.isBeforeUpate(trigger.new);
}
}
}
The actual business logic can be executed in a trigger handler, which you can also see below.
public OpportunityTriggerHandler {
public static void isBeforeUpdate(List<Opportunity> opps) {
validateBusinessNeed(opps);
}
public static void validateBusinessNeed(List<Opportunity> opps) {
List<Id> oppIds = new List<Id>();
List<Date> productDates = new List<Date>();
for (Opportunity o : opps) {
oppIds.add(o.Id);
productDates.add(o.Product_Date__c);
}
List<Opportunity> openNonProfitOpps = [SELECT
Id
FROM Opportunity
WHERE Business_Need__c = 'Non-Profit'
AND StageName = 'Open'
AND Product_Date__c IN :productDates
AND Id IN :oppIds];
if (openNonProfitOpps.size() > 0) {
o.addError('Error message goes here');
}
}
}
It is a very bad idea to put query inside of loops. It's a very easy way to see a "Too many SOQL queries" error, so you should typically do something like I've done above. In this case, since it seems like you only care that there are any open non-profit opps, a list is fine, but you could also use the query to create a map. You could then iterate over the opps passed from the trigger handler and compare them with the ones in the map created by the SOQL query.