Suitescript 2.x: Create a Customer Deposit upon Approval of Sales Order - suitescript

Can you please check my code. I want to create a Customer Deposit upon approval of Sales Order. However, the Customer Deposit is both being created in Pending Approval and Pending Fulfillment. Can you please help fix my code. Thank you!
/**
*#NApiVersion 2.x
*#NScriptType UserEventScript
*/
define(["N/record"], function (record) {
try {
function afterSubmit(scriptContext) {
if (scriptContext.type == scriptContext.UserEventType.CREATE || scriptContext.type == scriptContext.UserEventType.EDIT) {
var objSoRecord = scriptContext.newRecord;
log.debug("Load the Sales Order", objSoRecord);
var intsoId = objSoRecord.id;
log.debug("Get the Sales Order Id: " , intsoId)
var stCustomer = objSoRecord.getValue({
fieldId: 'entity'
});
log.debug("Customer ", stCustomer)
//Get Values
var checkStatus = objSoRecord.getValue('status');
log.debug("SO status: " , checkStatus);
if (checkStatus !== "Pending Approval"|| checkStatus == "Pending Fulfillment") {
var objcustDeposit = record.create({
type: record.Type.CUSTOMER_DEPOSIT,
isDynamic: true,
defaultValues: {
'entity': stCustomer,
'salesorder': intsoId
}
});
//Insert Code here
var saveCustDep = objcustDeposit.save();
}
}
}
} catch (error) {
log.debug("Capture Error:", error);
}
return {
afterSubmit: afterSubmit
}
});
N/AN/AN/AN/AN/AN/AN/AN/AN/AN/A

Your checkStatus condition
if (checkStatus !== "Pending Approval"|| checkStatus == "Pending Fulfillment") {
essentially breaks down to "if checkStatus is anything except Pending Approval, create the Deposit". You'll need to fix the condition.
I might suggest you compare the old value of the status field and the new value of the status field, and only create the Deposit when the status changes to Pending Approval. If you only look at the current status, then any edits while in the Pending Approval status will trigger further Deposit creations.

Related

Firestore query "onSnapshot" called at the same time does not work (

I created an app with Ionic and Firestore that features live chat and I'm having a problem with it.
The conversation is loaded with the method:
refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(20).get()
To this is added an "onSnapshot" request to retrieve the last message sent live
this.unsubscribeDataUneConversation = refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(1).onSnapshot(result => {
console.log(result.docs[0].data());
if (this.isCalledBySnapshot === false) {
this.isCalledBySnapshot = true;
} else if (result.docs[0].data().expediteur !== this.authentificationService.uidUserActif) {
const data = result.docs[0].data();
const id = result.docs[0].id;
this.dataUneConversation.push({ id, ...data } as UneConversation);
}
});
It will work perfectly however, when I send a message at the same time (with 2 different accounts talking to each other), I encounter a problem, the onSnapshot is triggered only once and I only receive one message.
I specify that the two messages are sent well in the database, they are only not displayed both during the live session
Do you have any idea why?
Thank you
(Here is the whole method)
async getDataUneConversation(idI: string) {
if (this.loadedDataUneConversation !== idI) {
/* ANCHOR Msg en direct */
this.isCalledBySnapshot = false;
if (this.unsubscribeDataUneConversation) {
await this.unsubscribeDataUneConversation();
}
const refUneConversationMyUserCol = this.afs.collection<User>('users').doc<User>(this.authentificationService.uidUserActif).collection<Conversations>('conversations');
const result = await refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(20).get();
/* ANCHOR Msg en direct */
this.unsubscribeDataUneConversation = refUneConversationMyUserCol.ref.orderBy('date', 'desc').limit(1).onSnapshot(result => {
console.log(result.docs[0].data());
if (this.isCalledBySnapshot === false) {
this.isCalledBySnapshot = true;
} else if (result.docs[0].data().expediteur !== this.authentificationService.uidUserActif) {
const data = result.docs[0].data();
const id = result.docs[0].id;
this.dataUneConversation.push({ id, ...data } as UneConversation);
}
});
/* ANCHOR Msg en brut */
if (result.docs.length < 20) {
this.infiniteLastUneConversationMax = true;
} else {
this.infiniteLastUneConversationMax = false;
}
this.infiniteLastUneConversation = result.docs[result.docs.length - 1];
this.dataUneConversation = result.docs.map(doc => {
const data = doc.data();
const id = doc.id;
return { id, ...data } as UneConversation;
});
this.dataUneConversation.reverse();
this.loadedDataUneConversation = idI;
}
}
EDIT for working :
this.unsubscribeDataUneConversation = refUneConversationMyUserCol.ref.orderBy('date', 'asc').startAfter(this.dataUneConversation[this.dataUneConversation.length
- 1].date).onSnapshot(result => {
result.docs.forEach(element => {
const data = element.data();
const id = element.id;
if (!this.dataUneConversation.some(e => e.id === element.id)) {
this.dataUneConversation.push({ id, ...data } as UneConversation);
}
});
});
You're limiting live messages to only one last message. In a chat app, you want to listen to all new messages. So the issue is probably in your .limit(1) clause.
But if you do that, I understand that you'll get the whole conversation, with all messages, since the conversation started.
My approach would be like this:
Get the date of the last message from your refUneConversationMyUserCol... conversation loader.
When you do the onSnapshot() to get the last message, do not limit to 1 message, instead, start at a date after the date of the last loaded message.
Since you're ordering by date anyway, this will be an easy fix. Look into "Adding a cursor to your query".
Basically, you'll be saying to Firestore: give me LIVE new messages but start at NOW - and even if there are many messages posted at the same time, you'll get them all, since you're not limiting to 1.
Feel free to ask if this is not clear enough.

How to retrieve checkout information when using Stripe SDK

Here is my Cart and Checkout process in my web application
My purpose is after checkout, I retrieve the payment information like transaction Id or payment reference
In my ASP.NET MVC 5 application, I have following code in Cart View
<div>
#using (Html.BeginForm("", "Cart", FormMethod.Post))
{
... here I show products in cart ...
... NOTE that I don't have any action in my form ...
<input type="submit" id="stripe-checkout-button" value="Checkout" />
}
</div>
<script type="text/javascript">
var stripe = Stripe('#ViewBag.StripePublishKey');
var checkoutButton = document.getElementById('stripe-checkout-button');
checkoutButton.addEventListener('click', function () {
fetch('https://localhost:44323/Cart/CreateCheckoutSession', {
method: 'POST'
})
.then(function(response) {
return response.json();
})
.then(function(session) {
return stripe.redirectToCheckout({ sessionId: session.id });
})
.then(function(result) {
if (result.error) {
alert(result.error.message);
}
})
.catch(function(error) {
console.error('Error:', error);
});
});
</script>
In Cart controller I have following actions
[HttpPost]
public ActionResult CreateCheckoutSession()
{
var cart = (ShoppingCart)Session["cart"];
var options = new SessionCreateOptions
{
PaymentMethodTypes = new List<string> { "card" },
Mode = "payment",
SuccessUrl = Url.Action("CheckoutSuccessful", "Cart", null, Request.Url.Scheme),
CancelUrl = Url.Action("CheckoutCancelled", "Cart", null, Request.Url.Scheme),
PaymentIntentData = new SessionPaymentIntentDataOptions
{
CaptureMethod = "manual"
},
LineItems = new List<SessionLineItemOptions>()
};
foreach (var item in cart.Items)
{
// here I add the purchased product. I remove the code for simplification
}
var service = new SessionService();
Session session = service.Create(options);
ViewBag.SessionId = session.Id;
return Json(new { id = session.Id });
}
public ActionResult CheckoutSuccessful(string sessionId)
{
// sessionId is null here. I'm even not sure if I need this parameter
ShoppingCart cart = (ShoppingCart)Session["cart"];
return RedirectToAction("Index", "Checkout");
}
public ActionResult CheckoutCancelled()
{
return RedirectToAction("Index", "Cart");
}
When I click on Checkout button, the application redirects to Stripe payment page and I can enter email and billing information and click pay. It works well and payment goes through.
Based on my setting, the application redirects to CheckoutSuccessful action after that.
What I am missing here is, in CheckoutSuccessful action, I need to retrieve email address and billing address plus payment or transaction Id. I don't know how to do that.
In CheckoutSuccessful, the parameter sessionId is null. I don't know what I've done wrong here or what I'm missing.
Thanks for help.
You can ensure that the newly created Checkout Session ID is included in your success URL by appending {CHECKOUT_SESSION_ID to the success URL: https://stripe.com/docs/payments/checkout/custom-success-page#modify-success-url
Then on your success URL you can grab the Session ID from the query params and retrieve the Checkout Session.
After a few days research and try out, I figured it out.
After creating session, I need to store the session.Id in Http Session and in CheckoutSuccessful method (which does not need any parameter), I need to call couple of APIs to retrieve session and payment information like below:
var service = new SessionService();
var session = service.Get(the stored session.Id);
var custService = new CustomerService();
var customer = custService.Get(session.CustomerId);
var paymentService = new PaymentIntentService();
var payment = paymentService.Get(session.PaymentIntentId);
Then, I will have all required infromation in session, customer and payment objects.

Create documents in different firestore collections, with same reference ID

My question is actually twofold, so I m not sure I should ask both in one post or create another post. Anyway, here it is:
I am creating users in firestore database. I do not want to put all details in a single document because it will be requested a lot, and all details will be retrieved, even if not needed. So I decided to create a collection members_full with all details of users I may not need often, and another collection called members_header to keep the few most important details. On creation of a new user, I want reference ID in both collections to be the same for a specific user.
- members_full -+
|
+ --- abnGMbre --- +
|
+ --- mother : 'His mom'
+ --- Father: 'daddy'
- members_header+
|
+ ---- abnGMbre -- +
|
+ ---- fullname: 'john Doe'
+ ---- pictURL: 'path to his profile pic'
I want something looking like the above.
So this is what I did in the cloud function:
/** Create / Update a member
* ------------------------- */
exports.updateMember = functions.https.onCall( (data, context) =>{
// root member and secretaries are allowed to update members
const authParams:any = {
uid: context.auth.uid,
email: context.auth.token.email,
};
// Check if user is allowed to perform operation
return checkPermission(authParams, ['root', 'secretary']).then(res => {
if(res==false){
return { // Permission denied
status: STATUS.permission_denied,
}
}
// set object to add/ update
const member:any = data;
// Check if uid of member object is present (true:update, false: create)
var fullRef : admin.firestore.DocumentReference;
var headRef : admin.firestore.DocumentReference;
var countRef: admin.firestore.DocumentReference;
var createNewMember = false;
if(member.uid!==undefined && member.uid!==null){ // update
fullRef = fsDB.collection('members_full').doc(member.uid);
headRef = fsDB.collection('members_header').doc(member.uid);
} else {
fullRef = fsDB.collection('members_full').doc();
headRef = fsDB.collection('members_header').doc(fullRef.id);
countRef = fsDB.collection('counters').doc('members');
createNewMember = true;
}
return fsDB.runTransaction(t => {
return t.get(fullRef).then(doc => {
// Update full details
t.set(fullRef, {
surname : member.surname ,
firstName : member.firstName ,
birthDate : member.birthDate ,
birthPlace : member.birthPlace ,
email : member.email ,
phone : member.phone ,
occupation : member.occupation ,
father : member.father ,
mother : member.mother ,
spouse : member.spouse ,
children : member.children ,
addressHome : member.addressHome ,
addressLocal: member.addressLocal,
contactHome : member.contactHome ,
contactLocal: member.contactLocal,
comment : member.comment ,
regDate : member.regDate ,
});
// Update header details
t.set(headRef, {
fullName : member.fullName ,
gender : member.gender ,
active : member.active ,
picURL : member.picURL ,
});
// Increment number of members
if(createNewMember ){
t.update(countRef, {count: admin.firestore.FieldValue.increment(1)});
}
}).then(() => {
return { status : STATUS.ok }
}).catch(err => {
return {
status: STATUS.fail,
message: err.message,
error: err
}
});
}).then(() => {
return { status : STATUS.ok }
}).catch(error =>{
return {
status: STATUS.fail,
message: error.message,
debug: 'run transaction err',
error: error
}
});
}).catch(err => {
return {
status: STATUS.fail,
message: err.message,
debug: 'check permission err',
error: err
}
});
});
/** Check if authenticated user's roles are among the ones allowed
* --------------------------------------------------------------- */
function checkPermission(authParams:any, allowedRoles:any[]):Promise<boolean>{
// Check if authenticated user as any of the roles in array 'allowedRoles'
return new Promise((resolve, reject) => {
// If one of allowed roles is root, check against global variables
if(allowedRoles.indexOf('root')>=0 &&
( root_auth.email.localeCompare(authParams.email)==0 ||
root_auth.uid.localeCompare(authParams.uid)==0)){
resolve(true);
}
// Get autID
const uid = authParams.uid;
// Get corresponding user in collection roles
admin.firestore().collection('userRoles').doc(uid).get().then(snap => {
// Get roles of user and compare against all roles in array 'allowedRoles'
const memRoles = snap.data().roles;
var found = false;
var zz = memRoles.length;
for(let z=0; z<zz; z++){
if(allowedRoles.indexOf(memRoles[z])){
found = true;
break;
}
}
resolve(found);
}).catch(err => {
reject(err);
});
});
}
When I call this cloud function, it only writes in document members_full, and increment number of members. It does not create entry in members_header.
My first question: where did I go wrong? the way I' m getting ID from the first document to create second document, isn't it valid?
The second question, will it be better to create subcollections rather than having 2 collections? if yes, how to do I do that in a transaction?
Help much appreciated
You need to chain the method calls in the Transaction. It is not extremely clear in the documentation, but if you look at the reference document for a Transaction (https://firebase.google.com/docs/reference/node/firebase.firestore.Transaction) you will see that the update() and set() methods return a Transaction, which is
the "Transaction instance. [and is] used for chaining method calls".
So you should adapt your code along these lines:
return fsDB.runTransaction(t => {
return t.get(fullRef)
.then(doc => {
t.set(fullRef, {
surname : member.surname ,
firstName : member.firstName
//....
})
.set(headRef, {
//....
gender : member.gender
//....
})
.update(countRef, {count: admin.firestore.FieldValue.increment(1)});
});
});
You also need to correctly chain all the different promises, as follows:
return checkPermission(authParams, ['root', 'secretary'])
.then(res => {
//...
return fsDB.runTransaction(t => {
//.....
});
.then(t => {
return { status : STATUS.ok }
})
.catch(error => {...})
However, you may use a batched write instead of a transaction, since it appears that you don't use the document returned by t.get(fullRef) in the transaction.
For your second question, IMHO there is no reason to use sub-collections instead of two (root) collections.

Firebase Displaying Other Users' username except yours Using Presence

Hi I'm new to firebase and was trying out the presence example on firebase everything is working normal. My issue is how do I display the username of others ONLY because everything I cant seem to find the solution for this because
I tried googling for an answer but none of the results are what I'm looking for.
I'm new to Firebase and non-mysql database so I dont know how to do a WHERE Statement on firebase
here is my code:
<body>
<div id="presenceDiv" class="l-demo-container example-base">
</div>
<script>
var name = "<?php echo $uname;?>";
var currentStatus = "★ online";
// Get a reference to the presence data in Firebase.
var userListRef = new Firebase("https://<URL>.firebaseio.com/");
// Generate a reference to a new location for my user with push.
var myUserRef = userListRef.push();
// Get a reference to my own presence status.
var connectedRef = new Firebase("https://<URL>.firebaseio.com//.info/connected");
connectedRef.on("value", function(isOnline) {
if (isOnline.val()) {
// If we lose our internet connection, we want ourselves removed from the list.
myUserRef.onDisconnect().remove();
// Set our initial online status.
setUserStatus("★ online");
}
else {
// We need to catch anytime we are marked as offline and then set the correct status. We
// could be marked as offline 1) on page load or 2) when we lose our internet connection
// temporarily.
setUserStatus(currentStatus);
}
});
// A helper function to let us set our own state.
function setUserStatus(status) {
// Set our status in the list of online users.
currentStatus = status;
myUserRef.set({ name: name, status: status });
}
function getMessageId(snapshot) {
return snapshot.name().replace(/[^a-z0-9\-\_]/gi,'');
}
// Update our GUI to show someone"s online status.
userListRef.on("child_added", function(snapshot) {
var user = snapshot.val();
$("<div/>")
.attr("id", getMessageId(snapshot))
.text(user.name + " is currently " + user.status)
.appendTo("#presenceDiv");
});
// Update our GUI to remove the status of a user who has left.
userListRef.on("child_removed", function(snapshot) {
$("#presenceDiv").children("#" + getMessageId(snapshot))
.remove();
});
// Update our GUI to change a user"s status.
userListRef.on("child_changed", function(snapshot) {
var user = snapshot.val();
$("#presenceDiv").children("#" + getMessageId(snapshot))
.text(user.name + " is currently " + user.status);
});
// Use idle/away/back events created by idle.js to update our status information.
document.onIdle = function () {
setUserStatus("☆ idle");
}
document.onAway = function () {
setUserStatus("☄ away");
}
document.onBack = function (isIdle, isAway) {
setUserStatus("★ online");
}
setIdleTimeout(5000);
setAwayTimeout(10000);
</script>
</body>
</html>
This script keeps on loading my 1st dummy username along the other dummy users that i tried logging on with. The same goes for the other dummy accounts the browser loads their username along with the others.. Whats causing this and how do I solve it? Please help
I'd simply identify and exclude the current user in you on(child_ handlers.
So for example:
// Update our GUI to show someone"s online status.
userListRef.on("child_added", function(snapshot) {
var user = snapshot.val();
if (user.name != name) {
$("<div/>")
.attr("id", getMessageId(snapshot))
.text(user.name + " is currently " + user.status)
.appendTo("#presenceDiv");
}
});

async nodejs querying and processing results

I have an array of objects taken from mongodb. Every element in the array is a post, with author as user_id. Now i wish to find the user info related to the user_id.
Since node uses async methods to find the data from db, the forEach loop finishes before the callbacks finish.
docs.forEach(function(doc, index){
//get the user for this doc
User.find({_id: mongo.BSONPure.ObjectID(doc.user_id)}, {name: 1, username: 1, email: 1}).skip(0).limit(1).toArray(function(err, user){
user = user[0]
if(err) {
throw new Error(err)
} else {
docs[index].user = user
if(doc.for_id) {
User.find({_id: mongo.BSONPure.ObjectID(doc.for_id)}, {name: 1, username: 1, email: 1}).skip(0).limit(1).toArray(function(err, for_user){
for_user = for_user[0]
if(err) {
throw new Error(err)
} else {
docs[index].for_user = for_user
}
})
}
}
})
})
So at the end of this loop, if i send a cb(docs), docs do not have the user and for_user attribute. How do I overcome this?
Use Step for node.js. It will run your functions in serial order
var Step = require('step');
Step( docs.forEach(...), function() { cb(docs); } );
Or if you know the total number of records, you can call the callback when you're done processing the last one. Something like this
var count = docs.count(); // or something
var processed = 0;
docs.forEach(... if (++processed == count) cb(docs); );

Resources