0

I am really stuck to convert this idea to a script or a formula.

The problem scenario

The task is sequential A then B and then C. I have this google sheet that has a column with only checkboxes. I want to click the checkbox when a payment is done. And then the sequence of things need to take place.

  1. Pay count has to be increased by 1.
  2. Renewal date has to be reset to the new date.
  3. The Checkbox should go dimmed(unclickable) after the previous operations are done and remain there until there are only about 20 days left when it should become active(clickable) again.

Now there are formulae in some of the cells:

A. Pay Count column (contains how many times a payment is done) has this formula:

     =IF(ISBLANK(PAIDON),,IF(RENEWON="",1,ROUNDUP(DAYS(RENEWON,PAIDON)/period)))

B. RENEWON column (calculates the next renewal date) has this formula:

=IF(ISBLANK(PAIDON),,IF(OR(SUBSCRIPTION="LifeTime",SUBSCRIPTION="OneTime"),,DATE(YEAR(L2),MONTH(L2)+IFS(SUBSCRIPTION="Yearly",12,SUBSCRIPTION="2Yearly",24,SUBSCRIPTION="3Yearly",36,SUBSCRIPTION="4Yearly",48,SUBSCRIPTION="5Yearly",60, SUBSCRIPTION="Monthly",1),DAY(L2)-1)))

You can understand that I am kind of a newbie here. So please do ask me for any information I missed here.

Need suggestions of how to convert the whole idea to a script or formula.

Any idea/guidance is helpful to me.

UPDATE: Additional info: My ranges are given in here for further help: enter image description here

Also thanks for the right formatting! I definitely need lessons on them

Snail-Horn
  • 101
  • 11
  • Could you provide a copy of the spreadsheet you are working on, free of sensitive information? That could be very useful in solving this issue. Things like the `subscription` type and the periodicity of payments seem relevant to this issue, so these should be added for clarification purposes. – Iamblichus Sep 21 '20 at 14:35
  • @lamblichus thanks, I have added the definitions of my ranges that may help you to get some help. Thanks – Snail-Horn Sep 21 '20 at 15:23
  • I think that you should split your question and add a brief description of what you have learned so far as well a brief description of your search / research efforts for each specific part. As you already got an answer, remove the parts that were not answered yet and post each of them as new questions. Consider to post one and the next after you get the previous questions answered. – Rubén Sep 21 '20 at 17:36
  • Yeah! That's an idea worth pursuing. I am trying to split it as you just said. But don't know how exactly to do that. Coming up with different way of asking it soon. I guess it's become kind of vague as it possibly is too big for one question! – Snail-Horn Sep 21 '20 at 17:56
  • A copy of the spreadsheet (not screenshots) would probably be needed here. But in any case, I agree that your question is very broad. Please consider editing it in order to give it more focus. – Iamblichus Sep 22 '20 at 14:39
  • Here is the [link to the development: My Google Drive](https://docs.google.com/spreadsheets/d/18ZvLxpuoJTA7BL-4MaZmGwh4RqV6YxM5qTxbZkCetZM/edit?usp=sharing) . You can fiddle with it, without a problem. Hope it helps. – Snail-Horn Sep 22 '20 at 15:58
  • The file you shared is private. Can you provide a publicly accessible copy? – Iamblichus Sep 23 '20 at 12:57
  • Good lord! It was so easy but I didn't know what to do!! Here is the [file](https://docs.google.com/spreadsheets/d/18ZvLxpuoJTA7BL-4MaZmGwh4RqV6YxM5qTxbZkCetZM/edit?usp=sharing) I think will open now. – Snail-Horn Sep 25 '20 at 05:26
  • Thank you. I have a question regarding your situation. The column `Date Paid` is supposed to get updated only the first time a payment is done? It wouldn't update when the checkbox is clicked and a new payment is made? – Iamblichus Sep 25 '20 at 12:39
  • 1
    I posted an answer. I hope this can help solve your issue. – Iamblichus Sep 25 '20 at 13:36
  • @lamblichus, because of your Q regarding `Date Paid`, I realised that I actually need 2 fields/columns. So I made 2 different columns -- one for the `initial pament date` and the other for `last payment date`. It did help. And all updates are on the second one now. Thanks – Snail-Horn Sep 26 '20 at 18:00
  • Avoid asking full code feature questions See https://meta.stackoverflow.com/a/284237/ and https://meta.stackoverflow.com/questions/261592/ – TheMaster Sep 26 '20 at 21:30

2 Answers2

2

you can get checkboxes column values first using :

// This function gets the full column Range like doing 'A1:A9999' in excel
// @param {String} column The column name to get ('A', 'G', etc)
// @param {Number} startIndex The row number to start from (1, 5, 15)
// @return {Range} The "Range" object containing the full column: https://developers.google.com/apps-script/class_range
function getFullColumn(column, startIndex){
  var sheet = SpreadsheetApp.getActiveSpreadsheet();
 // sheet.setActiveSheet(sheet.getSheetByName('sheet_name')); //use this if you have different sheets
  sheet = SpreadsheetApp.getActiveSheet();
  var lastRow = sheet.getLastRow();
  return sheet.getRange(column+startIndex+':'+column+lastRow);
}

//make a call to the function to get all cells' values.
var checkboxesValues = getFullColumn('O', 1).getValues();

Now you have all the values, create a simple array and make for-loop on checkboxesValues length , where you check inside it if the value of the checkbox is TRUE or FALSE (checked - unchecked), and add it's index tp the newly created array.

example

var arr =[]
for (var i = 0; i < checkboxesValues.length; i++) {
    if(checkboxesValues[i][0] === true){
      
      arr.push(i +1); //adding row index (+1 because index start from zero in loop)
}

After that, you can make a new loop on the new array "arr" where it contains only true checkboxes values, and change values as you want.

So you go like:

for (var i = 0; i < arr.length; i++) {
    //Step A (Pay count)
    var payCount = SpreadsheetApp.getActiveSheet().getRange('N'+arr[i]).getValue()
    payCount ++
    SpreadsheetApp.getActiveSheet().getRange('N'+arr[i]).setValue(payCount);
    //step B should go here
    //Finally Step C
    SpreadsheetApp.getActiveSheet().getRange('O'+arr[i]).setValue(false); // this will uncheck it
}

I think checkboxes can't be dimmed or disabled, they are only allowed to be checked or unchecked.

Step B, I don't really get it.

I hope this helps you by any means, and that I understood your question correctly.

Doom
  • 206
  • 1
  • 4
  • 14
  • Thanks @Doom, StepB actually keeps track of how many times payments are made for a given 'subscription' in its lifetime so far. This is to determine if this subscription needs some more attention. This need to be a counter but has to be a very accurate one. That's why when it handled one time, options has to be blocked till the next payment time comes in. – Snail-Horn Sep 21 '20 at 15:32
1

Issue:

Every time a checkbox is checked, you want to do the following:

  • Update the Pay Count (+1).
  • Update the Date Paid with current date.
  • Update Renewal Due on date based on the currently existing formula.
  • If the difference between the current date and the renewal date is more than 20 days, disable the corresponding checkbox.

Also, you want to re-enable the checkbox when the renewable date is less than 20 days from now.

Solution:

  • There is no option for disabling checkboxes, but you just can remove them with removeCheckboxes().
  • In order to track when a checkbox is checked, I'd suggest you to use an onEdit trigger. This should (1) check if a checkbox was checked and, if that's the case (2) update the dates and (3) remove the corresponding checkbox if there're more than 20 days remaining. Check the code sample below for an example of how this could be done.
  • In order to enable the checkboxes again when the renewal date approaches (or to insert them again, which you can do with insertCheckboxes()), I'd suggest you to create a time-driven trigger which will periodically check the dates, and create the corresponding checkboxes.
  • I think, in this case, checking this once a day could be an appropriate periodicity. So you could use everyDays(n). This trigger can either be installed manually, or programmatically via executing the createDailyTrigger function below. Once the trigger is installed, the function enableCheckboxes (check code sample below) would run daily and check if the renewable date is less than 20 days from now (and insert the checkbox if that's the case).

Code sample:

function onEdit(e) {
  const range = e.range;
  const column = range.getColumn();
  const row = range.getRow();
  const value = e.value;
  if (column === 15 && row > 1 && value == "TRUE") {
    const sheet = e.source.getActiveSheet();
    const countCell = sheet.getRange(row, 14);
    countCell.setValue(countCell.getValue() + 1);
    const now = new Date();
    sheet.getRange(row, 12).setValue(now);
    SpreadsheetApp.flush(); // Force update: pay date and renewable date
    const renewalDate = sheet.getRange(row, 13).getValue();
    // Remove checkbox if renewal date is more than 20 days from now
    if (!isPaymentTime(renewalDate)) {
      sheet.getRange(row, 15).removeCheckboxes();
    }
  }
}

function isPaymentTime(date) { // Check if renewal date is less than 20 days from now
  const twentyDays = 1000 * 3600 * 24 * 20; // 20 days in milliseconds
  const now = new Date();
  if (date instanceof Date) return date.getTime() - now.getTime() < twentyDays;
  else return false;
}

function enableCheckboxes() {
  const sheet = SpreadsheetApp.getActive().getSheetByName("PODexpenses");
  const firstRow = 2;
  const renewalColumn = 13;
  const numRows = sheet.getLastRow() - firstRow + 1;
  const renewalDates = sheet.getRange(firstRow, renewalColumn, numRows).getValues().flat();
  renewalDates.forEach((renewalDate, i) => {
    if (isPaymentTime(renewalDate)) { // Check if less than 20 days
      sheet.getRange(i + firstRow, 15).insertCheckboxes(); // Insert checkbox
    }
  })
}

function createDailyTrigger() {
  ScriptApp.newTrigger("enableCheckboxes")
    .timeBased()
    .everyDays(1)
    .create();
}

Note:

  • I saw there is another onEdit function in your current script. Please integrate this on the same function. There can only be one onEdit.
Iamblichus
  • 18,540
  • 2
  • 11
  • 27
  • 1
    @Snail-Horn There was a typo in `enableCheckboxes`. Just fixed that. – Iamblichus Sep 26 '20 at 06:35
  • @lamblichus, I was troubleshooting that `renewaldates` thingy. I thought I did copy your routine after the correction...lol. However, it's in order now. Thanks. BTW, awesome simple solution for my problem. The Layout of the problem was clearer even to me when you've outlined it here! Now incorporating a [global constant declaration as in here](https://stackoverflow.com/a/42536794/2378780) by [KrzFra](https://stackoverflow.com/users/7642490/krzfra). Very unique way to me.! – Snail-Horn Sep 26 '20 at 17:49
  • Oh I had to rename the `createDailyTrigger()` to `createTimeDrivenTriggers()` as per the [github example](https://github.com/gsuitedevs/apps-script-samples/blob/master/triggers/triggers.gs). It's still not working for me. Do I have to put it somewhere like a trigger.gs file or something? I mean if i need to do it by GAS. It is not clear to me yet! – Snail-Horn Sep 26 '20 at 17:55
  • @Snail-Horn Not sure if I understand your question, but in order to install the trigger programmatically, you have to copy the function `createDailyTrigger()` (or whatever name you decide, that doesn't matter) to any script file in your project (`.gs`, can be the same file where the rest of your functions are) and run it once. Or you can do it [manually](https://developers.google.com/apps-script/guides/triggers/installable#managing_triggers_manually). Also, you can check if it's been installed by selecting **Edit > Current project's triggers**. – Iamblichus Sep 26 '20 at 18:39
  • @lamblichus, I understood that part as you've mentioned that also in your solution. 1) I put the script in my `Code.gs` file with the other scripts. 2) I added the trigger in the **Edit > Current project's triggers**. Now, it's my understanding that if this runs correctly then it should clear the existing checkboxes at least and mark the checkboxes that are required. But seemingly it is not doing so! I don't know if my expectations are wrong or not! – Snail-Horn Sep 26 '20 at 19:20
  • GAS error: `date.getTime()` is not a function in `isPaymentTime(date)` function. Any idea why so? I was running `enableCheckboxes()` from the editor. – Snail-Horn Sep 26 '20 at 19:55
  • @Snail-Horn the checkboxes are cleared by the onEdit function, not by the time trigger. The time trigger inserts the checkboxes back. About the problem you mentioned, I cannot take a look at it right now, I'll check it on Monday. – Iamblichus Sep 26 '20 at 22:09
  • 1
    @Snail-Horn My apologies. The function `isPaymentTime` was not checking whether the corresponding `date` argument is actually a date, and it failed if that's not the case. I fixed that in the code sample, and it should give no error now. It is now working for me. Please let me know if that works for you. – Iamblichus Sep 28 '20 at 07:20