Trying to use Sheets as db to update prices in WooCommerce - javascript

So I'm trying to use a single sheet as a price db to update prices in WooCommerce through the Woo API, using fetch.
It works, my problem is that it apparently works depending on the size of the dataset? I'm not sure because I can't understand the error.
UPDATED CODE
function getDataloopwoo() {
const ck = 'ck_fd0992917fbbb0464d4146ad5861f51adcb36369';
const cs = 'cs_6f8061efce415355fb6cac520bd1506ad126578a';
const website = 'https://www.atopems-desarrollo.com.ar';
const optionsGet =
{
'method': 'GET',
'contentType': 'application/x-www-form-urlencoded;charset=UTF-8',
'muteHttpExceptions': true,
};
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('PreciosBULK');
const codigos = sheet.getRange('A2:A').getValues();
const precios = sheet.getRange('B2:B').getValues();
const data = codigos.map(function(codigos, indice) {
return {
sku: codigos[0],
price: precios[indice][0]
}
})
const container = [];
var surl = website + '/wp-json/wc/v3/products?consumer_key=' + ck + '&consumer_secret=' + cs + '&per_page=100' + '&orderby=id' + '&order=asc' + '&status=publish' + '&page=1';
var url = surl
Logger.log(url)
var result = UrlFetchApp.fetch(url, optionsGet);
var headers = result.getAllHeaders();
var total_pages = 45;
var pages_count = 0;
while (pages_count < total_pages) {
if (result.getResponseCode() == 200) {
var wooProducts = JSON.parse(result.getContentText());
//Logger.log(result.getContentText());
}
for (var i = 0; i < wooProducts.length; i++) {
//Logger.log(i);
container.push({
sku: wooProducts[i]['sku'],
id: wooProducts[i]['id'],
price: wooProducts[i]['price']
});
}
pages_count++;
if (pages_count < total_pages) {
var surl = website + '/wp-json/wc/v3/products?consumer_key=' + ck + '&consumer_secret=' + cs + '&per_page=100' + '&orderby=id' + '&order=asc' + '&status=publish' + '&page=' + (pages_count + 1);
var url = surl
var result = UrlFetchApp.fetch(url, optionsGet);
Logger.log(url);
}
}
var data_obj = {}
for (let obj of data)
data_obj[obj.sku] = {'price': obj.price};
console.log(data_obj);
var container_obj = {}
for (let obj of container)
container_obj[obj.sku] = {'price': obj.price, 'id': obj.id};
console.log(container_obj);
const output = [];
for (let sku in container_obj) {
let data_subObj = data_obj[sku];
let container_subObj = container_obj[sku];
if (data_subObj.price > container_subObj.price) {
output.push({'id':container_subObj.id, 'regular_price':data_subObj.price});
}
}
console.log(output);
var temporary, chunk = 100;
for (let i = 0;i < output.length; i += chunk) {
temporary = output.slice(i, i + chunk);
var payloadUp = {
update: temporary
}
var headPost =
{
'method' : 'POST',
'contentType': 'application/json',
'payload': JSON.stringify(payloadUp)
};
console.log(payloadUp);
var urlPost = website + '/wp-json/wc/v3/products/batch?consumer_key=' + ck + '&consumer_secret=' + cs;
var result = UrlFetchApp.fetch(urlPost, headPost);
console.log(urlPost);
if (result.getResponseCode() == 200) {
console.log(result.getContentText());
};
}
}
I use the var headers to get all headers but since I'm testing ATM I don't use it. So if I get around 15-20 products from WooCoommerce everything works like a charm, I get all product data, create a new array with sku, id and price. Then compare that array with an array from my sheets with the updated price value and then push sku, id and updated price to a new array and POST that array to woocommerce batch update. Works fine, if I try to process more I get this error:
TypeError: Cannot read property 'price' of undefined
I'm really wracking my brains mostly because I'm a total novice in JS.
I'll post the logs,
Logger.log(url);
https://atopems.com/wp-json/wc/v3/products?consumer_key=ck_xxx&consumer_secret=cs_xxx&per_page=100&orderby=id&order=asc&status=publish&page=51
console.log(data_obj);
Logging output too large. Truncating output. { '200': { price: 299.98 },
'201': { price: 156.9 },
'202': { price: 112.05 },
'203': { price: 100.58 },
'204': { price: 126.33 },
'205': { price: 126.53 },
'206': { price: 2858.42 },
'207': { price: 2336.79 },
'208': { price: 401.25 },
'209': { price: 378.32 },
'210': { price: 282.78 },
'211': { price: 252.21 },
'212': { price: 292.34 },
'213': { price: 309.53 },
'214': { price: 385.96 },
'215': { price: 554.1 },
console.log(container_obj);
Logging output too large. Truncating output. { '60026': { price: '2319.6', id: 24942 },
'60032': { price: '4050.7', id: 24943 },
'60033': { price: '4050.7', id: 24944 },
'60119': { price: '7195.72', id: 24945 },
BR9010: { price: '984.5', id: 24067 },
BR9013: { price: '1744.32', id: 24068 },
BR9014: { price: '1869.03', id: 24069 },
BR9015: { price: '1869.03', id: 24070 },
BR9016: { price: '984.5', id: 24071 },
BR9017: { price: '747.66', id: 24072 },
BR9026: { price: '664.52', id: 24073 },
BR9037: { price: '830.62', id: 24074 },
BR9042: { price: '830.62', id: 24075 },
BR9043: { price: '747.66', id: 24076 },
BR9048: { price: '1204.44', id: 24077 },
BR9049: { price: '955.23', id: 24078 },
BR9050: { price: '955.23', id: 24079 },
BR9052: { price: '1079.9', id: 24080 },
BR9055: { price: '955.23', id: 24081 },
BR9056: { price: '1266.63', id: 24082 },
BR9059: { price: '955.23', id: 24083 },
BR9067: { price: '830.62', id: 24084 },
BR9068: { price: '1349.13', id: 24085 }
The exact error
17:07:38 Error
TypeError: Cannot read property 'price' of undefined
getDataloopwoo # JSONsheet.gs:63
The sheet in question, in case anyone wants to see the type of data I'm working with.
const data = codigos.map etc
uses 2 variables, SKU and PRICE, which are column A and B respectively in the sheet as simple objects.
EDIT:
Ok, Test sheet that can be copied with CK and CS Keys to a testing WooCommerce site.
https://docs.google.com/spreadsheets/d/14afFyj1geaNCWt_e4DE9S41wlgepWAtasK2z5_u1hdc/edit?usp=sharing
If run as is it can be reproduced without doing anything else.
Tried with 20 pages it works, with 45 it doesn't.
I'm not sure what more I could do to make it reproducible.
I legit don't understand what could be the root of the problem.

Modification points:
In your situation, it seems that the values of sku of container is not existing in the values of sku of data_obj. I thought that the reason for your issue might be due to this.
As a script for checking this, you can use const res = container.filter(e => !data_obj[e.sku]) for your script. In this case, [ { sku: 'L4943-0ULT', id: 3195, price: '5083.33' } ] is returned. When this value is searched from your sample Spreadsheet, l4943-0ult is found. In this case, the character case is different. By this, your issue occurs. I resulted in the reason of your issue is due to this.
When this issue was removed, how about the following modification?
From:
data_obj[obj.sku] = {'price': obj.price};
To:
data_obj[isNaN(obj.sku) ? obj.sku.toUpperCase() : obj.sku] = {'price': obj.price};
And,
From:
container_obj[obj.sku] = {'price': obj.price, 'id': obj.id};
To:
container_obj[isNaN(obj.sku) ? obj.sku.toUpperCase() : obj.sku] = { 'price': obj.price, 'id': obj.id };
And also, in order to avoid no keys in the if statement, how about adding the following modification?
From:
if (data_subObj.price > container_subObj.price) {
To:
if (data_subObj && data_subObj.price > container_subObj.price) {

Related

Array.push not pushing properly

EDIT: I've added the res.send(json), wasn't included the snippet.
I'm trying to extract/scrape a website and combine their output into json data. However when I run the endpoint, the response combines every result into its key, instead of adding a record per iteration. To expound, I get:
{
item: "item1item2item3item4item5item6",
title: "title1title2title3title4title5title5",
price: "price1price2price3price4price5price6"
}
This is my target output format though..:
{ item: "item1",
title: "title1",
price: "price1",
itemlink: "itemlink1" },
{ item: "item2",
title: "title2",
price: "price2",
itemlink: "itemlink2" },
{ item: "item3",
title: "title3",
price: "price3",
itemlink: "itemlink3" }, etc...
Here's the below snippet:
request(url, function(error, response, html){
if(!error){
var $ = cheerio.load(html);
var json = [];
/* Pulls out all the titles
$('.item-name').each(function() {
var title = $(this).text();
json2.push({title: title});
})
*/
function getID(str) {
return str.split('viewitem.php?iid=')[1];
}
$('.catlist').each(function(key, index) {
var title = $('.item-name').text();
var price = $('.catprice').text();
var id = getID($('h2').children().attr('href'));
var itemlink = $('h2').children().attr('href');
json.push({
id: id,
title: title,
price: price,
itemlink: itemlink
});
})
}
res.send(json)
})
I'm out of my wits, already spent hours on this. Any idea why they are not iterating properly for me? Thanks in advance!
$('.catlist').each(function(key, index) {
var title = $(this).find('.item-name').text();
var price = $(this).find('.catprice').text();
var id = getID($(this).find('h2').children().attr('href'));
var itemlink = $(this).find('h2').children().attr('href');
var temp =
{
id: id,
title: title,
price: price,
itemlink: itemlink
};
json.push(temp);
})
You need to find children for each .catlist and push them to array one by one
As you can see in your code you are assigning value to json in every iteration. So that's why it's not adding new record to it.
Have a look to this code
$('.catlist').each(function(key, index) {
var title = $('.item-name').text();
var price = $('.catprice').text();
var id = getID($('h2').children().attr('href'));
var itemlink = $('h2').children().attr('href');
var temp =
{
id: id,
title: title,
price: price,
itemlink: itemlink
};
json.push(temp);
})
Try this
json.push( {
id: id,
title: title,
price: price,
itemlink: itemlink
});

AngularJS pass filtered value to scope value

Good morning, i'm new to angularjs and want to practice it more, and here i have a question regarding a simple case while trying to learn to build my own angularjs webpage.
I have these two sets of data
$scope.data1 = [{ id: 1, name: "abc" },
{ id: 2, name: "efg" }]
$scope.data2 = [{ id: 1, info: "this is abc" },
{ id: 2, info: "absolutely not abc"}]
$scope.user = {
id: "",
name: "",
info: ""
}
and i have this input
<input ng-blur="passTheValue()" ng-model="user.idx" ng-type="text" placeholder="write name here"></input>
where i can write name on the text box.
my question is, how to pass all the value from data1 and data2 to $scope.user based on what input i have entered? For example, i write "abc" on the textbox, then my $scope.user will contain
id: 1, name: "abc", info: "this is abc"
i've tried to use $filter but i'm stuck at passing the value to the scope.
i'm sorry for my English, it's not my main language.
This is not the classical usecase for a filter: Do the processing of your data in the passTheValue() function.
this.passTheValue = function(){
$scope.data1.forEach(function(value, index, array){
if(value.name == $scope.idx){
$scope.user = {id: value.id, name: value.name, info: $scope.data2[index] }
}
})
}
HTML
<input ng-blur="passTheValue(user.idx)" ng-model="user.idx" ng-type="text" placeholder="write name here"></input>
Angular
$scope.passTheValue = function(name) {
angular.forEach($scope.data1, function(value, key){
if(value.name == name){
$scope.user.id = value.id;
$scope.user.name= value.name;
$scope.user.info = $scope.data2.filter(function(v) {
return v.id === value.id; // Filter out the appropriate one
})[0].info;
console.log($scope.user.id);
console.log($scope.user.name);
console.log($scope.user.info);
}
});
}
In your HTML , I've replaced the user.idx by name because you're searching by name. Sample on Plunker : https://plnkr.co/edit/bumDWC713dVWGnKoO5G3?p=preview
<body ng-app='app'>
<div ng-controller='appCtrl'>
<input ng-blur="passTheValue()" ng-model="name" ng-type="text" placeholder="write name here">
</div>
</body>
In your javascript, I add to simply search methods.
var app = angular.module('app',[])
.controller('appCtrl',function(){
$scope.data1 = [{
id: 1,
name: "abc"
},
{
id: 2,
name: "efg"
}];
$scope.data2 = [{
id: 1,
info: "this is abc"
},
{
id: 2,
info: "absolutely not abc"
}];
$scope.name = "";
$scope.user = {
id: "",
name: "",
info: ""
};
function findIdByName(name) {
for (var i = 0 ; i< $scope.data1 ; i++) {
if($scope.data1[i].name == name)
return $scope.data1[i].id;
}
return -111 ; //Assume that it's an impossible ID
}
function findInfoById(id) {
for (var i = 0 ; i< $scope.data2 ; i++) {
if($scope.data1[i].id == id)
return $scope.data1[i].info;
}
return -111 ; //Assume that it's an impossible Info
}
$scope.passTheValue = function(){
var id = findIdByName($scope.name);
if(id != -111){
var info = findInfoById(id);
if(info != -111){
$scope.user.id= id;
$scope.user.name = $scope.name;
$scope.info = info;
}else {
console.log(id,"Id doesn't exist in $scope.data2")
}
}else {
console.log(name,"name doesn't exist in $scope.data1")
}
}
});

Getting id or name of folder in fuelux tree

I'm trying to get the id or name of the selected folder in a fuelux tree but couldnt manage to get it done.
My tree is a classic folder/file type tree and I want to be able to see the id of the folder when I click on a file.
this is my datasource for tree
var treeDataSource = new DataSourceTree({
data: [
{ name: 'Elektronik Belgelerim', type: 'folder', 'icon-class': 'blue', additionalParameters: { id: 'F1' } },
{ name: 'Gelen Kutusu', type: 'folder', 'icon-class': 'blue', additionalParameters: { id: 'F2' } },
{ name: 'Giden Kutusu', type: 'folder', 'icon-class': 'blue', additionalParameters: { id: 'F3' } },
{ name: 'Çöp Kutusu', type: 'folder','icon-class':'green', additionalParameters: { id: 'I1' } },
//{ name: 'Çöp Kutusu', type: 'item', 'icon-class': 'success', additionalParameters: { id: 'F4' } },
//{ name: 'Reports', type: 'item', additionalParameters: { id: 'I1' } },
//{ name: 'Finance', type: 'item', additionalParameters: { id: 'I2' } }
],
delay: 400
});
js function for tree begins like this inside tree-custom.js
var e = function (e, i) {
this.$element = t(e), this.options = t.extend({}, t.fn.tree.defaults, i), this.$element.on("click", ".tree-item", t.proxy(function (t) {
this.selectItem(t.currentTarget)
}, this)), this.$element.on("click", ".tree-folder-header", t.proxy(function (t) {
this.selectFolder(t.currentTarget)
}, this)), this.render()
};
and this is where I add the links under folders again inside trree-custom.js. Very primitive I know but that's all I can do with my current skillset. The part I added is between quotes. Rest came with beyondadmin theme and looks like usual fuelux.
selectFolder: function (e) {
//alert("testselectFolder");
//
//alert($('#myTree').tree({ dataSource: dataSource }));
var i, n, r, o = t(e),
s = o.parent(),
a = s.find(".tree-folder-content"),
l = a.eq(0);
//-----------------------------------------------
var li = $('<li>');
var TcgbLink = $('<a href=/E-Belge/Main/Folder/Inbox/?Type=1&DocumentTypeId=3>e-TCGB</div>' +"</br>");
var FaturaLink = $('<a href=/E-Belge/Main/Folder/Inbox/?Type=1&DocumentTypeId=4>e-Fatura</div>' + "</br>");
var Dolasim = $('<a href=>e-Dolasim Belgesi</div>');
li.append(FaturaLink);
a.append(li);
li.append(TcgbLink);
a.append(li);
li.append(Dolasim);
a.append(li);
//-----------------------------------------------
o.find(".fa.fa-folder").length ? (i = "opened", n = ".fa.fa-folder", r = "fa fa-folder-open", l.show(), a.children().length || this.populate(o)) : (i = "closed", n = ".fa.fa-folder-open", r = "fa fa-folder", l.hide(), this.options.cacheItems || l.empty()), s.find(n).eq(0).removeClass("fa fa-folder fa-folder-open").addClass(r), this.$element.trigger(i, o.data())
},
Now these links are being generated under all 4 folders. I want to be able to get the id (or name, preferably Id) of the folder so I can assign new Type parameters to querystring.
So far I tried to reach the id with this.data.id to no avail.
Instead of injecting the folder's children in the selectFolder callback, it is recommended to add the children via the dataSource callback (as in this example code: http://getfuelux.com/javascript.html#tree-usage-javascript).
The first argument to the dataSource is the "parent data" when you click on a tree node (with the second argument being the callback that you send the new array of child data).
This way you can use the selected event for getting your ID, because it gets the jQuery data passed to it.

Javascript that loops and stores keys as variables and outputs additional fields from keys stored?

I'm new to JS and coding in general so I'm not sure how to effectively write a function for this. I want to write a function that takes an object as an argument and returns another object.
OrderFormContents = {
servicesSelected: {
hdrPhotos: "selected",
panos: "selected",
twilightPhotos: "selected"
}
}
hdrPhotos, panos, and twilightPhotos are all SKUs / unique identifiers.
I want to return an object like like:
CompletedOrderFormContents = {
servicesSelected: {
hdrPhotos: {
sku: "hdrPhotos",
calculatedPrice: 100, // returned from an object stored as a Session variable called calculatedPrices
title: "HDR Photography" //returned from looking up the sku from a Services collection.
},
panos: {
sku: "panos",
calculatedPrice: 125,
title: "Panoramas"
},
twilightPhotos: {
sku: "twilightPhotos",
calculatedPrice: 200,
title: "Twilight Photography"
}
}
}
So far I have been brute forcing it, explicitely defining all of the skus, and it is dumb:
var myFunction = function(OrderFormContents) {
CompletedOrderFormContents = {
servicesSelected: ""
};
CompletedOrderFormContents.servicesSelected.hdrPhotos = {
sku: "hdrPhotos",
calculatedPrice: Session.get("calculatedPrices").hdrPhotos,
title: Services.find({"sku" : "hdrPhotos"}).fetch()[0].title
};
CompletedOrderFormContents.servicesSelected.panos = {
sku: "panos",
calculatedPrice: Session.get("calculatedPrices").panos,
title: Services.find({"sku" : "panos"}).fetch()[0].title
};
CompletedOrderFormContents.servicesSelected.twilightPhotos = {
sku: "twilightPhotos",
calculatedPrice: Session.get("calculatedPrices").twilightPhotos,
title: Services.find({"sku" : "twilightPhotos"}).fetch()[0].title
};
};
How would I refactor this code so I'm at least not explicitly defining the SKU for each statement and explicitly defining each statement for each SKU? I've got UnderscoreJS installed.
EDIT Got it working.
completedOrderFormContents = {
servicesSelected: {}
};
for (sku in OrderFormContents.servicesSelected) {
if (OrderFormContents.servicesSelected.hasOwnProperty(sku)) {
completedOrderFormContents.servicesSelected[sku] = {
sku: sku,
price: Session.get("calculatedPrices")[sku],
title: Services.find( { "sku" : sku }).fetch()[0].title
}
}
}
I got it working.
//servicesSelected does not currently exist in completedOrderFormContents,
//so gotta create it - ie. simply doing completedOrderFormContents = {} would not work
//because the for loop is going to try and assign something to .servicesSelected
//later on and it needs that .servicesSelected key to already be there
completedOrderFormContents = {
servicesSelected: {}
};
for (sku in OrderFormContents.servicesSelected) {
if (OrderFormContents.servicesSelected.hasOwnProperty(sku)) {
completedOrderFormContents.servicesSelected[sku] = {
sku: sku,
price: Session.get("calculatedPrices")[sku],
title: Services.find( { "sku" : sku }).fetch()[0].title
}
}
}

items not within the <ul></ul>

I create a menu with a javascript object and jquery. I have certain items that needs to be in the <ul></ul> but instead they're beneath it.
http://jsfiddle.net/MWBt6/
I have 'Index' for example. In there I want to append a list of items.
I know the category id is 0 and the item id is 2, i stored those things in the data attribute.
Now how can I append a ul to that one?
That's not how append works. You need to create the elements on their own. Here:
var loadPath = "resources/books/book1/";
var menu = {
data: [{
name: "the book",
id: 0,
items: [{
name: "Introduction",
id: 0,
target: "inleiding.html"
}, {
name: "Content",
id: 1
}, {
name: "Index",
id: 2
}]
}, {
name: "my stuff",
id: 1,
items: [{
name: "Notes",
id: 0
}, {
name: "Marks",
id: 1
}]
}, {
name: "other",
id: 2,
items: [{
name: "Search",
id: 0
}, {
name: "Continue Reading",
id: 1
}]
}]
}
$(document).ready(function() {
var $menu = $('#menu');
for(var i = 0; i < menu.data.length; i++) {
var categorie = menu.data[i];
var categorieName = categorie.name;
var categorieId = categorie.id;
var items = categorie.items;
console.log("categorieName: " + categorieName);
var list = $('<ul>');
for(var j = 0; j < items.length; j++) {
var itemId = items[j].id;
list.append($('<li>').attr('data-itemId', itemId).text(items[j].name));
}
$menu.append(
$('<li>').attr('data-categorieId', categorieId).append(categorieName, list)
);
}
});​
Here's the updated jsFiddle.
You are trying to append as if the DOM is a text editor. You can't append the beginning of an element with it's opening tag, then later close that element with an append of a closing tag. Only full valid elements can be inserted into the DOM.
Instead, build an html string, then only make one append after the string is completed. This method is far more efficient than doing multiple appends also
Working demo: http://jsfiddle.net/MWBt6/3/

Categories