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

This commit is contained in:
Juan 2018-03-21 12:57:29 +01:00
commit 2a7c73988c
37 changed files with 4371 additions and 127 deletions

View File

@ -8,14 +8,26 @@ Salix is also the scientific name of a beautifull tree! :)
Required applications.
* Visual Studio Code
* Node.js = 8.9.4
* NGINX
* Docker
In Visual Studio Code we use the ESLint extension. Open Visual Studio Code, press Ctrl+P and paste the following command
```
ext install dbaeumer.vscode-eslint
```
You will need to install globally the following items.
```
$ npm install -g karma-cli gulp webpack nodemon
```
## Linux Only Prerequisites
Your user must be on the docker group to use it so you will need to run this command:
```
$ sudo usermod -G docker yourusername
```
## Getting Started // Installing

View File

@ -30,6 +30,7 @@
value-field="id"
select-fields="name"
label="Salesperson"
vn-acl="salesAssistant"
where="{or: [{firstName: {regexp: 'search'}}, {name: {regexp: 'search'}}]}">
</vn-autocomplete>
<vn-autocomplete vn-one

View File

@ -2,5 +2,4 @@ User: Usuario
Enable web access: Habilitar acceso web
New password: Nueva contraseña
Repeat password: Repetir contraseña
Change password: Cambiar contraseña
Client must be checked to activate: No se puede activar un cliente si no esta verificado (036)
Change password: Cambiar contraseña

View File

@ -13,8 +13,7 @@
vn-one
label="Enable web access"
field="$ctrl.account.active"
vn-acl="employee"
acl-conditional-to-employee="{{$ctrl.canEnableCheckBox}}">
vn-acl="employee">
</vn-check>
</vn-horizontal>
<vn-horizontal>
@ -23,7 +22,6 @@
vn-one
margin-medium-top
label="User"
info="Client must be checked to activate"
field="$ctrl.account.name">
</vn-textfield>
</vn-horizontal>

View File

@ -16,17 +16,21 @@
<vn-autocomplete vn-one
url="/item/api/ItemTypes"
label="Type"
show-field="name"
select-fields=["code","name"]
value-field="id"
field="$ctrl.item.typeFk">
field="$ctrl.item.typeFk"
where="{or: [{code: {regexp: 'search'}}, {name: {regexp: 'search'}}]}">
<tpl-item>{{code}} : {{name}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete vn-one
url="/item/api/Intrastats"
label="Intrastat"
value-field="id"
show-field="description"
field="$ctrl.item.intrastatFk">
</vn-autocomplete>
url="/item/api/Intrastats"
label="Intrastat"
show-field="description"
value-field="id"
field="$ctrl.item.intrastatFk"
where="{or: [{id: {regexp: 'search'}}, {description: {regexp: 'search'}}]}">
<tpl-item>{{id}} : {{description}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one

View File

@ -2,7 +2,7 @@
"module": "ticket",
"name": "Tickets",
"icon": "icon-ticket",
"validations": false,
"validations": true,
"routes": [
{
"url": "/ticket",
@ -77,16 +77,36 @@
}
},
{
"url" : "/review",
"state": "ticket.card.review",
"component": "vn-ticket-review",
"url" : "/tracking",
"state": "ticket.card.tracking",
"component": "vn-ticket-tracking",
"params": {
"ticket": "$ctrl.ticket"
},
"menu": {
"description": "Review",
"description": "Tracking",
"icon": "remove_red_eye"
}
},
{
"url": "/create",
"state": "ticket.card.tracking.create",
"component": "vn-ticket-tracking-create",
"params": {
"client": "$ctrl.client"
}
},
{
"url" : "/sale",
"state": "ticket.card.sale",
"component": "vn-ticket-sale",
"params": {
"ticket": "$ctrl.ticket"
},
"menu": {
"description": "Sale",
"icon": "icon-lines"
}
}
]
}

View File

@ -0,0 +1,9 @@
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

@ -1,6 +1,13 @@
Tickets: Tickets
Amount: Importe
Basic data: Datos básicos
Description: Descripción
Discount: Descuento
Item: Articulo
Notes: Notas
Observation type: Tipo de observación
Description: Descripción
Price: Precio
Quantity: Cantidad
Sale: Lineas del pedido
Some fields are invalid: Algunos campos no son válidos
The observation type must be unique: El tipo de observación debe ser único
Some fields are invalid: Algunos campos no son válidos
Tickets: Tickets

View File

@ -1,56 +1,68 @@
<mg-ajax
path="/ticket/api/Tickets/{{index.params.id}}/packages"
options="mgIndex">
options="mgIndex" actions="$ctrl.getPackages()">
</mg-ajax>
<vn-card pad-large>
<vn-title>Packages</vn-title>
<vn-one>
<vn-horizontal ng-repeat="package in index.model track by package.id">
<vn-autocomplete vn-one
margin-large-right
url="/ticket/api/Packagings/listPackaging"
label="Package"
show-field="name"
value-field="packagingFk"
field="package.packagingFk">
<tpl-item>{{id}} : {{name}}</tpl-item>
</vn-autocomplete>
<vn-textfield
vn-one
margin-large-right
label="Quantity"
model="package.quantity">
</vn-textfield>
<vn-textfield
vn-one
margin-large-right
label="Added"
model="package.created | date: 'dd/MM/yyyy'"
disabled="true"
ng-readonly="true">
</vn-textfield>
<vn-auto pad-medium-top>
<vn-icon
pointer
medium-grey
vn-tooltip="Remove package"
tooltip-position = "left"
icon="remove_circle_outline"
ng-click="$ctrl.removePackage($index)">
</vn-icon>
</vn-one>
</vn-horizontal>
</vn-one>
<vn-one>
<vn-icon
pointer
margin-medium-left
vn-tooltip="Add package"
tooltip-position = "right"
orange
icon="add_circle"
ng-click="$ctrl.addPackage()">
</vn-icon>
</vn-one>
</vn-card>
<vn-watcher
vn-id="watcher"
data="$ctrl.packages"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.submit()">
<vn-card pad-large>
<vn-title>Packages</vn-title>
<vn-one>
<vn-horizontal ng-repeat="package in index.model track by $index">
<vn-autocomplete vn-one
margin-large-right
url="/ticket/api/Packagings/listPackaging"
label="Package"
show-field="name"
value-field="packagingFk"
field="package.packagingFk">
<tpl-item>{{id}} : {{name}}</tpl-item>
</vn-autocomplete>
<vn-textfield
vn-one
margin-large-right
label="Quantity"
model="package.quantity"
rule="TicketPackaging.quantity">
</vn-textfield>
<vn-textfield
vn-one
margin-large-right
label="Added"
model="package.created | date: 'dd/MM/yyyy'"
disabled="true"
ng-readonly="true">
</vn-textfield>
<vn-auto pad-medium-top>
<vn-icon
pointer
medium-grey
vn-tooltip="Remove package"
tooltip-position = "left"
icon="remove_circle_outline"
ng-click="$ctrl.removePackage($index)">
</vn-icon>
</vn-one>
</vn-horizontal>
</vn-one>
<vn-one>
<vn-icon
pointer
margin-medium-left
vn-tooltip="Add package"
tooltip-position = "right"
orange
icon="add_circle"
ng-click="$ctrl.addPackage()">
</vn-icon>
</vn-one>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -2,9 +2,63 @@ import ngModule from '../../module';
class Controller {
construct($http, $scope) {
constructor($http, $scope, $translate, vnApp) {
this.$http = $http;
this.$ = $scope;
this.$translate = $translate;
this.vnApp = vnApp;
this.removedPackages = [];
this.updatedPackages = [];
}
submit() {
let query = `/ticket/api/TicketPackagings/crudTicketPackaging`;
let packagesObj = {
delete: this.removedPackages,
create: [],
update: []
};
this.packages.forEach(item => {
if (typeof item.id === 'undefined')
packagesObj.create.push(item);
if (typeof item.id !== 'undefined' && angular.equals(item, this.oldPackages[item.id]))
packagesObj.update.push(item);
});
this.$http.post(query, packagesObj).then(res => {
this.$.index.accept();
});
}
removePackage(index) {
if (this.packages[index] && this.packages[index].id)
this.removedPackages.push(this.packages[index].id);
this.packages.splice(index, 1);
}
addPackage() {
let data = {
packagingFk: null,
quantity: null,
created: Date.now(),
ticketFk: this.ticket.id
};
this.packages.push(data);
}
getPackages() {
this.packages = this.$.index.model;
this.setOldPackages();
}
setOldPackages() {
this.oldPackages = [];
this.packages.forEach(item => {
this.oldPackages[item.id] = item;
});
}
}

View File

@ -0,0 +1,59 @@
import './package-list.js';
describe('Ticket', () => {
describe('Component vnTicketPackageList', () => {
let $componentController;
let controller;
let $httpBackend;
let $scope;
beforeEach(() => {
angular.mock.module('ticket');
});
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_, $rootScope) => {
$componentController = _$componentController_;
$httpBackend = _$httpBackend_;
$scope = {
index: {
accept: function() {}
}
};
controller = $componentController('vnTicketPackageList', {$scope: $scope});
}));
describe('removePackage()', () => {
it('should push a package to removedPackages in the controller', () => {
controller.packages = [{id: 1}, {id: 2}];
controller.removePackage(0);
expect(controller.removedPackages).toEqual([1]);
});
});
describe('submit()', () => {
it('should perform a post', () => {
spyOn(angular, 'equals').and.returnValue(true);
let query = '/ticket/api/TicketPackagings/crudTicketPackaging';
controller.removedPackages = [];
controller.oldPackages = [
{id: 1, quantity: 5, ticketFk: 1}
];
controller.packages = [
{quantity: 5, ticketFk: 1},
{id: 1, quantity: 25, ticketFk: 1}
];
let packagesObj = {
delete: controller.removedPackages,
create: [],
update: []
};
$httpBackend.whenPOST(query, packagesObj).respond('omg YEAH');
$httpBackend.expectPOST(query, packagesObj);
controller.submit();
$httpBackend.flush();
});
});
});
});

View File

@ -1,4 +0,0 @@
date : Fecha
Employee : Empleado
State: Estado
Review: Revision

View File

@ -1,23 +0,0 @@
<mg-ajax path="" options="vnIndexNonAuto"></mg-ajax>
<vn-vertical pad-medium>
<vn-card pad-large>
<vn-vertical>
<vn-title>Review</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
<vn-column-header vn-one pad-medium-h field="date" text="date"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="employee" text="Employee" default-order="ASC"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="state" text="State" order-locked></vn-column-header>
</vn-grid-header>
<vn-one class="list list-content">
</vn-horizontal>
</vn-one>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal>
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
</vn-vertical>
</vn-card>
</vn-vertical>
<a ui-sref="clientCard.ticket.create" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -1,18 +0,0 @@
import ngModule from '../module';
class ticketReview {
construct($http, $scope) {
this.$http = $http;
this.$ = $scope;
}
}
ticketReview.$inject = ['$http', '$scope'];
ngModule.component('vnTicketReview', {
template: require('./review.html'),
controller: ticketReview,
bindings: {
ticket: '<'
}
});

View File

View File

View File

@ -7,4 +7,5 @@ import './summary/ticket-summary';
import './data/ticket-data';
import './notes/ticket-observations';
import './package/list/package-list';
import './review/review';
import './sale/sale';
import './tracking/tracking';

View File

@ -0,0 +1,5 @@
Date : Fecha
Employee : Empleado
State: Estado
Tracking: Revisión
Created : Añadido

View File

@ -0,0 +1,31 @@
<mg-ajax path="/ticket/api/TicketTrackings/filter" options="vnIndexNonAuto"></mg-ajax>
<vn-vertical pad-medium>
<vn-card pad-large>
<vn-vertical>
<vn-title>Tracking</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
<vn-column-header vn-one pad-medium-h field="state.name" text="State"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="employee" text="Employee"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="created" text="Created" default-order="ASC"></vn-column-header>
</vn-grid-header>
<vn-one class="list list-content">
<vn-horizontal
vn-one class="list list-element text-center"
pad-small-bottom
ng-repeat="ticket in index.model.instances track by ticket.id">
<vn-one pad-medium-h>{{::ticket.state.name}}</vn-one>
<vn-two pad-medium-h>{{::ticket.worker.firstName}} {{::ticket.worker.name}}</vn-two>
<vn-two pad-medium-h>{{::ticket.created | date:'dd/MM/yyyy HH:mm' }}</vn-two>
</vn-horizontal>
</vn-one>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal>
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
</vn-vertical>
</vn-card>
</vn-vertical>
<a ui-sref="ticket.card.tracking.create" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -0,0 +1,7 @@
import ngModule from '../module';
import FilterTicketList from '../filter-ticket-list';
ngModule.component('vnTicketTracking', {
template: require('./tracking.html'),
controller: FilterTicketList
});

3934
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@
"fs-extra": "^5.0.0",
"material-design-lite": "^1.3.0",
"mg-crud": "^1.1.2",
"npm": "^5.7.1",
"oclazyload": "^0.6.3",
"require-yaml": "0.0.1",
"validator": "^6.2.1"

View File

@ -509,4 +509,19 @@ INSERT INTO `vn`.`recovery`(`id`, `clientFk`, `started`, `finished`, `amount`, `
( 1, 101, CURDATE(), date_add(CURDATE(),INTERVAL 1 MONTH), 50, 7),
( 2, 102, CURDATE(), date_add(CURDATE(),INTERVAL 3 MONTH), 100, 1),
( 3, 102, CURDATE(), date_add(CURDATE(),INTERVAL 1 MONTH), 50, 7),
( 4, 103, CURDATE(), NULL, 50, 7);
( 4, 103, CURDATE(), NULL, 50, 7);
INSERT INTO `bi`.`rotacion`(`Id_Article`, `warehouse_id`, `total`, `rotacion`, `cm3`, `almacenaje`, `manipulacion`, `auxiliar`, `mermas`)
VALUES
( 1, 1, 0, 0.0000, 1500, 0.0015, 0.0250, 0.0085, 0.0000),
( 1, 2, 0, 0.0000, 100, 0.0060, 0.0200, 0.0080, 0.0000),
( 2, 1, 10, 3.5000, 0, 0.0000, 0.0000, 0.0080, 0.0000),
( 3, 1, 50, 5.5000, 100, 0.0000, 0.0000, 0.0080, 0.0000);
INSERT INTO `vn`.`annualAverageInvoiced`(`clientFk`, `invoiced`)
VALUES
( 101, 0),
( 102, 100),
( 103, 1000),
( 104, 500),
( 105, 5000);

View File

@ -12,3 +12,4 @@ INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `pri
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Expedition', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Expedition', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Expedition', '*', 'WRITE', 'ALLOW', 'ROLE', 'production');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('AnnualAverageInvoiced', '*', 'READ', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,10 @@
USE `vn`;
CREATE
OR REPLACE ALGORITHM = UNDEFINED
DEFINER = `root`@`%`
SQL SECURITY DEFINER
VIEW `vn`.`annualAverageInvoiced` AS
SELECT
`e`.`Id_Cliente` AS `clientFk`, `e`.`Consumo` AS `invoiced`
FROM
`bi`.`facturacion_media_anual` `e`;

View File

@ -5,5 +5,6 @@
"The default consignee can not be unchecked": "The default consignee can not be unchecked",
"Unable to default a disabled consignee": "Unable to default a disabled consignee",
"El método de pago seleccionado requiere que se especifique el IBAN": "El método de pago seleccionado requiere que se especifique el IBAN",
"Ya existe un usuario con ese nombre": "Ya existe un usuario con ese nombre"
"Ya existe un usuario con ese nombre": "Ya existe un usuario con ese nombre",
"Quantity cannot be zero": "Quantity cannot be zero"
}

View File

@ -8,7 +8,6 @@
"can't be blank": "can't be blank",
"DNI Incorrecto": "DNI Incorrecto",
"Ya existe un usuario con ese nombre": "Ya existe un usuario con ese nombre",
"ValidationError: The `Item` instance is not valid. Details: `originFk` Cannot be blank (value: undefined).": "ValidationError: The `Item` instance is not valid. Details: `originFk` Cannot be blank (value: undefined).",
"Error: ER_NO_REFERENCED_ROW_2: Cannot add or update a child row: a foreign key constraint fails (`vn2008`.`Articles`, CONSTRAINT `Articles_ibfk_5` FOREIGN KEY (`tipo_id`) REFERENCES `Tipos` (`tipo_id`) ON UPDATE CASCADE)": "Error: ER_NO_REFERENCED_ROW_2: Cannot add or update a child row: a foreign key constraint fails (`vn2008`.`Articles`, CONSTRAINT `Articles_ibfk_5` FOREIGN KEY (`tipo_id`) REFERENCES `Tipos` (`tipo_id`) ON UPDATE CASCADE)",
"Error: ER_NO_REFERENCED_ROW_2: Cannot add or update a child row: a foreign key constraint fails (`vn2008`.`Articles`, CONSTRAINT `expenceFk` FOREIGN KEY (`expenceFk`) REFERENCES `Gastos` (`Id_Gasto`) ON UPDATE CASCADE)": "Error: ER_NO_REFERENCED_ROW_2: Cannot add or update a child row: a foreign key constraint fails (`vn2008`.`Articles`, CONSTRAINT `expenceFk` FOREIGN KEY (`expenceFk`) REFERENCES `Gastos` (`Id_Gasto`) ON UPDATE CASCADE)"
"is invalid": "is invalid",
"Quantity cannot be zero": "La cantidad no puede ser cero"
}

View File

@ -12,6 +12,9 @@
"id": true,
"description": "Identifier"
},
"code": {
"type": "String"
},
"name": {
"type": "String"
},

View File

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

View File

@ -0,0 +1,26 @@
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: "itemTag",
scope: {
fields: ["id", "value", "priority", "tagFk"],
include: {
relation: "tag",
scope: {
fields: ["name"]
}
}
}
}]
};
}
};

View File

@ -0,0 +1,28 @@
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 || 'created DESC',
include: [
{
relation: "worker",
scope: {
fields: ["firstName", "name"]
}
},
{
relation: "state",
scope: {
fields: ["name"]
}
}
]
};
}
};

View File

@ -0,0 +1,24 @@
{
"name": "AnnualAverageInvoiced",
"base": "VnModel",
"options": {
"mysql": {
"table": "annualAverageInvoiced"
}
},
"properties": {
"invoiced": {
"type": "Number"
},
"clientFk": {
"id": true
}
},
"relations": {
"client": {
"type": "belongsTo",
"model": "client",
"foreignKey": "clientFk"
}
}
}

View File

@ -0,0 +1,3 @@
module.exports = function(Self) {
require('../methods/sale/filter.js')(Self);
};

View File

@ -41,6 +41,11 @@
"foreignKey": "itemFk",
"required": true
},
"itemTag": {
"type": "hasMany",
"model": "ItemTag",
"foreignKey": "itemFk"
},
"ticket": {
"type": "belongsTo",
"model": "Ticket",

View File

@ -0,0 +1,15 @@
module.exports = function(Self) {
require('../methods/packaging/crudTicketPackaging')(Self);
Self.validateBinded('quantity', validateQuantity, {
message: 'Quantity cannot be zero',
allowNull: false,
allowBlank: false
});
function validateQuantity(quantity) {
return quantity != 0;
}
Self.validatesPresenceOf('packagingFk', {message: 'Package cannot be blank'});
};

View File

@ -0,0 +1,3 @@
module.exports = function(Self) {
require('../methods/ticketTracking/filter')(Self);
};

View File

@ -28,6 +28,9 @@
},
"Expedition": {
"dataSource": "vn"
},
"AnnualAverageInvoiced": {
"dataSource": "vn"
}
}