Google Scripts - Ошибка при вызове функции в другой функции - PullRequest
0 голосов
/ 17 февраля 2020

Я публикую полный код ниже.

Я работаю в Google Scripts, извлекаю некоторые данные из листа, который постоянно изменяется, выполняю вычисления и затем заново заполняю лист.

Данные вызываются в приложение, поэтому я Я пытаюсь выполнить вычисления как можно быстрее (поэтому, если кто-нибудь может подсказать мне более эффективные способы сделать это, это тоже будет полезно).

Когда я пытаюсь вызвать функцию invoicePrice() или tiers() функция Я получаю ошибку TypeError: функция не может быть найдена.

function invoicePrice() {
      // define row to do calculations on
      var AVals = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange("A1:A1000").getValues();
      var ALast = AVals.filter(String).length;

      // Set order No. here

      var orderNoPrev = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast - 1, 3).getValue();
      var orderNo = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 3).setValue(orderNoPrev + 1);
      var total = 0;

      // invoicePrice the total number of items
      var itemRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 4, 1, 14).getValues()[0];

      // set the values of the constants on the Products page

      var priceRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Products").getRange("F1:F1000").getValues();
      var pLast = priceRange.filter(String).length;
      var pGrab = priceRange.splice(0,pLast);

      var invoicePrice = 0;

      // set weight of case values

      var weightRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Products").getRange("V1:V1000").getValues();
      var wLast = weightRange.filter(String).length;
      var wGrab = weightRange.splice(0,wLast);


      for (var i = 0, len = 13; i <= len; i++) {
          // SUBTOTAL THE ORDER AMOUNT
          var orderAmount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, i + 4).getValue();

          total += orderAmount;

          // SET/KG PRICES
          var perKGInvoice = pGrab[i + 1].map(Number);

          // SET WEIGHTS
          var weights = wGrab[i + 1].map(Number);

          // SET CASE PRICE
          var casePrice = perKGInvoice * weights;

          // SUM OF PRODUCT PRICE * ORDER AMOUNT * WEIGHT OF CASE
          invoicePrice += orderAmount * perKGInvoice * weights;


      }
    Logger.log(invoicePrice, ALast, pGrab, wGrab);
    return [invoicePrice, ALast, pGrab, wGrab];



  }

  function tiers() {
      var invoicePriceCall = invoicePrice();
      var invoicePrice = invoicePriceCall[0];
      var ALast = invoicePriceCall[1];

      var beefDiscount = 0;
      var sfDiscount = 0;
      var currentTier = ""

      // Set the invoice order value required ($ amount)

      var tier1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(3, 3).getValue();
      var tier2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(4, 3).getValue();
      var tier3 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(5, 3).getValue();
      var tier4 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(6, 3).getValue();
      var tier5 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(7, 3).getValue();
      var minNotmet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(10, 3).getValue();

      // Set the discount tier levels for beef (% discount)
      var tier1BeefDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(3, 4).getValue();
      var tier2BeefDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(4, 4).getValue();
      var tier3BeefDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(5, 4).getValue();
      var tier4BeefDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(6, 4).getValue();
      var tier5BeefDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(7, 4).getValue();

      var tier1SfDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(3, 5).getValue();
      var tier2SfDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(4, 5).getValue();
      var tier3SfDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(5, 5).getValue();
      var tier4SfDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(6, 5).getValue();
      var tier5SfDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(7, 5).getValue();




      if (invoicePrice < tier1) {
          beefDiscount = tier1BeefDiscount;
          sfDiscount = tier1SfDiscount;
          currentTier = "Minimum Not Met"
      }
      else if (invoicePrice < tier2) {
          beefDiscount = tier1BeefDiscount;
          sfDiscount = tier1SfDiscount;
          currentTier = "Tier 1"
      }
      else if (invoicePrice < tier3) {
          beefDiscount = tier2BeefDiscount;
          sfDiscount = tier2SfDiscount;
          currentTier = "Tier 2"
      }
      else if (invoicePrice < tier4) {
          beefDiscount = tier3BeefDiscount;
          sfDiscount = tier3SfDiscount;
          currentTier = "Tier 3";
      }
      else if (invoicePrice < tier5) {
          beefDiscount = tier4BeefDiscount;
          sfDiscount = tier4SfDiscount;
          currentTier = "Tier 4";
      }
      else {
          beefDiscount = tier5BeefDiscount;
          sfDiscount = tier5SfDiscount;
          currentTier = "Tier 5";
      }


      SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 20).setValue(beefDiscount);
      SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 21).setValue(sfDiscount);
      SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 22).setValue(currentTier);

      return [beefDiscount, sfDiscount, tier1];

  }


  function calcsOnColumns2() {

      // call price range and weight from previous function 
      var invoicePriceCall = invoicePrice();
      var invoicePrice = invoicePriceCall[0];
      var ALast = invoicePriceCall[1];
      var priceRange = invoicePriceCall[2];
      var weightRange = invoicePriceCall[3];

      // call tiers function
      var tiers = tiers();
      var beefDiscount = tiers[0];
      var sfDiscount = tiers[1];
      var tier1 = tiers[2];

      // set sums to 0

      var beefSum = 0;
      var sfSum = 0

      for (var i = 0, len = 13; i <= len; i++) {
          // SUBTOTAL THE ORDER AMOUNT
          var orderAmount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, i + 4).getValue();

          // SET/KG PRICES
          var perKGInvoice = priceRange[i + 1].map(Number);

          // SET WEIGHTS
          var weights = weightRange[i + 1].map(Number);

          // SET CASE PRICE
          var casePrice = perKGInvoice * weights;

          // Split up seafood and beef with if statement:

          if (i < 9) {
              // set discount case price
              var discountCaseBeef = perKGInvoice - (perKGInvoice * beefDiscount);
              // set the total price of the discounted cases in the order (ie, order price * discount)
              var beefCaseOrder = discountCaseBeef * orderAmount * weights;
              beefSum += beefCaseOrder;
              // Nice and Formatted sheet
              var casePrice = perKGInvoice * weights;

              if (orderAmount) { SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, i + 32).setValue(orderAmount + " cases @" + discountCaseBeef.toFixed(2) + "/kg = ~$" + beefCaseOrder.toFixed(2)); }

          }

          else {
              var discountCaseSF = perKGInvoice - (perKGInvoice * sfDiscount);
              // set the total price of the discounted cases in the order (ie, order price * discount)
              var sfCaseOrder = discountCaseSF * orderAmount * weights;
              sfSum += sfCaseOrder;
              // Nice and Formatted sheet
              var casePrice = perKGInvoice * weights;
              if (orderAmount) { SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, i + 32).setValue(orderAmount + " cases @" + discountCaseSF.toFixed(2) + "/kg = ~$" + sfCaseOrder.toFixed(2)); }

          }



          if (i == len) {
              SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 24).setValue(beefSum);
              SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 25).setValue(sfSum);
              // if statement to go here if min order size not met

              var sumOfSums = beefSum + sfSum;
              // swap out 75 with referenced value
              if (sumOfSums > tier1) { SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 27).setValue(sumOfSums) }
              // swap out phrase with changable phrase
              else { SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 27).setValue("Minimum order size not met") }

              var totalDiscount = invoicePrice - sumOfSums

              SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 29).setValue(totalDiscount);
              SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 30).setValue(invoicePrice);


          }



      }


  }

1 Ответ

2 голосов
/ 18 февраля 2020

Проблема

Правильно объявленная функция (invoicePrice) при вызове из другой функции (calcsOnColumns2) имеет значение undefined.

Шаги для воспроизведения

Представьте, что у вас есть три функции с именами A, B, C, затем вызов B вызывает вашу ошибку:

function A() {
  var A = 0;
  return [A];
}

function C() {
  var a = A();
  var A = a[0];
}

function B() {
  A();
  C();
}

Вы, вероятно, заметил, что это то, к чему сводятся ваши функции - поведение связано с подъемом :

  1. функцией B, вызывающей A, которая возвращает Array с первым элемент 0;
  2. [никогда не происходит] функция C вызывает функцию A и переопределяет A на выход A;

Вместо этого на втором шаге A объявляется равным первому элементу a и только тогда вызывается A. Поскольку a получает свое значение от вызова к A, A будет undefined и, как следствие: [undefined]() -> "не является функцией"

Оптимизация

  1. Оставайтесь DRY (не повторяйте себя) - когда вы видите код, который выглядит одинаково или имеет ту же структуру, подумайте над тем, чтобы сделать его многократно используемым (сделать его функцией, классом, объектом) переменная - это зависит). Это делает ваш код читабельным и проще в отладке, но большую часть времени экономит вам дополнительные вызовы API, повторные вызовы и, следовательно, время (особенно если вы повторяете ввод-вывод).
  2. Не используйте комментарии, если только строго необходимо (значительно снижает читабельность - крайне важно, если вам нужны другие, чтобы помочь отладить ваш код или сделать это самостоятельно [может показаться нелогичным, но вы будете удивлены]).
  3. Не ставьте I / O (getRange(), getValue()) в циклах, если нет выбора - ввод / вывод всегда медленный, поэтому обычно лучше загружать все в память и работать с данными там.
  4. Сохранять согласованную область видимости: если вы объявляете переменную, каждый метод доступа должен находиться либо в одной и той же области видимости, либо во вложенной (например, не объявляйте две переменные с одинаковым именем внутри if...else, перемещайте его во внешнюю область видимости).
  5. Используйте TypeScript или по крайней мере JSDo c - удержит вас от сумасшествия, пытаясь понять, почему ваша функция ожидала один тип и получила другой (как в вашем случае) [UPD: я также добавил JSDO * 110 2 * комментарии к решению].

Я применил несколько оптимизаций к вашему сценарию, посмотрите (пожалуйста, проверьте перед использованием, так как у нас нет примеров данных, я не смог Попробуй это). Вы можете сделать гораздо больше (особенно в отношении l oop в calcsOnColumns2), но это должно быть началом:

/**
 * [Your decription here]
 * @returns {Number[]}
 */
function invoicePrice() {
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var subtotalDisplaySheet = ss.getSheetByName('SubtotalDisplay');

    // define row to do calculations on
    var AVals = subtotalDisplaySheet.getRange("A1:A1000").getValues();
    var ALast = AVals.filter(String).length;

    // Set order No. here
    var orderNoPrev = subtotalDisplaySheet.getRange(ALast - 1, 3).getValue();
    var orderNo = subtotalDisplaySheet.getRange(ALast, 3).setValue(orderNoPrev + 1);

    // invoicePrice the total number of items
    var itemRange = subtotalDisplaySheet.getRange(ALast, 4, 1, 14).getValues()[0];

    // set the values of the constants on the Products page
    var productsSheet = ss.getSheetByName("Products");

    var priceRange = productsSheet.getRange("F1:F1000").getValues();
    var pLast = priceRange.filter(String).length;
    var pGrab = priceRange.splice(0, pLast);

    var outputInvoicePrice = 0;

    var weightRange = productsSheet.getRange("V1:V1000").getValues();
    var wLast = weightRange.filter(String).length;
    var wGrab = weightRange.splice(0, wLast);

    var orderAmounts = subtotalDisplaySheet.getRange(Alast, 17).getValues();

    var total = 0;
    for (var i = 0, len = 13; i <= len; i++) {
        var nextI = i + 1;

        var orderAmount = orderAmounts[0][i + 4];

        total += orderAmount;

        var perKGInvoice = pGrab[nextI].map(Number);
        var weights = wGrab[nextI].map(Number);

        var casePrice = perKGInvoice * weights;

        outputInvoicePrice += orderAmount * casePrice;
    }

    Logger.log(outputInvoicePrice, ALast, pGrab, wGrab);
    return [outputInvoicePrice, ALast, pGrab, wGrab];
}

/**
 * Gets values from 5 rows in 
 * a column starting from row 3
 * @param {Sheet} sheet 
 * @param {Number} column 
 * @returns {*[]}
 */
function getGridValues(sheet, column) {
    var range = sheet.getRange(3, column, 5, 1);
    return range
        .getValues()
        .map(function (row) {
            return row[0];
        });
}

/**
 * [Your decription here]
 * @returns {Number[]}
 */
function tiers() {
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var discountGridSheet = ss.getSheetByName("DiscountGrid");

    var invoicePriceCall = invoicePrice();
    var outputInvoicePrice = invoicePriceCall[0];
    var ALast = invoicePriceCall[1];

    var beefDiscount = 0;
    var sfDiscount = 0;
    var currentTier = "";

    var tierValues = getGridValues(discountGridSheet, 3);
    var tier1 = tierValues[0];
    var tier2 = tierValues[1];
    var tier3 = tierValues[2];
    var tier4 = tierValues[3];
    var tier5 = tierValues[4];

    var minNotmet = discountGridSheet.getRange(10, 3).getValue();

    var tierBeefDiscountValues = getGridValues(discountGridSheet, 4);
    var tier1BeefDiscount = tierBeefDiscountValues[0];
    var tier2BeefDiscount = tierBeefDiscountValues[1];
    var tier3BeefDiscount = tierBeefDiscountValues[2];
    var tier4BeefDiscount = tierBeefDiscountValues[3];
    var tier5BeefDiscount = tierBeefDiscountValues[4];

    var tierSfDiscountValues = getGridValues(discountGridSheet, 5);
    var tier1SfDiscount = tierSfDiscountValues[0];
    var tier2SfDiscount = tierSfDiscountValues[1];
    var tier3SfDiscount = tierSfDiscountValues[2];
    var tier4SfDiscount = tierSfDiscountValues[3];
    var tier5SfDiscount = tierSfDiscountValues[4];

    if (outputInvoicePrice < tier1) {
        beefDiscount = tier1BeefDiscount;
        sfDiscount = tier1SfDiscount;
        currentTier = "Minimum Not Met";
    }
    else if (outputInvoicePrice < tier2) {
        beefDiscount = tier1BeefDiscount;
        sfDiscount = tier1SfDiscount;
        currentTier = "Tier 1";
    }
    else if (outputInvoicePrice < tier3) {
        beefDiscount = tier2BeefDiscount;
        sfDiscount = tier2SfDiscount;
        currentTier = "Tier 2";
    }
    else if (outputInvoicePrice < tier4) {
        beefDiscount = tier3BeefDiscount;
        sfDiscount = tier3SfDiscount;
        currentTier = "Tier 3";
    }
    else if (outputInvoicePrice < tier5) {
        beefDiscount = tier4BeefDiscount;
        sfDiscount = tier4SfDiscount;
        currentTier = "Tier 4";
    }
    else {
        beefDiscount = tier5BeefDiscount;
        sfDiscount = tier5SfDiscount;
        currentTier = "Tier 5";
    }

    var subtotalDisplaySheet = ss.getSheetByName("SubtotalDisplay");
    var subtotalDisplayRange = subtotalDisplaySheet.getRange(ALast, 20, 3, 1);
    subtotalDisplayRange.setValues([
        [beefDiscount],
        [sfDiscount],
        [currentTier]
    ]);

    return [beefDiscount, sfDiscount, tier1];
}

/**
 * Foramts amount string
 * @param {Number} amount 
 * @returns {Function}
 */
function formatAmount(amount) {
    return function (discount, caseOrder) {
        return amount + " cases @" + discount.toFixed(2) + "/kg = ~$" + caseOrder.toFixed(2);
    };
}

/**
 * Counts sum by discount, amount and weights
 * @param {Number} amount 
 * @param {Number} weights 
 * @returns {Number}
 */
function countSum(amount, weights) {
    return function (discount) {
        return discount * amount * weights;
    };
}

/**
 * Counts discount case per Kg
 * @param {Number} perKG 
 * @returns {Number}
 */
function countDiscountCase(perKG) {
    return function (discount) {
        return perKG - (perKG * discount);
    };
}

/**
 * [Your decription here]
 */
function calcsOnColumns2() {
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var subtotalDisplaySheet = ss.getSheetByName("SubtotalDisplay");

    var invoicePriceCall = invoicePrice();
    var outputInvoicePrice = invoicePriceCall[0];
    var ALast = invoicePriceCall[1];
    var priceRange = invoicePriceCall[2];
    var weightRange = invoicePriceCall[3];

    var tiers = tiers();
    var beefDiscount = tiers[0];
    var sfDiscount = tiers[1];
    var tier1 = tiers[2];

    var beefSum = 0;
    var sfSum = 0;

    var orderAmounts = subtotalDisplaySheet.getRange(ALast, 17).getValues();

    for (var i = 0, len = 13; i <= len; i++) {
        var orderAmount = orderAmounts[0][i + 4];

        var nextI = i + 1;

        var perKGInvoice = priceRange[nextI].map(Number);
        var weights = weightRange[nextI].map(Number);

        var casePrice = perKGInvoice * weights;

        var subtotalRange = subtotalDisplaySheet.getRange(ALast, i + 32);

        var formatAmountOrder = formatAmount(orderAmount);
        var countDisountSum = countSum(orderAmount, weights);
        var countPerKGCase = countDiscountCase(perKGInvoice);

        // Split up seafood and beef with if statement:
        if (i < 9) {
            var discountCaseBeef = countPerKGCase(beefDiscount);
            var beefCaseOrder = countDisountSum(discountCaseBeef);
            beefSum += beefCaseOrder;
        } else {
            var discountCaseSF = countPerKGCase(sfDiscount);
            var sfCaseOrder = countDisountSum(discountCaseSF);
            sfSum += sfCaseOrder;
        }

        if (orderAmount) {
            subtotalRange.setValue(
                i < 9 ?
                    formatAmountOrder(discountCaseBeef, beefCaseOrder) :
                    formatAmountOrder(discountCaseSF, sfCaseOrder)
            );
        }

        if (i == len) {
            subtotalDisplaySheet.getRange(ALast, 24).setValue(beefSum);
            subtotalDisplaySheet.getRange(ALast, 25).setValue(sfSum);
            // if statement to go here if min order size not met

            var subtotalRange = subtotalDisplaySheet.getRange(ALast, 27);

            var sumOfSums = beefSum + sfSum;

            subtotalRange.setValue(sumOfSums > tier1 ? "Minimum order size not met" : sumOfSums);

            var totalDiscount = outputInvoicePrice - sumOfSums;

            subtotalDisplaySheet.getRange(ALast, 29).setValue(totalDiscount);
            subtotalDisplaySheet.getRange(ALast, 30).setValue(outputInvoicePrice);
        }
    }

}

Примечания

  1. Хотя современный JavaScript поможет вам избежать этой проблемы, вы никогда не должны переопределять имена переменных если вы точно не знаете, почему вы это делаете (например, i++, однако функциональный стиль скажет, что даже это проблематично c).
  2. Никогда не обрезайте сообщения об ошибках / код при публикации в SO для ради читабельности - знание того, что именно произошло, неоценимо для нас, чтобы помочь вам (invoicePrice - это undefined говорит о многом другом).

Ссылки

  1. Подъем in JavaScript
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...