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. Required applications.
* Visual Studio Code
* Node.js = 8.9.4 * Node.js = 8.9.4
* NGINX * NGINX
* Docker * 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. You will need to install globally the following items.
``` ```
$ npm install -g karma-cli gulp webpack nodemon $ 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 ## Getting Started // Installing

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
"module": "ticket", "module": "ticket",
"name": "Tickets", "name": "Tickets",
"icon": "icon-ticket", "icon": "icon-ticket",
"validations": false, "validations": true,
"routes": [ "routes": [
{ {
"url": "/ticket", "url": "/ticket",
@ -77,16 +77,36 @@
} }
}, },
{ {
"url" : "/review", "url" : "/tracking",
"state": "ticket.card.review", "state": "ticket.card.tracking",
"component": "vn-ticket-review", "component": "vn-ticket-tracking",
"params": { "params": {
"ticket": "$ctrl.ticket" "ticket": "$ctrl.ticket"
}, },
"menu": { "menu": {
"description": "Review", "description": "Tracking",
"icon": "remove_red_eye" "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 Notes: Notas
Observation type: Tipo de observación 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 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 <mg-ajax
path="/ticket/api/Tickets/{{index.params.id}}/packages" path="/ticket/api/Tickets/{{index.params.id}}/packages"
options="mgIndex"> options="mgIndex" actions="$ctrl.getPackages()">
</mg-ajax> </mg-ajax>
<vn-card pad-large> <vn-watcher
<vn-title>Packages</vn-title> vn-id="watcher"
<vn-one> data="$ctrl.packages"
<vn-horizontal ng-repeat="package in index.model track by package.id"> form="form">
<vn-autocomplete vn-one </vn-watcher>
margin-large-right
url="/ticket/api/Packagings/listPackaging" <form name="form" ng-submit="$ctrl.submit()">
label="Package" <vn-card pad-large>
show-field="name" <vn-title>Packages</vn-title>
value-field="packagingFk" <vn-one>
field="package.packagingFk"> <vn-horizontal ng-repeat="package in index.model track by $index">
<tpl-item>{{id}} : {{name}}</tpl-item> <vn-autocomplete vn-one
</vn-autocomplete> margin-large-right
<vn-textfield url="/ticket/api/Packagings/listPackaging"
vn-one label="Package"
margin-large-right show-field="name"
label="Quantity" value-field="packagingFk"
model="package.quantity"> field="package.packagingFk">
</vn-textfield> <tpl-item>{{id}} : {{name}}</tpl-item>
<vn-textfield </vn-autocomplete>
vn-one <vn-textfield
margin-large-right vn-one
label="Added" margin-large-right
model="package.created | date: 'dd/MM/yyyy'" label="Quantity"
disabled="true" model="package.quantity"
ng-readonly="true"> rule="TicketPackaging.quantity">
</vn-textfield> </vn-textfield>
<vn-auto pad-medium-top> <vn-textfield
<vn-icon vn-one
pointer margin-large-right
medium-grey label="Added"
vn-tooltip="Remove package" model="package.created | date: 'dd/MM/yyyy'"
tooltip-position = "left" disabled="true"
icon="remove_circle_outline" ng-readonly="true">
ng-click="$ctrl.removePackage($index)"> </vn-textfield>
</vn-icon> <vn-auto pad-medium-top>
</vn-one> <vn-icon
</vn-horizontal> pointer
</vn-one> medium-grey
<vn-one> vn-tooltip="Remove package"
<vn-icon tooltip-position = "left"
pointer icon="remove_circle_outline"
margin-medium-left ng-click="$ctrl.removePackage($index)">
vn-tooltip="Add package" </vn-icon>
tooltip-position = "right" </vn-one>
orange </vn-horizontal>
icon="add_circle" </vn-one>
ng-click="$ctrl.addPackage()"> <vn-one>
</vn-icon> <vn-icon
</vn-one> pointer
</vn-card> 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 { class Controller {
construct($http, $scope) { constructor($http, $scope, $translate, vnApp) {
this.$http = $http; this.$http = $http;
this.$ = $scope; 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 './data/ticket-data';
import './notes/ticket-observations'; import './notes/ticket-observations';
import './package/list/package-list'; 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", "fs-extra": "^5.0.0",
"material-design-lite": "^1.3.0", "material-design-lite": "^1.3.0",
"mg-crud": "^1.1.2", "mg-crud": "^1.1.2",
"npm": "^5.7.1",
"oclazyload": "^0.6.3", "oclazyload": "^0.6.3",
"require-yaml": "0.0.1", "require-yaml": "0.0.1",
"validator": "^6.2.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), ( 1, 101, CURDATE(), date_add(CURDATE(),INTERVAL 1 MONTH), 50, 7),
( 2, 102, CURDATE(), date_add(CURDATE(),INTERVAL 3 MONTH), 100, 1), ( 2, 102, CURDATE(), date_add(CURDATE(),INTERVAL 3 MONTH), 100, 1),
( 3, 102, CURDATE(), date_add(CURDATE(),INTERVAL 1 MONTH), 50, 7), ( 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', '*', '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 ('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", "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", "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", "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", "can't be blank": "can't be blank",
"DNI Incorrecto": "DNI Incorrecto", "DNI Incorrecto": "DNI Incorrecto",
"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",
"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).", "is invalid": "is invalid",
"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)", "Quantity cannot be zero": "La cantidad no puede ser cero"
"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)"
} }

View File

@ -12,6 +12,9 @@
"id": true, "id": true,
"description": "Identifier" "description": "Identifier"
}, },
"code": {
"type": "String"
},
"name": { "name": {
"type": "String" "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", "foreignKey": "itemFk",
"required": true "required": true
}, },
"itemTag": {
"type": "hasMany",
"model": "ItemTag",
"foreignKey": "itemFk"
},
"ticket": { "ticket": {
"type": "belongsTo", "type": "belongsTo",
"model": "Ticket", "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": { "Expedition": {
"dataSource": "vn" "dataSource": "vn"
},
"AnnualAverageInvoiced": {
"dataSource": "vn"
} }
} }