Bug with Woocommerce Webhook and Google Sheet API? - wordpress

I have set up a link between Woocommerce orders (a ecommerce plugin for WordPress that we use for our NGO), and a Google Sheet table using this script in Google Sheet's script editor:
//this is a function that fires when the webapp receives a GET request
function doGet(e) {
return HtmlService.createHtmlOutput("request received");
}
//this is a function that fires when the webapp receives a POST request
function doPost(e) {
var myData = JSON.parse([e.postData.contents]);
var order_number = myData.number;
var order_created = myData.date_created;
var order_status = myData.status;
var order_total = myData.total;
var billing_first_name = myData.billing.first_name;
var billing_last_name = myData.billing.last_name;
var billing_email = myData.billing.email;
var billing_phone = myData.billing.phone;
var shipping_first_name = myData.shipping.first_name;
var shipping_last_name = myData.shipping.last_name;
var shipping_address_1 = myData.shipping.address_1;
var shipping_address_2 = myData.shipping.address_2;
var shipping_postcode = myData.shipping.postcode;
var shipping_city = myData.shipping.city;
var shipping_country = myData.shipping.country;
var payment_method = myData.payment_method_title;
var currency = myData.currency;
var timestamp = new Date();
var sheet = SpreadsheetApp.getActiveSheet();
for (var i = 0; i < myData.line_items.length; i++)
{ var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
sheet.appendRow([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]); }
}
Everything works as intended, every new order is populated in the Google Sheet table a few seconds later.
However, when I apply a filter on any column in Google Sheet, let's say for payment method, selecting "PayPal", no new order will populate the Google Sheet's table.
They are registered in the woocommerce plugin, payment is ok, all is fine, except that Google Sheet does not receive the order.
Even after removing the filter, it doesn't appear.
All next orders will appear if all filters are deactivated in Google Sheet.
So, there is an issue with Google Sheet filters, but I don't know what is causing it. Is it my script? Is it Google API's fault? Woocommerce webhook?
Please note that I am not a developer, I found this script online and tweaked it myself by try and guess for my own needs.

Modification points:
When the sheet of Google Spreadsheet uses the basic filter, when the values are put using appendRow(), the values are not appended. This might be the current specification.
I thought that this might be the reason of your issue.
In your script, the values are put using appendRow(), and appendRow() is used in a loop. In this case, the process cost of the script will become a bit high. When setValues() is used, this issue can be also removed.
In this case, I would like to propose to append the values using setValues(). When setValues() is used, the values can be put to the filtered sheet. But, when the values are put to the filtered sheet, the filtered sheet is not changed while the values are put.
So it is required to refresh the basic filter after the values are put.
When above points are reflected to your script, it becomes as follows.
Modified script:
Please modify your script as follows.
From:
var sheet = SpreadsheetApp.getActiveSheet();
for (var i = 0; i < myData.line_items.length; i++)
{ var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
sheet.appendRow([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]); }
To:
var sheet = SpreadsheetApp.getActiveSheet();
var values = [];
for (var i = 0; i < myData.line_items.length; i++) {
var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
values.push([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]);
}
// Put values using "setValues".
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
// Refresh basic filter.
var filter = sheet.getFilter();
if (filter) {
var range = filter.getRange();
for (var i = range.getColumn(), maxCol = range.getLastColumn(); i <= maxCol; i++) {
var filterCriteria = filter.getColumnFilterCriteria(i)
if (filterCriteria) {
filter.setColumnFilterCriteria(i, filterCriteria);
}
}
}
Note:
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.
References:
setValues(values)
getLastRow()
getFilter()
Class Filter

Related

Sending multiple products in a order data to Google sheets only sends the first product. Why?

I must admit that I donĀ“t have much experience so I tried my best so far-
I made a webhook on my woocommerce store and made a script on google apps to export new order data directly to spreadsheet.
The script partly works but the problem is that when there are more products in order it only exports one row and ignores the rest - so the order ID is correct the name of customer is exported but just 1 of his products from his order is exported to spreadsheet, second problem is that product ID is wrong.
The script is below:
//this is a function that fires when the webapp receives a GET request
function doGet(e) {
return HtmlService.createHtmlOutput("request received");
}
//this is a function that fires when the webapp receives a POST request
function doPost(e) {
var myData = JSON.parse([e.postData.contents]);
var order_number = myData.number;
var shipping_company = myData.shipping.company;
var shipping_method = myData.shipping_lines[0].method_title;
var customer_note = myData.customer_note;
var shipping_lastname = myData.shipping.last_name;
var lineitems=""
for (i in myData.line_items)
{
var product_id = myData.line_items[i].product_id;
var quantity = myData.line_items[i].quantity;
}
var sheet = SpreadsheetApp.getActive().getSheetByName("export");
sheet.appendRow([order_number,product_id,customer_note,shipping_method,shipping_lastname,shipping_company,quantity,lineitems]);
}
on the sheet there is one order - which actually has 3 products in the order but only one was exported, also product ID is 4135 and correct should be 4137.
If anyone has any feedback for me I would be very happy for any help, thanks.
Guys thanks for the helpful tips - I changed the looping and I also realized that I was exporting product_id but in fact what I was looking for variation_id.
Here is the finished code I hope it will help someone:
//this is a function that fires when the webapp receives a GET request
function doGet(e) {
return HtmlService.createHtmlOutput("request received");
}
//this is a function that fires when the webapp receives a POST request
function doPost(e) {
var myData = JSON.parse([e.postData.contents]);
var order_number = myData.number;
var shipping_company = myData.shipping.company;
var shipping_method = myData.shipping_lines[0].method_title;
var customer_note = myData.customer_note;
var shipping_lastname = myData.shipping.last_name;
var lineitems=""
for (i in myData.line_items)
{
var variation_id = myData.line_items[i].variation_id;
var quantity = myData.line_items[i].quantity;
var product_items = variation_id;
var lineitems =lineitems+product_items;
var sheet = SpreadsheetApp.getActive().getSheetByName("export");
sheet.appendRow([order_number,variation_id,customer_note,shipping_method,shipping_lastname,shipping_company,quantity]);
}
}

How to set the id of document of firestore using google sheets

I've got a table in google sheets which I want to upload to the firstore using a custom document id to prevent data duplication. How can I change the id of the document.
Full Code:
function classRoutineFunction() {
var firestore = FirestoreApp.getFirestore (email, key, projectId);
// get document data from ther spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "Sheet1";
var sheet = ss.getSheetByName(sheetname);
// get the last row and column in order to define range
var sheetLR = sheet.getLastRow(); // get the last row
var sheetLC = sheet.getLastColumn(); // get the last column
var dataSR = 2; // the first row of data
// define the data range
var sourceRange = sheet.getRange(2,1,sheetLR-dataSR+1,sheetLC);
// get the data
var sourceData = sourceRange.getValues();
// get the number of length of the object in order to establish a loop value
var sourceLen = sourceData.length;
// Loop through the rows
for (var i=0;i<sourceLen;i++){
if(sourceData[i][1] !== '') {
var data = {};
var dateSt = sourceData[i][0].toString();
var stDate = new Date(dateSt);
var stringfied = JSON.stringify(stDate);
var updatedDt = stringfied.slice(1,11);
data.date = updatedDt;
data.time = sourceData[i][1];
data.batch = sourceData[i][2];
data.topic = sourceData[i][3];
data._id = dateStr + batch; // Want to set a custom id
firestore.createDocument("classRoutine",data);
}
}
}
You can pass the name of the new document into the string argument when calling createDocument. From the documentation on creating documents in Firestore from your script:
Alternatively, we can create the document in the FirstCollection collection called FirstDocument:
firestore.createDocument("FirstCollection/FirstDocument", data)
Here FirstDocument is the name of the document in FirstCollection.
So for you this would look something like:
firestore.createDocument("classRoutine/yourCustomId",data);

Object overwritten in firebase

Data sending from google sheet:
Script using to send data to firebase from google sheet.
function writeData() {
var ss = SpreadsheetApp.openById("####");
var sheet = ss.getSheets()[0];
var data = sheet.getDataRange().getValues();
var dataToImport = {};
for(var i = 1; i < data.length; i++) {
var department = data[i][0];
var year = data[i][1];
var course = data[i][2];
dataToImport[department] = {};
dataToImport[department][year] = {}
dataToImport[department][year][course] = {}
dataToImport[department][year][course][i] = {
course: data[i][3],
dateAdded: data[i][4],
fileSize: data[i][5],
fileType:data[i][6],
downloadLink: data[i][7],
};
}
var firebaseUrl = "https:url";
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
base.setData("",dataToImport);
}
Data that is sent:
Q: My questions that since i had parent electrical and its two childs that were 1 and 2 and each child had a course dsp and aes respectively but only 1 child is send that is the second one.
Why first child is not sent to firebase ?
This code replacing previous entry with new entry. Replace this -
dataToImport[department] = {};
dataToImport[department][year] = {}
dataToImport[department][year][course] = {}
with this -
dataToImport[department] = dataToImport[department] || {};
dataToImport[department][year] = dataToImport[department][year] || {}
dataToImport[department][year][course] = dataToImport[department][year][course] || {}
As a side note to #ra89fi's answer (because I didn't pick up on the overwrite bug).
The line base.setData("",dataToImport); is setting all data at the root of your database ("") with the given data (dataToImport). Any other data in your database will be deleted.
Instead, you should use an update operation.
I unfortunately can't establish which version of the Firebase API you are using. It's not quite the REST API and not quite JavaScript. So here are some relevant documentation links.
REST Update Guide
REST Update Reference
JavaScript (Web) Update Guide
JavaScript (Web) Update Reference

How to browse to the next page in a datasource that is loaded into table in Google AppMaker

I'm working on a requirement where I have a datasource named 'emailSearchResults' where I search for email messages metadata and load the results in the datasource.
The fields in the datasource are not relevant, however I set the datasource to have 50 records per page as per the below screenshot:
The script I used to load the datasource is shown in the query field, that call the following script:
function getMessageDetails(userId, msgID)
{
var messageDetails = [];
var messageData;
var msgID_,subject_,from_,date_;
messageData=Gmail.Users.Messages.get(userId,msgID,{format:"metadata", metadataHeaders:["Message-ID", "Subject", "From", "Date"]});
console.log(messageData.payload.headers);
//console.log(msgID);
//console.log(messageData.payload.headers[3].value);
date_="<na>";
from_="<na>";
subject_="<na>";
msgID_="<na>";
for (var counter =0;counter<4;counter++)
{
if (messageData.payload.headers[counter].name=="Message-ID")
{
msgID_=messageData.payload.headers[counter].value;
}
if (messageData.payload.headers[counter].name=="Subject")
{
subject_=messageData.payload.headers[counter].value;
}
if (messageData.payload.headers[counter].name=="From")
{
from_=messageData.payload.headers[counter].value;
}
if (messageData.payload.headers[counter].name=="Date")
{
date_=messageData.payload.headers[counter].value;
}
}
messageDetails.push(date_);
messageDetails.push(from_);
messageDetails.push(subject_);
messageDetails.push(msgID_);
return messageDetails;
}
function searchMessages(userId,condition)
{
//
// first we build the conditions
// we can make it fixed
// or we can make it dynamic
var searchResult;
var deleteResult;
var currentMessage;
var results = [];
var pageToken;
var params = {};
var _stat;
var options = {
includeSpamTrash: "true",
pageToken: pageToken
};
var msgRecord = [];
do
{
searchResult=Gmail.Users.Messages.list(userId,options);
for (var i = 0; i < searchResult.messages.length; i++)
{
var record=app.models.emailSearchResults.newRecord();
msgRecord=getMessageDetails(userId,searchResult.messages[i].id);
record.msgMainID=searchResult.messages[i].id;
record.msgID=msgRecord[3];
record.subject=msgRecord[2];
record.senderAddress=msgRecord[1];
record.msgDate=msgRecord[0];
/*console.log(searchResult.messages[i].id);
console.log(msgRecord[3]);
console.log(msgRecord[2]);
console.log(msgRecord[1]);
console.log(msgRecord[0]);
return;*/
results.push(record);
msgRecord=null;
}
if (searchResult.nextPageToken) {
options.pageToken = searchResult.nextPageToken;
}
} while (searchResult.pageToken);
searchResult=null;
return results;
}
On the main page I put a table and linked it to the datasource, and I enabled pagination on the table, so I get the pager buttons at the bottom of the table as below:
When I execute the app and the datasource is filled, I see the first page results in a correct way, however when I want to move to the next page, I click the next page button and once the loading is complete I find out that I still see the same results from the first page on the table.
I am not familiar with how to make the table show the results of the second page then the third page, and I am going in circles on this...
Hope the explanation is clear and addresses the issue..
I would really appreciate any help on this!
Regards
Currently pagination isn't working as expected with calculated datasources. You can, however, build your own. There are several changes you'll need to make to accomplish this. First you'll want to refactor your searchMessages function to something like this:
function searchMessages(userId, pageToken){
var results = [];
var options = {
includeSpamTrash: "true",
pageToken: pageToken,
maxResults: 50
};
var searchResult = Gmail.Users.Messages.list(userId, options);
for (var i = 0; i < searchResult.messages.length; i++){
var record = app.models.emailSearchResults.newRecord();
var msgRecord = getMessageDetails(userId,searchResult.messages[i].id);
record.msgMainID = searchResult.messages[i].id;
record.msgID = msgRecord[3];
record.subject = msgRecord[2];
record.senderAddress = msgRecord[1];
record.msgDate = msgRecord[0];
results.push(record);
}
return {records: results, nextPageToken: searchResult.nextPageToken};
}
Then you'll want to change your datasource query. You'll need to add a number parameter called page.
var cache = CacheService.getUserCache();
var page = query.parameters.page || 1;
var pageToken;
if(page > 1){
pageToken = cache.get('pageToken' + page.toString());
}
var results = searchMessages('me', pageToken);
var nextPage = (page + 1).toString();
cache.put('pageToken' + nextPage, results.nextPageToken);
return results.records;
You'll need to modify the pagination widget's various attributes. Here are the previous/next click functions:
Previous:
widget.datasource.query.pageIndex--;
widget.datasource.query.parameters.page = widget.datasource.query.pageIndex;
widget.datasource.load();
Next:
widget.datasource.query.pageIndex++;
widget.datasource.query.parameters.page = widget.datasource.query.pageIndex;
widget.datasource.load();
You should be able to take it from there.

How do I sync entries from multiple columns on google sheets to the description box on google calendar

I am currently working off a script that generates a calendar event from a google sheet line entry. My issue is I am not able to add entries from more than one column to the description field on my calendar. The following code assumes I only want one column to be used in the entry of a description field. How do I modify this so that I can include another column's data into the description field of a google calendar entry?
//insert your google calendar ID
var calendarId = "mygmailcalendar.com";
//index (starting from 1) of each column in the sheet
var titleIndex = 2;
var descriptionIndex = 3;
var startDateIndex = 4;
var endDateIndex = 5;
var googleCalendarIndex = 6;
/*
find the row where the Google Calendar Event ID is blank or null
The data of this row will be used to create a new calendar event
*/
function findRow(sheet) {
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
if(values[i][googleCalendarIndex-1]=="" || values[i][googleCalendarIndex-1]==null)
newEvent(i+1);
}
};
/*
get the data of the new row by calling getSheetData() and
create a new Calendar event by calling submitToGoogleCalendar()
*/
function newEvent(row){
var sheet = SpreadsheetApp.getActiveSheet();
var eventId = submitToGoogleCalendar(getSheetData(sheet,row),null)
if(eventId!=null)
sheet.getRange(row,googleCalendarIndex,1,1).setValue(eventId);
};
/*
Store the data of a row in an Array
*/
function getSheetData(sheet,row)
{
var data = new Array();
data.title=sheet.getRange(row,titleIndex,1,1).getValue();
data.description=sheet.getRange(row,descriptionIndex,1,1).getValue();
data.startDate = sheet.getRange(row,startDateIndex,1,1).getValue();
data.endDate = sheet.getRange(row,endDateIndex,1,1).getValue();
return data;
};
/*
if a cell is edited in the sheet, get all the data of the corresponding row and
create a new calendar event (after deleting the old event) by calling submitToGoogleCalendar()
*/
function dataChanged(event){
var sheet = SpreadsheetApp.getActiveSheet();
var row = event.range.getRow();
var eventId = sheet.getRange(row,googleCalendarIndex,1,1).getValue();
var eventId = submitToGoogleCalendar(getSheetData(sheet,row),eventId)
if(eventId!=null)
sheet.getRange(row,googleCalendarIndex,1,1).setValue(eventId);
};
/*
This function creates an event in the Google Calendar and returns the calendar event ID
which is stored in the last column of the sheet
*/
function submitToGoogleCalendar(sheetData,eventId) {
// some simple validations ;-)
if(sheetData.title == "" || sheetData.startDate == "" || sheetData.startDate == null)
return null;
var cal = CalendarApp.getCalendarById(calendarId);
var start = new Date(sheetData.startDate);
var end = new Date(sheetData.endDate);
//end.setHours(23);
//end.setMinutes(59);
//end.setSeconds(59);
// some simple date validations
if(start > end)
return null;
var event = null;
//if eventId is null (when called by newEvent()) create a new calendar event
if(eventId==null)
{
event = cal.createEvent(sheetData.title, start, end, {
description : sheetData.description,
});
return event.getId();
}
/*
else if the eventid is not null (when called by dataChanged()), delete the calendar event
and create a new event with the modified data by calling this function again
*/
else
{
event = cal.getEventSeriesById(eventId);
event.deleteEventSeries();
return submitToGoogleCalendar(sheetData,null);
}
return event.getId();
};
You cannot populate the event description from two columns in an event export script as far as I'm aware.
You can however join multiple columns into the column you are exporting as the description column quite easily quite a few methods including formulas and scripts. For example :
=ARRAYFORMULA(L7:L&""&AL7:AL)

Resources