How Can I Add Website Decimal Quantity in Odoo - odoo-15

I Have a custom module website_decimal_quantity. after installing this module, In the website shop cart, when the user clicks on the + or - button to adjust quantity, It should add or deduct .1 value from it. i find out that a function onclickaddcartjson in the sale.variantmixin is responsible for the button click.i tried to extend the onclickaddcartjson function that present in the website_sale.website_sale. but it does not work.
Thanks For the Attention
My code is like :
publicWidget.registry.WebsiteSale.include({
onClickAddCartJSON: function (ev) {
ev.preventDefault();
var $link = $(ev.currentTarget);
var $input = $link.closest('.input-group').find("input");
var min = parseFloat($input.data("min") || 0);
var max = parseFloat($input.data("max") || Infinity);
var previousQty = parseFloat($input.val() || 0, 10);
var quantity = ($link.has(".fa-minus").length ? -0.1 : 0.1) + previousQty;
var newQty = quantity > min ? (quantity < max ? quantity : max) : min;
if (newQty !== previousQty) {
$input.val(newQty).trigger('change');
}
return false;
},
_changeCartQuantity: function ($input, value, $dom_optional, line_id, productIDs) {
_.each($dom_optional, function (elem) {
$(elem).find('.js_quantity').text(value);
productIDs.push($(elem).find('span[data-product-id]').data('product-id'));
});
$input.data('update_change', true);
this._rpc({
route: "/shop/cart/update_json",
params: {
line_id: line_id,
product_id: parseInt($input.data('product-id'), 10),
set_qty: value
},
}).then(function (data) {
$input.data('update_change', false);
var check_value = parseFloat($input.val());
if (isNaN(check_value)) {
check_value = 1;
}
if (value !== check_value) {
$input.trigger('change');
return;
}
if (!data.cart_quantity) {
return window.location = '/shop/cart';
}
wSaleUtils.updateCartNavBar(data);
$input.val(data.quantity);
$('.js_quantity[data-line-id='+line_id+']').val(data.quantity).text(data.quantity);
if (data.warning) {
var cart_alert = $('.oe_cart').parent().find('#data_warning');
if (cart_alert.length === 0) {
$('.oe_cart').prepend('<div class="alert alert-danger alert-dismissable" role="alert" id="data_warning">'+
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> ' + data.warning + '</div>');
}
else {
cart_alert.html('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> ' + data.warning);
}
$input.val(data.quantity);
}
});
},
_onChangeCartQuantity: function (ev) {
var $input = $(ev.currentTarget);
if ($input.data('update_change')) {
return;
}
var value = parseFloat($input.val());
if (isNaN(value)) {
value = 1;
}
var $dom = $input.closest('tr');
// var default_price = parseFloat($dom.find('.text-danger > span.oe_currency_value').text());
var $dom_optional = $dom.nextUntil(':not(.optional_product.info)');
var line_id = parseInt($input.data('line-id'), 10);
var productIDs = [parseInt($input.data('product-id'), 10)];
this._changeCartQuantity($input, value, $dom_optional, line_id, productIDs);
},
})
The change that I made is in _onclickaddcartjson; change the line
var quantity = ($link.has(".fa-minus").length ? -0.1 : 0.1) + previousQty;
and in _changeCartQuantity, change the check_value into:
var check_value = parseFloat($input.val())
And in _onChangeCartQuantity, change the value into:
var value = parseFloat($input.val()).
Even if I made these changes in source file without my custom module, the quantity increase or decrease by .1. But it automatically turns into an integer value. That means when I click on the + button, the value changes to 1.1, but it immediately changes to 1. Also, if I click on the - button, it will changes to 2.9 from 3, then it changes to 2 as its value. If anybody has an idea about this please share. Thanks for the attention.

Yes, it 's possible as the type of the underlying field in model SaleOrderLine is Float. And in website_sale/models/sale_order.py : int(sum(order.mapped('website_order_line.product_uom_qty'))) needs to be reülaced by: sum(order.mapped('website_order_line.product_uom_qty')):
class SaleOrderLine(models.Model):
_name = 'sale.order.line'
product_uom_qty = fields.Float(string='Quantity', digits='Product Unit of Measure', required=True, default=1.0)
#api.depends('order_line.product_uom_qty', 'order_line.product_id')
def _compute_cart_info(self):
for order in self:
order.cart_quantity = sum(order.mapped('website_order_line.product_uom_qty'))
order.only_services = all(l.product_id.type in ('service', 'digital') for l in order.website_order_line)
The underlying python method to add quantity is located in addons module website_sale/models/sale_oder.py :
def _cart_update(self, product_id=None, line_id=None, add_qty=0, set_qty=0, **kwargs):
""" Add or set product quantity, add_qty can be negative """
self.ensure_one()
product_context = dict(self.env.context)
product_context.setdefault('lang', self.sudo().partner_id.lang)
SaleOrderLineSudo = self.env['sale.order.line'].sudo().with_context(product_context)
...
...which is called by 2 methods in website_sale/controllers/main.py :
#http.route(['/shop/cart/update'], type='http', auth="public", methods=['GET', 'POST'], website=True, csrf=False)
def cart_update(self, product_id, add_qty=1, set_qty=0, **kw):
AND
#http.route(['/shop/cart/update_json'], type='json', auth="public", methods=['POST'], website=True, csrf=False)
def cart_update_json(self, product_id, line_id=None, add_qty=None, set_qty=None, display=True):
"""This route is called when changing quantity from the cart or adding
a product from the wishlist."""
"""This route is called when adding a product to cart (no options)."""
This controller method cart_update_json is called in sale/.../js/variant_mixin.js by:
onClickAddCartJSON: function (ev) {
ev.preventDefault();
var $link = $(ev.currentTarget);
var $input = $link.closest('.input-group').find("input");
var min = parseFloat($input.data("min") || 0);
var max = parseFloat($input.data("max") || Infinity);
var previousQty = parseFloat($input.val() || 0, 10);
var quantity = ($link.has(".fa-minus").length ? -1 : 1) + previousQty;
var newQty = quantity > min ? (quantity < max ? quantity : max) : min;
if (newQty !== previousQty) {
$input.val(newQty).trigger('change');
}
return false;
},
...where: var quantity = ($link.has(".fa-minus").length ? -1 : 1) + previousQty; shows us that is incremented +1 oder -1
That s why you need to override this function in website_sale/.../website_sale.js to integrate:
var $parent = $(ev.target).closest('.js_product');
var product_id = this._getProductId($parent);
if product_id == yourspecificproductid
var quantity = ($link.has(".fa-minus").length ? -0.1 : 0.1) +
previousQty;
else
var quantity = ($link.has(".fa-minus").length ? -1 : 1) +
previousQty;
When quantity input value is changed automatically, it might be caused by parseInt($input.val()) in _onChangeCartQuantity in the file: website_sale.js:
_onChangeCartQuantity: function (ev) {
var $input = $(ev.currentTarget);
if ($input.data('update_change')) {
return;
}
var value = parseFloat($input.val() || 0, 10);
if (isNaN(value)) {
value = 1;
}
var $dom = $input.closest('tr');
// var default_price = parseFloat($dom.find('.text-danger > span.oe_currency_value').text());
var $dom_optional = $dom.nextUntil(':not(.optional_product.info)');
var line_id = parseInt($input.data('line-id'), 10);
var productIDs = [parseInt($input.data('product-id'), 10)];
this._changeCartQuantity($input, value, $dom_optional, line_id, productIDs);
},

Related

In Qweb how to filter data based on a value chosen by the user (odoo)?

enter image description here
For portal user, I want the list of field values to appear based on the value chosen by this portal user for example:
If this portal user chooses the country, only institutions in the selected country must appear in the list of institutions field.
publicWidget.registry.portalMedical = publicWidget.Widget.extend({
selector: '.o_portal_medical',
events: {
'change select[name="country_id"]': '_onCountryChange',
'change select[name="state_id"]': '_onStateChange',
'change select[name="institution_id"]': '_onInstitutionChange',
},
start: function () {
var def = this._super.apply(this, arguments);
this.$state = this.$('select[name="state_id"]');
this.$stateOptions = this.$state.filter(':enabled').find('option:not(:first)');
this.$institution = this.$('select[name="institution_id"]');
this.$institutionOptions = this.$institution.filter(':enabled').find('option:not(:first)');
this.$doctor = this.$('select[name="doctor_id"]');
this.$doctorOptions = this.$doctor.filter(':enabled').find('option:not(:first)');
this._adaptAddressForm();
return def;
},
_adaptAddressForm: function () {
var $country = this.$('select[name="country_id"]');
var countryID = ($country.val() || 0);
this.$stateOptions.detach();
var $displayedState = this.$stateOptions.filter('[data-country_id=' + countryID + ']');
var nb = $displayedState.appendTo(this.$state).show().length;
//this.$state.parent().toggle(nb >= 1);
var $state = this.$('select[name="state_id"]');
var stateID = ($state.val() || 0);
this.$institutionOptions.detach();
var $displayedInstitution = this.$institutionOptions.filter('[data-state_id=' + stateID + ']');
var mb = $displayedInstitution.appendTo(this.$institution).show().length;
//this.$institution.parent().toggle(mb >= 1);
},
_onCountryChange: function () {
this._adaptAddressForm();
},
_onStateChange: function () {
this._adaptAddressForm();
},
});

Table, computed property and vuex data

I'm in need of help with computed properties .. it's hard to explain but I'll try ..
I have a table with filter and pagination that I created.
When I pass the data directly from a request the API works,
but when I have to work with data coming from vuex I'm having trouble ...
Because I do not know when the data will be filled in vuex
And then I use the computed property because the moment the vuex is filled it will capture the data ..
However as the function that fills the data is the same one that is executed when I click on the page I crash the process and the page stops working.
Below is the computed property:
list(){
if (this.url_get == '' && this.$store.state.table.list.length > 0){
this.total_data = this.$store.state.table.list.length
this.repos = this.$store.state.table.list
this.getPaginatedItems(1)
}
var filter = this.configs.filter.toString().toLowerCase()
var items = ''
if (filter == ''){
items = this.dados
}else{
items = this.repos
}
const list = this.$lodash.orderBy(items, this.configs.orderBy, this.configs.order)
this.reverse = 1;
if (this.$lodash.isEmpty(filter)) {
return list;
}
var result = this.$lodash.filter(list, repo => {
return repo[this.filter_term].toString().toLowerCase().indexOf(filter) >= 0
})
return result
},
And the function:
getPaginatedItems(data) {
var items = this.repos
var page = data
var per_page = 10
var offset = (page - 1) * per_page
var max_item = offset+per_page
var paginatedItems = this.$lodash.slice(items, offset, max_item)
var obj = {
offset: offset,
current_page: page,
per_page: per_page,
total: items.length,
total_pages: Math.ceil(items.length / per_page),
data: paginatedItems,
last: offset+paginatedItems.length,
max_item: max_item
}
this.pagination = obj
this.dados = this.pagination.data
},
Request who fill the data in vuex
axios
.get(`api/getusuariosgrupo/${id}`)
.then(res => {
if (res.data.dados !== undefined && res.data.dados.length !== 0){
vmThis.isEditing = true
var d = {
list: res.data.dados,
length: res.data.dados.length
}
this.$store.commit('SET_TABLE', d)
}else{
vmThis.isEditing = false
}
vmThis.$bus.$emit('processando',{val: false})
})

Sensenet Content Picker Customization

I created two custom content types, ProjectContract and PaymentRequest. Under PaymentRequest, I have a reference field Contract which I would like to use to reference ProjectContract. When I am creating/changing PaymentRequest, I need the following:
how can I initialize Content Picker to display ContractNumber field of available ProjectContracts?
how can I display selected ProjectContract's ContractNumber under ReferenceField Grid control?
The SN js code and the mvc contains/returns fix field values. I did not find any setting where I can add custom fields to show.
First of all, what is the version of that SN package, because the oData.svc request will not work on older versions. It is available from 6.2.
About the oData, here is a link: http://wiki.sensenet.com/OData_REST_API
There is another way to solve it, but with this, you need to modify the existion SN codes.
You need to copy (" /Root/Global/scripts/sn/SN.Picker.js ") file into your skin folder with the same structure. (" /Root/Skins/[yourskinfolder]/scripts/sn/SN.ReferenceGrid.js ")
You need to copy (" /Root/Global/scripts/sn/SN.ReferenceGrid.js ") file into your skin folder as well.
Do not modify the original SN file, because it will be overwrite after an SN update.
Next step: copy the following code to line 1068, before the ("$grid.jqGrid({") line, into the InitGrid function.
...
var neededTypeName = "ProjectContract";
var neededFieldName = "ContractNumber";
var findField = false;
o2 = (function () {
var result = [];
var itemArray = [];
$.each(o2, function (index, el) {
el.ContentField = "";
result.push(el);
if (el.ContentTypeName == neededTypeName) {
itemArray.push([index, el.Path]);
findField = true;
}
});
if (findField) {
$.each(itemArray, function (itemIndex, itemElArray) {
var itemId = itemElArray[0];
var itemEl = itemElArray[1];
var thisLength = itemEl.length;
var thislastSplash = itemEl.lastIndexOf("/");
var thisPath = itemEl.substring(0, thislastSplash) + "('" + itemEl.substring(thislastSplash + 1, thisLength) + "')";
$.ajax({
url: "/oData.svc" + thisPath + "?metadata=no$select=Path," + neededFieldName,
dataType: "json",
async: false,
success: function (d) {
result[itemId].ContentField = d.d[neededFieldName];
}
});
});
colNames.splice(6, 0, "ContentField");
colModel.splice(6, 0, { index: "ContentField", name: "ContentField", width: 100 });
return result;
}
return o2;
})();
...
$grid.jqGrid({
...
The "neededTypeName" may contains your content type value, and the "neededFieldName" may contains the field name you want to render.
The other will build up the grid.
This will modify the Content picker table.
You need to add this code into the GetResultDataFromRow function, at line 660 before the return of the function.
...
if (rowdata.ContentField != undefined) {
result.ContentField = rowdata.ContentField;
}
...
This will add the selected item properties from the Content picker to the reference field table.
Then you need to open the SN.ReferenceGrid.js and add the following code into the init function before the "var $grid = $("#" + displayAreaId);"
var neededTypeName = "CustomItem2";
var neededFieldName = "Custom2Num";
var findField = false;
var alreadyAdded = false;
var btnAttr = $("#"+addButtonId).attr("onClick");
if (btnAttr.indexOf(neededTypeName) > -1) {
alreadyAdded = true;
colNames[4].width = 150;
colModel[4].width = 150;
colNames.splice(3, 0, "ContentField");
colModel.splice(3, 0, { index: "ContentField", name: "ContentField", width: 60 });
}
initialSelection = (function () {
var result = [];
var itemArray = [];
$.each(initialSelection, function (index, el) {
el.ContentField = "";
result.push(el);
if (el.ContentTypeName == neededTypeName) {
itemArray.push([index, el.Path]);
findField = true;
}
});
if (findField) {
$.each(itemArray, function (itemIndex, itemElArray) {
var itemId = itemElArray[0];
var itemEl = itemElArray[1];
var thisLength = itemEl.length;
var thislastSplash = itemEl.lastIndexOf("/");
var thisPath = itemEl.substring(0, thislastSplash) + "('" + itemEl.substring(thislastSplash + 1, thisLength) + "')";
$.ajax({
url: "/oData.svc" + thisPath + "?metadata=no$select=Path," + neededFieldName,
dataType: "json",
async: false,
success: function (d) {
result[itemId].ContentField = d.d[neededFieldName];
}
});
});
if (!alreadyAdded) {
colNames.splice(3, 0, "ContentField");
colModel.splice(3, 0, { index: "ContentField", name: "ContentField", width: 100 });
}
return result;
}
return initialSelection;
})();
I hope this will help but the SN version should be helpful.

AdWord Script Export to BigQuery "Empty Response"

Utilizing the following AdWords Script to export to BigQuery, the BigQuery.Jobs.insert is causing the script to terminate due to "Empty response". Any reason the call is not getting a response?
var ACCOUNTS = ['xxx','xxx'];
var CONFIG = {
BIGQUERY_PROJECT_ID: 'xxx',
BIGQUERY_DATASET_ID: 'xxx',
// Truncate existing data, otherwise will append.
TRUNCATE_EXISTING_DATASET: true,
TRUNCATE_EXISTING_TABLES: true,
// Back up reports to Google Drive.
WRITE_DATA_TO_DRIVE: false,
// Folder to put all the intermediate files.
DRIVE_FOLDER: 'Adwords Big Query Test',
// Default date range over which statistics fields are retrieved.
DEFAULT_DATE_RANGE: '20140101,20140105',
// Lists of reports and fields to retrieve from AdWords.
REPORTS: [{NAME: 'KEYWORDS_PERFORMANCE_REPORT',
CONDITIONS: 'WHERE Impressions>0',
FIELDS: {'AccountDescriptiveName' : 'STRING',
'Date' : 'STRING',
'CampaignId' : 'STRING',
'CampaignName' : 'STRING',
'AdGroupId' : 'STRING',
'AdGroupName' : 'STRING',
'Id' : 'STRING',
'Criteria' : 'STRING',
'KeywordMatchType' : 'STRING',
'AdNetworkType1' : 'STRING',
'AdNetworkType2' : 'STRING',
'Device' : 'STRING',
'AveragePosition' : 'STRING',
'QualityScore' : 'STRING',
'CpcBid' : 'STRING',
'TopOfPageCpc' : 'STRING',
'Impressions' : 'STRING',
'Clicks' : 'STRING',
'ConvertedClicks' : 'STRING',
'Cost' : 'STRING',
'Conversions' : 'STRING'
}
}],
RECIPIENT_EMAILS: [
'xxx',
]
};
function main() {
createDataset();
for (var i = 0; i < CONFIG.REPORTS.length; i++) {
var reportConfig = CONFIG.REPORTS[i];
createTable(reportConfig);
}
folder = getDriveFolder();
// Get an account iterator.
var accountIterator = MccApp.accounts().withIds(ACCOUNTS).withLimit(10).get();
var jobIdMap = {};
while (accountIterator.hasNext()) {
// Get the current account.
var account = accountIterator.next();
// Select the child account.
MccApp.select(account);
// Run reports against child account.
var accountJobIds = processReports(folder, account.getCustomerId());
jobIdMap[account.getCustomerId()] = accountJobIds;
}
waitTillJobsComplete(jobIdMap);
sendEmail(jobIdMap);
}
function createDataset() {
if (datasetExists()) {
if (CONFIG.TRUNCATE_EXISTING_DATASET) {
BigQuery.Datasets.remove(CONFIG.BIGQUERY_PROJECT_ID,
CONFIG.BIGQUERY_DATASET_ID, {'deleteContents' : true});
Logger.log('Truncated dataset.');
} else {
Logger.log('Dataset %s already exists. Will not recreate.',
CONFIG.BIGQUERY_DATASET_ID);
return;
}
}
// Create new dataset.
var dataSet = BigQuery.newDataset();
dataSet.friendlyName = CONFIG.BIGQUERY_DATASET_ID;
dataSet.datasetReference = BigQuery.newDatasetReference();
dataSet.datasetReference.projectId = CONFIG.BIGQUERY_PROJECT_ID;
dataSet.datasetReference.datasetId = CONFIG.BIGQUERY_DATASET_ID;
dataSet = BigQuery.Datasets.insert(dataSet, CONFIG.BIGQUERY_PROJECT_ID);
Logger.log('Created dataset with id %s.', dataSet.id);
}
/**
* Checks if dataset already exists in project.
*
* #return {boolean} Returns true if dataset already exists.
*/
function datasetExists() {
// Get a list of all datasets in project.
var datasets = BigQuery.Datasets.list(CONFIG.BIGQUERY_PROJECT_ID);
var datasetExists = false;
// Iterate through each dataset and check for an id match.
if (datasets.datasets != null) {
for (var i = 0; i < datasets.datasets.length; i++) {
var dataset = datasets.datasets[i];
if (dataset.datasetReference.datasetId == CONFIG.BIGQUERY_DATASET_ID) {
datasetExists = true;
break;
}
}
}
return datasetExists;
}
function createTable(reportConfig) {
if (tableExists(reportConfig.NAME)) {
if (CONFIG.TRUNCATE_EXISTING_TABLES) {
BigQuery.Tables.remove(CONFIG.BIGQUERY_PROJECT_ID,
CONFIG.BIGQUERY_DATASET_ID, reportConfig.NAME);
Logger.log('Truncated dataset %s.', reportConfig.NAME);
} else {
Logger.log('Table %s already exists. Will not recreate.',
reportConfig.NAME);
return;
}
}
// Create new table.
var table = BigQuery.newTable();
var schema = BigQuery.newTableSchema();
var bigQueryFields = [];
// Add account column to table.
var accountFieldSchema = BigQuery.newTableFieldSchema();
accountFieldSchema.description = 'AccountId';
accountFieldSchema.name = 'AccountId';
accountFieldSchema.type = 'STRING';
bigQueryFields.push(accountFieldSchema);
// Add each field to table schema.
var fieldNames = Object.keys(reportConfig.FIELDS);
for (var i = 0; i < fieldNames.length; i++) {
var fieldName = fieldNames[i];
var bigQueryFieldSchema = BigQuery.newTableFieldSchema();
bigQueryFieldSchema.description = fieldName;
bigQueryFieldSchema.name = fieldName;
bigQueryFieldSchema.type = reportConfig.FIELDS[fieldName];
bigQueryFields.push(bigQueryFieldSchema);
}
schema.fields = bigQueryFields;
table.schema = schema;
table.friendlyName = reportConfig.NAME;
table.tableReference = BigQuery.newTableReference();
table.tableReference.datasetId = CONFIG.BIGQUERY_DATASET_ID;
table.tableReference.projectId = CONFIG.BIGQUERY_PROJECT_ID;
table.tableReference.tableId = reportConfig.NAME;
table = BigQuery.Tables.insert(table, CONFIG.BIGQUERY_PROJECT_ID,
CONFIG.BIGQUERY_DATASET_ID);
Logger.log('Created table with id %s.', table.id);
}
function tableExists(tableId) {
// Get a list of all tables in the dataset.
var tables = BigQuery.Tables.list(CONFIG.BIGQUERY_PROJECT_ID,
CONFIG.BIGQUERY_DATASET_ID);
var tableExists = false;
// Iterate through each table and check for an id match.
if (tables.tables != null) {
for (var i = 0; i < tables.tables.length; i++) {
var table = tables.tables[i];
if (table.tableReference.tableId == tableId) {
tableExists = true;
break;
}
}
}
return tableExists;
}
function processReports(folder, accountId) {
var jobIds = [];
// Iterate over each report type.
for (var i = 0; i < CONFIG.REPORTS.length; i++) {
var reportConfig = CONFIG.REPORTS[i];
Logger.log('Running report %s for account %s', reportConfig.NAME,
accountId);
// Get data as csv
var csvData = retrieveAdwordsReport(reportConfig, accountId);
// If configured, back up data.
if (CONFIG.WRITE_DATA_TO_DRIVE) {
var fileName = reportConfig.NAME + '_' + accountId;
folder.createFile(fileName, csvData, MimeType.CSV);
Logger.log('Exported data to Drive folder ' +
CONFIG.DRIVE_FOLDER + ' for report ' + fileName);
}
// Convert to Blob format.
var blobData = Utilities.newBlob(csvData, 'application/octet-stream');
// Load data
var jobId = loadDataToBigquery(reportConfig, blobData);
jobIds.push(jobId);
}
return jobIds;
}
function retrieveAdwordsReport(reportConfig, accountId) {
var fieldNames = Object.keys(reportConfig.FIELDS);
var report = AdWordsApp.report(
'SELECT ' + fieldNames.join(',') +
' FROM ' + reportConfig.NAME + ' ' + reportConfig.CONDITIONS +
' DURING ' + CONFIG.DEFAULT_DATE_RANGE);
var rows = report.rows();
var csvRows = [];
// Header row
csvRows.push('AccountId,'+fieldNames.join(','));
// Iterate over each row.
while (rows.hasNext()) {
var row = rows.next();
var csvRow = [];
csvRow.push(accountId);
for (var i = 0; i < fieldNames.length; i++) {
var fieldName = fieldNames[i];
var fieldValue = row[fieldName].toString();
var fieldType = reportConfig.FIELDS[fieldName];
/* Strip off % and perform any other formatting here.
if ((fieldType == 'FLOAT' || fieldType == 'INTEGER') &&
fieldValue.charAt(fieldValue.length - 1) == '%') {
fieldValue = fieldValue.substring(0, fieldValue.length - 1);
}*/
// Add double quotes to any string values.
if (fieldType == 'STRING') {
fieldValue = fieldValue.replace(',', ''); //Handle fields with comma in value returned
fieldValue = fieldValue.replace('"', ''); //Handle fields with double quotes in value returned
fieldValue = fieldValue.replace('+', ''); //Handle fields with "+" in value returned
fieldValue = '"' + fieldValue + '"';
}
csvRow.push(fieldValue);
}
csvRows.push(csvRow.join(','));
}
Logger.log('Downloaded ' + reportConfig.NAME + ' for account ' + accountId +
' with ' + csvRows.length + ' rows.');
return csvRows.join('\n');
}
function getDriveFolder() {
var folders = DriveApp.getFoldersByName(CONFIG.DRIVE_FOLDER);
// Assume first folder is the correct one.
if (folders.hasNext()) {
Logger.log('Folder name found. Using existing folder.');
return folders.next();
}
return DriveApp.createFolder(CONFIG.DRIVE_FOLDER);
}
function loadDataToBigquery(reportConfig, data) {
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + s4() + s4() + s4() + s4() + s4() + s4();
}
var makeId = guid();
var job = {
jobReference: {
jobId: makeId
},
configuration: {
load: {
destinationTable: {
projectId: CONFIG.BIGQUERY_PROJECT_ID,
datasetId: CONFIG.BIGQUERY_DATASET_ID,
tableId: reportConfig.NAME
},
skipLeadingRows: 1,
ignoreUnknownValues: true,
allowJaggedRows: true,
allowLargeResults: true
}
}
};
var insertJob = BigQuery.Jobs.insert(job, CONFIG.BIGQUERY_PROJECT_ID, data);
Logger.log('Load job started for %s. Check on the status of it here: ' +
'https://bigquery.cloud.google.com/jobs/%s', reportConfig.NAME,
CONFIG.BIGQUERY_PROJECT_ID);
return job.jobReference.jobId;
}
function waitTillJobsComplete(jobIdMap) {
var complete = false;
var remainingJobs = [];
var accountIds = Object.keys(jobIdMap);
for (var i = 0; i < accountIds.length; i++){
var accountJobIds = jobIdMap[accountIds[i]];
remainingJobs.push.apply(remainingJobs, accountJobIds);
}
while (!complete) {
if (AdWordsApp.getExecutionInfo().getRemainingTime() < 5){
Logger.log('Script is about to timeout, jobs ' + remainingJobs.join(',') +
' are still incomplete.');
}
remainingJobs = getIncompleteJobs(remainingJobs);
if (remainingJobs.length == 0) {
complete = true;
}
if (!complete) {
Logger.log(remainingJobs.length + ' jobs still being processed.');
// Wait 5 seconds before checking status again.
Utilities.sleep(5000);
}
}
Logger.log('All jobs processed.');
}
function getIncompleteJobs(jobIds) {
var remainingJobIds = [];
for (var i = 0; i < jobIds.length; i++) {
var jobId = jobIds[i];
var getJob = BigQuery.Jobs.get(CONFIG.BIGQUERY_PROJECT_ID, jobId);
if (getJob.status.state != 'DONE') {
remainingJobIds.push(jobId);
}
}
return remainingJobIds;
}
It appears the "Empty Response" error is being thrown on:
var insertJob = BigQuery.Jobs.insert(job, CONFIG.BIGQUERY_PROJECT_ID, data);
Have tried quite a few tweaks, but the answer doesn't appear to obvious to me. Thanks for any help!
I can be wrong but - I think that problem was with jobId because of issue with guid() function - missing "+" sign.
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + s4() + s4() + s4() s4() + s4() + s4();
}
Why not to use jobId from Response like below?
var job = {
configuration: {
load: {
destinationTable: {
projectId: CONFIG.BIGQUERY_PROJECT_ID,
datasetId: CONFIG.BIGQUERY_DATASET_ID,
tableId: reportConfig.NAME
},
skipLeadingRows: 1,
ignoreUnknownValues: true,
allowJaggedRows: true,
allowLargeResults: true
}
}
};
var insertJob = BigQuery.Jobs.insert(job, CONFIG.BIGQUERY_PROJECT_ID, data);
Logger.log('Load job started for %s. Check on the status of it here: ' +
'https://bigquery.cloud.google.com/jobs/%s', reportConfig.NAME,
CONFIG.BIGQUERY_PROJECT_ID);
return insertJob.jobReference.jobId;
Added
In this case I would suggest to log jobId (makeId = guid()) and get job status following below link https://cloud.google.com/bigquery/docs/reference/v2/jobs/get#try-it
Enter ProjectId and JobId and you at least will see what is going on with your job!!
AdWords places a "--" in for null values. If you define your report fields as anything but string (e.g., float, integer, etc.) the insert will fail because it can't convert the dash dash to a float or integer.
Try setting all of your fields to string and see if that solves the problem.
Have you tried setting the WRITE_DATA_TO_DRIVE parameter to true to confirm that the report export is successful? How large is the result? I get the same error when attempting an insert greater than 10MB (~25k rows depending on columns). If the file export to Google Drive looks good, you can add a condition to the while loop in retrieveAdwordsReport to limit the file size. There was also a post on https://groups.google.com/forum/#!forum/adwords-scripts mentioning an issue when including AdNetworkType columns: https://groups.google.com/forum/#!searchin/adwords-scripts/adnetworktype2%7Csort:relevance/adwords-scripts/yK57JHCt3Cw/Cl1SjFaQBQAJ.
Limit result size:
var processedRows = 0;
// Iterate over each row.
while (rows.hasNext() && ++processedRows < 5000) {
var row = rows.next();
var csvRow = [];
csvRow.push(accountId);
if (processedRows % 1000 == 0)
{
Logger.log('Processed %s rows.',processedRows);
}
...

how to to process result of google distance matrix api further?

i am new to programming.. i have this code which gives distance between two points but need to further multiply it by an integer say 10.. the project i am working on is abt calculating distance between two points and multiplying it with fare/Km like Rs.10/km (Indian Rupees) for the same. So if the distance is 30 km the fare would be 30*10 = Rs.300
Thanks in advance
following is the code
<script>
var map;
var geocoder;
var bounds = new google.maps.LatLngBounds();
var markersArray = [];
var origin1 = '';
var destinationA = '';
var destinationIcon = 'https://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=D|FF0000|000000';
var originIcon = 'https://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=O|FFFF00|000000';
function initialize() {
var opts = {
center: new google.maps.LatLng(55.53, 9.4),
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map'), opts);
var fromText = document.getElementById('FAdd');
var options = {
componentRestrictions: {country: 'in'}
};var fromAuto = new google.maps.places.Autocomplete(fromText, options);
fromAuto.bindTo('bound', map);
var toText = document.getElementById('TAdd');
var toAuto = new google.maps.places.Autocomplete(toText, options);
toAuto.bindTo('bound', map);
geocoder = new google.maps.Geocoder();
}
function calculateDistances() {
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix(
{
origins: [document.getElementById("FAdd").value],
destinations: [document.getElementById("TAdd").value],
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, callback);
}
function callback(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
alert('Error was: ' + status);
} else {
var origins = response.originAddresses;
var destinations = response.destinationAddresses;
var outputDiv = document.getElementById('outputDiv');
outputDiv.innerHTML = '';
deleteOverlays();
for (var i = 0; i < origins.length; i++) {
var results = response.rows[i].elements;
addMarker(origins[i], false);
for (var j = 0; j < results.length; j++) {
addMarker(destinations[j], true);
outputDiv.innerHTML += results[j].distance.text + '<br>';
}
}
}
}
function addMarker(location, isDestination) {
var icon;
if (isDestination) {
icon = destinationIcon;
} else {
icon = originIcon;
}
geocoder.geocode({'address': location}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
bounds.extend(results[0].geometry.location);
map.fitBounds(bounds);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location,
icon: icon
});
markersArray.push(marker);
} else {
alert('Geocode was not successful for the following reason: '
+ status);
}
});
}
function deleteOverlays() {
if (markersArray) {
for (i in markersArray) {
markersArray[i].setMap(null);
}
markersArray.length = 0;
}
}
</script>
I use an Ajax call to PHP, and haven't yet used getDistanceMatrix(), but this should be an easy fix.
First, if you know you will always only have one origin and one destination, you don't need the "for" loop in your callback function. Second, you're taking the distance text rather than the distance value.
function callback(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
[...]
} else {
deleteOverlays();
var outputDiv = document.getElementById('outputDiv'),
origin = response.originAddresses[0],
destination = response.destinationAddresses[0],
result = response.rows[0].elements[0],
distance = result.distance.value,
text = result.distance.text,
price = 10 * distance;
outputDiv.innerHTML = '<p>' + text + ': Rs.' + price + '</p>';
addMarker(origin, false);
addMarker(destination, false);
}
}
I haven't tested this, so it probably needs to be tweaked. ( See https://developers.google.com/maps/documentation/distancematrix/#DistanceMatrixResponses )