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

This commit is contained in:
gerard 2018-08-31 14:35:30 +02:00
commit f81c97dee9
89 changed files with 4008 additions and 5460 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ build
npm-debug.log npm-debug.log
docker-compose.yml docker-compose.yml
.eslintcache .eslintcache
.env.json

View File

@ -2,5 +2,6 @@
{ {
// Carácter predeterminado de final de línea. // Carácter predeterminado de final de línea.
"files.eol": "\n", "files.eol": "\n",
"vsicons.presets.angular": false "vsicons.presets.angular": false,
"eslint.autoFixOnSave": true
} }

View File

@ -1,9 +0,0 @@
import FilterList from 'core/src/lib/filter-list';
export default class FilterClientList extends FilterList {
constructor($scope, $timeout, $state) {
super($scope, $timeout, $state);
this.modelName = 'clientFk';
}
}
FilterClientList.$inject = ['$scope', '$timeout', '$state'];

View File

@ -12,13 +12,36 @@ describe('Component vnStepControl', () => {
beforeEach(angular.mock.inject((_$componentController_, _$state_) => { beforeEach(angular.mock.inject((_$componentController_, _$state_) => {
$componentController = _$componentController_; $componentController = _$componentController_;
$state = _$state_; $state = _$state_;
spyOn($state, 'go');
controller = $componentController('vnStepControl', {$state: $state}); controller = $componentController('vnStepControl', {$state: $state});
})); }));
describe('steps()', () => {
it(`should do nothing if called without args`, () => {
controller.steps = undefined;
expect(controller._steps).not.toEqual(null);
});
it(`should set _steps property in the controller and call state.go()`, () => {
controller.$state.current.name = 'test';
controller.steps = [{state: 'nothing'}, {state: 'test'}];
expect(controller._steps).toEqual([{state: 'nothing'}, {state: 'test'}]);
expect(controller.currentStepIndex).toEqual(1);
expect(controller.$state.go).toHaveBeenCalledWith('nothing');
});
});
describe('currentState()', () => { describe('currentState()', () => {
it(`should call the go method if the's no onStepChange`, () => {
controller.currentState = 'hello state!';
expect(controller.$state.go).toHaveBeenCalledWith('hello state!');
});
it(`should call the onStepChange method then return false and never call the go method`, () => { it(`should call the onStepChange method then return false and never call the go method`, () => {
controller.onStepChange = jasmine.createSpy(controller, 'onStepChange').and.returnValue(false); controller.onStepChange = jasmine.createSpy(controller, 'onStepChange').and.returnValue(false);
spyOn(controller.$state, 'go');
controller.currentState = "something"; controller.currentState = "something";
@ -28,7 +51,6 @@ describe('Component vnStepControl', () => {
it(`should call the onStepChange method then return true and finally call the go method`, () => { it(`should call the onStepChange method then return true and finally call the go method`, () => {
controller.onStepChange = jasmine.createSpy(controller, 'onStepChange').and.returnValue(true); controller.onStepChange = jasmine.createSpy(controller, 'onStepChange').and.returnValue(true);
spyOn(controller.$state, 'go');
controller.currentState = "something"; controller.currentState = "something";

View File

@ -1,45 +0,0 @@
/**
* Generic class to list models.
*/
export default class FilterList {
constructor($scope, $timeout, $state) {
this.$ = $scope;
this.$timeout = $timeout;
this.$state = $state;
this.waitingMgCrud = 0;
this.modelId = $state.params.id;
this.instances = [];
}
onOrder(field, order) {
this.filter(`${field} ${order}`);
}
filter(order) {
if (this.$.index && this.modelId && this.modelName) {
this.waitingMgCrud = 0;
this.$.index.filter = {
page: 1,
size: 10
};
this.$.index.filter[this.modelName] = this.modelId;
if (order) {
this.$.index.filter.order = order;
}
this.$.index.accept().then(res => {
this.instances = res.instances;
});
} else if (!this.modelId || !this.modelName) {
throw new Error('Error: model not found');
} else if (this.waitingMgCrud > 3) {
throw new Error('Error: Magic Crud is not loaded');
} else {
this.waitingMgCrud++;
this.$timeout(() => {
this.filter(order);
}, 250);
}
}
}

View File

@ -4,7 +4,6 @@ import './app';
import './interceptor'; import './interceptor';
import './acl-service'; import './acl-service';
import './storage-services'; import './storage-services';
import './filter-list';
import './template'; import './template';
import './spliting-register'; import './spliting-register';
import './interpolate'; import './interpolate';

View File

@ -15,8 +15,7 @@ export function factory($http, $window, $ocLazyLoad, $translatePartialLoader, $t
if (loaded[moduleName] instanceof Promise) if (loaded[moduleName] instanceof Promise)
return loaded[moduleName]; return loaded[moduleName];
if (loaded[moduleName] === false) if (loaded[moduleName] === false)
return $q.reject( return $q.reject(new Error(`Module dependency loop detected: ${moduleName}`));
new Error(`Module dependency loop detected: ${moduleName}`));
loaded[moduleName] = false; loaded[moduleName] = false;

View File

@ -9,7 +9,7 @@ describe('Service acl', () => {
$provide.value('aclConstant', {}); $provide.value('aclConstant', {});
})); }));
beforeEach(inject((_aclService_, $httpBackend) => { beforeEach(inject(_aclService_ => {
aclService = _aclService_; aclService = _aclService_;
})); }));

View File

@ -0,0 +1,48 @@
describe('factory vnModuleLoader', () => {
let vnModuleLoader;
let $scope;
beforeEach(() => {
angular.mock.module('vnCore');
});
beforeEach(angular.mock.inject((_vnModuleLoader_, $rootScope, $httpBackend) => {
vnModuleLoader = _vnModuleLoader_;
$scope = $rootScope;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
}));
describe('load()', () => {
it('should return truthy promise if the module was loaded', async() => {
vnModuleLoader._loaded.myModule = true;
let result = await vnModuleLoader.load('myModule', {myValidations: () => {}});
expect(result).toEqual(true);
});
it('should return a promise if the module was still a promise', () => {
vnModuleLoader._loaded.myModule = new Promise(() => {
return 'I promise you a module!';
});
let result = vnModuleLoader.load('myModule', {myValidations: () => {}});
expect(result).toEqual(jasmine.any(Promise));
});
it('should return an error if the module wasnt loaded', done => {
vnModuleLoader._loaded.myModule = false;
vnModuleLoader.load('myModule', {myValidations: () => {}})
.then(() => {
done.fail('this must fail');
})
.catch(error => {
expect(error.toString()).toEqual(`Error: Module dependency loop detected: myModule`);
done();
});
$scope.$apply();
});
});
});

View File

@ -1,9 +0,0 @@
import FilterList from 'core/src/lib/filter-list';
export default class FilterItemList extends FilterList {
constructor($scope, $timeout, $state) {
super($scope, $timeout, $state);
this.modelName = 'itemFk';
}
}
FilterItemList.$inject = ['$scope', '$timeout', '$state'];

View File

@ -1,7 +1,6 @@
export * from './module'; export * from './module';
import './index'; import './index';
import './filter-item-list';
import './search-panel'; import './search-panel';
import './diary'; import './diary';
import './create'; import './create';

View File

@ -1 +0,0 @@
export * from './src/locator';

View File

@ -1,14 +0,0 @@
/* {
"module": "locator",
"name": "Locator",
"icon": "add_location",
"validations" : false,
"routes": [
{
"url": "/locator",
"state": "locator",
"component": "vn-locator-index",
"acl": ["developer"]
}
]
} */

View File

@ -1,39 +0,0 @@
<vn-card margin-large>
<vn-vertical pad-medium>
<vn-horizontal vn-one margin-large-bottom class="locator-header">
<vn-title vn-one>Routes locator</vn-title>
<form vn-two vn-horizontal class="filterPanel" ng-submit="$ctrl.searchRoutes()">
<vn-textfield vn-one label="Filter" model="$ctrl.search"></vn-textfield>
<vn-icon
vn-none
margin-medium-right
pad-medium-top
icon="keyboard_arrow_down"
ng-click="$ctrl.moreFilters($event)">
</vn-icon>
<vn-button vn-none pad-small-top label="Filtrar" ng-click="$ctrl.searchRoutes()"></vn-button>
</form>
<vn-one vn-horizontal>
<vn-one></vn-one>
<vn-autocomplete vn-two
initial-value="$ctrl.filter.warehouseFk"
show-field="name"
value-field="id"
field="$ctrl.filter.warehouseFk"
url="/production/api/Warehouses"
on-change = "$ctrl.onChangeWareHouse(item)"
label="Store">
</vn-autocomplete>
<vn-icon-button vn-none pad-ten-top margin-medium-left icon="refresh" ng-click="$ctrl.refreshRoutes()"></vn-icon-button>
</vn-one>
</vn-horizontal>
<vn-horizontal vn-one margin-large-bottom>
<vn-one>
<vn-locator-actions routes="$ctrl.routes" states="$ctrl.states" hour-items="$ctrl.hourItems"></vn-locator-actions>
</vn-one>
<vn-two></vn-two>
</vn-horizontal>
<vn-locator-table check-all="$ctrl.checkAll" routes="$ctrl.routes" footer="$ctrl.footer"></vn-locator-table>
</vn-vertical>
</vn-card>

View File

@ -1,33 +0,0 @@
import ngModule from '../module';
class LocatorIndex {
constructor($state) {
this.$state = $state;
this.routes = [];
for (let i = 1; i < 100; i++) {
let route = {
id: i,
zoneFk: Math.floor(Math.random() * 6) + 1,
postalcode: 46006,
order: Math.floor(Math.random() * 3) + 1,
preparado: '25/08',
entrada: '26/08',
ticket: 1547890 + i,
routeFk: Math.floor(Math.random() * 9999) + 1000,
alias: `Flores X${Math.floor(Math.random() * 3) + 1}`,
bultos: Math.floor(Math.random() * 20) + 10,
m3: (Math.random()).toFixed(2),
error: (Math.floor(Math.random() * 3) + 1) === 1
};
route.success = (!route.error && (Math.floor(Math.random() * 3) + 1) === 1);
this.routes.push(route);
}
}
}
LocatorIndex.$inject = ['$state'];
ngModule.component('vnLocatorIndex', {
template: require('./index.html'),
controller: LocatorIndex
});

View File

@ -1,4 +0,0 @@
Routes locator: Routes locator
Filter: Filter
Store: Store
Address: Address

View File

@ -1,4 +0,0 @@
Routes locator: Localizador de rutas
Filter: Filtro
Store: Almacén
Address: Dirección

View File

@ -1,14 +0,0 @@
<vn-horizontal class="actionPanel">
<vn-none margin-medium-right>
<vn-icon-button icon="local_shipping"></vn-icon-button>
</vn-none>
<vn-none margin-medium-right>
<vn-icon-menu icon="local_shipping" selected="$ctrl.actionRoute" items="[{id:1,name:'test_01'},{id:2,name:'test_02'}]"></vn-icon-menu>
</vn-none>
<vn-none margin-medium-right>
</vn-none>
<vn-none margin-medium-right>
</vn-none>
</vn-horizontal>

View File

@ -1,13 +0,0 @@
import ngModule from '../module';
class LocatorActions {
constructor($state) {
this.$state = $state;
}
}
LocatorActions.$inject = ['$state'];
ngModule.component('vnLocatorActions', {
template: require('./locator-actions.html'),
controller: LocatorActions
});

View File

@ -1,55 +0,0 @@
<vn-vertical>
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
<vn-column-header vn-one pad-medium-h>
<vn-multi-check
check-all="$ctrl.checkAll"
models="$ctrl.routes"
options="[{id:'all',name:'Todos'},{id:'any',name:'Ninguno'}, {id:'error', name:'Con problema'}, {id:'no-error', name:'Sin problema'}]">
</vn-multi-check>
</vn-column-header>
<vn-column-header vn-two pad-medium-h field="zoneFk" text="Zona"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="postalcode" text="Postal Code"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="order" text="Order" default-order="DESC"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="preparado" text="Preparado"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="entrada" text="Entrada"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="ticket" text="Ticket"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="routeFk" text="ID de ruta"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="alias" text="Alias"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="bultos" text="Bultos"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="m3" text="m3"></vn-column-header>
</vn-grid-header>
<vn-one class="list list-content">
<vn-vertical
class="list list-element text-center"
ng-repeat="route in $ctrl.pageTable.model track by route.id"
ng-class="{warning: route.error, success: route.success}">
<vn-horizontal>
<vn-one pad-medium-h></vn-one>
<vn-two pad-medium-h>{{::route.zoneFk}}</vn-two>
<vn-two pad-medium-h>{{::route.postalcode}}</vn-two>
<vn-two pad-medium-h>{{::route.order}}</vn-two>
<vn-two pad-medium-h>{{::route.preparado}}</vn-two>
<vn-two pad-medium-h>{{::route.entrada}}</vn-two>
<vn-two pad-medium-h>{{::route.ticket}}</vn-two>
<vn-two pad-medium-h>{{::route.routeFk}}</vn-two>
<vn-two pad-medium-h>{{::route.alias}}</vn-two>
<vn-two pad-medium-h>{{::route.bultos}}</vn-two>
<vn-two pad-medium-h>{{::route.m3}}</vn-two>
</vn-horizontal>
<vn-horizontal margin-small-top>
<vn-none pad-medium-h margin--small-top>
<vn-check model="route.checked"></vn-check>
</vn-none>
<vn-none pad-medium-h></vn-none>
<vn-one text-left pad-small border-solid>
<strong translate>Address</strong>: {{::route.address}}
</vn-one>
</vn-horizontal>
</vn-vertical>
</vn-one>
<vn-horizontal vn-one class="list list-footer">
</vn-horizontal>
<vn-one>
<vn-paging page-change="$ctrl.paginate()" index="$ctrl.pageTable" total="$ctrl.totalFilter"></vn-paging>
</vn-one>
</vn-vertical>

View File

@ -1,46 +0,0 @@
import ngModule from '../module';
class LocatorTable {
constructor($filter) {
this.$filter = $filter;
this.itemsDisplayedInList = 7;
this.pageTable = {
filter: {
page: 1,
size: this.itemsDisplayedInList
},
model: []
};
this._routes = [];
}
set routes(value) {
this._routes = value;
this.totalFilter = this._routes.length;
this.pageTable.filter.page = 1;
this.paginate();
}
get routes() {
return this._routes;
}
onOrder(field, order) {
let reverse = order === 'DESC';
this.routes = this.$filter('orderBy')(this.routes, field, reverse);
this.paginate();
}
paginate() {
let init = (this.pageTable.filter.page - 1) * this.itemsDisplayedInList;
let fin = this.pageTable.filter.page * this.itemsDisplayedInList;
this.pageTable.model = this.routes.slice(init, fin);
}
}
LocatorTable.$inject = ['$filter'];
ngModule.component('vnLocatorTable', {
template: require('./locator-table.html'),
bindings: {
routes: '<'
},
controller: LocatorTable
});

View File

@ -1,5 +0,0 @@
export * from './module';
import './index/index';
import './locator-actions/locator-actions';
import './locator-table/locator-table';

View File

@ -1,5 +0,0 @@
import {ng} from 'vendor';
import 'core';
const ngModule = ng.module('locator', []);
export default ngModule;

View File

@ -1 +0,0 @@
export * from './src/production';

View File

@ -1,14 +0,0 @@
/* {
"module": "production",
"name": "Production",
"icon": "local_florist",
"validations" : false,
"routes": [
{
"url": "/production",
"state": "production",
"component": "vn-production-index",
"acl": ["developer"]
}
]
} */

View File

@ -1,39 +0,0 @@
<mg-ajax path="/production/api/States/list" options="vnIndex as states" actions="$ctrl.sharedData.states = states.model;"></mg-ajax>
<mg-ajax path="/production/api/FakeProductions/list" options="vnIndexNonAuto as index"></mg-ajax>
<vn-card margin-large>
<vn-vertical pad-medium>
<vn-horizontal vn-one margin-large-bottom class="locator-header">
<vn-title vn-one><span translate>Finder</span></vn-title>
<vn-searchbar
vn-two
index="index"
on-search="$ctrl.searchTickets(index.filter)"
advanced="true"
popover="vn-production-filter-panel"
ignore-keys = "['page', 'size', 'search', 'warehouseFk']"
data ="$ctrl.sharedData">
</vn-searchbar>
<vn-one vn-horizontal>
<vn-one></vn-one>
<vn-autocomplete
vn-two
initial-value="$ctrl.filter.warehouseFk"
show-field="name"
value-field="id"
field="$ctrl.filter.warehouseFk"
url="/production/api/Warehouses"
on-change = "$ctrl.onChangeWareHouse(item)"
label="Store">
</vn-autocomplete>
<vn-icon-button vn-none pad-ten-top margin-medium-left icon="refresh" ng-click="$ctrl.refreshTickets()"></vn-icon-button>
</vn-one>
</vn-horizontal>
<vn-horizontal vn-one margin-large-bottom>
<vn-one>
<vn-production-actions></vn-production-actions>
</vn-one>
<vn-two></vn-two>
</vn-horizontal>
<vn-production-table check-all="$ctrl.checkAll" tickets="$ctrl.tickets" footer="$ctrl.footer"></vn-production-table>
</vn-vertical>
</vn-card>

View File

@ -1,103 +0,0 @@
import ngModule from '../module';
import './style.scss';
export default class ProductionIndex {
constructor($element, $scope, $http, aclConstant) {
this.$element = $element;
this.$ = $scope;
this.$http = $http;
this.filter = {};
this.tickets = [];
this.checkAll = 0;
this.footer = {
total: null,
lines: null,
meters: null
};
this._search = null;
this.sharedData = {
states: [],
hourItems: []
};
this.child = undefined;
this.userProfile = aclConstant.userProfile;
this.filter.warehouseFk = this.userProfile.warehouseId;
}
get checkAll() {
return this._checkAll;
}
set checkAll(value) {
this._checkAll = value;
}
// Actions Callbacks
_changeState(ids, stateId, stateName, index) {
this.$http.put(`/production/api/TicketStates/${stateId}/changeState`, {tickets: ids}).then(() => {
index.forEach(val => {
this.tickets[val].state = stateName;
this.tickets[val].stateFk = stateId;
});
});
}
_sendMessage(tickets) {
this.$http.post(`/production/api/FakeProductions/messageSend`, {tickets: tickets}).then(() => {
this.vnApp.showMessage(this.$translate.instant('Success: message send!'));
});
}
_changeTime(ids, time, index) {
this.$http.put(`/production/api/Tickets/${time}/changeTime`, {tickets: ids}).then(() => {
index.forEach(val => {
this.tickets[val].hour = time;
});
});
}
searchTickets(filter) {
this.$.index.filter.filter = Object.assign({}, this.filter, filter || {});
this.checkAll = 0;
this.$.index.accept().then(json => {
this.tickets = json.tickets;
this.footer.lines = json.lines;
this.footer.meters = json.m3;
this.footer.total = json.total;
});
}
refreshTickets() {
this.filter = {};
this.filter.warehouseFk = this.$.displayValue = this.userProfile.warehouseId;
}
onChangeWareHouse(warehouse) {
if (warehouse && warehouse != this.filter.warehouseFk) {
this.filter.warehouseFk = warehouse;
this.searchTickets(this.filter);
}
}
$onInit() {
for (let i = 1; i <= 24; i++) {
let hour = [i].join('');
if (hour.length === 1) {
hour = [0, i].join('');
}
hour += ':00';
this.sharedData.hourItems.push({id: i, name: hour});
}
this.filter.warehouseFk = this.$.displayValue = this.userProfile.warehouseId;
}
}
ProductionIndex.$inject = ['$element', '$scope', '$http', 'aclConstant'];
ngModule.component('vnProductionIndex', {
template: require('./index.html'),
controller: ProductionIndex
});

View File

@ -1,39 +0,0 @@
import './index.js';
describe('Production', () => {
describe('Component vnProductionIndex', () => {
let $componentController;
let $httpBackend;
let $scope;
let controller;
let $element;
let aclConstant;
beforeEach(() => {
angular.mock.module('production');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
$element = angular.element('<div></div>');
$componentController = _$componentController_;
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
aclConstant = {userProfile: {warehouseId: 1}};
controller = $componentController('vnProductionIndex', {$scope: $scope, $element: $element, aclConstant: aclConstant});
}));
describe('_changeState()', () => {
it('should request to update the ticket state', () => {
let ids = [1, 2, 3, 4];
let stateId = 1;
let stateName = 'state';
let index = [];
controller.tickets = ['ticketVal'];
$httpBackend.whenPUT('/production/api/TicketStates/1/changeState', {tickets: ids}).respond({data: 'ticketVal'});
$httpBackend.expectPUT('/production/api/TicketStates/1/changeState', {tickets: ids});
controller._changeState(ids, stateId, stateName, index);
$httpBackend.flush();
});
});
});
});

View File

@ -1,36 +0,0 @@
vn-production-index {
flex: 1;
button {
height: 20px;
padding: 0 6px;
}
vn-searchbar {
vn-icon{
padding-top: 20px;
}
vn-icon-button{
padding-top: 10px;
}
}
[pad-ten-top] {
padding-top: 10px;
}
.icon-square{
height: 36px;
}
.locator-header{
i{
cursor: pointer;
}
.moreFilters{
position: absolute;
z-index: 99;
background-color: white;
padding: 15px;
border: 1px solid #9D9D9D;
margin-top: 60px;
width: 600px;
}
}
}

View File

@ -1 +0,0 @@
Production: Production

View File

@ -1,27 +0,0 @@
Finder: Localizador
Production: Producción
'Error: No tickets selected!': 'Error: ¡No hay tickets seleccionados!'
'Error: Action not implemented!': 'Error: ¡Acción no implementada!'
State: Estado
Alarm: Alarma
Agencies: Agencias
Agency: Agencia
Store: Almacén
Printed: Impreso
Commercial: Comercial
Hour: Hora
Lines: Líneas
Boxes: Cajas
Comment: Comentario
Message: Mensaje
Send: Enviar
Date: Fecha
Ticket ID: ID Ticket
Route ID: ID Ruta
Province: Provincia
Filter: Filtrar
Cancel: Cancelar
Worker: Trabajador
Town: Población
Client ID: ID Cliente
Ticket with incidence: Ticket con incidencia

View File

@ -1,5 +0,0 @@
import {ng} from 'vendor';
import 'core';
const ngModule = ng.module('production', []);
export default ngModule;

View File

@ -1,15 +0,0 @@
<vn-horizontal class="actionPanel">
<vn-button vn-none margin-medium-right label="Printed" ng-click="$ctrl.doAction('markPrinted')"></vn-button>
<vn-none margin-medium-right>
<vn-icon-menu icon="assignment" items="$ctrl.parent.sharedData.states" selected="$ctrl.actionState"></vn-icon-menu>
</vn-none>
<vn-none margin-medium-right>
<vn-icon-button icon="textsms" ng-click="$ctrl.doAction('addComment')"></vn-icon-button>
</vn-none>
<vn-none margin-medium-right>
<vn-icon-menu icon="person" url="/client/api/Clients/listWorkers" selected="$ctrl.actionWorker"></vn-icon-menu>
</vn-none>
<vn-none margin-medium-right>
<vn-icon-menu icon="query_builder" items="$ctrl.parent.sharedData.hourItems" selected="$ctrl.actionHours"></vn-icon-menu>
</vn-none>
</vn-horizontal>

View File

@ -1,118 +0,0 @@
import ngModule from '../module';
export default class ProductionActions {
constructor($http, $translate, vnApp) {
this.$http = $http;
this.$translate = $translate;
this.vnApp = vnApp;
this._actionState = null;
this._actionWorker = null;
this._actionHours = null;
}
set actionState(value) {
this._actionState = value;
this.doAction('changeState');
}
get actionState() {
return this._actionState;
}
set actionHours(value) {
this._actionHours = value;
this.doAction('changeTime');
}
get actionHours() {
return this._actionHours;
}
set actionWorker(value) {
this._actionWorker = value;
this.doAction('changeWorker');
}
get actionWorker() {
return this._actionWorker;
}
_changeState(ids, sateteId) {
this.$http.put(`/production/api/TicketStates/${sateteId}/changeState`, {tickets: ids})
.then(
() => {
this.parent.searchTickets();
}
);
}
_sendMessage(tickets) {
this.$http.post(`/production/api/FakeProductions/messageSend`, {tickets: tickets})
.then(
() => {
this.vnApp.showMessage(this.$translate.instant('Success: message send!'));
}
);
}
_changeTime(ids, time, index) {
this.$http.put(`/production/api/Tickets/${time}/changeTime`, {tickets: ids})
.then(
() => {
this.parent.searchTickets();
}
);
}
_changeWorker(ids, workerFk, index) {
this.$http.put(`/production/api/Tickets/${workerFk}/changeWorker`, {tickets: ids})
.then(
() => {
this.parent.searchTickets();
}
);
}
doAction(actionName) {
let ids = [];
let index = [];
let tickets = [];
this.parent.tickets.forEach(
(val, i) => {
if (val.checked) {
ids.push(val.ticketFk);
index.push(i);
tickets.push({ticketFk: val.ticketFk, salesPerson: val.salesPerson});
}
}
);
if (tickets.length) {
switch (actionName) {
case 'changeState' :
this._changeState(ids, this.actionState.id);
break;
case 'addComment':
this._sendMessage(tickets);
break;
case 'markPrinted':
this._changeState(ids, 4);
break;
case 'changeTime':
this._changeTime(ids, this.actionHours.name, index);
break;
case 'changeWorker':
this._changeWorker(ids, this.actionWorker.id, index);
break;
default:
this.vnApp.showMessage(this.$translate.instant('Error: Action not implemented!'));
}
} else {
this.vnApp.showMessage(this.$translate.instant('Error: No tickets selected!'));
}
}
}
ProductionActions.$inject = ['$http', '$translate', 'vnApp'];
ngModule.component('vnProductionActions', {
template: require('./production-actions.html'),
controller: ProductionActions,
require: {
parent: '^^vnProductionIndex'
}
});

View File

@ -1,6 +0,0 @@
<form ng-submit="$ctrl.onComment($ctrl.ids, $ctrl.comment)" pad-large>
<vn-horizontal>
<vn-textfield vn-one label="Message" model="$ctrl.comment"></vn-textfield>
<vn-submit vn-none pad-small-top label="Send"></vn-submit>
</vn-horizontal>
</form>

View File

@ -1,5 +0,0 @@
import ngModule from '../module';
ngModule.component('vnProductionComment', {
template: require('./production-comment.html')
});

View File

@ -1,32 +0,0 @@
<form ng-submit="$ctrl.onSubmit($ctrl.filter)" pad-large>
<vn-horizontal>
<vn-textfield vn-one label="Route ID" model="$ctrl.filter.routeFk"></vn-textfield>
<vn-date-picker vn-one label="Date" model="$ctrl.filter.date"></vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
field="$ctrl.filter.provinceFk"
url="/client/api/Provinces"
show-field="name"
value-field="id"
label="Province">
</vn-autocomplete>
<vn-autocomplete vn-one
data="$ctrl.data.hourItems"
label="Hour"
value-field="name"
field="$ctrl.filter.hour"
>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one margin-medium-right field="$ctrl.filter.stateFk" data="$ctrl.data.states" label="State"></vn-autocomplete>
<vn-autocomplete vn-one margin-medium-right field="$ctrl.filter.agencyFk" url="/production/api/Agencies" label="Agency"></vn-autocomplete>
</vn-horizontal>
<vn-horizontal margin-large-top>
<vn-one></vn-one>
<vn-none>
<vn-submit label="Filter"></vn-submit>
</vn-none>
</vn-horizontal>
</form>

View File

@ -1,5 +0,0 @@
import ngModule from '../module';
ngModule.component('vnProductionFilterPanel', {
template: require('./production-filters.html')
});

View File

@ -1,92 +0,0 @@
<vn-vertical>
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
<vn-none min-none></vn-none>
<vn-column-header vn-none min-none>
<vn-multi-check
check-all="$ctrl.checkAll"
models="$ctrl.tickets"
options="[{id:'all',name:'Todos'},{id:'any',name:'Ninguno'},{id:'problem',name:'Con incidencia'},{id:'no-problem',name:'Sin incidencia'}]">
</vn-multi-check>
</vn-column-header>
<vn-column-header vn-one pad-medium-h field="ticketFk" text="Ticket ID"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="agency" text="Agency"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="routeFk" text="Route ID"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="salesPerson" text="Commercial"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="worker" text="Worker"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="hour" text="Hour"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="state" text="State"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="lines" text="Lines"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="m3" text="m3"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="boxes" text="Boxes"></vn-column-header>
<vn-none min-none></vn-none>
</vn-grid-header>
<vn-one class="list list-content">
<vn-horizontal vn-one class="list list-element text-center" ng-repeat="ticket in $ctrl.pageTable.model" ng-class="{warning: ticket.problem==='RIESGO'}">
<vn-none>
<vn-icon
margin-small-left
icon="report_problem"
ng-if="ticket.problem"
vn-tooltip="{{ticket.problem}}">
</vn-icon>
</vn-none>
<vn-none>
<vn-check model="ticket.checked"></vn-check>
</vn-none>
<vn-one pad-medium-h>{{::ticket.ticketFk}}</vn-one>
<vn-two pad-medium-h>{{::ticket.agency}}</vn-two>
<vn-one pad-medium-h>{{::ticket.routeFk}}</vn-one>
<vn-two pad-medium-h>{{::ticket.salesPerson | ucwords}}</vn-two>
<vn-two pad-medium-h>{{::ticket.worker | ucwords}}</vn-two>
<vn-one pad-medium-h>{{::ticket.hour}}</vn-one>
<vn-one pad-medium-h>{{::ticket.state}}</vn-one>
<vn-one pad-medium-h>{{::ticket.lines}}</vn-one>
<vn-one pad-medium-h>{{::ticket.m3}}</vn-one>
<vn-one pad-medium-h>{{::ticket.boxes}}</vn-one>
<vn-none>
<vn-icon
icon="more"
vn-tooltip
vn-tooltip-id="more-tooltip">
</vn-icon>
<vn-tooltip position="left" id="more-tooltip">
<vn-vertical>
<vn-horizontal class="list list-header">
<vn-one margin-medium-right translate>Town</vn-one>
<vn-one margin-medium-right translate>Province</vn-one>
<vn-two margin-medium-right translate>Client ID</vn-two>
<vn-two translate>Worker</vn-two>
</vn-horizontal>
<vn-horizontal class="list list-element">
<vn-one margin-medium-right>{{::ticket.city | ucwords}}</vn-one>
<vn-one margin-medium-right>{{::ticket.province | ucwords}}</vn-one>
<vn-two margin-medium-right>{{::ticket.client | ucwords}}</vn-two>
<vn-two>{{::ticket.worker | ucwords}}</vn-two>
</vn-horizontal>
</vn-vertical>
</vn-tooltip>
</vn-none>
</vn-horizontal>
</vn-one>
<vn-horizontal vn-one class="list list-footer">
<vn-none></vn-none>
<vn-none></vn-none>
<vn-one pad-medium-h>
<span translate="Resultados"></span>:
<span>{{$ctrl.footer.total}}</span>
</vn-one>
<vn-two pad-medium-h></vn-two>
<vn-one pad-medium-h></vn-one>
<vn-two pad-medium-h></vn-two>
<vn-two pad-medium-h></vn-two>
<vn-one pad-medium-h></vn-one>
<vn-one pad-medium-h></vn-one>
<vn-one pad-medium-h text-center>{{$ctrl.footer.lines}}</vn-one>
<vn-one pad-medium-h text-center>{{$ctrl.footer.meters}}</vn-one>
<vn-one pad-medium-h></vn-one>
<vn-none></vn-none>
</vn-horizontal>
<vn-one>
<vn-paging page-change="$ctrl.pageTickets()" index="$ctrl.pageTable" total="$ctrl.totalFilter"></vn-paging>
</vn-one>
</vn-vertical>

View File

@ -1,56 +0,0 @@
import ngModule from '../module';
export class ProductionTable {
constructor($filter) {
this.$filter = $filter;
this._tickets = [];
this.itemsDisplayedInList = 14;
this._checkAll = 0;
this.pageTable = {
filter: {
page: 1,
size: this.itemsDisplayedInList
},
model: []
};
this.filteredField = null;
this.filteredReverse = null;
}
get checkAll() {
return this._checkAll;
}
set checkAll(value) {
this._checkAll = value;
}
set tickets(value) {
this._tickets = this.filteredField ? this.$filter('orderBy')(value, this.filteredField, this.filteredReverse) : value;
this.totalFilter = this._tickets.length;
this.pageTable.filter.page = 1;
this.pageTickets();
}
get tickets() {
return this._tickets;
}
onOrder(field, order) {
this.filteredField = field;
this.filteredReverse = order === 'DESC';
this.tickets = this.tickets; // call tickets setter
}
pageTickets() {
let init = (this.pageTable.filter.page - 1) * this.itemsDisplayedInList;
let fin = this.pageTable.filter.page * this.itemsDisplayedInList;
this.pageTable.model = this.tickets.slice(init, fin);
}
}
ProductionTable.$inject = ['$filter'];
ngModule.component('vnProductionTable', {
template: require('./production-table.html'),
bindings: {
tickets: '<',
footer: '<',
checkAll: '<'
},
controller: ProductionTable
});

View File

@ -1,7 +0,0 @@
export * from './module';
// import components
import './index/index';
import './production-filters/production-filters';
import './production-actions/production-actions';
import './production-table/production-table';

View File

@ -20,7 +20,6 @@ name: Nombre
phone: Teléfono phone: Teléfono
Preview: Vista previa Preview: Vista previa
Profile: Perfil Profile: Perfil
Production : Producción
Push on applications menu: Para abrir un módulo pulsa en el menú de aplicaciones Push on applications menu: Para abrir un módulo pulsa en el menú de aplicaciones
Return to module index: Volver a la página principal del módulo Return to module index: Volver a la página principal del módulo
Routes: Rutas Routes: Rutas

View File

@ -1,12 +1,8 @@
export default { export default {
client: client:
cb => require.ensure([], () => cb(require('client'))), cb => require.ensure([], () => cb(require('client'))),
production:
cb => require.ensure([], () => cb(require('production'))),
route: route:
cb => require.ensure([], () => cb(require('route'))), cb => require.ensure([], () => cb(require('route'))),
locator:
cb => require.ensure([], () => cb(require('locator'))),
item: item:
cb => require.ensure([], () => cb(require('item'))), cb => require.ensure([], () => cb(require('item'))),
ticket: ticket:

View File

@ -1,4 +1,10 @@
<mg-ajax path="/ticket/api/Sales/saleComponentFilter" options="vnIndexNonAuto" actions="$ctrl.sales = index.model.instances"></mg-ajax> <vn-crud-model
vn-id="model"
url="/ticket/api/Sales"
link="{ticketFk: $ctrl.$stateParams.id}"
filter="::$ctrl.filter"
data="components">
</vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-vertical> <vn-vertical>
@ -32,7 +38,7 @@
</td> </td>
</tr> </tr>
</tfoot> </tfoot>
<tbody ng-repeat="sale in $ctrl.sales track by sale.id"> <tbody ng-repeat="sale in components track by sale.id">
<tr> <tr>
<td rowspan="{{ <td rowspan="{{
::sale.components.length + 1 ::sale.components.length + 1
@ -61,13 +67,13 @@
first: $index == 0,last: $index == sale.components.length - 1 first: $index == 0,last: $index == sale.components.length - 1
}" number>{{::sale.quantity * component.value | currency:'€':3}}</td> }" number>{{::sale.quantity * component.value | currency:'€':3}}</td>
</tr> </tr>
<tr ng-if="index.model.count === 0" class="list list-element"> <tr ng-if="model.data.length === 0" class="list list-element">
<td colspan="7" style="text-align: center" translate>No results</td> <td colspan="7" style="text-align: center" translate>No results</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</vn-vertical> </vn-vertical>
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging> <!-- <vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging> -->
</vn-card> </vn-card>
</vn-vertical> </vn-vertical>
<vn-item-descriptor-popover vn-id="descriptor"></vn-item-descriptor-popover> <vn-item-descriptor-popover vn-id="descriptor"></vn-item-descriptor-popover>

View File

@ -1,46 +1,89 @@
import ngModule from '../module'; import ngModule from '../module';
import './style.scss'; import './style.scss';
import FilterTicketList from '../filter-ticket-list';
class Controller extends FilterTicketList { class Controller {
constructor($scope, $timeout, $stateParams) { constructor($scope, $stateParams) {
super($scope, $timeout, $stateParams); this.$stateParams = $stateParams;
this.$scope = $scope; this.$scope = $scope;
this.onOrder('itemFk', 'ASC'); this.filter = {
order: 'concept ASC',
include: [{
relation: 'item',
scope: {
include: {
relation: 'tags',
scope: {
fields: ['tagFk', 'value'],
include: {
relation: 'tag',
scope: {
fields: ['name']
}
},
limit: 6
}
},
fields: ['itemFk', 'name']
}
},
{
relation: 'components',
scope: {
fields: ['componentFk', 'value'],
include: {
relation: 'componentRate',
scope: {
fields: ['componentTypeRate', 'name'],
include: {
relation: 'componentType',
scope: {
fields: ['type']
}
}
}
}
}
}]
};
} }
total() { total() {
let sum; let sales = this.$scope.model.data;
if (this.sales) { let sum = 0;
sum = 0; if (!sales) return;
for (let sale of this.sales)
for (let sale of sales)
for (let component of sale.components) for (let component of sale.components)
sum += sale.quantity * component.value; sum += sale.quantity * component.value;
}
return sum; return sum;
} }
base() { base() {
let sum; let sales = this.$scope.model.data;
if (this.sales) { let sum = 0;
sum = 0;
for (let sale of this.sales) if (!sales) return;
for (let sale of sales)
for (let component of sale.components) for (let component of sale.components)
if (component.componentRate.name == 'valor de compra') if (component.componentRate.name == 'valor de compra')
sum += sale.quantity * component.value; sum += sale.quantity * component.value;
}
return sum; return sum;
} }
profitMargin() { profitMargin() {
let sum; let sales = this.$scope.model.data;
if (this.sales) { let sum = 0;
sum = 0;
for (let sale of this.sales) if (!sales) return;
for (let sale of sales)
for (let component of sale.components) for (let component of sale.components)
if (component.componentRate.name != 'valor de compra') if (component.componentRate.name != 'valor de compra')
sum += sale.quantity * component.value; sum += sale.quantity * component.value;
}
return sum; return sum;
} }
@ -55,7 +98,7 @@ class Controller extends FilterTicketList {
} }
} }
Controller.$inject = ['$scope', '$timeout', '$state']; Controller.$inject = ['$scope', '$stateParams'];
ngModule.component('vnTicketComponents', { ngModule.component('vnTicketComponents', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -1,25 +1,24 @@
import './index.js'; import './index.js';
import {crudModel} from '../../../helpers/crudModelHelper';
describe('ticket', () => { describe('ticket', () => {
describe('Component vnTicketComponents', () => { describe('Component vnTicketComponents', () => {
let $componentController; let $componentController;
let $state; let $state;
let $scope;
let controller; let controller;
beforeEach(() => { beforeEach(() => {
angular.mock.module('ticket'); angular.mock.module('ticket');
}); });
beforeEach(angular.mock.inject((_$componentController_, _$state_) => { beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$state_) => {
$componentController = _$componentController_; $componentController = _$componentController_;
$state = _$state_; $state = _$state_;
$state.params.id = '1'; $state.params.id = '1';
controller = $componentController('vnTicketComponents'); $scope = $rootScope.$new();
})); $scope.model = crudModel;
$scope.model.data = [{
describe('total()', () => {
it('should return the sum from all componenets in each sale', () => {
controller.sales = [{
components: [ components: [
{componentRate: {name: 'valor de compra'}, value: 5}, {componentRate: {name: 'valor de compra'}, value: 5},
{componentRate: {name: 'reparto'}, value: 5}, {componentRate: {name: 'reparto'}, value: 5},
@ -34,8 +33,12 @@ describe('ticket', () => {
{componentRate: {name: 'recobro'}, value: 1} {componentRate: {name: 'recobro'}, value: 1}
], ],
quantity: 5 quantity: 5
} }];
]; controller = $componentController('vnTicketComponents', {$scope});
}));
describe('total()', () => {
it('should return the sum from all componenets in each sale', () => {
let result = controller.total(); let result = controller.total();
expect(result).toEqual(30); expect(result).toEqual(30);

View File

@ -1,9 +0,0 @@
import FilterList from 'core/src/lib/filter-list';
export default class FilterTicketList extends FilterList {
constructor($scope, $timeout, $state) {
super($scope, $timeout, $state);
this.modelName = 'ticketFk';
}
}
FilterTicketList.$inject = ['$scope', '$timeout', '$state'];

View File

@ -21,7 +21,7 @@
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th></vn-th> <vn-th></vn-th>
<vn-th field="ticketFk" number>Id</vn-th> <vn-th field="id" number>Id</vn-th>
<vn-th field="salesPersonFk">Salesperson</vn-th> <vn-th field="salesPersonFk">Salesperson</vn-th>
<vn-th field="shipped">Date</vn-th> <vn-th field="shipped">Date</vn-th>
<vn-th>Hour</vn-th> <vn-th>Hour</vn-th>
@ -39,7 +39,7 @@
<vn-tbody> <vn-tbody>
<vn-tr ng-repeat="ticket in tickets" <vn-tr ng-repeat="ticket in tickets"
class="{{::$ctrl.compareDate(ticket.shipped)}} clickable" class="{{::$ctrl.compareDate(ticket.shipped)}} clickable"
ui-sref="ticket.card.summary({id: {{::ticket.ticketFk}}})"> ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
<vn-td> <vn-td>
<vn-icon ng-show="ticket.problem" class="bright" <vn-icon ng-show="ticket.problem" class="bright"
vn-tooltip="{{ticket.problem}}" vn-tooltip="{{ticket.problem}}"

View File

@ -2,57 +2,11 @@ import ngModule from '../module';
export default class Controller { export default class Controller {
constructor($scope) { constructor($scope) {
this.$ = $scope; this.$scope = $scope;
this.ticketSelected = null; this.ticketSelected = null;
this.filter = { this.filter = {
include: [ order: 'shipped DESC'
{
relation: 'address',
scope: {
fields: ['provinceFk'],
include: {
relation: 'province',
scope: {
fields: ['name']
}
}
}
}, {
relation: 'warehouse',
scope: {
fields: ['name']
}
}, {
relation: 'agencyMode',
scope: {
fields: ['name']
}
}, {
relation: 'tracking',
scope: {
fields: ['stateFk'],
include: {
relation: 'state',
scope: {
fields: ['name']
}
}
}
}, {
relation: 'client',
scope: {
fields: ['salesPersonFk'],
include: {
relation: 'salesPerson',
scope: {
fields: ['name']
}
}
}
}
],
order: 'shipped DESC, ticketFk'
}; };
} }
@ -61,13 +15,13 @@ export default class Controller {
case 'search': case 'search':
return /^\d+$/.test(value) return /^\d+$/.test(value)
? {id: value} ? {id: value}
: {nickname: {regexp: value}}; : {nickname: {like: value}};
case 'from': case 'from':
return {shipped: {gte: value}}; return {shipped: {gte: value}};
case 'to': case 'to':
return {shipped: {lte: value}}; return {shipped: {lte: value}};
case 'nickname': case 'nickname':
return {[param]: {regexp: value}}; return {[param]: {like: value}};
case 'id': case 'id':
case 'clientFk': case 'clientFk':
case 'agencyModeFk': case 'agencyModeFk':
@ -92,21 +46,21 @@ export default class Controller {
} }
showDescriptor(event, clientFk) { showDescriptor(event, clientFk) {
this.$.descriptor.clientFk = clientFk; this.$scope.descriptor.clientFk = clientFk;
this.$.descriptor.parent = event.target; this.$scope.descriptor.parent = event.target;
this.$.descriptor.show(); this.$scope.descriptor.show();
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
} }
onDescriptorLoad() { onDescriptorLoad() {
this.$.popover.relocate(); this.$scope.popover.relocate();
} }
preview(event, ticket) { preview(event, ticket) {
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
this.$.dialogSummaryTicket.show(); this.$scope.dialogSummaryTicket.show();
this.ticketSelected = ticket; this.ticketSelected = ticket;
} }
} }

View File

@ -28,7 +28,7 @@ describe('ticket', () => {
let value = 'Bruce'; let value = 'Bruce';
let result = controller.exprBuilder(param, value); let result = controller.exprBuilder(param, value);
expect(result).toEqual({nickname: {regexp: 'Bruce'}}); expect(result).toEqual({nickname: {like: 'Bruce'}});
}); });
it('should return a formated object with the date in case of from', () => { it('should return a formated object with the date in case of from', () => {
@ -52,7 +52,7 @@ describe('ticket', () => {
let value = 'Bruce'; let value = 'Bruce';
let result = controller.exprBuilder(param, value); let result = controller.exprBuilder(param, value);
expect(result).toEqual({nickname: {regexp: 'Bruce'}}); expect(result).toEqual({nickname: {like: 'Bruce'}});
}); });
it('should return a formated object with the warehouseFk in case of warehouseFk', () => { it('should return a formated object with the warehouseFk in case of warehouseFk', () => {
@ -84,14 +84,14 @@ describe('ticket', () => {
it('should call preventDefault and stopImmediatePropagation from event and show', () => { it('should call preventDefault and stopImmediatePropagation from event and show', () => {
let event = jasmine.createSpyObj('event', ['preventDefault', 'stopImmediatePropagation']); let event = jasmine.createSpyObj('event', ['preventDefault', 'stopImmediatePropagation']);
controller.$ = {dialogSummaryTicket: {show: () => {}}}; controller.$scope = {dialogSummaryTicket: {show: () => {}}};
spyOn(controller.$.dialogSummaryTicket, 'show'); spyOn(controller.$scope.dialogSummaryTicket, 'show');
let ticket = {}; let ticket = {};
controller.preview(event, ticket); controller.preview(event, ticket);
expect(event.preventDefault).toHaveBeenCalledWith(); expect(event.preventDefault).toHaveBeenCalledWith();
expect(event.stopImmediatePropagation).toHaveBeenCalledWith(); expect(event.stopImmediatePropagation).toHaveBeenCalledWith();
expect(controller.$.dialogSummaryTicket.show).toHaveBeenCalledWith(); expect(controller.$scope.dialogSummaryTicket.show).toHaveBeenCalledWith();
}); });
}); });
}); });

View File

@ -2,7 +2,8 @@
import Nightmare from 'nightmare'; import Nightmare from 'nightmare';
export default function createNightmare(width = 1280, height = 720) { export default function createNightmare(width = 1280, height = 720) {
const nightmare = new Nightmare({show: true, typeInterval: 10, x: 0, y: 0}).viewport(width, height); const nightmare = new Nightmare({show: process.env.E2E_SHOW, typeInterval: 10, x: 0, y: 0})
.viewport(width, height);
nightmare.on('page', (type, message, error) => { nightmare.on('page', (type, message, error) => {
fail(error); fail(error);

View File

@ -195,27 +195,22 @@ export default {
tagsButton: `vn-menu-item a[ui-sref="item.card.tags"]`, tagsButton: `vn-menu-item a[ui-sref="item.card.tags"]`,
firstRemoveTagButton: `vn-item-tags vn-horizontal:nth-child(2) vn-icon-button[icon="remove_circle_outline"]`, firstRemoveTagButton: `vn-item-tags vn-horizontal:nth-child(2) vn-icon-button[icon="remove_circle_outline"]`,
firstTagSelect: `vn-item-tags vn-horizontal:nth-child(2) > vn-autocomplete[field="itemTag.tagFk"] input`, firstTagSelect: `vn-item-tags vn-horizontal:nth-child(2) > vn-autocomplete[field="itemTag.tagFk"] input`,
firstTagDisabled: `vn-item-tags vn-horizontal:nth-child(2) > vn-autocomplete > div > div > input`,
firstTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(2) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`, firstTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(2) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`,
firstValueInput: `vn-item-tags vn-horizontal:nth-child(2) > vn-textfield[label="Value"] input`, firstValueInput: `vn-item-tags vn-horizontal:nth-child(2) > vn-textfield[label="Value"] input`,
firstRelevancyInput: `vn-horizontal:nth-child(2) > vn-textfield[label="Relevancy"] input`, firstRelevancyInput: `vn-horizontal:nth-child(2) > vn-textfield[label="Relevancy"] input`,
secondTagSelect: `vn-item-tags vn-horizontal:nth-child(3) > vn-autocomplete[field="itemTag.tagFk"] input`, secondTagSelect: `vn-item-tags vn-horizontal:nth-child(3) > vn-autocomplete[field="itemTag.tagFk"] input`,
secondTagDisabled: `vn-item-tags vn-horizontal:nth-child(3) > vn-autocomplete > div > div > input`,
secondTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(3) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`, secondTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(3) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`,
secondValueInput: `vn-item-tags vn-horizontal:nth-child(3) > vn-textfield[label="Value"] input`, secondValueInput: `vn-item-tags vn-horizontal:nth-child(3) > vn-textfield[label="Value"] input`,
secondRelevancyInput: `vn-horizontal:nth-child(3) > vn-textfield[label="Relevancy"] input`, secondRelevancyInput: `vn-horizontal:nth-child(3) > vn-textfield[label="Relevancy"] input`,
thirdTagSelect: `vn-item-tags vn-horizontal:nth-child(4) > vn-autocomplete[field="itemTag.tagFk"] input`, thirdTagSelect: `vn-item-tags vn-horizontal:nth-child(4) > vn-autocomplete[field="itemTag.tagFk"] input`,
thirdTagDisabled: `vn-item-tags vn-horizontal:nth-child(4) > vn-autocomplete > div > div > input`,
thirdTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(4) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`, thirdTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(4) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`,
thirdValueInput: `vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Value"] input`, thirdValueInput: `vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Value"] input`,
thirdRelevancyInput: `vn-horizontal:nth-child(4) > vn-textfield[label="Relevancy"] input`, thirdRelevancyInput: `vn-horizontal:nth-child(4) > vn-textfield[label="Relevancy"] input`,
fourthTagSelect: `vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete[field="itemTag.tagFk"] input`, fourthTagSelect: `vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete[field="itemTag.tagFk"] input`,
fourthTagDisabled: `vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete > div > div > input`,
fourthTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`, fourthTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`,
fourthValueInput: `vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Value"] input`, fourthValueInput: `vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Value"] input`,
fourthRelevancyInput: `vn-horizontal:nth-child(5) > vn-textfield[label="Relevancy"] input`, fourthRelevancyInput: `vn-horizontal:nth-child(5) > vn-textfield[label="Relevancy"] input`,
fifthTagSelect: `vn-item-tags vn-horizontal:nth-child(6) > vn-autocomplete[field="itemTag.tagFk"] input`, fifthTagSelect: `vn-item-tags vn-horizontal:nth-child(6) > vn-autocomplete[field="itemTag.tagFk"] input`,
fifthTagDisabled: `vn-item-tags vn-horizontal:nth-child(6) > vn-autocomplete > div > div > input`,
fifthValueInput: `vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Value"] input`, fifthValueInput: `vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Value"] input`,
fifthRelevancyInput: `vn-horizontal:nth-child(6) > vn-textfield[label="Relevancy"] input`, fifthRelevancyInput: `vn-horizontal:nth-child(6) > vn-textfield[label="Relevancy"] input`,
seventhTagSelectOptionFive: `vn-item-tags vn-horizontal:nth-child(8) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(5)`, seventhTagSelectOptionFive: `vn-item-tags vn-horizontal:nth-child(8) > vn-autocomplete[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(5)`,
@ -285,9 +280,8 @@ export default {
notesButton: `vn-menu-item a[ui-sref="ticket.card.observation"]`, notesButton: `vn-menu-item a[ui-sref="ticket.card.observation"]`,
firstNoteRemoveButton: `vn-icon[icon="remove_circle_outline"]`, firstNoteRemoveButton: `vn-icon[icon="remove_circle_outline"]`,
addNoteButton: `vn-icon[icon="add_circle"]`, addNoteButton: `vn-icon[icon="add_circle"]`,
firstNoteSelect: `vn-autocomplete[field="ticketObservation.observationTypeFk"] input`, firstNoteSelect: `vn-autocomplete[field="observation.observationTypeFk"] input`,
firstNoteSelectSecondOption: `vn-autocomplete[field="ticketObservation.observationTypeFk"] vn-drop-down ul > li:nth-child(2)`, firstNoteSelectSecondOption: `vn-autocomplete[field="observation.observationTypeFk"] vn-drop-down ul > li:nth-child(2)`,
firstNoteDisabled: `vn-textfield[label="Observation type"] input`,
firstDescriptionInput: `vn-textfield[label="Description"] input`, firstDescriptionInput: `vn-textfield[label="Description"] input`,
submitNotesButton: `${components.vnSubmit}` submitNotesButton: `${components.vnSubmit}`
}, },

View File

@ -50,8 +50,10 @@ describe('Client', () => {
return nightmare return nightmare
.waitToClick(selectors.clientPayMethod.payMethodInput) .waitToClick(selectors.clientPayMethod.payMethodInput)
.waitToClick(selectors.clientPayMethod.payMethodIBANOption) .waitToClick(selectors.clientPayMethod.payMethodIBANOption)
.waitForTextInInput(selectors.clientPayMethod.payMethodInput, 'PayMethod with IBAN')
.clearInput(selectors.clientPayMethod.dueDayInput) .clearInput(selectors.clientPayMethod.dueDayInput)
.type(selectors.clientPayMethod.dueDayInput, '60') .type(selectors.clientPayMethod.dueDayInput, '60')
.waitForTextInInput(selectors.clientPayMethod.dueDayInput, '60')
.waitToClick(selectors.clientPayMethod.receivedCoreLCRCheckbox) .waitToClick(selectors.clientPayMethod.receivedCoreLCRCheckbox)
.waitToClick(selectors.clientPayMethod.receivedCoreVNLCheckbox) .waitToClick(selectors.clientPayMethod.receivedCoreVNLCheckbox)
.waitToClick(selectors.clientPayMethod.receivedB2BVNLCheckbox) .waitToClick(selectors.clientPayMethod.receivedB2BVNLCheckbox)
@ -66,6 +68,7 @@ describe('Client', () => {
return nightmare return nightmare
.clearInput(selectors.clientPayMethod.IBANInput) .clearInput(selectors.clientPayMethod.IBANInput)
.type(selectors.clientPayMethod.IBANInput, 'ES91 2100 0418 4502 0005 1332') .type(selectors.clientPayMethod.IBANInput, 'ES91 2100 0418 4502 0005 1332')
.waitForTextInInput(selectors.clientPayMethod.IBANInput, 'ES91 2100 0418 4502 0005 1332')
.waitToClick(selectors.clientPayMethod.saveButton) .waitToClick(selectors.clientPayMethod.saveButton)
.waitForSnackbar() .waitForSnackbar()
.then(result => { .then(result => {

View File

@ -63,7 +63,7 @@ describe('Client', () => {
.waitToClick(selectors.clientBasicData.basicDataButton) .waitToClick(selectors.clientBasicData.basicDataButton)
.wait(selectors.clientBasicData.nameInput) .wait(selectors.clientBasicData.nameInput)
.waitToClick(selectors.clientWebAccess.webAccessButton) .waitToClick(selectors.clientWebAccess.webAccessButton)
.wait(selectors.clientWebAccess.enableWebAccessCheckbox) // ahora lo dejamos mejor .wait(selectors.clientWebAccess.enableWebAccessCheckbox)
.evaluate(selector => { .evaluate(selector => {
return document.querySelector(selector).checked; return document.querySelector(selector).checked;
}, selectors.clientWebAccess.enableWebAccessCheckbox) }, selectors.clientWebAccess.enableWebAccessCheckbox)

View File

@ -64,8 +64,8 @@ describe('Ticket', () => {
.click(selectors.ticketPackages.packagesButton) .click(selectors.ticketPackages.packagesButton)
.wait(selectors.ticketPackages.firstPackageSelect) .wait(selectors.ticketPackages.firstPackageSelect)
.click(selectors.ticketNotes.notesButton) .click(selectors.ticketNotes.notesButton)
.waitForTextInInput(selectors.ticketNotes.firstNoteDisabled, 'observation two') .waitForTextInInput(selectors.ticketNotes.firstNoteSelect, 'observation two')
.getInputValue(selectors.ticketNotes.firstNoteDisabled) .getInputValue(selectors.ticketNotes.firstNoteSelect)
.then(result => { .then(result => {
expect(result).toEqual('observation two'); expect(result).toEqual('observation two');
return nightmare return nightmare

View File

@ -71,6 +71,9 @@ gulp.task('services-only', async () => {
* Runs the e2e tests, restoring the fixtures first. * Runs the e2e tests, restoring the fixtures first.
*/ */
gulp.task('e2e', ['docker'], async () => { gulp.task('e2e', ['docker'], async () => {
if (argv.show || argv.s)
process.env.E2E_SHOW = true;
await runSequenceP('e2e-only'); await runSequenceP('e2e-only');
}); });

7322
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +0,0 @@
module.exports = Self => {
Self.installMethod('filter', filterParams);
function filterParams(params) {
return {
where: {
clientFk: params.clientFk
},
skip: (params.page - 1) * params.size,
limit: params.size,
order: params.order || 'created DESC',
include: {
relation: "worker",
scope: {
fields: ["id", "firstName", "name"]
}
}
};
}
};

View File

@ -98,7 +98,7 @@ BEGIN
-- El disponible es menor que 0 -- El disponible es menor que 0
INSERT INTO tmp.ticketProblems(ticketFk, problem) INSERT INTO tmp.ticketProblems(ticketFk, problem)
SELECT tt.ticketFk, i.name SELECT tt.ticketFk, i.name
FROM tmp.ticketGetProblems tt FROM tmp.ticketListFiltered tt
JOIN vn.ticket t ON t.id = tt.ticketFk JOIN vn.ticket t ON t.id = tt.ticketFk
LEFT JOIN vn.sale s ON s.ticketFk = t.id LEFT JOIN vn.sale s ON s.ticketFk = t.id
JOIN vn.item i ON i.id = s.itemFk JOIN vn.item i ON i.id = s.itemFk
@ -122,7 +122,7 @@ BEGIN
-- Amarillo: El disponible es mayor que cero y la cantidad supera el visible, estando aun sin preparar -- Amarillo: El disponible es mayor que cero y la cantidad supera el visible, estando aun sin preparar
INSERT INTO tmp.ticketProblems(ticketFk, problem) INSERT INTO tmp.ticketProblems(ticketFk, problem)
SELECT tt.ticketFk, CONCAT('Delay', i.name) SELECT tt.ticketFk, CONCAT('Delay', i.name)
FROM tmp.ticketGetProblems tt FROM tmp.ticketListFiltered tt
JOIN vn.ticket t ON t.id = tt.ticketFk JOIN vn.ticket t ON t.id = tt.ticketFk
LEFT JOIN vn.sale s ON s.ticketFk = t.id LEFT JOIN vn.sale s ON s.ticketFk = t.id
JOIN vn.item i ON i.id = s.itemFk JOIN vn.item i ON i.id = s.itemFk
@ -148,3 +148,4 @@ BEGIN
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -0,0 +1,115 @@
USE `vn`;
DROP procedure IF EXISTS `itemDiary`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `itemDiary`(IN vItemId INT, IN vWarehouse INT)
BEGIN
DECLARE vDateInventory DATETIME;
DECLARE vCurdate DATE DEFAULT CURDATE();
DECLARE vDayEnd DATETIME DEFAULT util.dayEnd(vCurdate);
-- traduccion: date, alertLevel, origin, reference, name, In, Out, Balance
SELECT Fechainventario INTO vDateInventory FROM vn2008.tblContadores;
SET @a = 0;
SELECT sql_no_cache DATE(date) AS date,
alertLevel,
stateName,
origin,
reference,
name,
`in`,
`out`,
@a := @a + IFNULL(`in`,0) - IFNULL(`out`,0) as balance,
isPicked,
isTicket
FROM
( SELECT tr.landed as date,
b.quantity as `in`,
NULL as `out`,
IF(tr.isReceived != FALSE,3, IF(tr.isDelivered,1,0)) as alertLevel,
st.name AS stateName,
s.name as name,
e.ref as reference,
e.id as origin,
TRUE isPicked,
FALSE AS isTicket
FROM vn.buy b
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.travel tr ON tr.id = e.travelFk
JOIN vn.supplier s ON s.id = e.supplierFk
JOIN vn.alertLevel al ON al.alertLevel =
CASE
WHEN tr.isReceived != FALSE THEN 3
WHEN tr.isDelivered THEN 1
ELSE 0
END
JOIN vn.state st ON st.code = al.code
WHERE tr.landed >= vDateInventory
AND vWarehouse = tr.warehouseInFk
AND b.itemFk = vItemId
AND e.isInventory = 0
UNION ALL
SELECT tr.shipped as date,
NULL as `in`,
b.quantity as `out`,
IF(tr.isReceived != FALSE,3, IF(tr.isDelivered,1,0)) as alertLevel,
st.name AS stateName,
s.name as name,
e.ref as reference,
e.id as origin,
TRUE isPicked,
FALSE AS isTicket
FROM vn.buy b
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.travel tr ON tr.id = e.travelFk
JOIN vn.warehouse w ON w.id = tr.warehouseOutFk
JOIN vn.supplier s ON s.id = e.supplierFk
JOIN vn.alertLevel al ON al.alertLevel =
CASE
WHEN tr.isReceived != FALSE THEN 3
WHEN tr.isDelivered THEN 1
ELSE 0
END
JOIN vn.state st ON st.code = al.code
WHERE tr.shipped >= vDateInventory
AND vWarehouse =tr.warehouseOutFk
AND s.id <> 4
AND b.itemFk = vItemId
AND e.isInventory = 0
AND w.isFeedStock = 0
UNION ALL
SELECT t.shipped as date,
NULL as `in`,
s.quantity as `out`,
al.alertLevel as alertLevel,
st.name AS stateName,
t.nickname as name,
t.refFk as reference,
t.id as origin,
TRUE as isPicked, -- stk.id as isPicked
TRUE as isTicket
FROM vn.sale s
JOIN vn.ticket t ON t.id = s.ticketFk
LEFT JOIN vn.ticketState ts ON ts.ticket = t.id
JOIN vn.client c ON c.id = t.clientFk
JOIN vn.alertLevel al ON al.alertLevel =
CASE
WHEN t.shipped < vCurdate THEN 3
WHEN t.shipped > vDayEnd THEN 0
ELSE IFNULL(ts.alertLevel, 0)
END
JOIN vn.state st ON st.code = al.code
-- LEFT JOIN vn.saleTracking stk ON stk.saleFk = s.id AND stk.stateFk = 14
WHERE t.shipped >= vDateInventory
AND s.itemFk = vItemId
AND vWarehouse =t.warehouseFk
) AS itemDiary
ORDER BY date, alertLevel DESC, isPicked DESC, `in` DESC, `out` DESC;
END$$
DELIMITER ;

View File

@ -47,7 +47,7 @@ INSERT INTO `vn`.`warehouse`(`id`, `name`, `isComparative`, `isInventory`, `hasA
(2, 'Warehouse Two', 0, 1, 1, 1), (2, 'Warehouse Two', 0, 1, 1, 1),
(3, 'Warehouse Three', 1, 1, 1, 1), (3, 'Warehouse Three', 1, 1, 1, 1),
(4, 'Warehouse Four', 1, 1, 1, 1), (4, 'Warehouse Four', 1, 1, 1, 1),
(5, 'Warehouse Five', 1, 1, 1, 0); (5, 'Warehouse Five', 1, 0, 1, 0);
INSERT INTO `vn`.`warehouseAlias`(`id`, `name`) INSERT INTO `vn`.`warehouseAlias`(`id`, `name`)
VALUES VALUES

View File

@ -1,3 +0,0 @@
module.exports = Self => {
Self.installCrudModel('crudItemBarcodes');
};

View File

@ -1,72 +0,0 @@
// const crudItemBarcodes = require('../crudItemBarcodes');
// const catchErrors = require('../../../../../../services/utils/jasmineHelpers').catchErrors;
// let mysql = require('mysql2');
// describe('Item crudItemBarcodes()', () => {
// let connection;
// beforeAll(() => {
// connection = mysql.createConnection({
// multipleStatements: true,
// host: 'localhost',
// user: 'root',
// password: '',
// database: 'salix'
// });
// });
// it('should call the destroyAll methodif there are ids in delete Array', done => {
// let self = jasmine.createSpyObj('self', ['remoteMethod', 'crudItemBarcodes', 'destroyAll', 'create', 'upsert']);
// crudItemBarcodes(self);
// self.crudItemBarcodes({
// delete: [1],
// create: [],
// update: []
// }).then(result => {
// expect(self.destroyAll).toHaveBeenCalledWith({id: {inq: [1]}});
// done();
// })
// .catch(catchErrors(done));
// });
// it('should call the create method if there are ids in create Array', done => {
// let self = jasmine.createSpyObj('self', ['remoteMethod', 'crudItemBarcodes', 'destroyAll', 'create', 'upsert']);
// crudItemBarcodes(self);
// self.crudItemBarcodes({
// delete: [],
// create: [1],
// update: []
// }).then(result => {
// expect(self.create).toHaveBeenCalledWith([1]);
// done();
// })
// .catch(catchErrors(done));
// });
// it('should call the upsert method as many times as ids in update Array', done => {
// let self = jasmine.createSpyObj('self', ['remoteMethod', 'crudItemBarcodes', 'destroyAll', 'create', 'upsert']);
// crudItemBarcodes(self);
// self.crudItemBarcodes({
// delete: [],
// create: [],
// update: [1, 2]
// }).then(result => {
// expect(self.upsert).toHaveBeenCalledWith(1);
// expect(self.upsert).toHaveBeenCalledWith(2);
// expect(self.upsert.calls.count()).toEqual(2);
// done();
// })
// .catch(catchErrors(done));
// });
// it('should return an error when attempting to save a duplicated barcode', done => {
// let callback = (err, res) => {
// expect(err.toString()).toBe("Error: Duplicate entry '4' for key 'PRIMARY'");
// done();
// };
// connection.query('INSERT INTO `vn`.`itemBarcode` VALUES (4, 2 ,4 );', callback);
// });
// });

View File

@ -1,3 +0,0 @@
module.exports = Self => {
Self.installCrudModel('crudItemNiches');
};

View File

@ -1,51 +0,0 @@
// const crudItemNiches = require('../crudItemNiches');
// const catchErrors = require('../../../../../../services/utils/jasmineHelpers').catchErrors;
// describe('Item crudItemNiches()', () => {
// it('should call the destroyAll method if there are ids in delete Array', done => {
// let self = jasmine.createSpyObj('self', ['remoteMethod', 'crudItemNiches', 'destroyAll', 'create', 'upsert']);
// crudItemNiches(self);
// self.crudItemNiches({
// delete: [1],
// create: [],
// update: []
// }).then(result => {
// expect(self.destroyAll).toHaveBeenCalledWith({id: {inq: [1]}});
// done();
// })
// .catch(catchErrors(done));
// });
// it('should call the create method if there are ids in create Array', done => {
// let self = jasmine.createSpyObj('self', ['remoteMethod', 'crudItemNiches', 'destroyAll', 'create', 'upsert']);
// crudItemNiches(self);
// self.crudItemNiches({
// delete: [],
// create: [1],
// update: []
// }).then(result => {
// expect(self.create).toHaveBeenCalledWith([1]);
// done();
// })
// .catch(catchErrors(done));
// });
// it('should call the upsert method as many times as ids in update Array', done => {
// let self = jasmine.createSpyObj('self', ['remoteMethod', 'crudItemNiches', 'destroyAll', 'create', 'upsert']);
// crudItemNiches(self);
// self.crudItemNiches({
// delete: [],
// create: [],
// update: [1, 2]
// }).then(result => {
// expect(self.upsert).toHaveBeenCalledWith(1);
// expect(self.upsert).toHaveBeenCalledWith(2);
// expect(self.upsert.calls.count()).toEqual(2);
// done();
// })
// .catch(catchErrors(done));
// });
// });

View File

@ -1,6 +1,4 @@
module.exports = Self => { module.exports = Self => {
require('../methods/item-barcode/crudItemBarcodes')(Self);
Self.validatesUniquenessOf('code', { Self.validatesUniquenessOf('code', {
message: `Barcode must be unique` message: `Barcode must be unique`
}); });

View File

@ -1,39 +0,0 @@
module.exports = Self => {
Self.installMethod('filter', filterParams);
function filterParams(params) {
return {
where: {
ticketFk: params.ticketFk
},
skip: (params.page - 1) * params.size,
limit: params.size,
order: params.order || 'concept ASC',
include: [{
relation: 'item',
scope: {
include: {
relation: 'tags',
scope: {
fields: ['tagFk', 'value'],
include: {
relation: 'tag',
scope: {
fields: ['name']
}
},
limit: 6
}
},
fields: ['itemFk', 'name']
}
},
{
relation: 'isChecked',
scope: {
fields: ['isChecked']
}
}]
};
}
};

View File

@ -1,51 +0,0 @@
module.exports = Self => {
Self.installMethod('saleComponentFilter', filterParams);
function filterParams(params) {
return {
where: {
ticketFk: params.ticketFk
},
skip: (params.page - 1) * params.size,
limit: params.size,
order: params.order || 'concept ASC',
include: [{
relation: 'item',
scope: {
include: {
relation: 'tags',
scope: {
fields: ['tagFk', 'value'],
include: {
relation: 'tag',
scope: {
fields: ['name']
}
},
limit: 6
}
},
fields: ['itemFk', 'name']
}
},
{
relation: 'components',
scope: {
fields: ['componentFk', 'value'],
include: {
relation: 'componentRate',
scope: {
fields: ['componentTypeRate', 'name'],
include: {
relation: 'componentType',
scope: {
fields: ['type']
}
}
}
}
}
}]
};
}
};

View File

@ -1,56 +0,0 @@
module.exports = function(Self) {
Self.remoteMethodCtx('changeState', {
description: 'Change state of tickets',
accepts: [
{
arg: 'state',
type: 'number',
required: true,
description: 'New state',
http: {source: 'path'}
}
],
returns: {
arg: 'response',
type: 'boolean'
},
http: {
verb: 'put',
path: '/:state/changeState'
}
});
Self.changeState = function(ctx, state, cb) {
var tickets = ctx.req.body.tickets;
Self.connectToService(ctx, 'client');
Self.app.models.Worker.findOne({where: {userFk: ctx.req.accessToken.userId}}, function(err, emp) {
if (err)
cb(err, null);
else
changeState(emp.id, tickets, state, cb);
});
Self.disconnectFromService('client');
};
function changeState(emp, tickets, state, cb) {
var inserts = [];
var FakeProduction = Self.app.models.FakeProduction;
tickets.forEach(function(t) {
inserts.push({ticketFk: t, stateFk: state, workerFk: emp});
}, this);
Self.create(inserts, function(err, res) {
if (err)
cb(err, null);
else {
FakeProduction.updateAll({ticketFk: {inq: tickets}}, {stateFk: state}, function(err, info) {
cb(err, info);
});
}
});
}
};

View File

@ -1,37 +0,0 @@
module.exports = function(Ticket) {
Ticket.remoteMethodCtx('changeTime', {
description: 'Change time of tickets',
accepts: [{
arg: 'time',
type: 'string',
required: true,
description: 'New time of tickets',
http: {source: 'path'}
}],
returns: {
arg: 'response',
type: 'boolean'
},
http: {
verb: 'put',
path: '/:time/changeTime'
}
});
Ticket.changeTime = function(ctx, time, cb) {
var tickets = ctx.req.body.tickets;
var FakeProduction = Ticket.app.models.FakeProduction;
var hour = `${time}:00`;
var query = `update Ticket set date = CONCAT(DATE(date), ' ', ?) where id in (?)`;
var params = [hour, tickets];
FakeProduction.updateAll({ticketFk: {inq: tickets}}, {hour: hour}, function(err, res) {
if (err)
cb(err, null);
else
Ticket.rawSql(query, params, cb).then(function(response) {
cb(null, response);
});
});
};
};

View File

@ -33,6 +33,7 @@ module.exports = Self => {
`CREATE TEMPORARY TABLE tmp.filter `CREATE TEMPORARY TABLE tmp.filter
(PRIMARY KEY (ticketFk)) ENGINE = MEMORY (PRIMARY KEY (ticketFk)) ENGINE = MEMORY
SELECT SELECT
t.id,
t.id AS ticketFk, t.id AS ticketFk,
t.shipped, t.shipped,
t.nickname, t.nickname,
@ -49,7 +50,7 @@ module.exports = Self => {
am.name AS agencyMode, am.name AS agencyMode,
st.name AS state, st.name AS state,
wk.name AS salesPerson, wk.name AS salesPerson,
0 AS total CAST(0 AS DECIMAL(10, 2)) AS total
FROM ticket t FROM ticket t
LEFT JOIN address a ON a.id = t.addressFk LEFT JOIN address a ON a.id = t.addressFk
LEFT JOIN province p ON p.id = a.provinceFk LEFT JOIN province p ON p.id = a.provinceFk
@ -88,7 +89,7 @@ module.exports = Self => {
tp.problem tp.problem
FROM tmp.filter f FROM tmp.filter f
LEFT JOIN tmp.ticketProblems tp ON tp.ticketFk = f.ticketFk`); LEFT JOIN tmp.ticketProblems tp ON tp.ticketFk = f.ticketFk`);
stmt.merge(Self.buildSuffix(filter, 'f')); stmt.merge(Self.buildOrderBy(filter, 'f'));
let ticketsIndex = stmts.push(stmt) - 1; let ticketsIndex = stmts.push(stmt) - 1;
stmts.push( stmts.push(

View File

@ -1,7 +1,5 @@
module.exports = Self => { module.exports = Self => {
require('../methods/sale/filter')(Self);
require('../methods/sale/getClaimableFromTicket')(Self); require('../methods/sale/getClaimableFromTicket')(Self);
require('../methods/sale/saleComponentFilter')(Self);
require('../methods/sale/priceDifference')(Self); require('../methods/sale/priceDifference')(Self);
require('../methods/sale/moveToTicket')(Self); require('../methods/sale/moveToTicket')(Self);
require('../methods/sale/reserve')(Self); require('../methods/sale/reserve')(Self);

View File

@ -1,3 +0,0 @@
module.exports = Self => {
require('../methods/ticket-state/changeState')(Self);
};

View File

@ -1,5 +1,4 @@
module.exports = Self => { module.exports = Self => {
require('../methods/ticket/changeTime')(Self);
require('../methods/ticket/changeWorker')(Self); require('../methods/ticket/changeWorker')(Self);
require('../methods/ticket/getVolume')(Self); require('../methods/ticket/getVolume')(Self);
require('../methods/ticket/getTotalVolume')(Self); require('../methods/ticket/getTotalVolume')(Self);

View File

@ -31,5 +31,5 @@
"permission": "ALLOW" "permission": "ALLOW"
} }
], ],
"scope" : {"where": {"isManaged": {"neq": 0}}} "scope" : {"where": {"isInventory": {"neq": 0}}}
} }

View File

@ -1,5 +0,0 @@
FROM vn-loopback:latest
COPY production /app
WORKDIR /app
CMD ["pm2-docker", "./server/server.js"]

View File

@ -1,90 +0,0 @@
module.exports = function(Self) {
Self.defineScope();
Self.list = function(ctx, filter, callback) {
let daysTickets = 0;
let warehouseFk = filter.warehouseFk;
delete filter.limit;
delete filter.page;
delete filter.warehouseFk;
let call = `call salix.production_control_source(? , ?)`;
var params = [warehouseFk, daysTickets];
let conn;
Self.getConnection((err, connection) => {
if (err) {
onFinish(err);
}
conn = connection;
conn.query(call, params, getValue);
});
function getValue(err) {
if (err) {
onFinish(err);
}
buildWhereObject();
let where = Self.dataSource.connector.buildWhere(Self.modelName, filter.where);
let query = `SELECT * FROM tmp.production ${where.sql} GROUP BY RouteFk ORDER BY routeFk`;
conn.query(query, where.params, onFinish);
}
function buildWhereObject() {
let newFilter = {};
if (filter.q) {
let regexQ = new RegExp(filter.q);
delete filter.q;
newFilter = {
and: [
{
or: [
{agency: {regexp: regexQ}},
{state: {regexp: regexQ}}
]
}
]
};
if (Object.keys(filter).length) {
Object.keys(filter).forEach(
key => {
let field = {};
field[key] = filter[key];
newFilter.and.push(field);
}
);
}
} else if (Object.keys(filter).length) {
newFilter = filter;
}
filter.where = newFilter;
}
function onFinish(err, results) {
conn.query('DROP TEMPORARY TABLE IF EXISTS tmp.production');
conn.release();
if (err)
callback(err);
callback(null, sum(results));
}
function sum(tickets) {
let obj = {lines: 0, m3: 0};
if (tickets && tickets.length)
tickets.forEach(function(t, i) {
obj.lines += t.lines;
obj.m3 += t.m3;
if (tickets[i].problem)
tickets[i].problem = tickets[i].problem.trim();
}, this);
obj.m3 = obj.m3.toFixed(2);
obj.total = tickets ? tickets.length : 0;
obj.tickets = tickets || [];
return obj;
}
};
};

View File

@ -1,80 +0,0 @@
var uuid = require('uuid');
module.exports = function(FakeProduction) {
FakeProduction.remoteMethodCtx('messageSend', {
description: 'Send message to salesPerson of one array of tickets',
returns: {
arg: 'response',
type: 'message'
},
http: {
path: '/messageSend',
verb: 'post'
}
});
FakeProduction.messageSend = function(ctx, cb) {
var tickets = ctx.req.body.tickets;
var userId = ctx.req.accessToken.$userId;
var User = FakeProduction.app.models.User;
User.findById(userId, function(err, user) {
var elements = [];
tickets.forEach(function(t) {
elements.push({sender: user.username, salesPerson: t.salesPerson, message: `Revisa el tickete ${t.ticketFk}`});
}, this);
messageSend(elements, cb);
});
};
function messageSend(elements, cb) {
var messages = [];
var messagesInbox = [];
elements.forEach(function(e) {
var id = uuid.v1();
var message = {uuid: id, sender: e.sender, recipient: e.salesPerson, message: e.message};
var messageInbox = {uuid: id, sender: e.sender, recipient: e.salesPerson, finalRecipient: e.salesPerson, message: e.message};
messages.push(message);
messagesInbox.push(messageInbox);
}, this);
createMessage(messages, messagesInbox, cb);
}
function createMessage(messages, messagesInbox, cb) {
var Message = FakeProduction.app.models.Message;
FakeProduction.beginTransaction({isolationLevel: FakeProduction.Transaction.READ_COMMITTED}, function(err, tx) {
Message.create(messages, {transaction: tx}, function(err, res) {
if (err) {
tx.rollback(function(error) {
if (error)
cb(error, null);
else
cb(err, null);
});
} else {
createMessageInbox(messagesInbox, tx, cb);
}
});
});
}
function createMessageInbox(messagesInbox, tx, cb) {
var MessageInbox = FakeProduction.app.models.MessageInbox;
MessageInbox.create(messagesInbox, {transaction: tx}, function(err, res) {
if (err) {
tx.rollback(function(error) {
if (error)
cb(error, null);
else
cb(err, null);
});
} else {
tx.commit(function(err) {
if (err)
cb(err, null);
else
cb(null, res);
});
}
});
}
};

View File

@ -1,26 +0,0 @@
module.exports = function(FakeProduction) {
FakeProduction.remoteMethodCtx('routeList', {
description: 'Route list',
returns: {
arg: 'response',
type: 'message'
},
http: {
path: '/routeList',
verb: 'get'
}
});
FakeProduction.routeList = function(ctx, cb) {
// var query = `select routeFk from FakeProduction where routeFk is not null group by RouteFk order by routeFk`;
let query = `call salix.production_control_source(1,1)`;
var params = [];
FakeProduction.rawSql(query, params, cb)
.then(function(response) {
cb(null, response);
})
.catch(function(response) {
cb(response, null);
});
};
};

View File

@ -1,6 +0,0 @@
module.exports = function(Self) {
require('../methods/fake-production/list.js')(Self);
require('../methods/fake-production/message-send.js')(Self);
require('../methods/fake-production/route.js')(Self);
};

View File

@ -1,77 +0,0 @@
{
"name": "FakeProduction",
"base": "VnModel",
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"ticketFk": {
"type": "Number"
},
"clientFk": {
"type": "Number"
},
"client":{
"type": "String"
},
"date":{
"type": "Date"
},
"hour":{
"type": "String"
},
"city":{
"type": "String"
},
"province":{
"type": "String"
},
"provinceFk":{
"type": "Number"
},
"agency":{
"type": "String"
},
"agencyFk":{
"type": "Number"
},
"lines":{
"type": "Number"
},
"m3":{
"type": "Number"
},
"problems":{
"type": "Number"
},
"problem":{
"type": "String"
},
"state":{
"type": "String"
},
"stateFk":{
"type": "Number"
},
"worker":{
"type": "String"
},
"workerFk":{
"type": "Number"
},
"salesPerson":{
"type": "String"
},
"salesPersonFk":{
"type": "Number"
},
"boxes":{
"type": "Number"
},
"routeFk":{
"type": "Number"
}
}
}

View File

@ -1,34 +0,0 @@
{
"name": "MessageInbox",
"base": "VnModel",
"options": {
"mysql": {
"table": "messageInbox"
}
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"uuid":{
"type": "String"
},
"sender":{
"type": "String"
},
"recipient":{
"type": "String"
},
"finalRecipient":{
"type": "String"
},
"message":{
"type": "String"
},
"sendDate":{
"type": "date"
}
}
}

View File

@ -1,31 +0,0 @@
{
"name": "Message",
"base": "VnModel",
"options": {
"mysql": {
"table": "message"
}
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"uuid":{
"type": "String"
},
"sender":{
"type": "String"
},
"recipient":{
"type": "String"
},
"message":{
"type": "String"
},
"sendDate":{
"type": "date"
}
}
}

View File

@ -1,20 +0,0 @@
{
"name": "vn-production",
"version": "1.0.0",
"main": "server/server.js",
"scripts": {
"lint": "eslint .",
"start": "node .",
"posttest": "npm run lint && nsp check"
},
"repository": {
"type": "git",
"url": "https://git.verdnatura.es/salix"
},
"license": "GPL-3.0",
"description": "vn-production",
"dependencies": {
"uuid": "^3.1.0",
"vn-loopback": "file:../loopback"
}
}

View File

@ -1,11 +0,0 @@
{
"FakeProduction": {
"dataSource": "vn"
},
"Message": {
"dataSource": "vn"
},
"MessageInbox": {
"dataSource": "vn"
}
}

View File

@ -1,4 +0,0 @@
var vnLoopback = require('vn-loopback/server/server.js');
var app = module.exports = vnLoopback.loopback();
vnLoopback.boot(app, __dirname, module);

View File

@ -1,3 +0,0 @@
module.exports = Self => {
Self.installCrudModel('crudTicketObservation');
};

View File

@ -1,22 +1,17 @@
const app = require(`${servicesDir}/ticket/server/server`); const app = require(`${servicesDir}/ticket/server/server`);
describe('ticket listSaleTracking()', () => { describe('ticket listSaleTracking()', () => {
it('should call the listSaleTracking method and return the response', done => { it('should call the listSaleTracking method and return the response', async() => {
let filter = {where: {ticketFk: 1}}; let filter = {where: {ticketFk: 1}};
app.models.SaleTracking.listSaleTracking(filter) let result = await app.models.SaleTracking.listSaleTracking(filter);
.then(response => {
expect(response[0].concept).toEqual('Gem of Time'); expect(result[0].concept).toEqual('Gem of Time');
done();
});
}); });
it(`should call the listSaleTracking method and return zero if doesn't have lines`, done => { it(`should call the listSaleTracking method and return zero if doesn't have lines`, async() => {
let filter = {where: {ticketFk: 2}}; let filter = {where: {ticketFk: 2}};
app.models.SaleTracking.listSaleTracking(filter) let result = await app.models.SaleTracking.listSaleTracking(filter);
.then(response => {
expect(response.length).toEqual(0); expect(result.length).toEqual(0);
done();
});
}); });
}); });

View File

@ -1,6 +1,4 @@
module.exports = function(Self) { module.exports = function(Self) {
require('../methods/notes/crudTicketObservation.js')(Self);
/* Self.validateAsync('observationTypeFk', validateObservationUniqueness, { /* Self.validateAsync('observationTypeFk', validateObservationUniqueness, {
message: `The observation type can't be repeated` message: `The observation type can't be repeated`
}); });