0
votes

I am still learning Google Script, Now I am working in a project to automate the shift schedule creation using Google sheet

I managed to find some scripts in how to get a certain range and fill it in a specific cell with certain values with some simple conditions and loops

var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = sheet.getRange(2,2, lastRow-1, lastColumn-1);

function onOpen() {
  ui.createMenu('Checker')
  .addItem('Bad Way', 'badWay')
  .addItem('Good Way', 'goodWay')
  .addToUi();
};

/* 
BAD - Go to each cell and see if it contains a value
and then fill in the background if it contains a dash or 
zero 
*/
function badWay() {
  //Loop through each column and each row in the sheet.
  for(i = 1; i < lastColumn; i++){
    for (j = 1; j < lastRow ; j++){
      var cell = searchRange.getCell(j,i).getValue();
      if (cell === "-"){
        sheet.getRange(j+1,i+1).setBackground("#cc4125");
      }else if (cell === 0){
        sheet.getRange(j+1,i+1).setBackground("#e69138");
      };
    };
  };

};

/*
GOOD - Create a client-side array of the relevant data
search through the array and if there is a dash or zero,
then add the relevant background color. 
*/
function goodWay() {
  // Get array of values in the search Range
  var rangeValues = searchRange.getValues();
  // Loop through array and if condition met, add relevant
  // background color.
  for ( i = 0; i < lastColumn - 1; i++){
    for ( j = 0 ; j < lastRow - 1; j++){
      if(rangeValues[j][i] === "-"){
        sheet.getRange(j+2,i+2).setBackground("#cc4125");
      }else if (rangeValues[j][i] === 0){
        sheet.getRange(j+2,i+2).setBackground("#e69138");
      }; 
    };
  };

};

But the problem is more complicated, I need to ensure a few conditions before allowing the script to fill the schedule like:

  • Every day there must be a minimum No. of employees in each shift.
  • Each employee can't work more than 5 days a week, (2 days off but not necessarily sequenced)

Shifts would look like this:

enter image description here Any idea how to implement this in google script and without using other external software?

Thanks in advance

1
When an employee doesn't work how it should appear in the cell? As blank? I mean how do you know when they don't work? In your Sheet what is consider a week, from day 1 to day 7 and so on? Do you want to change a cell color when both conditions are met? Could you please share a spreadsheet with some dummy data for testing? it would be easier to help you in that way, better than an image. - alberto vielma
oh yeah it should be empty or any letter would work, color can be changed based on the cell value Above is the initial shift fill, and just tried manually to show how off days should look like - Moh'd Mahfadi
here is the google sheet with demo data docs.google.com/spreadsheets/d/… - Moh'd Mahfadi

1 Answers

1
votes

This code will help you with what you want (Days off are empty cells):

var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var rangeData = sheet.getDataRange();
var lastColumn = rangeData.getLastColumn();
var lastRow = rangeData.getLastRow();
var searchRange = sheet.getRange(2, 2, lastRow-1, lastColumn-1);
var daysNotWorkedCounter = 0;
var numberEmpls = 0;
var emplWorkingDay = 0;
var emplWorkingNight = 0;

function onOpen() {
  var entries = [{
    name : "Good Way",
    functionName : "goodWay"
  }]
  ss.addMenu('Checker', entries);
};


/*
GOOD - Create a client-side array of the relevant data
search through the array and if there is a dash or zero,
then add the relevant background color. 
*/
function goodWay() {
  var rangeValues = searchRange.getValues();
  daysWorkedByWeek(rangeValues);
  numbDailyEmp(rangeValues);
}

/*
  It checks for every user if he/she has worked more than two days
  It also sets to red the cells where are days off
*/
function daysWorkedByWeek(rangeValues){
  // It checks all cols in a row
  for(row = 0 ; row < lastRow - 1; row++){
    daysNotWorkedCounter = 0;
    for (col = 0; col < lastColumn - 1; col++){
      // If there is a day off (empty cell), then set a color to that cell
      // and count the day the empl is not working
      if(!rangeValues[row][col] && col !== 0){
        sheet.getRange(row+2,col+2).setBackground("#ff0000");
        daysNotWorkedCounter++;
        // If there are more than two that week, Log a message 
        if(daysNotWorkedCounter > 2){
          Logger.log("This employee has worked more than two days in a week: " + sheet.getRange(row+2,1).getValues());
        }
      }
      // If 7 days passed (one week), restart the counter
      if(col % 7 === 0) daysNotWorkedCounter = 0;  
    }
  }
}

/*
  For every day it checks how many employees are working in each shift 
*/
function numbDailyEmp(rangeValues){
  // It checks all rows in a col
  for (col = 0; col < lastColumn - 1; col++){
    emplWorkingDay = 0;
    emplWorkingNight = 0;
    for(row = 0 ; row < lastRow - 1; row++){
      // Checks empls working that day
      if(rangeValues[row][col] && col !== 0){
        // Count the numbers of empls depending in the shift
        if(rangeValues[row][0] === "Morning") emplWorkingDay++;
        else if (rangeValues[row][0] === "Night") emplWorkingNight++; 
      }
    }
    // Log a message with the number of empls working in a shift that day
    if(col !== 0){
      Logger.log("Number of employees working at day " +  emplWorkingDay);
      Logger.log("Number of employees working at night " + emplWorkingNight);
      // if there are less than a min, then Log a message
      if(emplWorkingDay < 6 || emplWorkingNight < 6){
        Logger.log("Not enough working that day")
      }
    }
  }
}

Notice how I added two functions (daysWorkedByWeek and numbDailyEmp) to separate the two tasks you were requiring

daysWorkedByWeek checks for every employee the number of days he/she has had off by a week.

/*
  It checks for every user if he/she has worked more than two days
  It also sets to red the cells where are days off
*/
function daysWorkedByWeek(rangeValues){
  // It checks all cols in a row
  for(row = 0 ; row < lastRow - 1; row++){
    daysNotWorkedCounter = 0;
    for (col = 0; col < lastColumn - 1; col++){
      // If there is a day off (empty cell), then set a color to that cell
      // and count the day the empl is not working
      if(!rangeValues[row][col] && col !== 0){
        sheet.getRange(row+2,col+2).setBackground("#ff0000");
        daysNotWorkedCounter++;
        // If there are more than two that week, Log a message 
        if(daysNotWorkedCounter > 2){
          Logger.log("This employee has worked more than two days in a week: " + sheet.getRange(row+2,1).getValues());
        }
      }
      // If 7 days passed (one week), restart the counter
      if(col % 7 === 0) daysNotWorkedCounter = 0;  
    }
  }
}

numbDailyEmp checks for every day if the minimum amount of employees are working that day depending in their shifts.

/*
  For every day it checks how many employees are working in each shift 
*/
function numbDailyEmp(rangeValues){
  // It checks all rows in a col
  for (col = 0; col < lastColumn - 1; col++){
    emplWorkingDay = 0;
    emplWorkingNight = 0;
    for(row = 0 ; row < lastRow - 1; row++){
      // Checks empls working that day
      if(rangeValues[row][col] && col !== 0){
        // Count the numbers of empls depending in the shift
        if(rangeValues[row][0] === "Morning") emplWorkingDay++;
        else if (rangeValues[row][0] === "Night") emplWorkingNight++; 
      }
    }
    // Log a message with the number of empls working in a shift that day
    if(col !== 0){
      Logger.log("Number of employees working at day " +  emplWorkingDay);
      Logger.log("Number of employees working at night " + emplWorkingNight);
      // if there are less than a min, then Log a message
      if(emplWorkingDay < 6 || emplWorkingNight < 6){
        Logger.log("Not enough working that day")
      }
    }
  }
}

Docs

For best practices I recommend you check: