1
votes

So I managed to combine Google form, google calendar, as well as google sheets. When people submit the form (with a start date and end date), it will automatically appear in the google sheets as well as google calendar.

I modified the script to find conflict (to prevent double-booking), however, I just realized that even when the same person is trying to edit starting and ending date (via edit response), it will still show CONFLICT.

For example, someone books from date April 15th to April 17th, and he decided to change to April 16th to April 18th, because he previously booked 15-17, his new submission is having conflict with his own previous submission.

How can I add a function that will detect the same email to edit and submit data? (within empty day slot. Thanks in advance!

This is the function to create an object from sheet data

//Calendars to Output appointments
var cal = CalendarApp.getCalendarById('ID');
// Create an object from user submission
function Submission(){
  var row = lastRow;
  this.timestamp = sheet.getRange(row, 1).getValue();
  this.email = sheet.getRange(row, 2).getValue();
  this.name = sheet.getRange(row, 3).getValue();
  this.date = sheet.getRange(row, 4).getValue();
  this.dateout = sheet.getRange(row, 5).getValue();
  this.room = sheet.getRange(row, 6).getValue();
  this.day = sheet.getRange(row,8).setFormula("=(R[0]C[-3]-R[0]C[-4])+1").getValue();
  var fillDownRange = sheet.getRange(2, 8, lastRow-1);
  sheet.getRange(row,8).copyTo(fillDownRange);
  
   // Info not from spreadsheet
  this.status;
  this.dateString = (this.date.getMonth() + 1) + '/' + this.date.getDate() + '/' + this.date.getYear();
  this.dateOutString = (this.dateout.getMonth() + 1) + '/' + this.dateout.getDate() + '/' + this.dateout.getYear();
  this.calendar = eval('cal' + String(this.room));
  return this;
}

This is the function to detect conflict

function getEndTime(request){
  request.endTime = new Date(request.dateout.getTime() + 24 * 60 * 60 * 1000);
}
// Check for appointment conflicts
function getConflicts(request){
  var conflicts = request.calendar.getEvents(request.date, request.endTime);
  if (conflicts.length < 1) {
    request.status = "Approve";
  } else {
    request.status = "Conflict";
  }
}

This is the function to edit calendar

//update calendar (add)
function updateCalendar(request){
  var event = request.calendar.createEvent(
    "Booked",
    request.date,
    request.endTime
    )
}
2

2 Answers

1
votes

Instead of always retrieving the last row, you should retrieve the actual row of the latest submission

Mind that if people update their Google Form response, the submission row in the spreadsheet will not change - only the content.

  • You can retrieve the latest submitted / modified form response row with the event object event.range (provided your function is bound to a Google Sheets form submit trigger)
  • You can compare the modified row to the last row in the sheet
  • If the form response row is equal to the last row - a new response has been submitted

Sample:

function bindMeOnFormSubmitTrigger(event){
  var sheet = SpreadsheetApp.getActive().getActiveSheet();
  var lastRow = sheet.getLastRow();
  var row = event.range.getRow();
  if (row == lastRow){
    // insert a new event as done before
    ...
  }
  else {
    // the submitted response was an edit of a previously submitted one
    // identify the already existing event (e.g. if you specified the respondent email address in the event description) and modify the event data to the newly submitted values
    var newFormResponseValues = event.values;
    this.timestamp = newFormResponseValues[0];
    ...
    
  }
}
  • If the form response row is differernt (smaller) then the last row in the sheet - it means that an existing form response has been edited (and consequently the event data might need to be modified in the calendar).
  • If you want to acccess the email of the respondent, you have to previously activate this option in the Google Forms UI settings:

enter image description here

1
votes

I found a much better solution by using e.range. The better way to use this method is to create two separate sheets inside same spreadsheet. The main sheet will first retrieve response from the form, and then automatically create a copy to the second sheet. And if there is edited submission, it will go through the second sheet to find the edited row, then modify the row (as well as the calendar event). Credit to Tedinoz

Feel free to add comment/copy, cheers

    function updateCalendarTwo(e) {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var responsename = "Main sheet"
  var copyname = "Copy Sheet";
  var responsesheet = ss.getSheetByName(responsename);
  var copysheet = ss.getSheetByName(copyname);
  var calendar = CalendarApp.getCalendarById('Your Calendar ID');
  // columns on copysheet
  var checkInCol = 4;
  var checkOutCol = 5;
  var roomNumCol = 6;
  var appCol = 11
  var eventIDCol = 12;
  var revCol = 14;

  var response = e.range;
  var rRow = response.getRow()

  var rLC = responsesheet.getLastColumn();
  var cLC = copysheet.getLastColumn();
  var rLR = responsesheet.getLastRow();
  var cLR = copysheet.getLastRow();

  if (rLR > cLR){ 
    var resprange = responsesheet.getRange(rLR,1,1,rLC);
    var respdata = resprange.getValues();
    copysheet.appendRow(respdata[0]);
    var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
    var startDate = copysheet.getRange(rRow,checkInCol).getValue();
    var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
    var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
    if (conflicts.length < 1) {
    var event = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
    var eventID = event.getId().split('@')[0];
    copysheet.getRange(rRow,appCol).setValue("approve");
    copysheet.getRange(rRow,eventIDCol).setValue(eventID);
    } else {
    copysheet.getRange(rRow,appCol).setValue("conflict");
    }
  } else {
    var resprange = responsesheet.getRange(rRow,1,1,9);
    var respdata = resprange.getValues();
    var copyrespRange = copysheet.getRange(rRow,1,1,9);
    copyrespRange.setValues(respdata);

    var respAppRange = copysheet.getRange(rRow,appCol);
    var respApp = respAppRange.getValue();

    if (respApp == 'conflict') {
    var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
    var startDate = copysheet.getRange(rRow,checkInCol).getValue();
    var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
    var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
    if (conflicts.length < 1) {
      var editedEvent = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
      var editedEventID = editedEvent.getId().split('@')[0];;
      copysheet.getRange(rRow,appCol).setValue("edited");
      copysheet.getRange(rRow,eventIDCol).setValue(editedEventID);
      } else {
      copysheet.getRange(rRow,appCol).setValue("conflict");
      };
    } else {
      var eventEditId = copysheet.getRange(rRow,eventIDCol).getDisplayValue();
      var editedEvent = calendar.getEventSeriesById(eventEditId);
      editedEvent.deleteEventSeries();
      var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
      var startDate = copysheet.getRange(rRow,checkInCol).getValue();
      var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
      var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
      if (conflicts.length < 1) {
        var editedEvent = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
        var editedEventID = editedEvent.getId().split('@')[0];;
        copysheet.getRange(rRow,appCol).setValue("edited");
        copysheet.getRange(rRow,eventIDCol).setValue(editedEventID);
        } else {
        copysheet.getRange(rRow,appCol).setValue("conflict");
        };
      };
  
    var revRange = copysheet.getRange(rRow,revCol);
    var revOldValue = revRange.getValue();
    if (revOldValue == null || revOldValue == ""){
      revOldValue = 0;
    }
    var revNewValue = revOldValue+1;
    revRange.setValue(revNewValue); 
  }
}