Shopify: how to retrieve shop level metafield in the cartpage? - shopify

At a shop level, I created a metafield for disabled dates as shown in the image below, that I want to retrieve and assign to a LIQUID variable in the code following the image.
Code in cart page:
window.addEventListener("load", function() {
// Don't add 0 before month and date to make it two digit.
//var disabledDays = ["2022-5-30","2022-7-4","2022-9-5","2022-11-24","2022-12-23","2022-12-24","2022-12-25","2022-12-30","2022-12-31","2023-1-1","2023-1-2"];
disabledDays = {{ shop.metafields.disabledDays.value }};
var minDate = new Date();
var maxDate = new Date();
maxDate.setDate((maxDate.getDate()) + 60);
minDaysToShip = 2; // Default minimum days
if (minDate.getDay() == 5) {
// Friday. Set min day to Tuesday. 4 days from now.
minDaysToShip = 4;
} else if (minDate.getDay() == 6) {
I see that {{ shop.metafields.disabledDates.value }}; is not reading the metadata content. Please show me the right way to do it.

If you are in a .liquid file, the following syntax should work for string type metafields:
{{ shop.metafields.disabledDays.disabledDays }}
Shopify added more metafield types and the new ones can indeed be accessed through the .value key, see this thread for more details:
https://community.shopify.com/c/technical-q-a/how-to-access-json-data-from-the-new-shopify-native-metafields/td-p/1258088

Related

Suitescript: copying sublist data from one record to another

I have a before load user event function on an invoice record that create a button called 'create vendor bill'.
When this button is pressed, a new vendor bill record is opened. The UE script:
/**
*#NApiVersion 2.x
*#NScriptType UserEventScript
*/
define([
"N/url",
"N/record",
"N/runtime",
"N/ui/serverWidget",
"N/redirect",
], function (url, record, runtime, serverWidget, redirect) {
var exports = {};
/**
* #param {UserEventContext.beforeLoad} context
*/
function beforeLoad(context) {
if (
context.type == context.UserEventType.EDIT ||
context.type == context.UserEventType.VIEW
) {
var record = context.newRecord;
var recordId = record.id;
var recordType = record.type;
var customer = record.getValue({ fieldId: "entity" });
log.debug("entity", customer);
var scriptObj = runtime.getCurrentScript();
var customForm = scriptObj.getParameter({
name: "custscript_custom_form_vb",
});
var recordSublist = record.getSublist({ sublistId: "item" });
log.debug("item", recordSublist);
var form = context.form;
log.debug("form", form);
var userVarStr = record;
log.debug("uservarstr", userVarStr);
var userVarURL = url.resolveRecord({
recordType: "vendorbill",
params: {
entity: parseInt(customer),
supportcase: recordId,
cf: parseInt(customForm),
},
});
form.addButton({
id: "custpage_button_test",
label: "Create Vendor Bill",
functionName: "getDetails('" + userVarURL + "')",
});
}
}
exports.beforeLoad = beforeLoad;
return exports;
});
Once the page redirects to the vendor bill form, a client script (deployed on the form), sets the field values on the body of the vendor bill using the parameters passed in the url
This is working as expected.
Where I am getting stuck is trying to work out how to pass the 'item' sublist values to from the invoice to the vendor bill?
Would I pass this as an array?
From what I understand, there is a limit to the number of characters that can be passed via the url.
I can't find anything online or in the Netsuite documentation that deals with passing sublist values between records
For starters I would want to see the Client Script.
One option would be to only pass the Invoice Record ID and Type. Then you can create a Suitelet to be used as a proxy and get the sublist data by a saved search.
Something to keep in mind is that if the sublist is very very long you may reach a execution timeout so you may want to consider triggering a MapReduce script to populate the sublist again you would pass it the recType and ID of the invoice and vendor bill and then use a saved search to get the data.
There are other approaches but I would need to see the client script.

Javascript with a for in loop

working on the follow code. Having some issues with taking the user input from the day and the temp. I have a start but again running into an issue with Step 2 & 3 unable to pass the information to the array and figure out how to display it. Any insight and direction would be greatly appreciated. Thanks
var temperatures = [];
var days = ["Monday", "Tuesday","Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
var $ = function (id) {
return document.getElementById(id);
}
var takeTemps = function () {
//###STEP 2
//Get the user inputted temp, validate it making sure it's a number
//if it's valid add it to the temperatures array at the index that
// corresponds with the day of week, e.g. 0 for Monday, 2 for Wednesday
var userTemp=(parseFloat($("tempIn").value));
while (!isNaN(tempIn)==true)
{
alert("Please enter a numeric value");
$("tempIn").focus();
}
//This gets the value from the selected menu option
var index = $("daySelect").value;
for(var dayTemp in temperatures)
{
var daily=temperatures[index]
}
//remove this when done, this just tests your menu you wrote for step 1
alert( index + " indexes day " + days[index]+ userTemp);
//Call displayTemps ONLY if the temp input was valid.
displayTemps();
//EXTRA work / not credit
// have it auto advance the selected day in the menu
// by assigning into $("daySelect").value
// If it was on Sunday change it to Monday and only on valid input
}
var displayTemps = function () {
//###STEP 3
//loop through non-undefined indexes in the temperatures array
//appended them to tempString adding the day .e.x
//Tuesday: 89
//Friday: 98
//display the string to the page by setting the value of the textarea
//
//In the same loop sum the temperatures and count
// how many there are so you can calculate the average
// and output the average temp on the page.
tempString = "";
tempTemp = 0;
for(var i in temperatures) {
tempString += index + ": " + temperatures[i];
}
document.write(tempString + "<br>");
var average =tempTemp+10;
$("tempList").value=tempString;
$("avgOut").value=average;
}
window.onload = function () {
$("addTemp").onclick = takeTemps;
//###STEP 1
//Use a for loop here to write options to the select for each day of the week
// <option value="0">Monday</option>
// using += here with innerHTML property takes the existing values and concats this on the end
for (var i =0; i<7; i++)
{
$("daySelect").innerHTML += "<option value=\""+ i + "\">" + days[i] + "</option>\n";
$("daySelect").value = "";
//var day=i-1;
//var day = days[i];
}
$("tempIn").focus();
}
Struggles with Step 2 and 3 Beleive i have #1 good to go I have enclosed the HTML for reference
<html>
<head>
<script src=script.js></script>
<head>
<body>
<section>
<select id="daySelect">
<option value="">Select a day</option>
</select>
<input type="text" id="tempIn">
<input type="button" id="addTemp" value="add temperature">
<br>
<br>
<label for="tempList">Temperature List</label>
<br>
<textarea id="tempList" rows="7" cols="50"></textarea>
<br>
<label>Average Temperature</label>
<input type="text" id="avgOut" disabled>
</section>
</body></html>
I would suggest to provide more specific information about which part of the code you are concerned with, and especially, provide the HTML code as well since that would enable us to see more clearly what you are trying to do.
When you are done, I will edit this answer and you will get appropriate guidance.
Keep coding!
EDIT
Take a look:
var userTemp=(parseFloat($("tempIn").value));
while (!isNaN(tempIn)==true)
{
alert("Please enter a numeric value");
$("tempIn").focus();
}
Looking at this little piece of code from "Step 2", I can think of a number of bugs already. Of course I'm not sure since I haven't seen your HTML yet, but it seems like:
You have put the value of the input into a variable named userTemp, yet you are checking for a variable named "tempIn" for validation. The second one probably doesn't exist at that point in time. "tempIn" was the name of your DOM element, not the JS variable you've assigned its value. You have to check for the userTemp variable.
In your validation, you are checking for the opposite of isNaN. NaN means "Not a Number", so the opposite of that would be actually number, so the statement is wrong. Not to mention that in this, you would not need to explicitly express "==true", you can check like this: while(isNaN(userTemp))
If you want to iterate a while statement for validation until you get a valid number, you need to put the variable assignment inside the while statement, since you'll need to try to assign the new number each time the validation loop iterates.
EDIT 2 - finished
Your code is live here:
https://codepen.io/bradib0y/pen/OBEdvp?editors=1010
Please note that if you are working your way through a course and this was your assessment, you've gained exactly nothing with me making these tasks for you. You will only gain from getting throught these challenges yourself.
I suggest to spend at least 1 hour with analyzing this code step by step and try to replicate it in a similar project. If you still have struggle with understanding, do yourself a favor and start over with basic javascript once again. You will be an expert on this within a week, if you put the basics down right. But if you still can't grasp the basics and keep pushing forward with more complex issues, you will have a hard time.

new objects created in Vuejs getting updated to last value

I am new to Vuejs but am having an issue with some code.
I am trying to 'flatten' a series of line_items of orders into a list of items and then create meta information for each object. The first part works fine but it appears as if the reactive nature of Vuejs is causing the last value of these newly created objects to extend across all previous incarnations.
I have created a fiddle here. http://jsfiddle.net/trestles/ruj7hzcf/3/
I presume that the problem is in creating my item (~ line 70 in fiddle).
// here's the problem - why is quantity being updated to the last value?
var item = {item_id: line_item.menu_item_id, header: line_item.menu_item_header, quantity: line_item.quantity, locations: locations }
this.items.push(item);
Why in this case would item be updated to the last value?
In the table, the results are:
test item 3 5 8
spiced shrimp with horseradish cocktail sauce (per dozen) 9 5 8
dates with bacon & parmesan (per dozen) 5 5 8
marcona almonds (serves 8) 6 5 8
marinated olives (serves 8) 8 5 8
and should be
test item 3 3 0
spiced shrimp with horseradish cocktail sauce (per dozen) 9 2 7
dates with bacon & parmesan (per dozen) 5 5 0
marcona almonds (serves 8) 6 0 6
marinated olives (serves 8) 8 0 8
What am I doing wrong?
The root of most problems in your existing code is reusing the same objects - elements of this.locations and this.orderPickupTimes arrays - again and again as you pass those through createNewItem. Augment those lines in your code:
// instead of var location = this.locations[j];
var location = Object.assign({}, this.locations[j]);
// and within the internal loop
// instead of var order_pickup_time = this.orderPickupTimes[k];
var order_pickup_time = Object.assign({}, this.orderPickupTimes[k]);
... and see the difference it makes.
Still, it's only part of the problem. The key idea of your code is storing -alongside each 'item' object - the whole list of locations and pickup times, having the ones with some orders set their 'quantity' attribute changed.
But to do this, you need to differentiate between the items already processed (with their structures already filled) - and fresh items. It's cumbersome, yes, but might work with something like this:
var locations = [];
var item = this.items.find(it => it.item_id === line_item.menu_item_id);
if (item) {
locations = item.locations;
}
else {
item = {item_id: line_item.menu_item_id, header: line_item.menu_item_header, quantity: 0, locations: locations };
this.items.push(item);
}
... and have this repeated all the time you need to choose between creating a new location or orderPickupTime - or reusing the existing ones.
Here's the demo illustrating this approach.
Still, I'd change two major parts here.
First, I'd create a dedicated - private - function to group the order items by their locations and order pickup times. Something like this:
function _groupItemsByOrders(orders) {
return orders.reduce(function(groupedItems, order) {
var locationId = order.location_id;
var orderPickupTimeKey = order.order_pickup_time_short;
order.line_items.forEach(function(lineItem) {
if (!groupedItems.hasOwnProperty(lineItem.menu_item_id)) {
groupedItems[lineItem.menu_item_id] = {
header: lineItem.menu_item_header,
quantity: 0,
locations: {}
};
}
var groupedItem = groupedItems[lineItem.menu_item_id];
if (!groupedItem.locations.hasOwnProperty(locationId)) {
groupedItem.locations[locationId] = {};
}
var groupedItemLocation = groupedItem.locations[locationId];
if (!groupedItemLocation.hasOwnProperty(orderPickupTimeKey)) {
groupedItemLocation[orderPickupTimeKey] = 0;
}
groupedItemLocation[orderPickupTimeKey] += lineItem.quantity;
groupedItem.quantity += lineItem.quantity;
});
return groupedItems;
}, {});
}
Second, I'd rearranged that template so that it takes the order of locations and orderPickupTimes from header arrays, then maps it to groupedData. Something like this:
<tr v-for="(item, item_id) in groupedItems">
<td>{{item_id}}</td>
<td>{{item.header}}</td>
<td>{{item.quantity}}</td>
<template v-for="location in locations">
<td v-for="opt in orderPickupTimes">
{{item.locations[location.id][opt.short]}}
</td>
</template>
</tr>
Now, your 'mounted' hook would look like this:
mounted(){
axios.get('http://www.mocky.io/v2/59f8ceb52d00004d1dad41ec')
.then(response => {
this.locations = response.data.locations;
this.orderPickupTimes = response.data.order_pickup_times;
this.groupedItems = _groupItemsByOrders(response.data.orders);
});
}
Here's the demo.

Coding a slider to update price per unit as quantity changes

http://jsfiddle.net/2e7gq/633/
I am trying to create a slider that will display 3 html input boxes.
Quantity
Price per unit
Total (quantity*price per unit)
the goal is to change var price per unit when the slider slides to increments of 50.
thanks!
I am new to programming here is my code:
`$(function() {
var inputValue = $(".mpd_value").val();
$( ".mpq_slider" ).slider({
min:50,
max:50000,
step:50,
value:50,
slide:function(event,ui){
$(".mpd_value").val(ui.value);
$(function(){
calculate();
});
}
});
$(".mpq_value").val($(".mpq_slider").slider("value"));
$(".mpq_value").change(function(event){
$(".mpq_slider").slider("option","value",inputValue);
})
function calculate(){
var quantity = $(".mpd_value").val();
var ppu = .84;
var total = quantity*ppu
$("#total").val(total);
$("#ppu").val(ppu);
};
});
It seems that you're missing a <div class="mpq_slider"></div>. Put it as the first line in the HTML spot. This is the part that makes room for the actual slider. Also, if I understand what you are trying to do correctly, you want the Quantity to increase with the slider and the ppu to change also (like things gets cheaper the more you buy)? If so, you are using the switch statement incorrectly. You could, instead do something with ifs and else ifs:
var ppu = defaultValue;
if (quantity <= 50) {
ppu = 1.117;
}
else if (quantity <= 75) {
ppu = .941;
}
...
Also note how I changed the >= into <=, since that'll make the program check in the right order. Here's what I changed to your code http://jsfiddle.net/2e7gq/637/ .
Now that I think about it, there's another way too. Y&ou can keep the switch if it says switch(true){ and then change all the >=s into <=s.
Glad to hear you're learning Javascript!

YUI Datatable - Get ID of DOM Element after page has loaded and use it in other YUI events

Okay so I have a YUI Datatable. Most of it is exactly as the how to guide says to construct it.
I have an event that governs changing the rows per page. It's linked to the rows per page drop down element and it saves the value of that drop down as a cookie when the drop down is changed.
var onRPPChange1 = YAHOO.util.Event.addListener("yui-pg0-1-rpp24", "change", getRPP_1);
The problem is that "yui-pg0-1-rpp24" (the ID of the drop down) changes whenever I make updates to my data table. I would like to extend this so that when the page loads it will dynamically insert the ID of that drop down into this event listener so that I don't have to keep editing it after future updates.
I've managed to construct that following that will capture the ID and I can alert it after the table loads, but so far, including the result of this function in the above addListener code isn't working.
var setRPPVars = function() {
YAHOO.util.Event.onAvailable("rppSpan", this.handleOnAvailable, this);
}
var rppSpanIds = new Array();
var rppArray = new Array();
setRPPVars.prototype.handleOnAvailable = function() {
var spans = document.getElementsByTagName("span");
var n = 0;
for(var i=0; i<spans.length; i++){
if(spans[i].id == "rppSpan"){
rppSpanIds[n] = spans[i];
if(n == 0){
rppTopID = rppSpanIds[n].firstChild.id;
rppArray[0] = rppTopID;
}
else if(n==1){
rppBottomID = rppSpanIds[n].firstChild.id;
rppArray[1] = rppBottomID;
}
n++;
}
}
alert(rppTopID);
alert(rppBottomID);
alert(rppArray);
}
var rppEvent = new setRPPVars();
//this is the part that doesn't work:
var onRPPChange0 = YAHOO.util.Event.addListener(rppArray[0], "onchange", getRPP_0);
function getRPP_0(){setRPPVars();oRPP = rppTopID;alert("rppTopID: "+rppTopID); alert("oRPP: "+oRPP);};
Any suggestions you've got would be awesome!
EDIT: For clarity's sake, this element is the rows per page drop down:
<span id="rppSpan">
<select id="yui-pg0-1-rpp24" class="yui-pg-rpp-options" title="Rows per page">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</span>
Subscribe to YAHOO.widget.Paginator's rowsPerPageChange instead:
http://developer.yahoo.com/yui/docs/YAHOO.widget.Paginator.html#event_rowsPerPageChange
Then you don't have to find the actual element.