Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase Cloud function triggers twice onUpdate

I have below rules set for my firebase collection - payments

"payments": {
    "$paymentId": {
        "totalAmount": {
        },
        "balanceAmount": {
            ".validate": "newData.val()<=data.child('totalAmount').val()"
        },
        "paymentDetails": {
            "$detailId": {
                "amount": {
                    ".validate": "newData.isString()"
                },
            }
        },
    }
}

and below cloud function written to handle certain updates on that collection:

exports.calculateBalance = functions.database
.ref('payments/{pushId}')
.onUpdate(event => {
    const paymentRef = event.data.adminRef;
    const payment = event.data.val();
    return paymentRef.once('value').then(snapshot => {
        var paidAmount = 0;
        snapshot.child("paymentDetails").forEach(function(child) {
            paidAmount += parseFloat(child.child("amount").val());
        });
        return paidAmount;
    }).then(snap => {
        payment.balanceAmount = parseFloat(payment.totalAmount) - snap;
        return paymentRef.set(payment);
    })
});

Its simple, whenever I add a payment details, I want to update the balanceAmount. The problem here is, whenever an update occurs at that collection, the function triggers twice. First time its obvious from application and 2nd time its because of paymentRef.set(payment); line.

Is there any possible way I can avoid this 2nd trigger on cloud function? I cannot use any flag on collection level since the update on payment details happens multiple times. Can someone guide me in the right direction on this?

EDIT

Note - I've an edit option for payment details entered.

like image 898
Guruprasad J Rao Avatar asked Aug 01 '17 10:08

Guruprasad J Rao


1 Answers

If you want update the balance amount only when you add a payment details you can use the onCreate trigger directly on the payment details.

Try something like that (You have to update the code for your case) :

exports.calculateBalance = functions.database.ref('payments/{pushId}/paymentDetails/{detailId}').onCreate(event => {
    var paymentRef = event.data.adminRef.parent.parent;
    var paymentDetailSnapshot = event.data;
    var paymentDetailAmountSnapshot = paymentDetailSnapshot.child('amount');

    return paymentRef.child('balanceAmount').transaction(current => {
        return current - paymentDetailAmountSnapshot.val(); // Balance can be negative
    });
});

(Use the transaction to manage concurrent modifications)

Transactions documentation.

NOTE

Please, use directly a Number for your amount data not a String.

UPDATE

Add this function to manage the update :

exports.recalculateBalance = functions.database.ref('payments/{pushId}/paymentDetails/{detailId}').onUpdate(event => {
    var paymentRef = event.data.adminRef.parent.parent;

    var paymentDetailSnapshot = event.data;
    var previousPaymentDetailSnapshot = event.data.previous;

    var paymentDetailAmountSnapshot = paymentDetailSnapshot.child('amount');
    var previousPaymentDetailAmountSnapshot = previousPaymentDetailSnapshot.child('amount');

    return paymentRef.child('balanceAmount').transaction(current => {
        return current + previousPaymentDetailAmountSnapshot.val() - paymentDetailAmountSnapshot.val(); // Balance can be negative
    });
});
like image 186
Pipiks Avatar answered Nov 15 '22 01:11

Pipiks