Click here for picture Overview of the classes/entities
Hi guys, It could be great if someone could re-code and help me on this.I am new in D365 and JS. Basically, how can I query the parent to case_adjustment from adjustment invoice record using JS. I have provide my current code, please help me to review it. I have try everything but so far no luck. Sorry for my unprofessional picture. But I hope you understand it and could help me to code for this situation.
I have try to enable the debugger and it shows that the code cant run the adjustmentTypeLookup. and thats why it cant pass the value to retrieveRecord .Thank you.
function adjustmentInvoiceApproveAmount(executionContext) {
try {
// Get the form context
const formContext = executionContext.getFormContext();
// Extract attribute values from the form
const adjustmentAmount = formContext.getAttribute("case_adjustmentamount").getValue();
const amountDue = formContext.getAttribute("case_amountdue").getValue();
const adjustmentTypeLookup = formContext.getAttribute("case_adjustmenttype").getValue();
// Exit as adjustmenttype is not set
if (!adjustmentTypeLookup) return;
// Extract the adjustment type record ID from the payment type lookup
const adjustmentTypeId = adjustmentTypeLookup[0].id.substring(1, 37);
//console.log("GUID \"case_adjustmenttype\" = " + adjustmentTypeId + " ; " + typeof adjustmentTypeId);
//console.log(adjustmentTypeId);
// Retrieve a SINGLE case_adjustmenttype based on lookup ID on form
Xrm.WebApi.retrieveRecord("case_adjustmenttype", adjustmentTypeId, "$select=case_name").then(
function success(adjustmentType)
{
// If the payment type is credit notes then check payment amount and resit amount
if (adjustmentType.case_name.toLowerCase() == "Credit notes".toLowerCase())
{
if (adjustmentAmount >= amountDue) {
formContext.getEventArgs().preventDefault();
Xrm.Navigation.openErrorDialog({message:"Payment Amount cannot be more than Resit Amount."})
}
}
//Otherwise do nothing
},
function (error)
{
console.log(error.message);
}
);
}
catch (error)
{
console.log(error);
}
}
If you are struggling with the code of retrieving data using web API, I would suggest checking out "CRM REST Builder".
https://github.com/jlattimer/CRMRESTBuilder
Import the solution and refresh the solutions page in dynamics which will show up the button to start this tool. This tool is awesome at generating code for different scenarios.
Related
I want to make a website by implementing the use of sparql in vue js. The scenario I want is to create a special place to write Sparql Query and then execute it with vue.js. Is all that possible? if possible how should i start it? if not possible, is there any other alternative than using vue.js? Please help.
I am not a JS pro by any means. Anyway, for a similar problem, I used axios for the HTTP request.
This worked fine for my use case. Anyway, you will find a precise description of the JSON format at https
var params = new URLSearchParams();
// queryArtistsByNameTpl contains the actual SPARQL query,
// with $artistName as a placeholder
let similarGroupsQuery = queryArtistsByNameTpl.replace("$artistName", this.artistName);
params.append('query', similarGroupsQuery);
let getArtistsHandler = sparqlResponseHandler(this, 'artistList');
axios.post(sparqlEndPoint, params).then(getArtistsHandler);
function sparqlResponseHandler(currentObj, currList) {
return function(response) {
const rows = response.data.results.bindings;
currentObj[currList] = [];
if (rows.length > 0) {
rows.forEach(function(item, index) {
var record = {};
for (var prop in item) {
if (item.hasOwnProperty(prop)) {
record[prop] = item[prop].value;
}
}
currentObj[currList].push(record);
})
} else {
console.log("no data from SPARQL end point");
}
}
}
The JSON resturned by SPARQl endpoints is specified in https://www.w3.org/TR/sparql11-results-json/ , which is rather short and very understandable.
The code can certainly be improved by someone more knowledgeable then me, but it was fine for the tech demo we used it for.
To check if paymenttype equal credit account [entity form: auto_paymenttype]
Then , if payment amount <= resit amount, it will save else > prevent save (popup message invalid: payment amount should lower than resit amount)[entity form: auto_resittype]
Hi guys, It could be great if someone could re-code and help me on this.I am new in D365 and JS. Basically, I have entities which is auto_paymenttype and auto_resittype and their parent is Payment. How can I query the parent adjustment record using JS. I have provide my current code, please help me to review it. I have try everything but so far no luck. Sorry for my unprofessional picture. But I hope you understand it and could help me to code for this situation. Thank you.
function resitApproveAmount(executionContext) {
try {
const object = {};
object.fctx = executionContext.getFormContext();
object.saveEvent = object.fctx.getEventArgs();
object.paymentamount = object.fctx.getAttribute("auto_paymentamount").getValue();
object.resitamount = object.fctx.getAttribute("auto_resitamount").getValue();
object.paymenttype = Xrm.Page.getAttribute("auto_paymenttype").getValue();
if (object.paymenttype != null) {
object.autoGUID = object.paymenttype[0].id.substring(1, 37);
}
Xrm.WebApi.retrieveMultipleRecords("auto_paymenttype", "$select=auto_name").then(
function success(result) {
for (var i = 0; i < result.entities.length; i++) {
object.auto_name = result.entities[i]["auto_name"];}
if(object.auto_name == "Credit Account"){
if (object.paymenttamount >= object.resitamount) {
alert("Payment Amount cannot be more than Resit Amount.");
object.saveEvent.preventDefault();
}
else
{object.fctx.data.save();}
}
},
function (error) {
console.log(error.message);
}
);
}
catch (error) {
console.log(error);
}
}
click here for picture overview
You code implies auto_paymentamount and auto_resitamount exist on entity Payment but you ERD shows them existing on entity Resit Type.
If they are on the Payment entity then your can retrieve their values as in your question:
const paymentAmount = formContext.getAttribute("auto_paymentamount").getValue();
const resitAmount = formContext.getAttribute("auto_resitamount").getValue();
Otherwise you need to retrieve the Resit Type record related to your Payment record, using the WebApi. If this is the case I assume you have a lookup from Payment to Resit Type.
Assuming auto_paymentamount and auto_resitamount exist on entity Payment your code can be simplified to the following two solutions. Both solutions assume this function is called during the onsave event.
Solution 1 - Retrieve the related Payment Type Record
function resitApproveAmount(executionContext) {
try {
// Get the form context
const formContext = executionContext.getFormContext();
// Extract attribute values from the form
const paymentAmount = formContext.getAttribute("auto_paymentamount").getValue();
const resitAmount = formContext.getAttribute("auto_resitamount").getValue();
const paymentTypeLookup = formContext.getAttribute("auto_paymenttype").getValue();
// Exit as payment type is not set
if (!paymentTypeLookup) return;
// Extract the payment type record ID from the payment type lookup
const paymentTypeId = paymentTypeLookup[0].id.substring(1, 37);
// Retrieve a SINGLE auto_paymenttype based on lookup ID on form
Xrm.WebApi.retrieveRecord("auto_paymenttype", paymentTypeId, "$select=auto_name").then(
function (paymentType)
{
// If the payment type is credit account then check payment amount and resit amount
if (paymentType.auto_name.toLowerCase() == "Credit Account".toLowerCase())
{
if (paymentAmount >= resitAmount) {
formContext.getEventArgs().preventDefault();
Xrm.Navigation.openErrorDialog({message:"Payment Amount cannot be more than Resit Amount."})
}
}
//Otherwise do nothing
},
function (error)
{
console.log(error.message);
}
);
}
catch (error)
{
console.log(error);
}
}
Solution 2 - Hard Code the Payment Type Record ID
This can be used if you can guarantee your payment type record ID's will be the same across all environments (i.e. Payment type records are migrated using a data migration tool)
function resitApproveAmount(executionContext) {
try {
// Get the form context
const formContext = executionContext.getFormContext();
// Extract attribute values from the form
const paymentAmount = formContext.getAttribute("auto_paymentamount").getValue();
const resitAmount = formContext.getAttribute("auto_resitamount").getValue();
const paymentTypeLookup = formContext.getAttribute("auto_paymenttype").getValue();
// Exit as payment type is not set
if (!paymentTypeLookup) return;
// Extract the payment type record ID from the payment type lookup
const paymentTypeId = paymentTypeLookup[0].id.substring(1, 37);
// Exit if payment type id is not "Credit Account"
if (paymentTypeId.toLowerCase() !== "Hard Code Credit Account GUID here / retrieve environment variable") return;
if (paymentAmount >= resitAmount) {
formContext.getEventArgs().preventDefault();
Xrm.Navigation.openErrorDialog({message:"Payment Amount cannot be more than Resit Amount."})
}
}
catch (error)
{
console.log(error);
}
}
Notes
To help with questions like this in future I would recommend drawing an Entity Relationship Diagram. This helps us understand your entity model and relationships, which helps us answer questions accurately the first time.
Diagrams.net is a great way to draw an ERD for free
I've had another read of the question and think I better understand your entity model:
Like my previous answer this solution assumes this function is called during the onsave event on Payment. I use async/await and async onsave events to simplify the retrieval of Payment Type and Resit Type. Like my other answer, if you know Payment Type GUIDs will be the same across environments you can hard code the Payment Type ID, and check the ID against the lookup ID on payment, to save yourself a WebApi call.
/**
* On Save of Payment record
* #param executionContext
* #returns
*/
async function onSave(executionContext) {
try {
// Get the form context
const formContext = executionContext.getFormContext();
const paymentTypeLookup = formContext.getAttribute("auto_paymenttype").getValue();
const resitTypeLookup = formContext.getAttribute("aut_resittype").getValue();
// Exit as payment type is not set
if (!paymentTypeLookup || !resitTypeLookup) return;
// Extract related entity IDs
const paymentTypeId = paymentTypeLookup[0].id.substring(1, 37);
const resitTypeId = resitTypeLookup[0].id.substring(1, 37);
// Get related records
const [paymentTypeResult, resitTypeResult] = await Promise.all([
Xrm.WebApi.retrieveRecord("auto_paymenttype", paymentTypeId),
Xrm.WebApi.retrieveRecord("auto_resittype", resitTypeId)
]);
// Evaluate payment type and resit type and prevent save if required
if (paymentTypeResult.auto_name.toLowerCase() === "Credit Account".toLowerCase()
&& resitTypeResult.auto_paymentAmount >= resitTypeResult.auto_resitAmount)
{
formContext.getEventArgs().preventDefault();
Xrm.Navigation.openErrorDialog({message:"Payment Amount cannot be more than Resit Amount."})
}
}
catch (error)
{
console.log(error);
}
}
I know I can get the current logged in user's profile picture, but how do I get someone else's based on their uid?
Here's what I tried:
database.ref("classes/" + currentClassId + "/chat/main").on('value', (snapshot) => {
var p = 0
console.log(snapshot.val())
if(snapshot.val() != null){p = snapshot.val().data.msgCount}
var min = p - 100
if(min <= 0){min = 0}
var max = p
$("#log").empty()
if(snapshot.val() != null){
for(i=min; i<=max - 1; i++){
console.log(i)
database.ref("classes/" + currentClassId + "/chat/main/log/" + i).once('value').then(s => {
$("#log").append([
$("<div/>").append([
$("<img/>").attr("src", firebase.auth().getUser(s.val().sender.uid).then(function(profile){
return profile.PhotoUrl // <<<<<< !!!!!! This part doesnt work !!!!!! <<<<<<
})),
$("<p/>").text(s.val().message).addClass("message")
]).css("display: inline;")
])
})
}
}
})
Here's what I got:
Uncaught (in promise) TypeError: firebase.auth(...).getUser is not a function
at <anonymous>:178:65
Is there another way to get the profile picture url of another registered user in firebase auth? (I'm only using Javascript, no NodeJS, so I don't think I can use firebase admin?)
What you're trying to do isn't possible from client app code. For security reasons, users can not directly access other users' profile information at all. There is simply no API for it, and no permission at a low level.
If you want users to be able to see information about others, you will need to write that data to a database, then query the database to get a hold of it later.
I'm creating my custom order id with auto-increment generator function for my project. I will state my question here, if you want to know the whole story please read below.
As written in the title, I need a way to reject my set to Firebase and it has to be done in 1 query. Currently, it will write my orderID to Firebase without rejecting it. But I need to reject if there is the same ID in the table.
The short version of my code will be posted here, the whole function will be posted below.
firebase.database().ref('orderCounter/orderIDsChecker/'+orderID).set({
id: orderID,
}, function(error) {
if (error) {
console.log('Order ID fail to generate. Regenerating new ID')
createOrderID(orderCounterRef);
} else {
console.log('Order ID created!')
}
});
}
The story,
I'm creating my own custom order id with auto-increment generator function for my project. The problem is that if multiple users creating order at the same time, it will generate the same id. Yes, I can use transaction() to solve the problem but I have no idea how to use it. Therefore, I have created my own version of the "transaction". With my method, I am able to prevent duplicates id unless 2 or more users create order within 1 second of gap. Or if anyone is kind enough to show me an example of how to write a transaction for my function, I thank you in advance.
The flow of the code is,
Get "currentMonth" and "orderIdCounter" from Firebase -> orderIdCounter +1 and update to Firebase -> start the process of generating order id -> Send the generated id to firebase -> If return success "order ID created", If not "got duplicate id" Re-run the whole process.
Below is the code for my order id generator function.
function createOrderID(orderCounterRef){
var childData = [];
var orderID;
//Get the Current Month and Order ID Counter from Firebase
orderCounterRef.on('value', function(snap) { childData = snapshotToArrayWithoutID(snap); });
var currentMonth = childData[0];
var orderIDCounter = childData[1];
if (orderIDCounter !== undefined){
//Update orderIDCounter on Firebase.
//This is to prevent duplicate orderID when multiple users is creating order at the same time.
var IDCounter = parseInt(orderIDCounter) + 1;
//Set IDCounter to 3 digits
IDCounter = ('00' + IDCounter.toString()).slice(-3);
firebase.database().ref('orderCounter/orderIDCounter').set(IDCounter);
//Handle the process to generate Order ID. Return in YYMMxxx(auto increment) format.
orderID = handleCreateOrderID(currentMonth, (parseInt(orderIDCounter) - 1));
//Check if duplicate ID on firebase
firebase.database().ref('orderCounter/orderIDsChecker/'+orderID).set({
id: orderID,
}, function(error) {
if (error) {
console.log('Order ID fail to generate. Regenerating new ID')
createOrderID(orderCounterRef);
} else {
console.log('Order ID created!')
}
});
}
return orderID;
}
My DB:
You should indeed use a transaction as you have mentioned in your question.
The following should do the trick:
//Declare a function that increment a counter in a transaction
function createOrderID() {
var orderIdRef = firebase.database().ref('orderId');
return orderIdRef.transaction(function(currentId) {
return currentId + 1;
});
}
//Call the asynchronous createOrderID() function
createOrderID().then(function(transactionResult) {
console.log(transactionResult.snapshot.val());
});
If you want to start the counter at a specific value, just create an orderId node in your database and assign a specific value to it, e.g; 1912000.
If you just want to start at 1, you don't need to create a node, it will be automatically created with the first call to the createOrderID() function.
Thank you, #samthecodingman & #Renaud Tarnec for your advice.
I took #samthecodingman's code and change a bit to fit my project. But I use generateOrderID() only to call the result and it works well. But you won't get any value with just the code. I call out another function (connectToFirebase) whenever users enter the page. I am not sure why it works or if this is the right way, but it works for me and that's good enough.
export function generateOrderID(){
var orderId;
var childData = [];
const orderCounterRef = firebase.database().ref('orderCounter/');
//Get the Current Month from Firebase
orderCounterRef.on('value', function(snap) { childData = snapshotToArrayWithoutID(snap); });
//Check ID format YYMMXXX (XXX=auto_increment). Hanlde auto_increment for Year and Month
handleOrderIdFormat(childData[0], orderCounterRef)
//transaction
orderCounterRef.child('orderId').transaction(function(currentId) {
orderId = (currentId||0) +1;
return orderId;
}, function(err) {
if( err ) {
console.log(err)
}
});
return orderId;
}
export function connectToFirebase(){
//Connection Firebase Database
const orderCounterRef = firebase.database().ref('orderCounter/');
orderCounterRef.on('value', function(snap) { });
}
It's my first time posting here so apologies in advance if it's not asked correctly.
I'm working on a project where I have a twitter bot that tweets every time a certain account tweets.
I can get it working when I set up a test account but when I do it with the real account (a popular one) my bot will just keep tweeting continuously.
I am basing my code on Daniel Shiffman's twitter bot tutorials on YouTube:
...OK as an edit I have discovered via another forum that the reason is that I am also incoporating any retweets/loves etc of the tweet that I want my bot to react to..as per Twitter API Documentation:
follow
A comma-separated list of user IDs, indicating the users whose Tweets should be delivered on the stream. Following protected users is not supported. For each user specified, the stream will contain:
Tweets created by the user.
Tweets which are retweeted by the user.
Replies to any Tweet created by the user.
Retweets of any Tweet created by the user.
Manual replies, created without pressing a reply button (e.g. “#twitterapi I agree”).
So what I now need to do is implement some logic so that my bot will only react to the actual tweet of the account I am following and not to any retweets/favourites etc of it..
I have included an if/else statement along with my original code below:
console. log('The streambot is starting');
var Twit = require('twit');
var config = require('./config');
var T = new Twit(config);
var stream = T.stream('statuses/filter', { follow: '(//TWITTERID OF ACCOUNT GOES HERE' });
stream.on('tweet',thisTweet);
/*
if ('statuses/filter' != userID){
console.log("error")
} else {
thisTweet;
} */
//this IF is to try and limit reaction of my bot only to the tweets of the account I am following
function thisTweet(){
var randomWords= "//a list of random words"
var splitrandom = randomWords.split(",");
//picks a random word from list
var oneWord = splitrandom [Math.floor(Math.random() * splitrandom .length)];
tweetIt(oneWord);
}
function tweetIt(txt){
var tweet = {
status : txt
}
T.post('statuses/update',tweet,tweeted);
}
function tweeted(err, data, response) {
if (err){
console.log("something went wrong!");
}else{
console.log("It Worked");
}
}
Any help would be great!
I got some help from twitter developers on this one:
This should do the trick:
// define the ID of the user we are interested in
var userID = 'some number';
// open a stream following events from that user ID
var stream = T.stream('statuses/filter', { follow: ( userID ) });
stream.on('tweet', function (tweet) {
// compare the user ID inside the Tweet object we passed in
// to check it matches
if (tweet.user.id == userID) {
console.log("this was sent by the user we want to track")
// now do something else
} else {
console.log(tweet.user.id + " - " + tweet.user.screen_name)
// so we can ignore it
}
});