Programmatically add products to a cart – Odoo 13 - javascript

I have a custom module that creates a form. Based on the answers inside this form I’m generating order line. After user sends this form I’m creating sale order with all products from the generated order line.
So from JavaScript I’m sending an JSON with products to buy:
order_data = [{product_id: 1, amount: 10, …},{product_id: 2, …}, …];
note = '';
this._rpc({
route: '/api/create_order',
params: { order_products: order_data, note: note }
}).then((data) => {
window.location = '/contactus-thank-you';
}).catch((error) => {
console.error(error);
});
And then inside Python I’m creating sale order based on the JSON:
#http.route('/api/create_order', type='json', auth='user', website=True)
def create_order(self, **kw):
uid = http.request.env.context.get('uid')
partner_id = http.request.env['res.users'].search([('id','=',uid)]).partner_id.id
order_products = kw.get('order_products', [])
note = kw.get('note', '')
order_line = []
for product in order_products:
amount = 0
if 'custom_amount' in product:
amount = product['custom_amount']
else:
amount = product['amount']
if amount > 0:
order_line.append(
(0, 0, {
'product_id': product['product_id'],
'product_uom_qty': amount,
}))
order_data = {
'name': http.request.env['ir.sequence'].with_user(SUPERUSER_ID).next_by_code('sale.order') or _('New'),
'partner_id': partner_id,
'order_line': order_line,
'note': note,
}
result_insert_record = http.request.env['sale.order'].with_user(SUPERUSER_ID).create(order_data)
return result_insert_record.id
But instead of generating sale order directly I need to use workflow from Odoo’s eCommerce addon. That way user can for example edit delivery address, choose payment etc. So I think I just need to programmatically put all the product inside a cart and then rest will be taken care of by Odoo built-in functionality.
But how? I’ve tried to find something inside Odoo source code but it is quite hard to grasp anything.

Odoo uses a typical Sale Order for handling products inside a cart. But the process isn't as simple as just creating Sale Order with some products. Odoo needs to know which order is linked with which cart etc.
Luckily Odoo has a method for dealing with it. There is a sale_get_order() method that lets you get an order that is currently linked with a cart or create new one if there isn't any.
I'm not sure if it is documented anywhere outside the source code so here is a slice from the code (/addons/website_sale/models/website.py):
def sale_get_order(self, force_create=False, code=None, update_pricelist=False, force_pricelist=False):
""" Return the current sales order after mofications specified by params.
:param bool force_create: Create sales order if not already existing
:param str code: Code to force a pricelist (promo code)
If empty, it's a special case to reset the pricelist with the first available else the default.
:param bool update_pricelist: Force to recompute all the lines from sales order to adapt the price with the current pricelist.
:param int force_pricelist: pricelist_id - if set, we change the pricelist with this one
:returns: browse record for the current sales order
"""
# ...
I'm using it alongside another method _cart_update() that lets me easily update products inside this order. There is also sale_reset() and I'm using it just to be sure that current session will be updated with particular sale order every time.
sale_order = request.website.sale_get_order(force_create=True)
request.website.sale_reset()
sale_order.write({'order_line':[(5, 0, 0)]})
for product in order_products:
sale_order._cart_update(product_id=product['product_id'], line_id=None, add_qty=None, set_qty=product['amount'])

Related

Troubleshooting SuiteScript 1.0 Line Level Field Sourcing Code (List/Record)

I am an inexperience technical developer working on my first SuiteScript using SuiteScript 1.0. I am getting an SSS_MISSING_REQD_ARGUMENT error, but I am sure there are many more in my code. The purpose of the script is to populate the department field on the expense record line item from a joined record. The end user will select a Project on the expense line, and the script should look up the department on the project record (a custom field) and add the value to the native department field. Code is copied below.
function ProjectSegment ()
{
var record = nlapiLoadRecord(nlapiGetRecordType(), nlapiGetRecordId());
var recordID = nlapiGetRecordId(record);
//internal ID of project record
var project = nlapiGetField ('custcol_nra_expense_project');
//load project record
var precord = nlapiLoadRecord('job', project);
//get department on project record (internal ID)
var pdepartment = precord.GetFieldValue('custentity_nra_dept_project');
//get project name from project record
var projectName = precord.GetFieldText('entityid');
//load existing search
var search = nlapiLoadSearch('job','customsearch161');
//add filter to include project name
search.addFilter(new nlobjSearchFilter('entityid',null,'is',projectName));
//run search
var resultSet = search.runSearch();
//get department line
var departmentResult = new nlobjSearchColumn('custentity_nra_dept_project');
//set value
nlapiSetFieldTexts('job','department',1,departmentResult)
//record.commitLineItem('department');
nlapiSubmitRecord(record, true);
}
//internal ID of project record
var project = nlapiGetFieldValue ('custcol_nra_expense_project');
Praveen Kumar's response is correct regarding the missing required argument, but there are many other issues with the script as you've surmised.
Side notes:
The getFieldValue and getFieldText methods of nlobjRecord are not capitalized.
For performance reasons, you could/should use a search to get the values you need from the job record. Loading a record just to get field values is wasteful unless you mean to change the record.
Your search filter should probably be based on the value (not text) of the entityid in the job record.
Your desired search column probably is invalid (I don't think a custentity field could be on a job record).
Getting the search result is incorrect.
You want something like this instead:
var result = resultSet.getResults(0, 1);
if (result) {
var department = result.getValue('custentity_nra_dept_project');
// etc.
}
All that said, though, from your description, I don't think you need the search anyway. Once you have pdepartment (again, using precord.getFieldValue), I think all you need is:
record.setFieldValue('department', pdepartment);
Or if you're setting the line-level department, it would be different.
What kind of script is this? I'd like to make more recommendations, but it depends on what's going on.

Shopify Access a product with its id on thank you page without using '/admin' in url

I am trying to access a specific product using its id from the below url,
https://tempstore.myshopify.com/products/1234.json
Its giving me 404 error.
Although, I am able to access all products as below:
https://tempstore.myshopify.com/products.json
I have to access the product which was just processed in checkout process.
I have its id as below:
var products = Shopify.checkout.line_items;
products will contain an array of product id's only which are processed in checkout.Now I need to access all other properties of these products.
I can surely do this:
https://tempstore.myshopify.com/admin/products/1234.json
But it requires Authentication.
Any thoughts?
From the frontend, you need to have the product handle to get the JSON object:
https://tempstore.myshopify.com/products/[handle].js
or
https://tempstore.myshopify.com/products/[handle].json
(Note that the returned values from the .js and .json endpoints are quite different from each other!)
Like you point out, the Shopify.checkout.line_items array of objects only has the product IDs, not the product handles. We're not completely out-of-luck, though, because we can get the entire list of products in the store including the product handles by hitting the /products.json endpoint.
Of course, this means grabbing a potentially huge JSON object just to get information that we should've had included in the checkout line items... but unless there's some alternate source of the line item information available on the checkout page, looping through the entire list may be what you need to do.
So your end code would look something like this:
Checkout.jQuery.getJSON( // Or whatever your preferred way of getting info is
'https://tempstore.myshopify.com/products.json',
function(prodlist){
for(var p = 0; p < prodlist.length; p++){
var prod = prodlist[p];
// Find if Shopify.checkout.line_items contains prod.id, left as exercise for the reader
if(found){
Checkout.jQuery.getJSON(
'https://tempstore.myshopify.com/products/' + prod.handle + '.js',
function(product){
/* Whatever needs to be done */
})
}
}
}
)
Hope this helps!
var shop = Shopify.shop;
var lineItems = Shopify.checkout.line_items;
var url = 'https://' + shop + '/products.json?callback=?';
var requiredData = [];
$.getJSON(url).done(function(data){
lineItems.forEach(function(lineItemProduct){
data.products.find(function(product){
if(lineItemProduct.product_id == product.id){
requiredData.push(product);
}
});
});
});
console.log(requiredData);
This is how I solved it, If it helps anybody :)

Dynamically adding new item extremly slow

I use ASP.NET MVC and Razor. User needs to populate some form which consists of list of objects. So I pass list of empty objects from controller to view. This is part of my main view:
foreach ( var product in Model.Products )
{
Html.RenderPartial( "ProductPartial", product );
}
In ProductPartial user enters some fields for each product.
User can dynamically add or remove products from list. Removing I solved with jquery live function, and it is fine. But I have problem with adding new products.
I solved it in this way: On plus sign click javascript function calls controller action:
public ActionResult NewProduct()
{
Product product = new Product();
product.UniqueId = Guid.NewGuid();
return PartialView( "ProductPartial", product );
}
I need unique id for every product because I want to be able to access products from jquery by ids.
Adding works correctly, but it is extremly slow, since I go on server for every new product. Is there some good way to make it faster?
Well, instead of asking the server for more of the same HTML, you could use jQuery.clone();
In the example I'm copying the first product in the list and giving it a new id, and then adding the copy to the end of the list.
var newProductHtml = $('.MyProducts')[0].clone();
newProductHtml.attr('id', MyNewId);
newProductHtml.appendTo('.MyProductsContainer');

How to push a second product into the data layer with GTM

I'm patching enhanced eccommerce reoprting code for Google analytics using Google Tag Manager - by screen scraping data for a checkout step in the process. The code look like this:
var els = document.querySelectorAll('div.field_cart');
Array.prototype.forEach.call(els,function(element){
dataLayer.push({
'event': 'checkout',
'ecommerce': {
'checkout': {
'actionField': {'step': 1, 'option': 'Visa'},
'products': [{
'id': $('div.field_cart')[0].innerText,
'price': $('div.field_cart')[1].innerText.match(/[0-9]+/),
'quantity': $('div.field_cart')[2].innerText
}//,{}...//
]
}
});
})
the problem is that if there are is more than one item - the second ovewrites the first etc instead of getting pushed ... I'd like to know how to append a new product to the products object?
Because you can't push the product alone you need to build object that has all the products inside.
first step is to retrieve object that is already in dataLayer (even if empty)
get the products object
push new item inside
push the whole object to dataLayer
If you have troubles with any of this steps let me know.
Try to define dataLayer varible first with:
var dataLayer = window.dataLayer || [];
It will take existing dataLayer variable and add use it or if there is none it will make array for it.
Also, I can see that you are using lower case "datalayer". It should be "dataLayer".
I hope this will help.
I turns out google tag manager rewired dataLayer.push() method to fire events and guard the dataLayer's size. A side effect is that it does not operate as expected. The way to overcome this issue was to push all the scraped product data into a javascript variable and once it was ready, to push that variable into the dataLayer as follows:
function scrape2DL() {
dataLayer.push({'ecommerce.checkout.actionField': {step: 2}}); //action object
var base = 0;
var products=[];
while (base < $('div.field_cart').length/4) {
products.push([{id: $('div.field_cart')[4 * base + 0].innerText, price: $('div.field_cart')[4 * base + 1].innerText.match(/[0-9]+/), quantity: $('div.field_cart')[4 * base + 2].innerText}]);
base += 1;
}//while
dataLayer.push({'ecommerce.checkout.products': products}); // add a product
}
scrape2DL();
dataLayer.push({event: 'checkout'}); // for GTM triggers
However it turns out that it is possible to fetch the products from the datalayer into a javascript variable using the datalayer get method and subsequently append more products into it.
However I ultimately avoided this aproach as it skips the event and size guard check wired into dataLayer.push().
The folowing resources were of some help:
SO question titled Console returns value but Google Tag Manager variable does not
Google Tag Manager For Nerds by Simo Ahava.

Adding taxes - paypal variables

I'm trying to add taxes to my paypal settings for my shopping cart.
Here's what I got in the Javascript code:
//static paypal request arguments
var pp_settings = {
cmd: '_cart',
upload: 1,
no_note: 0,
bn: 'JQPayPalShop_ShoppingCart_EC_US',
rm: 2,
custom: '',
discount_rate_cart:50,
tax_rate:9
};
I added "discount_rate_cart" to test if I was adding the variables at the right place, and yes the total is 50% cheaper. So I'm at the right place.
But then, when adding "tax_rate", I can type whatever I want next to it, it's just not working. No taxes added on the checkout page.
Any idea why?
EDIT: For those who wants to see the full code : http://jsfiddle.net/uNV6f/
You have the wrong variable name. For individual items, it's ok, but it looks like you're trying to set it up for the whole cart.
In that case it should be tax_cart
Check out this document with the full list of variables that you can use:
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_Appx_websitestandard_htmlvariables#id08A6HH0D0TA

Categories

Resources