Multiple save operations in async.forEachOf - mongodb

I have a loop through some payments in a mongo collection. All the payments with payoutdate == today() must be exported and written to an sepa file, so we can handle the payments by bank.
The payments doesn't have an invoicenumber while they are created and we generating one when the payment is processed (exported via the above function).
The problem is, that when we run the function with multiple payments to be exported, all the payments are getting the same invoice number. So it looks like, that the last save operation is not completed before the next payment is processed.
How can I archieve that every payment is getting an increasing number?
This is the loop function:
const fs = require('fs');
const async = require('async');
const DateDiff = require('date-diff');
const SEPA = require('sepa');
const shopService = require(path.join(__dirname, '..', 'services', 'shop.service'));
async.forEachOf(payments, function(payment, key, paymentDone){
var diff = new DateDiff(new Date(payment.payoutDate), new Date());
if(payment.payoutDate && payment.amount > 0 && payment.completed == false && payment.exported == false && diff.days() <= 0){
//payment has amount, is not completed and is not exported, create an SEPA transfer, and set the payment to completed
//but first create an invoicenumber
orderService.updateOrderPayment(payment.orderId, {generateInvoiceNumber: true}, function(err, result){
if(err){
console.log("error updating payment", err);
}
//reget the payment to avoid duplicated invoice numbers
orderService.getPayment(result.orderId, function(err, payment){
if(err){
console.log("error getting payment", err);
}
Shop.findOne({_id: payment.shopId}).exec(function(err, shop){
if(shop && shop.bankAccountNumber && shop.accountHolder && shop.bicCode){
//create transaction and add this to the file
}else{
var result = {
paymentID: payment._id,
orderId: payment.orderId,
status: payment.status,
message: "shop does not have an iban, accountholder or biccode",
shop: shop.nameSlug
}
resultArray.push(result);
console.log("shop does not have an iban, accountholder or biccode", shop.nameSlug);
paymentDone();
}
orderService.updateOrderPayment(payment.orderId, {status: 'completed'}, function(err, result){
orderService.updateOrderStatusById(payment.orderId, {status: 'Granted', date: new Date(), comment: null});
var result = {
paymentID: payment._id,
orderId: payment.orderId,
status: payment.status,
message: "payment exported",
}
resultArray.push(result);
counter++;
paymentDone();
})
})
})
})
}else{
var result = {
paymentID: payment._id,
orderId: payment.orderId,
status: payment.status,
message: "order already processed"
}
resultArray.push(result);
paymentDone();
}
}, function(){
if(resultArray.length == payments.length){
//console.log("Result", resultArray);
if(counter == 0){
res.status(200).json({"message":"No orders to export", resultArray});
}else{
res.set({"Content-Disposition":"attachment; filename=\"sepa.xml\""});
res.send(doc.toString());
}
}
})
The orderService contains the following functions (relevant to this question)
function updateOrderPayment(orderId, paymentStatus, callback){
console.log("updateOrderPayment");
if(!paymentStatus){
return callback("No payment details provided");
}else{
if(!paymentStatus.comment){
paymentStatus.comment = null;
}
}
getPayment(orderId, function(err, payment){
if(err)
return callback(err);
handlePayment(payment, paymentStatus, function(result){
result.save(function(err, result){
if(err){
return callback(err);
}
console.log("payment saved");
return callback(null, result);
})
})
})
}
function handlePayment(payment, paymentStatus, callback){
if(paymentStatus.status){
var status = {
status: paymentStatus.status,
comment: paymentStatus.comment,
date: Date.now()
}
payment.status.push(status);
}
if(paymentStatus.generateInvoiceNumber){
console.log("generateInvoiceNumber");
var invoiceNumber =0;
Payment.findOne({invoiceNumber: {$exists:true}}).sort({_id: -1}).exec(function(err, latestPaymentsWithNumber){
if(latestPaymentsWithNumber && latestPaymentsWithNumber.invoiceNumber){
invoiceNumber = latestPaymentsWithNumber.invoiceNumber.split("-")[1];
}
var date = new Date();
payment.invoiceNumber = date.getFullYear().toString() + date.getMonth().toString() + "-" + (parseInt(invoiceNumber)+1);
console.log("number", payment.invoiceNumber);
return callback(payment);
})
}
if(paymentStatus.status == 'returned' || paymentStatus.status == 'cancelled'){
payment.cancelled = true;
payment.amount = 0;
payment.payoutDate = null;
return callback(payment);
}
if(paymentStatus.status == 'completed'){
payment.completed = true;
payment.exported = true;
payment.payoutDate = null;
return callback(payment);
}
}
function getPayment(orderId, callback){
Payment.findOne({orderId: orderId}).exec(function(err, payment){
if(err){
return callback(err);
}
return callback(null, payment);
})
}

you have 2 choices:
1) implement callbacks to your save operation within scope
x.forEach(function(_x) {
_x.save(function(err) { });
});
2) break out your functions to async units or use an async library
function async(x, cb) {
x.operations(cb)
}
function series(x) {
if (x) {
async(x, function() { series(xs.pop()); });
} else // finished
}
series(xs.pop()); // xs is the array you're iterating

Thanks to both of the reply's! A combination was the solution.
I have changed the query to find the last invoiceNumber to
Payment.find({invoiceNumber: {$ne:null}}).sort({date: -1}).limit(1).exec(function(err, latestPaymentsWithNumber){
I use now async.eachSeries to iterate over payments:
async.eachSeries(payments, function(payment, paymentDone){
And I do a result.save in the first callback to asume I have the right data
result.save(function(err, payment){

Related

Tez Payment Request API

I'm trying to integrate Google-Tez payment in my site, But When I'm calling request.show() getting error msg as
"Payee isn't a valid merchant"
, When i call checkCanMakePayment() getting error msg as
"Cannot query payment request"
and I enclose my code below. My UPI ID is "hsbc". Can anyone help me to troubleshoot this issue? and I need to know is there any process like merchant has to register with Google Tez
function onBuyClicked() {
const creditCardPaymentMethod = {
supportedMethods: 'basic-card',
data: {
supportedNetworks: ['visa', 'mastercard'],
supportedTypes: ['credit', 'debit']
}
};
const supportedInstruments = [
{
supportedMethods: ['https://tez.google.com/pay'],
//supportedMethods:[creditCardPaymentMethod],
data: {
pa:'**********',
pn:'**********',
tr:'123456ABCDEFSD',
url:'***********',
mc:'*********',
tn:'Purchase in Merchant'
}
}
];
var details =
{
total:
{
label:'Total',
amount: {
currency:'INR',
value:'10.01' //sample amount
}
},
displayItems: [
{
label:'Original Amount',
amount: {
currency:'INR',
value:'10.01'
}
}
]
};
var request =null;
try {
request = new PaymentRequest(supportedInstruments, details);
console.log(request);
/*request.show()
.then(function(result){
alert("hai");
})
.catch(function(err){
alert('Payment Request Error: '+ err.message+' 74');
});*/
}catch (e) {
alert('Payment Request Error: '+ e.message+'77');
console.log('Payment Request Error: '+ e.message);
//return;
}
if (!request) {
alert('Web payments are not supported in this browser 77');
console.log('Web payments are not supported in this browser.');
// return;
} 
var canMakePaymentPromise = checkCanMakePayment(request);
canMakePaymentPromise
.then(function(result){
showPaymentUI(request,result)
})
.catch(function(err){
console.log('Error in calling checkCanMakePayment: ' + err);
});
}
const canMakePaymentCache = 'canMakePaymentCache';
function checkCanMakePayment(request){
// Checks canMakePayment cache, and use the cache result if it exists.
if(sessionStorage.hasOwnProperty(canMakePaymentCache)){
return Promise.resolve(JSON.parse(sessionStorage[canMakePaymentCache]));
}
// If canMakePayment() isn't available, default to assuming that the method is supported
var canMakePaymentPromise = request.canMakePayment();
if(request.canMakePayment){
canMakePaymentPromise = request.canMakePayment();
}
return canMakePaymentPromise
.then(function(result){
sessionStorage[canMakePaymentCache] = result;
return result;
})
.catch(function (err){
alert('Error calling canMakePayment: '+ err);
console.log('Error calling canMakePayment: '+ err);
});
}
function showPaymentUI(request, canMakePayment){
if(!canMakePayment){
handleNotReadyToPay();
return;
}
// Set payment timeout.
var paymentTimeout = window.setTimeout(function(){
window.clearTimeout(paymentTimeout);
request.abort()
.then(function(){
alert('Payment timed out after 20 mins 129');
console.log('Payment timed out after 20 mins');
}).catch(function(){
alert('Unable to abort,user is in process of paying 132');
console.log('Unable to abort,user is in process of paying');
});
}, 20 * 60 * 1000);
request.show()
.then(function(paymentResponse){
window.clearTimeout(paymentTimeout);
alert("Request Success");
console.log(paymentResponse);
processResponse(paymentResponse); // Handle response from browser
})
.catch(function (err){
alert(err +'142');
console.log(err);
});
}
function handleNotReadyToPay(){
alert("Tez is not ready to handle 149");
}
function processResponse(paymentResponse){
var paymentResponseString = paymentResponseToJsonString(paymentResponse);
console.log(paymentResponseString);
/* fetch('/buy',{
method: 'POST',
headers: new Headers({'Content-Type':'application/json'}),
body:paymentResponseString
})
.then(function(buyResult){
console.log('Buy Result'+buyResult);
})
.catch(function(err){
console.log('Unable to process payment. '+err);
});*/
}
onBuyClicked();
"Payee isn't a valid merchant" error comes when you use customer VPA in place of merchant VPA in the 'pa' field.
Resolution:
Use VPA which is issued for the merchant.

Why does collection show as blank in console, but available in Meteor server?

I create a function that creates a whole bunch of tasks and it prints them all out out in the server function and displays on the server log. When I run Tasks.find().fetch() in the console, it returns a blank array. What am I doing wrong here? I have a few other objects that appear to be set up the same way and DO show in the console.
let createDummyTasks = function(){
var numberToFake=1000;
var equipment = ["Auto Folding Machine", "Binding Machine",
"Scissor Machine", "Folding Machine",
"Cutting Machine"];
for (let i = 0; i < numberToFake; i++) {
createDummyTask();
}
console.log(Tasks.find().fetch())
function createDummyTask(){
let name = faker.name.jobArea();
let status = randomStatus();
let duration = Math.floor(Math.random() * 40) + 1;
let startDate = faker.date.between('2017-09-10', '2017-09-17');
let endDate = faker.date.between('2017-09-18', '2017-09-30');
let equipment = Equipment.aggregate({$sample: {size: 1}});
// let thisEquipment = equipment[Math.floor(Math.random() * equipment.length)]
Tasks.insert({name: name,
status: status,
duration: duration,
startDate: startDate,
endDate: endDate
}, function(error){
if(error){
console.log("error");
} else {
console.log("success");
}
})
}
}
In 'collections' folder off of app root I have a task.js
Tasks = new Mongo.Collection('tasks');
Tasks.allow({
insert() {
// When we will ALLOW inserts on the client.
return false;
},
update() {
// When we will ALLOW updates on the client.
return false;
},
remove() {
// When we will ALLOW removes on the client.
return false;
}
});
Tasks.deny({
insert() {
// When we will DENY inserts on the client.
return true;
},
update() {
// When we will DENY updates on the client.
return true;
},
remove() {
// When we will DENY removes on the client.
return true;
}
});
and then I subscribe to the items on the client js
// *************************************************************
Template.schedule.onCreated( () => {
Template.instance().subscribe( 'customers' );
Template.instance().subscribe( 'jobs' );
Template.instance().subscribe( 'tasks' );
Template.instance().subscribe( 'equipment' );
});
Template.widget.onRendered(function(){
if(Meteor.isDevelopment){
Meteor.call('populateDummyInfo', (error)=>{
if(error){
console.log(error);
}
})
}
})
I didn't publish....
Meteor.publish('tasks', function () {
return Tasks.find();
});

How to stop inserting duplicate records before saving in db?

I'm trying to save students records, but it should not take duplicate records. How is it possible? In below code i have tried to do
app.post("/save",function(req,res){
var std=new student(req.body);
student.findOne({},function(err,success){
if(err)
{
console.log(err);
}
else
{
// console.log(success);
std.save(function(err,success){
if(err)
{
console.log(err);
}
else
{
console.log("inserted");
console.log(success);
}
});
}
})
});
Here is the sample code. Please note that the existence of the value in MongoDB database depends on the req.body as mentioned in the OP.
In the below code, I have only name attribute in the Student collection. So, the duplicate check is based on the name attribute only.
You may need to change the code if you would like to check for the specific attribute in the collection to determine the duplicate value.
Please note that my Student collection has only attribute in the schema as well.
var express = require('express');
var MongoClient = require('mongodb').MongoClient;
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var Student = mongoose.model('Student', { name: String });
var app = express();
var bodyParser = require('body-parser');
var app = express();
var urlencoded_body_parser = bodyParser.urlencoded({
extended: true
});
app.use(bodyParser.json());
app.use(urlencoded_body_parser);
app.post("/save", function (req, res) {
console.log(req.body);
var student = new Student(req.body);
Student.findOne(req.body, function (err, success) {
if (err) {
console.log(err);
res.send(err);
}
else {
console.log(success);
if (success == null) {
student.save(function (err, success) {
if (err) {
console.log(err);
res.send(err);
}
else {
console.log("inserted");
console.log(success);
res.send("success");
}
});
} else {
res.send("Student already present");
}
}
})
});
app.listen(3000);
Output:-
First time execution:-
Input:-
{
"name" : "john"
}
Output:-
success
Subsequent executions with the same input json:-
Output:-
Student already present

Waiting for meteor cursor in method

I have a large aggrogate query that required me to pass "allowDiskUse: true" as an option. This would not work with the aggegate as described here:
https://github.com/meteorhacks/meteor-aggregate/issues/11
My meteor method is defined here. When I call the method I need to wait for ondata to complete before anything is returned to the client, but nothing I try allows me to get that data in a safe way up to the front end.
Meteor.methods({
'getSummary': function (dept,startDate,endDate,filterType) {
f = myQuery(startdate,enddate,dayFinalGroup);
f.on("data", Meteor.bindEnvironment(function(row) {
//load an array or something here to return
}));
f.once("end", Meteor.bindEnvironment(function() {
// tidy up, in my case end the stream
}));
//here I'd return the array loaded
},
});
This is my front end.
Meteor.call(
'getSummary',0,Session.get('start_date'),Session.get('end_date'),1,
function(error, result){
if(error){
console.log(error);
} else {
Session.set('sumTotals',result);
}
}
);
Finally Got it. I utilized wrapSync
'getSummary': function (dept,startDate,endDate,filterType) {
console.log(dept);
console.log(startDate);
console.log(endDate);
console.log(filterType);
var startdate = new Date(startDate);
var enddate = new Date(endDate);
var arr = [];
f = myQuery(startdate,enddate,dayFinalGroup);
var fetchCursor = Meteor.wrapAsync(function fetchCursor (cursor, cb) {
cursor.each(function (err, doc) {
if (err) return cb(err);
if (!doc) return cb(null, { done: true }); // no more documents
arr.push(doc);
});
});
var myData = fetchCursor(f);
return arr;

nightwatch custom command callback

I'm trying to create a custom command in nightwatch that runs a query on a Postgres database and returns the result. The query runs just fine and outputs the result to the console but then the execution of the test stops. I don't understand how callbacks work. How can I fix this custom command?
exports.command = function(sql, callback) {
var self = this;
var pg = require('pg');
var conString = self.globals.testinfo.connectionString;
var db = new pg.Client(conString);
db.connect(function(err) {
if(err) {
console.error('could not connect', err);
}
else {
db.query(sql, function(err, result) {
if(err) {
console.log('error running query', err);
}
else {
console.log(result.rows.length);
db.end();
}
});
}
}),
function(result) {
if (typeof callback === 'function') {
callback.call(self, result);
}
}
return this;
};
I had to wrap the database connection in a perform command to get this working. I'm not sure if this is the best way to handle the callback, but it works. Here's the updated version of the custom command:
exports.command = function(sql,callback) {
var self = this;
var pg = require('pg');
var cs = self.globals.testinfo.connectionString;
self.perform(function(self,done) {
pg.connect(cs,function(err,db,done) {
if(err) {
return console.error(err);
}
db.query(sql, function(err,result) {
done();
if(err) {
return console.error(err);
}
console.log(result.rows.length);
callback(result.rows[0]);
});
});
pg.end();
done();
});
};
Here's how I call the custom command in the test:
browser.myCustomCommand('select * from table limit 1;', function(row) {
browser.assert.deepEqual(row.column,'some value');
});
Can you try this:
exports.command = function(sql, callback) {
var self = this;
var pg = require('pg');
var conString = self.globals.testinfo.connectionString;
var db = new pg.Client(conString);
var cb= function(result) {
if (typeof callback === 'function') {
callback.call(self, result);
}
};
db.connect(function(err) {
if(err) {
console.error('could not connect', err);
cb(false);
}
else {
db.query(sql, function(err, result) {
if(err) {
console.log('error running query', err);
cb(false);
}
else {
console.log(result.rows.length);
db.end();
cb(true);
}
});
}
}),
return this;
};
And in your test :
'test' : function(browser){
browser.yourCommandName(sql,function(result){
console.log(result); //if connect is good result would be true and false if fail to connect.
});
}
Ps: the result in callback can be as an object(contain rows or anything you want), instead of boolean only in this example.
And Nightwatch is used for end-to-end testing, it is not aimed for Database testing,i think you should find another framework to test database connection.