How can I get business unit details of the logged-in user with javascript? I tried Xrm.Utility.getGlobalContext().userSettings but I couldn't get any information for business unit
If you write Xrm.Utility.getGlobalContext().userSettings I assume you are working with client-side javascript inside a Model-driven app.
From the userSettings you can get the userId property, it returns the GUID of the current user.
After you have this value in order to get details from the business unit of the user you need to do a retrieve request, something like this:
// get the userId
var userId = Xrm.Utility.getGlobalContext().userSettings.userId;
// remove { and } from the userId
userId = userId.replace("{", "").replace("}", "");
// Xrm.WebApi call to retrieve details of the user (fullname)
// and the name of the businessunit (name from expand)
Xrm.WebApi.online.retrieveRecord("systemuser",
userId,
"?$select=fullname&$expand=businessunitid($select=name)").then(
function success(result) {
console.log(result);
// Columns
var systemuserid = result["systemuserid"]; // Guid
var fullname = result["fullname"]; // Text
// Many To One Relationships
if (result.hasOwnProperty("businessunitid")) {
var businessunitid_name = result["businessunitid"]["name"]; // Text
}
},
function(error) {
console.log(error.message);
}
);
Related
This is the database structure i have i want to get logged in user data.
i want to make table of data: Columns: Date,Status
Also i want to make percentage piechart wheel by calculating success and failure rate. but not able to get data from firebase.
I tried this but not working. I'm able to log in log out successfully. I'm also able to add data in firebase only once per date.
I'm just not able to fetch and show in table.
Here's what i tried:
`
// Get the user's attendance records
firebase.database().ref("attendance").once("value", function(snapshot) {
// Get the attendance data
var attendanceData = snapshot.val();
var userId = firebase.auth().currentUser.uid;
// Display the attendance history
for (var email in attendanceData) {
var attendance = attendanceData[email][userId];
if (attendance) {
for (var date in attendance) {
var status = attendance[date].status;
var tr = document.createElement("tr");
tr.innerHTML = `<td>${date}</td><td>${status}</td>`;
attendanceHistoryTable.appendChild(tr);
}
}
}
});
If I understand correctly, you have a data structure like this:
attendance: {
user: {
"$uid": {
"$date": {
Status: "..."
}
}
}
}
And from this you want to show the status per date for the current user.
If that's indeed the use-case, you can do this with:
const userId = firebase.auth().currentUser.uid;
const attendanceRef = firebase.database().ref("attendance");
const userRef = attendanceRef.child("users").child(userId);
userRef.once("value", function(userSnapshot) {
userSnapshot.forEach((dateSnapshot) => {
const status = dateSnapshot.child("Status").val();
console.log(`User: ${userSnapshot.key}, Date: ${dateSnapshot.key}, Status: ${status}`);
... // TODO: add the data to the HTML as you're already doing
});
});
The main changes I made here:
This only loads the data for the current user, instead of for all users.
This code uses the built-in forEach operation of a DataSnapshot.
This code gives more meaningful names to the variables, so that it's easier to parse what is going on.
This code uses "Status" rather then status, since that's the key in your database screenshot too.
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'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) { });
}
I am faced with the problem of retrieving two data values of a single node from my firebase database and reference it in my javascript file but don't know how to go about it. I have been able to retrieve just one data value from a node (in this case "message") but I would like to add "from" as well. Most tutorials just reference one so I am really confused. So how do I get multiple data values?
This is my code...
JS file
exports.sendNotification7 = functions.database.ref('/GroupChat/{Modules}/SDevtChat/{SDevtChatId}/message')
.onWrite(( change,context) =>{
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = change.after.val();
var str = "New message from System Development Group Chat: " + eventSnapshot;
console.log(eventSnapshot);
var topic = "Management.Information.System";
var payload = {
data: {
name: str,
click_action: "Student_SystemsDevt"
}
};
// Send a message to devices subscribed to the provided topic.
return admin.messaging().sendToTopic(topic, payload)
.then(function (response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
return;
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});
You can read from however many nodes you want in a Cloud Function. However, only one can trigger the function to run.
To read from your database use the following code:
admin.database().ref('/your/path/here').once('value').then(function(snapshot) {
var value = snapshot.val();
});
You will probably want to read from the same place that the Cloud Function was triggered. Use context.params.PARAMETER to get this information. For the example you posted your code would turn out looking something like this:
admin.database().ref('/GroupChat/'+context.params.Modules+'/SDevtChat/'+context.params.SDevtChatId+'/from').once('value').then(function(snapshot) {
var value = snapshot.val();
});
Just trigger your function one level higher in the JSON:
exports.sendNotification7 =
functions.database.ref('/GroupChat/{Modules}/SDevtChat/{SDevtChatId}')
.onWrite(( change,context) =>{
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = change.after.val();
console.log(eventSnapshot);
var str = "New message from System Development Group Chat: " + eventSnapshot.message;
var from = eventSnapshot.from;
...
Is it possible to to change a user's UID in Firebase programmatically? There can't seem to be a way to do so manually within Firebase's console.
TL;DR: If you need to specify the UID, you'll need to create a new user with that UID.
You can't directly change the UID, but I was able to hack something together using the firebase admin API (docs)
My use case was that I needed to change a user's email address. I tried update email with "Update a User", but this actually ended up changing the UID under the hood. In my app, the UID is tied to so much stuff, that I'd have to do a huge architecture change, so this wasn't an option.
The general way I did this with the API was:
Pull Down a user using admin.auth().getUserByEmail
Delete the user with admin.auth().deleteUser
Create a new user with admin.auth().createUser, using relevant data from the getUserByEmail call above, replacing the email address with the new email.
"reset password" in the firebase admin console (I think there's a way to do this programmatically too)
User gets an email to reset their password and they have a new account with their old UID.
Unlike admin.auth().updateUser, createUser actually lets you specify a UID.
Building on the answer by RoccoB, the below is a complete set of instructions for changing a user's UID:
Create a new folder, and run npm init with default values.
Run npm install firebase-admin.
Create a NodeJS script file (eg. UpdateUserUID.js), with this code:
let admin = require("firebase-admin");
// config
let email = "XXX";
let serviceAccountData = require("XXX.json");
let adminConfig = {
credential: admin.credential.cert(serviceAccountData),
databaseURL: "https://XXX.firebaseio.com",
};
let newUserOverrides = {
uid: "XXX",
};
Start();
async function Start() {
console.log("Initializing firebase. databaseURL:", adminConfig.databaseURL);
admin.initializeApp(adminConfig);
console.log("Starting update for user with email:", email);
let oldUser = await admin.auth().getUserByEmail(email);
console.log("Old user found:", oldUser);
await admin.auth().deleteUser(oldUser.uid);
console.log("Old user deleted.");
let dataToTransfer_keys = ["disabled", "displayName", "email", "emailVerified", "phoneNumber", "photoURL", "uid"];
let newUserData = {};
for (let key of dataToTransfer_keys) {
newUserData[key] = oldUser[key];
}
Object.assign(newUserData, newUserOverrides);
console.log("New user data ready: ", newUserData);
let newUser = await admin.auth().createUser(newUserData);
console.log("New user created: ", newUser);
}
Replace email and adminConfig.databaseURL with the correct values.
Replace newUserOverrides.uid with the desired new uid. (you can change some other fields too)
Generate/download a private key for your project's Firebase Admin service account: https://firebase.google.com/docs/admin/setup (can skip to the "Initialize the SDK" section)
Update the serviceAccountData variable's import to point to the key json-file from the previous step.
Run node ./UpdateUserUID.js.
If applicable (I didn't seem to need it), use the "reset password" option in the Firebase Admin Console, to have a password-reset email sent to the user, apparently completing the account update. (Perhaps I didn't need this step since I don't use the accounts/authentications for anything besides sign-in on my website...)
The UID of a user is controlled by the identity provider that creates that user. This means that you can't change the UID for any of the built-in providers.
But you can control the UID if you create a custom identity provider. Note that this is quite a bit more involved than changing something in the Firebase console. It requires you to write code that runs in a secure/trusted environment, such as a server you control, or Cloud Functions.
You can't, since is the main tree node of possibles more entries inside it, you can get it, modify and then put it inside the same UID (or create a new one) but you can have things inside, for example take this.
You create your main UID which will hold user data (name, phone, email etc) lets say the structure is this:
-9GJ02kdj2GKS55kg
-Name:
-Phone:
-Email:
so, you can get the main user UID 9GJ02kdj2GKS55kg with mAuth.getCurrentUser().getUid(); and then change it and set a new value inside 9GJ02kdj2GKS55kg, this new value should be the same UID you got but changed, and then inside your main UID you can still have the same structure
-9GJ02kdj2GKS55kg
-6GL02kZj2GKS55kN (this is your changed UID)
-Name:
-Phone:
-Email:
or you can get that changed UID and make a new child, and that will be your parent node with custom UID for the data.
Piggybacking on #Vinrynx's post.
I recently created a migration tool where I am migrating collections from 1 Firebase Project to another and it required that after I insert users to "users" collection I also create an authentication record with the same doc.id
Variables in the functions below:
outCollData : Data that I am inserting for the user (contains the email inside it)
sourceDBApp : output of the admin.initializeApp({/*service-account.json file location for source firebase project */});
destDBApp : output of the admin.initializeApp({/*service-account.json file location for destination firebase project */});
async function updateUsersUID(
outCollData: any,
sourceDBApp: admin.app.App | undefined,
destDBApp: admin.app.App | undefined
) {
if (destDBApp === undefined) return;
const admin = destDBApp;
const email = outCollData.personali.email ? outCollData.personali.email : "";
console.log("Email is ", email);
if (email === "" || email === undefined) return;
console.log("Inside updateUsersUID");
let newUserOverrides = {
uid: outCollData._id,
};
let oldUser: any;
try {
console.log("Starting update for user with email:", email);
oldUser = await admin.auth().getUserByEmail(email!);
//console.log("Old user found:", oldUser);
if (oldUser.uid === outCollData._id) {
console.log(
"User " +
email +
" already exists in the destination DB with UID " +
outCollData._id
);
return;
}
await admin.auth().deleteUser(oldUser.uid);
console.log("Old user deleted.");
} catch (e) {
console.log("User not found in destination DB ", email);
console.log("Copying the user data from source DB");
oldUser = await sourceDBApp?.auth().getUserByEmail(email);
}
let dataToTransfer_keys = [
"disabled",
"displayName",
"email",
"emailVerified",
"phoneNumber",
"photoURL",
"uid",
"providerData",
];
let newUserData: any = {};
for (let key of dataToTransfer_keys) {
newUserData[key] = oldUser[key];
}
Object.assign(newUserData, newUserOverrides);
//console.log("New user data ready: ", newUserData);
let newUser = await admin.auth().createUser(newUserData);
console.log("New user created ");
}