Merge pull request #1879 from matiaspando/iss1857
Fixing transaction history CSV download
This commit is contained in:
commit
d511103081
4 changed files with 165 additions and 83 deletions
|
|
@ -27,76 +27,20 @@ angular.module('copayApp.controllers').controller('HistoryController',
|
||||||
if (!w) return;
|
if (!w) return;
|
||||||
|
|
||||||
$scope.generating = true;
|
$scope.generating = true;
|
||||||
w.getTransactionHistory(function(err, res) {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
if (!res) return;
|
w.getTransactionHistoryCsv(function(csvContent) {
|
||||||
|
if (csvContent && csvContent !== 'ERROR') {
|
||||||
|
var filename = "copay_history.csv";
|
||||||
|
|
||||||
var unit = w.settings.unitName;
|
var encodedUri = encodeURI(csvContent);
|
||||||
var data = res.items;
|
var link = document.createElement("a");
|
||||||
var filename = "copay_history.csv";
|
link.setAttribute("href", encodedUri);
|
||||||
var csvContent = "data:text/csv;charset=utf-8,";
|
link.setAttribute("download", filename);
|
||||||
csvContent += "Date,Amount(" + unit + "),Action,AddressTo,Comment";
|
|
||||||
|
|
||||||
if (w.isShared()) {
|
link.click();
|
||||||
csvContent += ",Signers\n";
|
|
||||||
} else {
|
|
||||||
csvContent += "\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data.forEach(function(it, index) {
|
|
||||||
var dataString = formatDate(it.minedTs || it.sentTs) + ',' + it.amount + ',' + it.action + ',' + formatString(it.addressTo) + ',' + formatString(it.comment);
|
|
||||||
if (it.actionList) {
|
|
||||||
dataString += ',' + formatSigners(it.actionList);
|
|
||||||
}
|
|
||||||
csvContent += index < data.length ? dataString + "\n" : dataString;
|
|
||||||
});
|
|
||||||
|
|
||||||
var encodedUri = encodeURI(csvContent);
|
|
||||||
var link = document.createElement("a");
|
|
||||||
link.setAttribute("href", encodedUri);
|
|
||||||
link.setAttribute("download", filename);
|
|
||||||
|
|
||||||
link.click();
|
|
||||||
$scope.generating = false;
|
$scope.generating = false;
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
|
|
||||||
function formatDate(date) {
|
|
||||||
var dateObj = new Date(date);
|
|
||||||
if (!dateObj) {
|
|
||||||
log.error('Error formating a date');
|
|
||||||
return 'DateError'
|
|
||||||
}
|
|
||||||
if (!dateObj.toJSON()) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return dateObj.toJSON().substring(0, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatString(str) {
|
|
||||||
if (!str) return '';
|
|
||||||
|
|
||||||
if (str.indexOf('"') !== -1) {
|
|
||||||
//replace all
|
|
||||||
str = str.replace(new RegExp('"', 'g'), '\'');
|
|
||||||
}
|
|
||||||
|
|
||||||
//escaping commas
|
|
||||||
str = '\"' + str + '\"';
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatSigners(item) {
|
|
||||||
if (!item) return '';
|
|
||||||
var str = '';
|
|
||||||
item.forEach(function(it, index) {
|
|
||||||
str += index == 0 ? w.publicKeyRing.nicknameForCopayer(it.cId) : '|' + w.publicKeyRing.nicknameForCopayer(it.cId);
|
|
||||||
});
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1367,8 +1367,8 @@ Wallet.prototype.generateAddress = function(isChange) {
|
||||||
return addr;
|
return addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: get this out of here
|
|
||||||
* @desc get list of actions (see {@link getPendingTxProposals})
|
* @desc get list of actions (see {@link getPendingTxProposals})
|
||||||
*/
|
*/
|
||||||
Wallet.prototype._getActionList = function(txp) {
|
Wallet.prototype._getActionList = function(txp) {
|
||||||
|
|
@ -2543,6 +2543,87 @@ Wallet.prototype.isComplete = function() {
|
||||||
return this.publicKeyRing.isComplete();
|
return this.publicKeyRing.isComplete();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Return a list of transactions on CSV format
|
||||||
|
* @return {Object} the list of transactions on CSV format
|
||||||
|
*/
|
||||||
|
Wallet.prototype.getTransactionHistoryCsv = function(cb) {
|
||||||
|
var self = this;
|
||||||
|
self.getTransactionHistory(function(err, res) {
|
||||||
|
preconditions.checkState(res);
|
||||||
|
if (err) {
|
||||||
|
log.warn(err);
|
||||||
|
return cb(new Error('TXHISTORY: ' + err.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
var unit = self.settings.unitName;
|
||||||
|
var data = res.items;
|
||||||
|
|
||||||
|
var csvContent = "data:text/csv;charset=utf-8,";
|
||||||
|
csvContent += "Date,Amount(" + unit + "),Action,AddressTo,Comment";
|
||||||
|
|
||||||
|
if (self.isShared()) {
|
||||||
|
csvContent += ",Signers\n";
|
||||||
|
} else {
|
||||||
|
csvContent += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
data.forEach(function(it, index) {
|
||||||
|
if (!it) {
|
||||||
|
return cb(new Error('TXHISTORY: The item is null'));
|
||||||
|
}
|
||||||
|
var dataString = formatDate(it.minedTs || it.sentTs) + ',' + it.amount + ',' + it.action + ',' + formatString(it.addressTo) + ',' + formatString(it.comment);
|
||||||
|
if (self.isShared() && it.actionList) {
|
||||||
|
dataString += ',' + formatSigners(it.actionList);
|
||||||
|
}
|
||||||
|
csvContent += index < data.length ? dataString + "\n" : dataString;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return cb(csvContent);
|
||||||
|
|
||||||
|
function formatDate(date) {
|
||||||
|
var dateObj = new Date(date);
|
||||||
|
if (!dateObj) {
|
||||||
|
log.warn('Error formating a date');
|
||||||
|
return 'DateError'
|
||||||
|
}
|
||||||
|
if (!dateObj.toJSON()) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateObj.toJSON().substring(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatString(str) {
|
||||||
|
if (!str) return '';
|
||||||
|
|
||||||
|
if (str.indexOf('"') !== -1) {
|
||||||
|
//replace all
|
||||||
|
str = str.replace(new RegExp('"', 'g'), '\'');
|
||||||
|
}
|
||||||
|
|
||||||
|
//escaping commas
|
||||||
|
str = '\"' + str + '\"';
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSigners(item) {
|
||||||
|
if (!item) return '';
|
||||||
|
var str = '';
|
||||||
|
item.forEach(function(it, index) {
|
||||||
|
str += index == 0 ? self.publicKeyRing.nicknameForCopayer(it.cId) : '|' + self.publicKeyRing.nicknameForCopayer(it.cId);
|
||||||
|
});
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc Return a list of past transactions
|
* @desc Return a list of past transactions
|
||||||
*
|
*
|
||||||
|
|
@ -2691,7 +2772,9 @@ Wallet.prototype.getTransactionHistory = function(opts, cb) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
_.each(res.items, function(tx) {
|
_.each(res.items, function(tx) {
|
||||||
decorateTx(tx);
|
if (tx) {
|
||||||
|
decorateTx(tx);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return cb(null, paginate(res, opts.currentPage, opts.itemsPerPage));
|
return cb(null, paginate(res, opts.currentPage, opts.itemsPerPage));
|
||||||
|
|
|
||||||
|
|
@ -2391,14 +2391,6 @@ describe('Wallet model', function() {
|
||||||
items: txs,
|
items: txs,
|
||||||
totalItems: txs.length,
|
totalItems: txs.length,
|
||||||
});
|
});
|
||||||
w.getAddressesInfo = sinon.stub().returns([{
|
|
||||||
addressStr: 'addr_in_1'
|
|
||||||
}, {
|
|
||||||
addressStr: 'addr_in_2'
|
|
||||||
}, {
|
|
||||||
addressStr: 'change',
|
|
||||||
isChange: true,
|
|
||||||
}]);
|
|
||||||
|
|
||||||
w.addressBook = {
|
w.addressBook = {
|
||||||
'addr_out_1': {
|
'addr_out_1': {
|
||||||
|
|
@ -2441,14 +2433,6 @@ describe('Wallet model', function() {
|
||||||
items: txs,
|
items: txs,
|
||||||
totalItems: txs.length,
|
totalItems: txs.length,
|
||||||
});
|
});
|
||||||
w.getAddressesInfo = sinon.stub().returns([{
|
|
||||||
addressStr: 'addr_in_1'
|
|
||||||
}, {
|
|
||||||
addressStr: 'addr_in_2'
|
|
||||||
}, {
|
|
||||||
addressStr: 'change',
|
|
||||||
isChange: true,
|
|
||||||
}]);
|
|
||||||
|
|
||||||
w.txProposals.txps = [{
|
w.txProposals.txps = [{
|
||||||
sentTxid: 'id0',
|
sentTxid: 'id0',
|
||||||
|
|
@ -2472,6 +2456,7 @@ describe('Wallet model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
describe.skip('#onPayProPaymentAck', function() {
|
describe.skip('#onPayProPaymentAck', function() {
|
||||||
it('should emit', function() {
|
it('should emit', function() {
|
||||||
|
|
@ -2484,6 +2469,76 @@ describe('Wallet model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#getTransactionHistoryCsv', function() {
|
||||||
|
it('should return list of txs', function(done) {
|
||||||
|
var w = cachedCreateW2();
|
||||||
|
var txs = [{
|
||||||
|
vin: [{
|
||||||
|
addr: 'in_1',
|
||||||
|
valueSat: 1000
|
||||||
|
}],
|
||||||
|
vout: [{
|
||||||
|
scriptPubKey: {
|
||||||
|
addresses: ['out_1'],
|
||||||
|
},
|
||||||
|
value: '0.00000900',
|
||||||
|
}],
|
||||||
|
fees: 0.00000100
|
||||||
|
}, {
|
||||||
|
vin: [{
|
||||||
|
addr: 'in_2',
|
||||||
|
valueSat: 2000
|
||||||
|
}],
|
||||||
|
vout: [{
|
||||||
|
scriptPubKey: {
|
||||||
|
addresses: ['out_2'],
|
||||||
|
},
|
||||||
|
value: '0.00001900',
|
||||||
|
}],
|
||||||
|
fees: 0.00000100
|
||||||
|
}, {
|
||||||
|
vin: [{
|
||||||
|
addr: 'in_3',
|
||||||
|
valueSat: 3000
|
||||||
|
|
||||||
|
}],
|
||||||
|
vout: [{
|
||||||
|
scriptPubKey: {
|
||||||
|
addresses: ['out_3'],
|
||||||
|
},
|
||||||
|
value: '0.00002900',
|
||||||
|
|
||||||
|
}],
|
||||||
|
fees: 0.00000100
|
||||||
|
}];
|
||||||
|
|
||||||
|
w.blockchain.getTransactions = sinon.stub().yields(null, {
|
||||||
|
items: txs,
|
||||||
|
totalItems: txs.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
sinon.stub(w, 'getAddresses').returns(['in_1', 'in_2', 'in_3', 'out_1', 'out_2', 'out_3']);
|
||||||
|
var s = sinon.stub(w.publicKeyRing, 'addressIsOwn');
|
||||||
|
s.withArgs('in_1').returns(true);
|
||||||
|
s.withArgs('out_1').returns(false);
|
||||||
|
|
||||||
|
s.withArgs('in_2').returns(false);
|
||||||
|
s.withArgs('out_2').returns(true);
|
||||||
|
|
||||||
|
s.withArgs('in_3').returns(true);
|
||||||
|
s.withArgs('out_3').returns(true);
|
||||||
|
|
||||||
|
|
||||||
|
w.getTransactionHistoryCsv(function(data) {
|
||||||
|
data.should.exist;
|
||||||
|
data.should.equal('data:text/csv;charset=utf-8,Date,Amount(bits),Action,AddressTo,Comment,Signers\n,9,sent,"out_1",\n,0,moved,"out_2",\n,29,sent,"out_3",\n');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
describe.skip('#read', function() {
|
describe.skip('#read', function() {
|
||||||
var network, blockchain;
|
var network, blockchain;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@
|
||||||
<div class="large-9 columns">
|
<div class="large-9 columns">
|
||||||
<pagination page="currentPage" total-items="totalItems" items-per-page="itemsPerPage" on-select-page="selectPage(page)" max-size="10" />
|
<pagination page="currentPage" total-items="totalItems" items-per-page="itemsPerPage" on-select-page="selectPage(page)" max-size="10" />
|
||||||
</div>
|
</div>
|
||||||
<div class="large-3 columns m5t text-right size-12 show-for-large-only">
|
<div class="large-3 columns m5t text-right size-12 show-for-large-up">
|
||||||
<div ng-if="generating">
|
<div ng-if="generating">
|
||||||
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
<i class="fi-bitcoin-circle icon-rotate spinner"></i>
|
||||||
<span translate>Generating file...</span>
|
<span translate>Generating file...</span>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue