The problem about using Firebase Cloud Functions in an Ionic application


#1

Hello everyone,
I use Firebase Cloud Functions in my Ionic app,
But I have a problem,
Can someone help me solve it? Please ~
The problem is shown in the figure below.

'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const STATUS_PENDING = 'pending';
const STATUS_COMPLETE = 'complete';

// Create and Deploy Your First Cloud Functions
// https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
//  response.send("Hello from Firebase!");
// });

// init app
admin.initializeApp(functions.config().firebase);

// listen for new order created then calculate order value
exports.calculateOrder = functions.database.ref('/orders/{userId}/{orderId}').onWrite(function (event) {
  // Exit when the data is deleted.
  if (!event.data.exists()) {
    return;
  }

  // Grab the current value of what was written to the Realtime Database
  const original = event.data.val();

  // if order is created
  if (!event.data.previous.exists()) {

    // log record detail
    console.log('Received order', event.params.userId, event.params.orderId, original);

    // set status to pending
    event.data.ref.child('status').set(STATUS_PENDING);

    // set order number
    admin.database().ref('settings/last_order_number').once('value').then(function (snapshot) {
      let val = snapshot.val() ? snapshot.val() : 2018000000;
      event.data.ref.child('number').set(val + 1);
      admin.database().ref('settings/last_order_number').set(val + 1);
    });

    // set user
    admin.auth().getUser(event.params.userId).then(function (snapshot) {
      event.data.ref.child('user').set({
        email: snapshot.email,
        name: snapshot.displayName
      });
    });

    // set created_at
    event.data.ref.child('created_at').set(Date.now());

    // set update_at
    event.data.ref.child('updated_at').set(Date.now());

    // calculate order data
    var item;
    var total = 0,
      subtotal = 0;

    // apply tax to items
    admin.database().ref('/taxes').once('value').then(function (snapshot) {
      var taxes = snapshot.val();

      Object.keys(original.restaurants).forEach(function (restId) {
        var rest = original.restaurants[restId];
        var restSubtotal = 0;

        for (var i = 0; i < rest.items.length; i++) {
          item = rest.items[i];
          item.taxes = [];
          // subtotal = size.price + sum(option.price)

          subtotal = parseFloat(item.size ? item.size.price : item.price);
          if (item.options) {
            for (let j = 0; j < item.options.length; j++) {
              if (item.options[j].checked) {
                subtotal += parseFloat(item.options[j].price);
              }
            }
          }

          // total = SUM (subtotal * quantity + tax)
          restSubtotal += subtotal * item.quantity;

          if (taxes[restId]) {
            // tax = tax_rate * quantity * subtotal / 100
            Object.keys(taxes[restId]).forEach(function (taxId) {
              var tax = taxes[restId][taxId];
              if (tax.enable && tax.apply_items && (tax.apply_items.indexOf(item.item_id) > -1)) {
                console.log('Tax ok', item.name);
                var itemTax = tax.rate * subtotal * item.quantity / 100;
                restSubtotal += itemTax;

                item.taxes.push({
                  name: tax.name,
                  rate: tax.rate,
                  value: itemTax
                });
              }
            });
          }

          // write subtotal to firebase
          event.data.ref.child('restaurants/' + restId + '/items/' + i + '/subtotal').set(subtotal);
          if (item.taxes) {
            event.data.ref.child('restaurants/' + restId + '/items/' + i + '/taxes').set(item.taxes);
          }
        }

        // update total
        total += restSubtotal;

        // default status is pending
        event.data.ref.child('restaurants/' + restId + '/status').set(STATUS_PENDING);
        event.data.ref.child('restaurants/' + restId + '/subtotal').set(restSubtotal.toFixed(2));
        admin.database().ref('reports/' + restId + '/order/pending').once('value').then(function (snapshot) {
          let val = snapshot.val() ? snapshot.val() : 0;
          admin.database().ref('reports/' + restId + '/order/pending').set(parseInt(val) + 1);
        });

        // notify to admin
        admin.database().ref('notifications/' + restId).push({
          object_type: 'order'
        });
      });

      // write total to firebase
      // should convert total to float
      event.data.ref.child('total').set(total.toFixed(2) / 1);
    });

  }
});

// listen for order updated and build report
exports.buildOrderReport = functions.database.ref('/orders/{userId}/{orderId}/restaurants/{restaurantId}').onWrite(function (event) {
  // Exit when the data is deleted.
  if (!event.data.exists()) {
    return;
  }

  // Grab the current value of what was written to the Realtime Database
  const original = event.data.val();

  // if order is created
  if (!event.data.previous.exists()) {
    return;
  }

  // send notification if order changed value
  var statusSnapshot = event.data.child('status');
  if (statusSnapshot.changed() && (statusSnapshot.val() != STATUS_PENDING)) {
    // notify to user
    admin.database().ref('notifications/' + event.params.userId).push({
      object_type: 'order',
      order_id: event.params.orderId
    });
  }

  // update report by order status
  if (statusSnapshot.changed()) {
    var oldStatus = statusSnapshot.previous.val();
    var total = parseFloat(original.subtotal);
    var reportPath = 'reports/' + event.params.restaurantId + '/order/';
    var salePath = 'reports/' + event.params.restaurantId + '/sale/';

    console.log(oldStatus, statusSnapshot.val());

    // minus previous status
    if (statusSnapshot.previous.exists()) {
      admin.database().ref(reportPath + oldStatus).once('value').then(function (snapshot) {
        if (snapshot.val() > 0) {
          admin.database().ref(reportPath + oldStatus).set(snapshot.val() - 1);
        }
      });
    }

    // plus new status
    admin.database().ref(reportPath + statusSnapshot.val()).once('value').then(function (snapshot) {
      console.log('status');
      console.log(snapshot.val());
      admin.database().ref(reportPath + statusSnapshot.val()).set(snapshot.val() + 1);
    });

    // if order is complete, calculate order data
    if (statusSnapshot.val() == STATUS_COMPLETE) {
      var date = new Date();

      // total sale
      admin.database().ref(salePath + '/total').once('value').then(function (snapshot) {
        var snapshotVal = snapshot.val() ? parseFloat(snapshot.val()) : 0;
        admin.database().ref(salePath + 'total').set(parseFloat(snapshotVal) + total);
      });

      // by year
      var yearPath = salePath + '/' + date.getFullYear();
      admin.database().ref(yearPath + '/total').once('value').then(function (snapshot) {
        var snapshotVal = snapshot.val() ? parseFloat(snapshot.val()) : 0;
        admin.database().ref(yearPath + '/total').set(parseFloat(snapshotVal) + total);
      });

      // by month
      var monthPath = yearPath + '/' + (date.getMonth() + 1);
      admin.database().ref(monthPath + '/total').once('value').then(function (snapshot) {
        console.log(snapshot.val(), total);
        var snapshotVal = snapshot.val() ? parseFloat(snapshot.val()) : 0;
        admin.database().ref(monthPath + '/total').set(parseFloat(snapshotVal) + total);
      });

      // by date
      var datePath = monthPath + '/' + date.getDate();
      admin.database().ref(datePath + '/total').once('value').then(function (snapshot) {
        var snapshotVal = snapshot.val() ? parseFloat(snapshot.val()) : 0;
        admin.database().ref(datePath + '/total').set(parseFloat(snapshotVal) + total);
      });

      // report by items
      original.items.forEach(function (item) {
        // by items
        admin.database().ref(salePath + '/items/' + item.item_id).push(item.quantity);

        // by cateogories
        admin.database().ref('items/' + event.params.restaurantId + '/' + item.item_id + '/category_id').once('value').then(function (catId) {
          admin.database().ref(salePath + '/categories/' + catId.val()).push(item.quantity)
        });
      });
    }

    // update parent order status
    admin.database().ref('orders/' + event.params.userId + '/' + event.params.orderId).once('value').then(function (snapshot) {
      let status = '';
      let isSame = true;
      let rest;

      Object.keys(snapshot.val().restaurants).forEach(function (restId) {
        rest = snapshot.val().restaurants[restId];
        if (status) {
          isSame = rest.status == status;
        } else {
          status = rest.status;
        }
      });

      // if all child orders has same status and parent has difference status
      if (isSame && (snapshot.val().status != status)) {
        admin.database().ref('orders/' + event.params.userId + '/' + event.params.orderId + '/status').set(status);
      }
    });
  }
});

// add/remove item ID to at /taxes/taxId/apply_items
exports.appyItemTaxSetting = functions.database.ref('/items/{restId}/{itemId}').onWrite(function (event) {
  // Exit when the data is deleted.
  if (!event.data.exists()) {
    return;
  }

  // Grab the current value of what was written to the Realtime Database
  const original = event.data.val();
  const itemId = event.params.itemId;

  if (original.taxes) {

    Object.keys(original.taxes).forEach(function (taxId) {
      var taxPath = '/taxes/' + event.params.restId + '/' + taxId + '/apply_items';

      admin.database().ref(taxPath).once('value').then(function (snapshot) {
        var items = snapshot.val();
        // if list of apply_items is not empty
        if (items) {
          // find itemId in array of apply_items
          var index = items.indexOf(itemId)
          // if this item enables this tax
          if (original.taxes[taxId]) {
            // push itemId to apply_items if not in array
            if (index == -1) {
              items.push(itemId);
            }
          } else { // disable this tax
            if (index != -1) {
              items.splice(index, 1);
            }
          }
        } else {
          // if this item enables this tax
          if (original.taxes[taxId]) {
            items = [itemId];
          }
        }

        admin.database().ref(taxPath).set(items);
      });
    });
  }
});


#2

Hi Melon, have figured out the solutions? I’m also facing the same error.