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

This commit is contained in:
Gerard 2018-10-15 13:20:18 +02:00
commit a78ffbdaa5
20 changed files with 356 additions and 52 deletions

View File

@ -1,7 +1,7 @@
Claimed: Reclamados Claimed: Reclamados
Disc.: Dto. Disc.: Dto.
Attended by: Atendida por Attended by: Atendida por
Landed: Recibido Landed: F. entrega
Price: Precio Price: Precio
Claimable sales from ticket: Lineas reclamables del ticket Claimable sales from ticket: Lineas reclamables del ticket
Detail: Detalles Detail: Detalles

View File

@ -10,11 +10,12 @@ export default class Controller extends Input {
this.input.addEventListener('change', () => this.onChange()); this.input.addEventListener('change', () => this.onChange());
} }
set field(value) { set field(value) {
this._field = value;
this.input.checked = value == true; this.input.checked = value == true;
this.mdlUpdate(); this.mdlUpdate();
} }
get field() { get field() {
return this.input.checked == true; return this._field;
} }
$onInit() { $onInit() {
if (this.model) { if (this.model) {
@ -30,6 +31,7 @@ export default class Controller extends Input {
} }
} }
onChange() { onChange() {
this._field = this.input.checked == true;
this.$.$applyAsync(); this.$.$applyAsync();
} }
} }

View File

@ -15,6 +15,7 @@ export default class Tooltip extends Component {
this.$timeout = $timeout; this.$timeout = $timeout;
$element.addClass('vn-tooltip mdl-shadow--4dp'); $element.addClass('vn-tooltip mdl-shadow--4dp');
this.position = 'down'; this.position = 'down';
this.margin = 10;
} }
/** /**
@ -64,8 +65,8 @@ export default class Tooltip extends Component {
axis = 'y'; axis = 'y';
} }
let arrowSize = 10; let arrowSize = this.margin;
let tipMargin = arrowSize; let tipMargin = this.margin;
let rect = this.parent.getBoundingClientRect(); let rect = this.parent.getBoundingClientRect();
let tipRect = this.element.getBoundingClientRect(); let tipRect = this.element.getBoundingClientRect();
@ -73,8 +74,8 @@ export default class Tooltip extends Component {
let bgColor = tipComputed.getPropertyValue('background-color'); let bgColor = tipComputed.getPropertyValue('background-color');
let min = tipMargin; let min = tipMargin;
let maxTop = window.innerHeight - tipRect.height - tipMargin; let maxTop = this.window.innerHeight - tipRect.height - tipMargin;
let maxLeft = window.innerWidth - tipRect.width - tipMargin; let maxLeft = this.window.innerWidth - tipRect.width - tipMargin;
// Coordinates // Coordinates

View File

@ -7,59 +7,112 @@ describe('Component vnTooltip', () => {
let $httpBackend; let $httpBackend;
let $timeout; let $timeout;
let controller; let controller;
let $tooltip;
let $controller;
let parent;
beforeEach(() => { beforeEach(() => {
angular.mock.module('client'); angular.mock.module('client');
}); });
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_, _$timeout_) => { beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_, _$timeout_, $compile, $document) => {
$scope = $rootScope.$new(); $scope = $rootScope.$new();
$timeout = _$timeout_; $timeout = _$timeout_;
$element = angular.element(`<div>${template}</div>`); $element = angular.element(`<div>${template}</div>`);
$tooltip = $compile(`<vn-tooltip class="text">test</span></vn-tooltip>`)($rootScope);
$document.find('body').append($tooltip);
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({}); $httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
controller = _$componentController_('vnTooltip', {$element, $scope, $timeout, $transclude: null}); controller = _$componentController_('vnTooltip', {$element, $scope, $timeout, $transclude: null});
$controller = $tooltip[0].$ctrl;
parent = angular.element('<div/>')[0];
})); }));
describe('show()', () => { describe('show()', () => {
it(`should define parent property and add a class named 'show' to the element`, () => { it(`should check that tooltip is visible into the screen`, () => {
spyOn(controller, 'relocate'); $controller.show(parent);
spyOn(controller, 'cancelTimeout');
controller.show({name: 'parentElement'}); let clientRect = $controller.element.getBoundingClientRect();
let style = window.getComputedStyle($controller.element);
expect(controller.parent).toEqual({name: 'parentElement'}); expect(style.visibility).toEqual('visible');
expect($element[0].classList).toContain('show'); expect(style.display).not.toEqual('none');
expect(0).toBeLessThanOrEqual(clientRect.top);
expect(0).toBeLessThanOrEqual(clientRect.left);
expect($controller.window.innerHeight).toBeGreaterThan(clientRect.bottom);
expect($controller.window.innerWidth).toBeGreaterThan(clientRect.right);
}); });
}); });
describe('hide()', () => { describe('hide()', () => {
it(`should remove a class named 'show' from the element`, () => { it(`should check that tooltip is not visible`, () => {
spyOn(controller, 'cancelTimeout'); $controller.show(parent);
$controller.hide();
let style = window.getComputedStyle($controller.element);
controller.hide(); expect(style.display).toEqual('none');
expect($element[0].classList).not.toContain('show');
}); });
}); });
describe('cancelTimeout()', () => { describe('relocate()', () => {
it(`should call $timeout cancel method and unset relocateTimeout property`, () => { it(`should reallocate tooltip on top-left`, () => {
spyOn(controller.$timeout, 'cancel'); let parent = {getBoundingClientRect() {
controller.relocateTimeout = {name: 'MyTimeout'}; return {top: 0, left: 0, height: 10, width: 10};
}};
$controller.show(parent);
let clientRect = $controller.element.getBoundingClientRect();
controller.cancelTimeout(); expect($controller.margin).toBeLessThan(clientRect.top);
expect($controller.margin).toEqual(clientRect.left);
expect(controller.$timeout.cancel).toHaveBeenCalledWith({name: 'MyTimeout'});
expect(controller.relocateTimeout).toBeNull();
}); });
it(`should not call $timeout cancel method`, () => { it(`should reallocate tooltip on bottom-left`, () => {
spyOn(controller.$timeout, 'cancel'); let parent = {
controller.relocateTimeout = {name: 'MyTimeout'}; getBoundingClientRect() {
return {top: $controller.window.innerHeight, left: 0, height: 10, width: 10};
}};
$controller.show(parent);
let clientRect = $controller.element.getBoundingClientRect();
expect(controller.$timeout.cancel).not.toHaveBeenCalledWith(); expect($controller.window.innerHeight).toBeGreaterThan(clientRect.top);
expect(controller.relocateTimeout).toBeDefined(); expect($controller.margin).toEqual(clientRect.left);
});
it(`should reallocate tooltip on bottom-right`, () => {
let parent = {
getBoundingClientRect() {
return {top: $controller.window.innerHeight, left: $controller.window.innerWidth, height: 10, width: 10};
}};
$controller.show(parent);
let clientRect = $controller.element.getBoundingClientRect();
expect($controller.window.innerWidth).toBeGreaterThan(clientRect.left);
expect($controller.window.innerWidth - $controller.margin).toEqual(clientRect.right);
});
it(`should reallocate tooltip on top-right`, () => {
let parent = {
getBoundingClientRect() {
return {top: 0, left: $controller.window.innerWidth, height: 10, width: 10};
}};
$controller.show(parent);
let clientRect = $controller.element.getBoundingClientRect();
expect($controller.margin).toBeLessThan(clientRect.top);
expect($controller.window.innerWidth - $controller.margin).toEqual(clientRect.right);
});
it(`should reallocate tooltip on center`, () => {
let parent = {
getBoundingClientRect() {
return {top: $controller.window.innerHeight / 2, left: $controller.window.innerWidth / 2, height: 10, width: 10};
}};
$controller.show(parent);
let clientRect = $controller.element.getBoundingClientRect();
expect($controller.window.innerHeight / 2).toBeLessThan(clientRect.top);
expect($controller.window.innerWidth / 2).toBeGreaterThan(clientRect.left);
}); });
}); });
}); });

View File

@ -1,5 +1,5 @@
Since: Desde Since: Desde
Landed: Llegada Landed: F. entrega
Stems: Tallos Stems: Tallos
Quantity: Cantidad Quantity: Cantidad
Cost: Coste Cost: Coste

View File

@ -0,0 +1,25 @@
<div pad-large style="min-width: 10em">
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-autocomplete
vn-one
label="Tag"
field="filter.tagFk"
url="/api/Tags"
show-field="name"
value-field="id">
<tpl-item>{{name}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Value"
model="filter.value">
</vn-textfield>
</vn-horizontal>
<vn-horizontal margin-large-top>
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -0,0 +1,7 @@
import ngModule from '../module';
import SearchPanel from 'core/src/components/searchbar/search-panel';
ngModule.component('vnOrderCatalogSearchPanel', {
template: require('./index.html'),
controller: SearchPanel
});

View File

@ -69,7 +69,7 @@
<vn-popover <vn-popover
vn-id="popover" vn-id="popover"
on-close="$ctrl.onPopoverClose()"> on-close="$ctrl.onPopoverClose()">
<vn-order-search-panel/> <vn-order-catalog-search-panel/>
</vn-popover> </vn-popover>
</vn-one> </vn-one>
</vn-horizontal> </vn-horizontal>

View File

@ -3,6 +3,7 @@ export * from './module';
import './card'; import './card';
import './descriptor'; import './descriptor';
import './search-panel'; import './search-panel';
import './catalog-search-panel';
import './filter'; import './filter';
import './index/'; import './index/';
import './summary'; import './summary';

View File

@ -6,6 +6,16 @@
data="orders"> data="orders">
</vn-crud-model> </vn-crud-model>
<div margin-medium> <div margin-medium>
<div class="vn-list">
<vn-card pad-medium-h>
<vn-searchbar
panel="vn-order-search-panel"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)"
vn-focus>
</vn-searchbar>
</vn-card>
</div>
<vn-card margin-medium-v pad-medium> <vn-card margin-medium-v pad-medium>
<vn-table model="model"> <vn-table model="model">
<vn-thead> <vn-thead>

View File

@ -18,6 +18,24 @@ export default class Controller {
}; };
} }
exprBuilder(param, value) {
switch (param) {
case 'search':
return {id: value};
case 'from':
return {landed: {gte: value}};
case 'to':
return {landed: {lte: value}};
case 'id':
case 'clientFk':
case 'workerFk':
case 'sourceApp':
case 'agencyModeFk':
case 'isConfirmed':
return {[param]: value};
}
}
showDescriptor(event, clientFk) { showDescriptor(event, clientFk) {
this.$scope.descriptor.clientFk = clientFk; this.$scope.descriptor.clientFk = clientFk;
this.$scope.descriptor.parent = event.target; this.$scope.descriptor.parent = event.target;

View File

@ -1,25 +1,61 @@
<div pad-large style="min-width: 10em"> <div pad-large style="min-width: 30em">
<form ng-submit="$ctrl.onSearch()"> <form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-autocomplete
vn-one
label="Tag"
field="filter.tagFk"
url="/api/Tags"
show-field="name"
value-field="id">
<tpl-item>{{name}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-textfield <vn-textfield
vn-one vn-one
label="Value" label="Order id"
model="filter.value"> model="filter.id">
</vn-textfield> </vn-textfield>
<vn-textfield
vn-one
label="Client id"
model="filter.clientFk">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
label="Agency"
field="filter.agencyModeFk"
url="/agency/api/AgencyModes"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete
vn-one
label="SalesPerson"
field="filter.workerFk"
url="/claim/api/Workers"
show-field="firstName"
value-field="id">
<tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker vn-one
label="From landed"
model="filter.from">
</vn-date-picker>
<vn-date-picker vn-one
label="To landed"
model="filter.to">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Application"
field="filter.sourceApp"
url="/order/api/Orders/getSourceValues"
show-field="value"
value-field="value">
</vn-autocomplete>
<vn-check vn-one style="padding-top: 1em"
label="Order confirmed"
field="filter.isConfirmed">
</vn-check>
</vn-horizontal> </vn-horizontal>
<vn-horizontal margin-large-top> <vn-horizontal margin-large-top>
<vn-submit label="Search"></vn-submit> <vn-submit label="Search"></vn-submit>
</vn-horizontal> </vn-horizontal>
</form> </form>
</div> </div>

View File

@ -0,0 +1,9 @@
Order id: Id ticket
Client id: Id cliente
From landed: Desde f. entrega
To landed: Hasta f. entrega
To: Hasta
Agency: Agencia
Application: Aplicación
SalesPerson: Comercial
Order confirmed: Pedido confirmado

View File

@ -117,7 +117,7 @@ module.exports = Self => {
async function getTicketId(params, transaction) { async function getTicketId(params, transaction) {
const currentDate = new Date(); const currentDate = new Date();
currentDate.setHours(0, 0, 0, 0); currentDate.setHours(null, null, null);
let ticket = await Self.app.models.Ticket.findOne({ let ticket = await Self.app.models.Ticket.findOne({
where: { where: {

View File

@ -0,0 +1,50 @@
module.exports = Self => {
/**
* Returns a set of allowed values defined on table scheme
* @param {String} column - Model or Table column name
* @return {Array} - Array of set values
*/
Self.getSetValues = async function(column) {
let model = this.app.models[this.modelName].definition;
let properties = model.properties;
let columnName;
let tableName;
if (model.settings && model.settings.mysql)
tableName = model.settings.mysql.table;
if (properties[column]) {
columnName = column;
if (properties[column].mysql)
columnName = properties[column].mysql.columnName;
}
let findColumn = Object.keys(properties).find(prop => {
return properties[prop].mysql && properties[prop].mysql.columnName === column;
});
if (findColumn)
columnName = properties[findColumn].mysql.columnName;
let type = await this.rawSql(`
SELECT DISTINCT column_type FROM information_schema.columns
WHERE table_name = ?
AND column_name = ?`, [tableName, columnName]);
if (!type) return;
let setValues;
setValues = type[0].column_type;
setValues = setValues.replace(/set\((.*)\)/i, '$1');
setValues = setValues.replace(/'/g, '');
setValues = setValues.match(new RegExp(/(\w+)+/, 'ig'));
let values = [];
setValues.forEach(setValue => {
values.push({value: setValue});
});
return values;
};
};

View File

@ -1,4 +1,9 @@
module.exports = Self => { module.exports = Self => {
/**
* Catches database errors overriding
* model.create() and model.upsert() methods
* @param {Object} replaceErrFunc - Callback
*/
Self.rewriteDbError = function(replaceErrFunc) { Self.rewriteDbError = function(replaceErrFunc) {
this.once('attached', () => { this.once('attached', () => {
let realUpsert = this.upsert; let realUpsert = this.upsert;

View File

@ -0,0 +1,23 @@
const app = require('../../../../../order/server/server');
describe('Model getSetValues()', () => {
it('should extend getSetValues properties to any model passed', () => {
let exampleModel = app.models.Order;
expect(exampleModel.getSetValues).toBeDefined();
});
it('should return an array of set values from model column sourceApp', async() => {
let result = await app.models.Order.getSetValues('sourceApp');
expect(result.length).toEqual(6);
expect(result[5].value).toEqual('TABLET_VN');
});
it('should return an array of set values from table column source_app', async() => {
let result = await app.models.Order.getSetValues('source_app');
expect(result.length).toEqual(6);
expect(result[5].value).toEqual('TABLET_VN');
});
});

View File

@ -2,6 +2,8 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = function(Self) { module.exports = function(Self) {
Self.ParameterizedSQL = ParameterizedSQL;
Self.setup = function() { Self.setup = function() {
Self.super_.setup.call(this); Self.super_.setup.call(this);
/* /*
@ -173,6 +175,14 @@ module.exports = function(Self) {
}; };
}; };
/**
* Executes an SQL query
* @param {String} query - SQL query
* @param {Object} params - Query data params
* @param {Object} options - Query options (Ex: {transaction})
* @param {Object} cb - Callback
* @return {Object} Connector promise
*/
Self.rawSql = function(query, params, options = {}, cb) { Self.rawSql = function(query, params, options = {}, cb) {
var connector = this.dataSource.connector; var connector = this.dataSource.connector;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
@ -187,6 +197,12 @@ module.exports = function(Self) {
}); });
}; };
/**
* Executes an SQL query from an Stmt
* @param {ParameterizedSql} stmt - Stmt object
* @param {Object} options - Query options (Ex: {transaction})
* @return {Object} Connector promise
*/
Self.rawStmt = function(stmt, options = {}) { Self.rawStmt = function(stmt, options = {}) {
return this.rawSql(stmt.sql, stmt.params, options); return this.rawSql(stmt.sql, stmt.params, options);
}; };
@ -195,6 +211,12 @@ module.exports = function(Self) {
return this.dataSource.connector.escapeName(name); return this.dataSource.connector.escapeName(name);
}; };
/**
* Constructs SQL where clause from Loopback filter
* @param {Object} filter - filter
* @param {String} tableAlias - Query main table alias
* @return {String} Builded SQL where
*/
Self.buildWhere = function(filter, tableAlias) { Self.buildWhere = function(filter, tableAlias) {
let connector = this.dataSource.connector; let connector = this.dataSource.connector;
let wrappedConnector = Object.create(connector); let wrappedConnector = Object.create(connector);
@ -208,12 +230,22 @@ module.exports = function(Self) {
return wrappedConnector.makeWhere(this.modelName, filter.where); return wrappedConnector.makeWhere(this.modelName, filter.where);
}; };
/**
* Constructs SQL limit clause from Loopback filter
* @param {Object} filter - filter
* @return {String} Builded SQL limit
*/
Self.buildLimit = function(filter) { Self.buildLimit = function(filter) {
let sql = new ParameterizedSQL(''); let sql = new ParameterizedSQL('');
this.dataSource.connector.applyPagination(this.modelName, sql, filter); this.dataSource.connector.applyPagination(this.modelName, sql, filter);
return sql; return sql;
}; };
/**
* Constructs SQL order clause from Loopback filter
* @param {Object} filter - filter
* @return {String} Builded SQL order
*/
Self.buildOrderBy = function(filter) { Self.buildOrderBy = function(filter) {
let order = filter.order; let order = filter.order;
@ -242,6 +274,11 @@ module.exports = function(Self) {
return `ORDER BY ${clauses.join(', ')}`; return `ORDER BY ${clauses.join(', ')}`;
}; };
/**
* Constructs SQL pagination from Loopback filter
* @param {Object} filter - filter
* @return {String} Builded SQL pagination
*/
Self.buildPagination = function(filter) { Self.buildPagination = function(filter) {
return ParameterizedSQL.join([ return ParameterizedSQL.join([
this.buildOrderBy(filter), this.buildOrderBy(filter),
@ -249,6 +286,13 @@ module.exports = function(Self) {
]); ]);
}; };
/**
* Constructs SQL filter including where, order and limit
* clauses from Loopback filter
* @param {Object} filter - filter
* @param {String} tableAlias - Query main table alias
* @return {String} Builded SQL limit
*/
Self.buildSuffix = function(filter, tableAlias) { Self.buildSuffix = function(filter, tableAlias) {
return ParameterizedSQL.join([ return ParameterizedSQL.join([
this.buildWhere(filter, tableAlias), this.buildWhere(filter, tableAlias),
@ -256,9 +300,10 @@ module.exports = function(Self) {
]); ]);
}; };
Self.ParameterizedSQL = ParameterizedSQL; // Action bindings
require('../methods/vn-model/installMethod')(Self);
require('../methods/vn-model/validateBinded')(Self); require('../methods/vn-model/validateBinded')(Self);
// Handle MySql errors
require('../methods/vn-model/rewriteDbError')(Self); require('../methods/vn-model/rewriteDbError')(Self);
// Get table set of values
require('../methods/vn-model/getSetValues')(Self);
}; };

View File

@ -0,0 +1,18 @@
module.exports = Self => {
Self.remoteMethod('getSourceValues', {
description: 'Gets the sourceApp type set',
accessType: 'READ',
returns: {
type: ['String'],
root: true
},
http: {
path: `/getSourceValues`,
verb: 'GET'
}
});
Self.getSourceValues = async () => {
return Self.getSetValues('sourceApp');
};
};

View File

@ -8,4 +8,5 @@ module.exports = Self => {
require('../methods/order/catalogFilter')(Self); require('../methods/order/catalogFilter')(Self);
require('../methods/order/summary')(Self); require('../methods/order/summary')(Self);
require('../methods/order/getVAT')(Self); require('../methods/order/getVAT')(Self);
require('../methods/order/getSourceValues')(Self);
}; };