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

This commit is contained in:
Daniel Herrero 2018-02-20 12:58:10 +01:00
commit 93134cb39f
14 changed files with 509 additions and 32 deletions

View File

@ -1,5 +1,48 @@
<vn-card>
<vn-vertical pad-large>
<vn-title>Niche</vn-title>
</vn-vertical>
</vn-card>
<form name="form" ng-submit="$ctrl.submit()">
<vn-card>
<vn-vertical pad-large>
<vn-one margin-medium-top>
<vn-title>Item Niches</vn-title>
<vn-horizontal ng-repeat="niche in $ctrl.niches track by $index">
<vn-autocomplete
vn-three
initial-data = "niche.warehouse"
field = "niche.warehouseFk"
data = "$ctrl.warehouses"
show-field = "name"
value-field = "id"
label = "Warehouse"
order = "name ASC"
vn-acl="buyer, replenisher">
</vn-autocomplete>
<vn-textfield
vn-three label="code"
model="niche.code"
vn-acl="buyer, replenisher">
</vn-textfield>
<vn-one pad-medium-top>
<vn-icon
vn-acl="buyer, replenisher"
pointer
medium-grey
icon="remove_circle_outline"
ng-click="$ctrl.removeNiche($index)">
</vn-icon>
<vn-icon
vn-acl="buyer, replenisher"
pointer
margin-medium-left
orange
icon="add_circle"
ng-if = "niche.showAddIcon"
ng-click="$ctrl.addNiche()">
</vn-icon>
</vn-one>
</vn-horizontal>
</vn-one>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -1,5 +1,142 @@
import ngModule from '../module';
export default class Controller {
constructor($state, $scope, $http, $q, $translate, vnApp) {
this.$state = $state;
this.$scope = $scope;
this.$http = $http;
this.$q = $q;
this.$translate = $translate;
this.vnApp = vnApp;
this.warehouses = [];
this.niches = [];
this.removedNiches = [];
this.oldNiches = {};
}
_setIconAdd() {
if (this.niches.length) {
this.niches.map(element => {
element.showAddIcon = false;
return true;
});
this.niches[this.niches.length - 1].showAddIcon = true;
} else {
this.addNiche();
}
}
_setDirtyForm() {
if (this.$scope.form) {
this.$scope.form.$setDirty();
}
}
_unsetDirtyForm() {
if (this.$scope.form) {
this.$scope.form.$setPristine();
}
}
addNiche() {
this.niches.push({code: null, itemFk: this.$state.params.id, showAddIcon: true});
this._setIconAdd();
}
removeNiche(index) {
let item = this.niches[index];
if (item) {
this.niches.splice(index, 1);
this._setIconAdd();
if (item.id) {
this.removedNiches.push(item.id);
this._setDirtyForm();
}
}
}
_equalNiches(oldNiche, newNiche) {
return oldNiche.id === newNiche.id && oldNiche.code === newNiche.code && oldNiche.warehouseFk === newNiche.warehouseFk;
}
submit() {
let warehousesDefined = [];
let repeatedWarehouse = false;
let canSubmit;
let nichesObj = {
delete: this.removedNiches,
create: [],
update: []
};
this.niches.forEach(niche => {
let isNewNiche = !niche.id;
if (warehousesDefined.indexOf(niche.warehouseFk) !== -1) {
repeatedWarehouse = true;
return;
}
warehousesDefined.push(niche.warehouseFk);
if (isNewNiche) {
nichesObj.create.push(niche);
}
if (!isNewNiche && !this._equalNiches(this.oldNiches[niche.id], niche)) {
nichesObj.update.push(niche);
}
});
if (repeatedWarehouse) {
return this.vnApp.showMessage(this.$translate.instant('The niche must be unique'));
}
canSubmit = nichesObj.update.length > 0 || nichesObj.create.length > 0 || nichesObj.delete.length > 0;
if (canSubmit) {
return this.$http.post(`/item/api/ItemNiches/crudItemNiches`, nichesObj).then(() => {
this.getNiches();
this._unsetDirtyForm();
});
}
this.vnApp.showMessage(this.$translate.instant('No changes to save'));
}
setOldNiches(response) {
this._setIconAdd();
response.data.forEach(niche => {
this.oldNiches[niche.id] = Object.assign({}, niche);
});
}
getWarehouse(id, warehouses) {
return warehouses.find(warehouse => warehouse.id === id);
}
getNiches() {
let filter = {
where: {itemFk: this.$state.params.id},
include: {relation: 'warehouse'}
};
this.$http.get(`/item/api/ItemNiches?filter=${JSON.stringify(filter)}`).then(response => {
this.niches = response.data;
this.setOldNiches(response);
});
}
getWarehouses() {
this.$http.get(`/item/api/Warehouses`).then(response => {
this.warehouses = response.data;
});
}
$onInit() {
this.getNiches();
this.getWarehouses();
}
}
Controller.$inject = ['$state', '$scope', '$http', '$q', '$translate', 'vnApp'];
ngModule.component('vnItemNiche', {
template: require('./item-niche.html')
template: require('./item-niche.html'),
controller: Controller
});

View File

@ -0,0 +1,150 @@
import './item-niche.js';
describe('Item', () => {
describe('Component vnItemNiche', () => {
let $componentController;
let $state;
let controller;
let $httpBackend;
beforeEach(() => {
angular.mock.module('item');
});
beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_) => {
$componentController = _$componentController_;
$state = _$state_;
$httpBackend = _$httpBackend_;
controller = $componentController('vnItemNiche', {$state: $state});
}));
describe('add / remove niche()', () => {
it('should add one empty niche into controller niches collection and call _setIconAdd()', () => {
controller.niches = [];
spyOn(controller, '_setIconAdd').and.callThrough();
controller.addNiche();
expect(controller._setIconAdd).toHaveBeenCalledWith();
expect(controller.niches.length).toEqual(1);
expect(controller.niches[0].id).toBe(undefined);
expect(controller.niches[0].showAddIcon).toBeTruthy();
});
it('should remove a niche that occupies the position in the index given and call _setIconAdd()', () => {
let index = 2;
controller.niches = [
{id: 1, warehouseFk: 1, code: '1111', showAddIcon: false},
{id: 2, warehouseFk: 2, code: '2222', showAddIcon: false},
{id: 3, warehouseFk: 3, code: '3333', showAddIcon: true}
];
spyOn(controller, '_setIconAdd').and.callThrough();
controller.removeNiche(index);
expect(controller._setIconAdd).toHaveBeenCalledWith();
expect(controller.niches.length).toEqual(2);
expect(controller.niches[0].showAddIcon).toBeFalsy();
expect(controller.niches[1].showAddIcon).toBeTruthy();
expect(controller.niches[index]).toBe(undefined);
});
});
describe('_equalNiches()', () => {
it('should return true if two niches are equals independent of control attributes', () => {
let niche1 = {id: 1, warehouseFk: 1, code: '1111', showAddIcon: true};
let niche2 = {id: 1, warehouseFk: 1, code: '1111', showAddIcon: false};
let equals = controller._equalNiches(niche2, niche1);
expect(equals).toBeTruthy();
});
it('should return false if two niches aint equals independent of control attributes', () => {
let niche1 = {id: 1, warehouseFk: 1, code: '1111', showAddIcon: true};
let niche2 = {id: 1, warehouseFk: 1, code: '2222', showAddIcon: true};
let equals = controller._equalNiches(niche2, niche1);
expect(equals).toBeFalsy();
});
});
describe('get Niches / Warehouses', () => {
it('should perform a GET query to receive the item niches', () => {
let res = [{id: 1, warehouseFk: 1, code: '1111'}];
$httpBackend.when('GET', `/item/api/ItemNiches?filter={"where":{},"include":{"relation":"warehouse"}}`).respond(res);
$httpBackend.expectGET(`/item/api/ItemNiches?filter={"where":{},"include":{"relation":"warehouse"}}`);
controller.getNiches();
$httpBackend.flush();
});
it('should perform a GET query to receive the all warehouses', () => {
let res = [
{id: 1, warehouseFk: 1, name: 'warehouse one'},
{id: 2, warehouseFk: 2, name: 'warehouse two'}
];
$httpBackend.when('GET', `/item/api/Warehouses`).respond(res);
$httpBackend.expectGET(`/item/api/Warehouses`);
controller.getWarehouses();
$httpBackend.flush();
});
});
describe('submit()', () => {
it("should return an error message 'The niche must be unique' when the niche code isnt unique", () => {
spyOn(controller.vnApp, 'showMessage').and.callThrough();
controller.niches = [
{warehouseFk: 1, code: 123454, itemFk: 1, id: 1},
{warehouseFk: 1, code: 123454, itemFk: 1}
];
controller.oldNiches = {1: {warehouseFk: 1, id: 1, code: 123454, itemFk: 1}};
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The niche must be unique');
});
it("should perfom a query to delete niches", () => {
controller.oldNiches = {1: {id: 1, warehouseFk: 1, code: '1111'}};
controller.niches = [];
controller.removedNiches = [1];
$httpBackend.when('GET', `/item/api/ItemNiches?filter={"where":{},"include":{"relation":"warehouse"}}`).respond([]);
$httpBackend.expectPOST(`/item/api/ItemNiches/crudItemNiches`).respond('ok!');
controller.submit();
$httpBackend.flush();
});
it("should perfom a query to update niches", () => {
controller.niches = [{id: 1, warehouseFk: 1, code: '2222'}];
controller.oldNiches = {1: {id: 1, warehouseFk: 1, code: '1111'}};
$httpBackend.whenGET(`/item/api/ItemNiches?filter={"where":{},"include":{"relation":"warehouse"}}`).respond([]);
$httpBackend.expectPOST(`/item/api/ItemNiches/crudItemNiches`).respond('ok!');
controller.submit();
$httpBackend.flush();
});
it("should perfom a query to create new niche", () => {
controller.niches = [{warehouseFk: 1, code: 1111, itemFk: 1}];
$httpBackend.whenGET(`/item/api/ItemNiches?filter={"where":{},"include":{"relation":"warehouse"}}`).respond([]);
$httpBackend.expectPOST(`/item/api/ItemNiches/crudItemNiches`).respond('ok!');
controller.submit();
$httpBackend.flush();
});
it("should return a message 'No changes to save' when there are no changes to apply", () => {
spyOn(controller.vnApp, 'showMessage').and.callThrough();
controller.oldNiches = [
{warehouseFk: 1, code: 1, itemFk: 1, id: 1},
{warehouseFk: 2, code: 2, itemFk: 1, id: 2}
];
controller.niches = [];
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No changes to save');
});
});
});
});

View File

@ -4,28 +4,27 @@
<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
<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"
>
data ="$ctrl.sharedData">
</vn-searchbar>
<vn-one vn-horizontal>
<vn-one></vn-one>
<vn-autocomplete vn-two
<vn-autocomplete
vn-two
initial-value="$ctrl.filter.warehouseFk"
show-field="name"
value-field="id"
field="$ctrl.filter.warehouseFk"
url="/production/api/Warehouses/production"
on-change = "$ctrl.onChangeWareHouse(item)"
label="Store"
></vn-autocomplete>
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>

View File

@ -12,15 +12,14 @@
<vn-title>Create Route</vn-title>
<vn-horizontal>
<vn-date-picker vn-one label="Date" model="$ctrl.delivery.date"></vn-date-picker>
<vn-autocomplete vn-one
<vn-autocomplete
vn-one
label="Agency"
url="/route/api/Agencies"
field="$ctrl.delivery.agency"
>
field="$ctrl.delivery.agency">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
field="$ctrl.delivery.driver"
url="/route/api/Vehicles/activeDrivers"
@ -31,10 +30,9 @@
url="/route/api/Vehicles/comboVehicles"
label="Vehicle"
order="tradeMark ASC"
filter="{where: {isActive:1, warehouseFk:1}}"
></vn-autocomplete>
filter="{where: {isActive:1, warehouseFk:1}}">
</vn-autocomplete>
</vn-horizontal>
</vn-vertical>
</vn-card>
<vn-button-bar>

View File

@ -7,8 +7,8 @@
label="Zone"
field="$ctrl.filter.zone"
url="/route/api/Zones"
order="printingOrder ASC"
></vn-autocomplete>
order="printingOrder ASC">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one label="Postcode" model="$ctrl.filter.postcode"></vn-textfield>

View File

@ -17,14 +17,6 @@
"type": "string",
"required": true
}
},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
}
}

View File

@ -0,0 +1,36 @@
module.exports = Self => {
Self.remoteMethod('crudItemNiches', {
description: 'create, update or delete niches',
accessType: 'WRITE',
accepts: [
{
arg: 'niches',
type: 'Object',
require: true,
description: 'object with niches to create, update or delete, Example: {create: [], update: [], delete: []}',
http: {source: 'body'}
}
],
http: {
path: `/crudItemNiches`,
verb: 'post'
}
});
Self.crudItemNiches = niches => {
let promises = [];
if (niches.delete && niches.delete.length) {
promises.push(Self.destroyAll({id: {inq: niches.delete}}));
}
if (niches.create.length) {
promises.push(Self.create(niches.create));
}
if (niches.update.length) {
niches.update.forEach(niche => {
promises.push(Self.upsert(niche));
});
}
return Promise.all(promises);
};
};

View File

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

@ -84,6 +84,11 @@
"type": "hasMany",
"model": "ItemBarcode",
"foreignKey": "itemFk"
},
"itemNiche": {
"type": "hasMany",
"model": "ItemNiche",
"foreignKey": "itemFk"
}
}
}

View File

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

View File

@ -0,0 +1,28 @@
{
"name": "ItemNiche",
"base": "VnModel",
"options": {
"mysql": {
"table": "itemPlacement",
"database": "vn"
}
},
"properties": {
"code": {
"type": "String",
"required": true
}
},
"relations": {
"item": {
"type": "belongsTo",
"model": "Item",
"foreignKey": "itemFk"
},
"warehouse": {
"type": "belongsTo",
"model": "Warehouse",
"foreignKey": "warehouseFk"
}
}
}

View File

@ -0,0 +1,29 @@
{
"name": "Warehouse",
"base": "VnModel",
"options": {
"mysql": {
"table": "warehouse",
"database": "vn"
}
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"name": {
"type": "String",
"required": true
}
},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
}

View File

@ -35,6 +35,9 @@
"Tag": {
"dataSource": "vn"
},
"ItemNiche": {
"dataSource": "vn"
},
"ItemLog": {
"dataSource": "vn"
},
@ -47,6 +50,9 @@
"ItemPlacement": {
"dataSource": "vn"
},
"Warehouse": {
"dataSource": "vn"
},
"Specie": {
"dataSource": "edi"
},