This is a great example of why so-called recursion protection like this:
public class MyTriggerHandler {
static Boolean hasAlreadyRun = false;
}
trigger MyTrigger on MyObject__c (after update) {
if (!MyTriggerHandler.hasAlreadyRun) {
// Do stuff
MyTriggerHandler.hasAlreadyRun = true;
}
}
is a terrible pattern.
Without seeing more of your code, you probably have two options.
The first is to fix the recursion. Fold your Process Builder and your Workflow Rule into the code of your trigger handler. Honestly, I would do this anyway - if you're doing bulk loads on this data you don't need to have extra trigger invocations taking up transaction time anyway.
The second is to use a static collection-based recursion guard, something like this:
public class MyTriggerHandler {
static Set<Id> recordsAlreadyProcessed = new Set<Id>();
public static void afterUpdate(List<MyObject__c> newList, Map<Id, MyObject__c> oldMap) {
List<MyObject__c> toProcess = new List<MyObject__c>();
for (MyObject__c m : newList) {
if (!recordsAlreadyProcessed.contains(m.Id)) {
recordsAlreadyProcessed.add(m.Id);
toProcess.add(m);
}
}
// Now process `toProcess` only.
}
trigger MyTrigger on MyObject__c (after update) {
MyTriggerHandler.afterUpdate(Trigger.new, Trigger.oldMap);
}
Note however that this isn't necessarily a copy-and-paste solution. If your trigger should reprocess records that undergo specific changes from workflow rules, or even depend upon those updates, this pattern will break that. You need to carefully evaluate how your trigger and your declarative automations interact before you add any kind of suppression logic, and be aware that using this pattern does have implications for any future implementations of declarative automation you may do.