Merge branch 'dev' of http://git.verdnatura.es/salix into dev

This commit is contained in:
Carlos Jimenez 2018-10-01 09:23:56 +02:00
commit f9bb1128a2
14 changed files with 297 additions and 35 deletions

View File

@ -332,6 +332,15 @@
"params": { "params": {
"client": "$ctrl.client" "client": "$ctrl.client"
} }
},
{
"url": "/web-payment",
"state": "client.card.webPayment",
"component": "vn-client-web-payment",
"description": "Web Payment",
"menu": {
"icon": "web"
}
} }
] ]
} }

View File

@ -31,3 +31,4 @@ import './credit-insurance/insurance/create';
import './contact'; import './contact';
import './sample/index'; import './sample/index';
import './sample/create'; import './sample/create';
import './web-payment';

View File

@ -0,0 +1,56 @@
<vn-crud-model
vn-id="model"
url="/client/api/clients/getTransactions"
filter="{}"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="transactions">
</vn-crud-model>
<vn-vertical>
<vn-card pad-large>
<vn-vertical>
<vn-title>Web Payment</vn-title>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th></vn-th>
<vn-th field="id">Id</vn-th>
<vn-th field="amount" number>Amount</vn-th>
<vn-th field="created" default-order="DESC">Payed</vn-th>
<vn-th></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="transaction in transactions">
<vn-td style="width: 3em; text-align: center">
<vn-icon orange pointer
vn-tooltip="Confirm transaction"
ng-click="$ctrl.confirm(transaction)"
ng-show="::!transaction.isConfirmed"
icon="check">
</vn-icon>
</vn-td>
<vn-td>{{::transaction.id}}</vn-td>
<vn-td number>{{::transaction.amount | currency: '€ '}}</vn-td>
<vn-td>{{::transaction.created | date:'dd/MM/yyyy'}}</vn-td>
<vn-td>
<vn-icon orange
vn-tooltip="{{$ctrl.getFormattedMessage(transaction)}}"
ng-show="::(transaction.errorMessage || transaction.responseMessage)"
icon="warning">
</vn-icon>
</vn-td>
</vn-tr>
</vn-tbody>
<vn-empty-rows ng-if="model.data.length === 0" translate>
No results
</vn-empty-rows>
</vn-table>
</vn-vertical>
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</vn-card>
</vn-vertical>

View File

@ -0,0 +1,33 @@
import ngModule from '../module';
class Controller {
constructor($scope, $http, $stateParams) {
this.$scope = $scope;
this.$http = $http;
this.$stateParams = $stateParams;
}
confirm(transaction) {
const path = '/client/api/Clients/confirmTransaction';
let data = {id: transaction.id};
this.$http.post(path, data).then(res => {
this.$scope.model.refresh();
});
}
getFormattedMessage(transaction) {
const errorMessage = transaction.errorMessage ? transaction.errorMessage : '';
const separator = transaction.errorMessage && transaction.responseMessage ? '<br/>' : '';
const responseMessage = transaction.responseMessage ? transaction.responseMessage : '';
return `<strong style="font-size:13px">${errorMessage}</strong>`
+ separator
+ `<span style="font-size:13px">${responseMessage}</span>`;
}
}
Controller.$inject = ['$scope', '$http', '$stateParams'];
ngModule.component('vnClientWebPayment', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,50 @@
import './index';
import {crudModel} from '../../../helpers/crudModelHelper';
describe('Component vnClientWebPayment', () => {
let $componentController;
let $httpBackend;
let $scope;
let vnApp;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_, _vnApp_) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$scope.model = crudModel;
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
vnApp = _vnApp_;
spyOn(vnApp, 'showError');
controller = $componentController('vnClientWebPayment', {$scope: $scope});
}));
describe('confirm()', () => {
it(`should confirm a transaction`, () => {
let transaction = {id: 1};
let query = '/client/api/Clients/confirmTransaction';
controller.confirm(transaction);
$httpBackend.when('POST', query, transaction).respond('ok');
$httpBackend.expect('POST', query, transaction);
$httpBackend.flush();
});
});
describe('getFormattedMessage()', () => {
it(`should return error message and response Message`, () => {
let transaction = {
errorMessage: 'My error message',
responseMessage: 'My response message'
};
let result = controller.getFormattedMessage(transaction);
expect(result).toContain('My error message');
expect(result).toContain('My response message');
});
});
});

View File

@ -0,0 +1,4 @@
Web Payment: Pago Web
Confirmed: Confirmado
Payed: Pagado
Confirm transaction: Confirmar transacción

View File

@ -4,8 +4,7 @@
filter="{}" filter="{}"
link="{ticketFk: $ctrl.$stateParams.id}" link="{ticketFk: $ctrl.$stateParams.id}"
limit="20" limit="20"
data="sales" data="sales">
on-data-change="$ctrl.getTags()">
</vn-crud-model> </vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>

View File

@ -50,5 +50,6 @@
"A claim with that sale already exists": "Ya existe una reclamación para esta línea", "A claim with that sale already exists": "Ya existe una reclamación para esta línea",
"You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo", "You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo",
"Warehouse cannot be blank": "El almacén no puede quedar en blanco", "Warehouse cannot be blank": "El almacén no puede quedar en blanco",
"Agency cannot be blank": "La agencia no puede quedar en blanco" "Agency cannot be blank": "La agencia no puede quedar en blanco",
"You don't have enough privileges to do that": "No tienes permisos para para hacer esto"
} }

View File

@ -0,0 +1,32 @@
const UserError = require('../../../common/helpers').UserError;
module.exports = Self => {
Self.remoteMethodCtx('confirmTransaction', {
description: 'Returns last entries',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'Transaction id'
}],
returns: {
type: 'Object',
root: true
},
http: {
path: `/confirmTransaction`,
verb: 'POST'
}
});
Self.confirmTransaction = async(ctx, id) => {
let userId = ctx.req.accessToken.userId;
let isAdministrative = await Self.app.models.Account.hasRole(userId, 'administrative');
if (!isAdministrative)
throw new UserError(`You don't have enough privileges to do that`);
return await Self.rawSql('CALL hedera.tpvTransactionConfirmById(?)', [id]);
};
};

View File

@ -0,0 +1,43 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethod('getTransactions', {
description: 'Returns last entries',
accessType: 'READ',
accepts: [{
arg: 'filter',
type: 'Object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}
}],
returns: {
type: ['Object'],
root: true
},
http: {
path: `/getTransactions`,
verb: 'GET'
}
});
Self.getTransactions = async filter => {
let stmt = new ParameterizedSQL(`
SELECT
t.id,
t.clientFk,
t.created,
t.amount / 100 amount,
t.receiptFk IS NOT NULL AS isConfirmed,
tt.message responseMessage,
te.message errorMessage
FROM hedera.tpvTransaction t
JOIN hedera.tpvMerchant m ON m.id = t.merchantFk
LEFT JOIN hedera.tpvResponse tt ON tt.id = t.response
LEFT JOIN hedera.tpvError te ON te.code = errorCode`);
stmt.merge(Self.buildSuffix(filter, 't'));
return await Self.rawStmt(stmt);
};
};

View File

@ -0,0 +1,32 @@
const app = require(`${servicesDir}/client/server/server`);
describe('Client confirmTransaction', () => {
const transactionId = 2;
afterAll(async() => {
await app.models.Client.rawSql(`
CALL hedera.tpvTransactionUndo(?)`, [transactionId]);
});
it('should call confirmTransaction() method and throw error for a non administrative person', async() => {
let ctx = {req: {accessToken: {userId: 1}}};
let error;
await app.models.Client.confirmTransaction(ctx, transactionId).catch(e => {
error = e;
}).finally(() => {
expect(error.message).toEqual(`You don't have enough privileges to do that`);
});
});
it('should call confirmTransaction() method to mark transaction as confirmed', async() => {
let ctx = {req: {accessToken: {userId: 5}}};
await app.models.Client.confirmTransaction(ctx, transactionId);
let [receipt] = await app.models.Client.rawSql(
`SELECT receiptFk
FROM hedera.tpvTransaction
WHERE id = ?`, [transactionId]);
expect(receipt.receiptFk).toBeGreaterThan(0);
});
});

View File

@ -0,0 +1,10 @@
const app = require(`${servicesDir}/client/server/server`);
describe('Client getTransations', () => {
it('should call getTransations() method to receive a list of Web Payments from Bruce Wayne', async() => {
let filter = {where: {clientFk: 101}};
let result = await app.models.Client.getTransactions(filter);
expect(result[1].id).toBeTruthy();
});
});

View File

@ -19,6 +19,8 @@ module.exports = Self => {
require('../methods/client/updateFiscalData')(Self); require('../methods/client/updateFiscalData')(Self);
require('../methods/client/updateBillingData')(Self); require('../methods/client/updateBillingData')(Self);
require('../methods/client/updateBasicData')(Self); require('../methods/client/updateBasicData')(Self);
require('../methods/client/getTransactions')(Self);
require('../methods/client/confirmTransaction')(Self);
// Validations // Validations

View File

@ -1,3 +1,6 @@
const ParameterizedSQL = require('vn-loopback/node_modules/loopback-connector').ParameterizedSQL;
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('listSaleTracking', { Self.remoteMethod('listSaleTracking', {
description: 'Returns all ticket sale trackings', description: 'Returns all ticket sale trackings',
@ -20,40 +23,27 @@ module.exports = Self => {
}); });
Self.listSaleTracking = async filter => { Self.listSaleTracking = async filter => {
let where = ''; let stmt = new ParameterizedSQL(`
let limit = ''; SELECT
let order = ''; st.id,
let params; s.ticketFk,
s.quantity,
s.concept,
s.itemFk,
st.originalQuantity,
st.created,
st.workerFk,
w.firstName,
w.name,
ste.name AS state
FROM saleTracking st
JOIN sale s ON s.id = st.saleFk
JOIN worker w ON w.id = st.workerFk
JOIN state ste ON ste.id = st.stateFk`);
if (filter) { stmt.merge(Self.buildSuffix(filter));
let connector = Self.dataSource.connector;
if (filter.where) {
where = 'WHERE s.ticketFk = ?';
params = [filter.where.ticketFk];
}
limit = connector._buildLimit(null, filter.limit, filter.skip); let trackings = await Self.rawStmt(stmt);
order = connector.buildOrderBy('Item', filter.order);
}
let query = `SELECT
st.id,
s.quantity,
s.concept,
s.itemFk,
st.originalQuantity,
st.created,
st.workerFk,
w.firstName,
w.name,
ste.name AS state
FROM saleTracking st
JOIN sale s ON s.id = st.saleFK
JOIN worker w ON w.id = st.workerFk
JOIN ticketState ts ON ts.ticketFk = s.ticketFk
JOIN state ste ON ste.id = ts.stateFK ${where} ${order} ${limit}`;
let trackings = await Self.rawSql(query, params);
let salesFilter = { let salesFilter = {
include: [ include: [