app scripts data validation from another workbook - validation

I'm trying to validate cells based on a list from a different sheet in a separate workbook. I tested it by copying the desired sheet into the current workbook, to build the code. I changed all the references to reflect the actual source I wanted and everything seemed fine. when I deleted the copy I then had issues as it seems the data validation was still using the copy even though all the references were changed to the original source.
var DbSheet = SpreadsheetApp.openByUrl(ingredientDbBookUrl).getSheetByName(ingredientDbBookName);
function nameValidation(){
var cell = sheet.getRange(3,nameColumn,recipe.getLastRow(),1);
var list = DbSheet.getRange(3,2,ingredientDbSheet.getLastRow());
var helpText = 'helptxt';
var rule = SpreadsheetApp.newDataValidation()
.requireValueInRange(list)
.setAllowInvalid(false)
.setHelpText(helpText)
.build();
cell.setDataValidation(rule);
is it possible to validate cells from a list in a separate workbook? does the fact that I'm using a bounded script matter?

I did it with this:
function validatefromanothersheet() {
const ss=SpreadsheetApp.openById("Another ssid");
const sh=ss.getSheetByName('Sheet1');
const list=sh.getRange(1,1,sh.getLastRow(),1).getValues().flat();
let rule=SpreadsheetApp.newDataValidation()
.requireValueInList(list)
.setAllowInvalid(false)
.build();
SpreadsheetApp.getActiveSheet().getRange(1,1,10,1).setDataValidation(rule);
}

Is it possible to validate cells from a list in a separate workbook?
Yes
Does the fact that I'm using a bounded script matter?
No
Why did the validation have errors?
This is likely because once you have "set" the validation on a cell, it doesn't matter what the code is that was used to generate it.
It is like building something from a plan, and then expecting the building to change because you have changed the plan.
You would need to first clear the rule and then build the rule and apply it again with the new source.
Just probably best to not use requireValueInRange(range) as the ranges may get crossed over. But if you are converting it to values as your code demonstrates said above, then it should be no issue.
Ref
DataValidationBuilder

Related

Use Apps Script to create data validation that uses a named range

Here's the code:
var sheet = spreadsheet.getSheetByName("Timesheet");
sheet.getRange('B27').setDataValidation(SpreadsheetApp.newDataValidation()
.setAllowInvalid(false)
.requireValueInRange(spreadsheet.getRange('TaskItems'), true)
.build());
The above code successfully creates a data validation drop-down menu in cell B27 that matches the named range "TaskItems". However, the data validation rule that apps script creates uses the actual address of "TaskItems" which is C2:1300, rather than "TaskItems" itself. So if I update the address of named range "TaskItems" to D2:1300, then my data validation rules no longer work because they are still using C2:1300.
I can set the data validation rules manually on each cell to the named range "TaskItems", and everything works great even when "TaskItems" changes. However, I can't get apps script to use the actual named range in the rule rather than the address of the named range when it the rule was created.
I tried switching out the range object with a string like so:
var sheet = spreadsheet.getSheetByName("Timesheet");
sheet.getRange('B27').setDataValidation(SpreadsheetApp.newDataValidation()
.setAllowInvalid(false)
.requireValueInRange('TaskItems', true)
.build());
but I get an error stating that requireValueInRange does not accept a string.
Does anyone know how to get apps script to use the actual named range in the data validation rule?
I had exactly the same issue and was disappointed to not find an answer here. It's very odd that something you can do manually isn't possible via a script.
But I just figures out a work-around.
Add the validation manually to a cell somewhere which refers to your named range. Then in the script, COPY the validation from that cell to wherever you want it. The copied validation rule uses the name of the named range - just as required.
Here the script I used for testing this.
function setvalidation() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("TEST");
var vrule = sheet.getRange(1,1).getDataValidation();
// Previously, you would have set up the validation in cell A1
sheet.getRange(1,2).setDataValidation(vrule); // Copy the validation rule to cell A2
}

Adding DynamicPropertyInstance doesn't mark the SalesOrderDetail as valid

I have the following code:
var propertyInstance = new DynamicPropertyInstance()
{
DynamicPropertyId = new EntityReference(DynamicProperty.EntityLogicalName, Guid.Parse("0ceedfcc-68b2-e711-8168-e0071b658ea1")),
ValueString = jobId.ToString(),
RegardingObjectId = line.ToEntityReference(),
};
crmContext.AddObject(dynamicPropertyInstance);
crmContext.SaveChanges();
It is successfully adding a DynamicPropertyInstance to a SalesOrderLine, but when viewing the Order in the CRM UI it does not pass the validation (as it is a required property). I've not managed to find a way to make this property valid. Editing the property that I've added in the UI (resetting the value) also fails to mark the instance as valid. Adding exactly the same property through the UI does mark it as valid.
The Id of the DynamicProperty is correct, as verified by loading the 2 instance records through the SDK and comparing the properties. Rather strangely, when I load the 2 records through the SDK the one I've created in code has a validationstatus of true (even though it's not) and the one that I've created in the UI has a validationstatus of false and ValueString returns null (which is wrong). All of the other properties either match or have relevant values (such as dates, object Ids etc)
I'm probably missing a method call to recalculate whether the instance is valid or not, but I can't find anything in the documentation to support that. Failing that, it's possibly a bug in CRM
Raised a case with Microsoft support, and was given some workaround code:
//Get DynamicPropertyInstance
UpdateProductPropertiesRequest UpdateRequest = new UpdateProductPropertiesRequest();
UpdateRequest.PropertyInstanceList = new EntityCollection();
UpdateRequest.PropertyInstanceList.EntityName = DynamicPropertyInstance.EntityLogicalName;
Entity dpInstance = new Entity(DynamicPropertyInstance.EntityLogicalName, Dpi.Id);
dpInstance.Attributes.Add(nameof(Dpi.ValueString).ToLower(), "Blarg");
dpInstance.Attributes.Add(nameof(Dpi.DynamicPropertyInstanceid).ToLower(), Dpi.Id);
dpInstance.Attributes.Add(nameof(Dpi.RegardingObjectId).ToLower(), new EntityReference(SalesOrderDetail.EntityLogicalName, line.Id));
dpInstance.Attributes.Add(nameof(Dpi.DynamicPropertyId).ToLower(), new EntityReference(DynamicProperty.EntityLogicalName, dpId));
UpdateRequest.PropertyInstanceList.Entities.Add(dpInstance);
crmContext.Execute(UpdateRequest);
Basically, it looks like you have to re-set or re-attach the entities for CRM to pick it up, so this is a workaround for a bug in CRM

Birt Reports - Don't generate an empty report

I have several Birt Reports that I am trying to set up to run on a cron job that will email pdfs of the reports every morning. Everything is working fine as far as the generation and emailing goes; the only issue I am stuck with is this: if there is nothing to report, a pdf with just the report title is generated and emailed (a blank report, basically). I'd like to stop this report from being generated at all, so i can skip the emailing, if the pdf file does not exist.
I have been all over Google for two days now, and the closest I can find is this: http://www.eclipse.org/forums/index.php/t/458779/ in which someone was trying to solve a similar problem and received a push in the right direction, but not a complete solution.
It appears as if this can be done during the beforerender script... but how?
I know I need to:
set a persistent global variable in the oncreate if there is indeed data to report.
get the persistent global variable in the beforerender script.
send the magic don't generate report command.
I'm doing all of generating and emailing from a php script, not Java, so I can't send commands like IEngineTask.cancel() (or can I???)
Yes, I know I can make a row in the report that says "No data to report", but that's not what my users want.
And yes, I could query the database outside of the report to determine if there is valid data to report or not, but i'd prefer not to.
And maybe I could even open and read the pdf, programmatically to see if there is anything there, but that sounds like more of a hassle than it's worth...
So, how do I do this?
Thanks.
My answer is a little bit late, but I'm doing it like this in a framework that is working for hundreds of reports, probably it could be simplified for a single report:
Note that all the code is written from memory (not copied from our framework), so maybe it contains some errors.
Add an external Javascript file myframework.js to your report.
In this file, define an object myframework like this:
if (myframework == undefined) {
myframework = {
dataFound: false,
afterReport: function() {
// Write it to the appContext.
// Using Java, you could read it after the
// runAndRenderTask is done.
reportContext.getAppContext().put("dataFound", this.dataFound);
// But since you probably cannot the context
// (don't like coding Java?), the report has to
// tell it to he world some other way...
var txt = "dataFound=" + (dataFound? "true": "false");
var fw = new java.io.FileWriter("c:\\reportcontext.out");
fw.write(txt);
fw.close();
}
};
}
Add the JS file to your report's resources.
In your report, at a place where you decide that the report has found something (e.g. typically in a data set's onFetch event), tell the framework so by calling
myframework.dataFound = true;
In the reports's afterFactory or afterRender event, call
myframework.afterReport();
Then your report should create an output file c:\reportcontext.out which contains the information you need.

Making improvements to code

I have been working on a magento module for sometime now (not because it is a large module, its my first and I really struggled with it). I now have it working, which I was really pleased with at first, but now I would like to improve it by increasing the reusability of the code.
While making my module I have looked at other modules to learn from them and have seen that many actions are only a line or two and I have tried keeping in with the skinny controller approach but failed, my example is as follows:
I have a function that get a users details that they have inputted into a custom form
protected function _setPostData()
{
$this->_salutation = $this->getRequest()->getPost('salutation');
$this->_f_name = $this->getRequest()->getPost('f_name');
$this->_l_name = $this->getRequest()->getPost('l_name');
$this->_email = $this->getRequest()->getPost('email');
$this->_emailAddressCheck = $this->getRequest()->getPost('emailAddressCheck');
$this->_gender = $this->getRequest()->getPost('gender');
$this->_country = $this->getRequest()->getPost('country');
$this->_pref_lang = $this->getRequest()->getPost('pref_lang');
}
I feel that this is not the correct way to do it and that there is a better way of achieving the same goal, as you can see this function gets the posts data and assigns it to attributes that i've set at the start of the class. I have several other examples that are very similar to the above and if someone could please offer some guidance on this one I am sure I will be able to work out the others
This example is held within the index action, should I put it in a helper as once it created correctly i am sure there will be a few occasions that I will be able to use it again?
you should put all the post data on an array and use it from there
$dataArray=$this->getRequest()->getPost();
$this->_salutation = $dataArray['salutation'];
$this->_f_name = $dataArray['f_name'];
$this->_l_name = $dataArray['l_name'];

Multiple GeoCoordinateWatchers in WP7 Cause Bad Data

If I have multiple GeoCoordinateWatchers in a WP7 application, they seem to cause conflict with one another. I would assume that if I have a watcher setup like:
new GeoCoordinateWatcher(GeoPositionAccuracy.High) { MovementThreshold = 0 }
and another setup like:
That the value from the first should be extremely accurate whereas the second one should be used as a point of reference.
new GeoCoordinateWatcher(GeoPositionAccuracy.Default) { MovementThreshold = 1000 };
However, the second one causes the first coordinates to jump all over the place. If I comment out the second, the first works as expected. Any idea why?
Try setting the same accuracy and see if you get the same values.

Resources