unflagged.destination
unflagged.destination

Reputation: 1566

Update Parent Object Field in Apex Trigger

I have three objects 1)Truck__c 2)Booking__C 3)Payment___C. Truck and Booking has master detail relationship where Truckk is master and Booking is detail. Booking and payment has lookup relationship where Booking is parent and payment is child.

I want to update a field present isBooked(checkbox) in Truck based on the value present in a field remainingAmount in payment object. I have added trigger on Payment object like below

trigger TestTrigger on Payment__c (after insert) {
 if(Trigger.isAfter && Trigger.isUpdate){
    for (Payment__c pay:Trigger.New)
    {
        payId.add(pay.id);
        paidAmount =Integer.valueOf(pay.Paid_Amount__c);
    }
    Map <id,Booking__c> bookingMap = new Map <id,Booking__c> ();
    for (Booking__c obj: [Select Id, Booking_ID__c from Booking__c where Booking_ID__c in:payId])
    {
        bookingMap.put(obj.Booking_ID__c, obj);
    }
    
    for(Booking__c objBooking:matchingIdsMap){
     Truck__c truckObj = [Select id,Price__c from Truck__c where Truck__c.id = objBooking.Truck__c.id];
     //Integer paidAmount = Payment__c.Paid_Amount__c;
     Integer totalAmount = Integer.valueOf(truckObj.Price__c);
     Integer remainingAmount = totalAmount-paidAmount;
        If(remainingAmount == 0){
            truckObj.Booked__c = true
        }
     update truckObj;
 } 
}

}

Here first I am getting payment ID's and based on that I am fetching Booking objects which is lookup parent of payment. After this I am trying to fetch the truck object which is master of Booking. But I don't know how to query on this as where clause in query giving error

Truck__c truckObj = [Select id,Price__c from Truck__c where Truck__c.id = objBooking.Truck__c.id];

Please note there is no direct relationship between Truck and Payment

How can I fetch truck object Thanks in Advance

Upvotes: 0

Views: 4093

Answers (1)

RubenDG
RubenDG

Reputation: 1405

Short answer: while referring to parent fields, you should use the relationship name, so Truck__r instead of Truck__c.
Anyway it is not the only problem with that code.


Long answer:

  • your trigger is in after insert, but you check for an after update event: if(Trigger.isAfter && Trigger.isUpdate). Probably you want to run this trigger in both after insert and after update.
  • You never declared payId nor paidAmount which you use in your first for-loop. Anyway paidAmount would just hold the last value, which probably you won't need.
  • [Select Id, Booking_ID__c from Booking__c where Booking_ID__c in:payId] this query should return an empty list because in payId you've stored the ids of Payment__c, that is a child of Booking__c, while in the first loop you should have stored the ids of parents Booking__c
  • [Select id,Price__c from Truck__c where Truck__c.id = objBooking.Truck__c.id] Here there is no reason to write where Truck__c.id It should be just WHERE Id = :objBooking.Truck__c.
  • Beware: putting a SOQL in a loop you will easily hit the Governor Limit about SOQL, which will raise a System.LimitException: Too many SOQL queries: 101. The same goes by putting a DML in a loop.

I'm going to assume that the API Name of the lookup fields is the same of the parent object, so that a Booking__c field exists on Payment__c object and a Truck__c exists on Booking__c object.
If I got the logic right about how setting the flag on Truck object, this should be the trigger code.

trigger TestTrigger on Payment__c (after insert, after update) {
    if(Trigger.isAfter && (Trigger.isInsert || Trigger.isUpdate)) {
        Map<Id, List<Payment__c>> mapBookingIdPaymentList = new Map<Id, List<Payment__c>>();
        for (Payment__c pay : Trigger.New) {
            List<Payment__c> paymentList = mapBookingIdPaymentList.get(pay.Booking__c);
            if (paymentList == null) {
                paymentList = new List<Payment__c>();
                mapBookingIdPaymentList.put(pay.Booking__c, paymentList);
            }
            paymentList.add(pay);
        }
        
        Map<Id, Decimal> mapTruckPrice = new Map<Id, Decimal>();
        Map<Id, Integer> mapTruckRemainingAmount = new Map<Id, Integer>();
        for (Booking__c booking: [SELECT Id, Truck__c, Truck__r.Price__c FROM Booking__c WHERE Id IN :mapBookingIdPaymentList.keySet()]) {
            mapTruckPrice.put(booking.Truck__c, booking.Truck__r.Price__c);
            
            Integer sumOfRemainingAmount = mapTruckRemainingAmount.containsKey(booking.Truck__c) ? mapTruckRemainingAmount.get(booking.Truck__c) : 0;
            for (Payment__c pay : mapBookingIdPaymentList.get(booking.Id)) {
                sumOfRemainingAmount += pay.Paid_Amount__c != null ? pay.Paid_Amount__c.intValue() : 0;
            }
            mapTruckRemainingAmount.put(booking.Truck__c, sumOfRemainingAmount);
        }

        List<Truck__c> trucksToUpdate = new List<Truck__c>();
        for (Id truckId : mapTruckPrice.keySet()) {
            // There is no need to query a record just to update it if you already have its Id.
            Truck__c truck = new Truck__c(Id = truckId);
            truck.Booked__c = mapTruckPrice.get(truckId) - mapTruckRemainingAmount.get(truckId) == 0;
            trucksToUpdate.add(truck);
        }
        update trucksToUpdate; // dml outside the loop
    }
}

By the way, you should move the logic in an handler class, following the best practices.

Upvotes: 1

Related Questions