item-barcode functionality plus front end tests
This commit is contained in:
parent
c99f22430b
commit
61cd4acca5
|
@ -1,5 +1,32 @@
|
||||||
<vn-card>
|
<form name="form" ng-submit="$ctrl.submit()">
|
||||||
<vn-vertical pad-large>
|
<vn-card>
|
||||||
<vn-title>Item barcode</vn-title>
|
<vn-vertical pad-large>
|
||||||
</vn-vertical>
|
<vn-one margin-medium-top>
|
||||||
</vn-card>
|
<vn-title>Item Barcodes</vn-title>
|
||||||
|
<mg-ajax path="/item/api/ItemBarcodes" options="mgIndex as barcodes"></mg-ajax>
|
||||||
|
<vn-horizontal ng-repeat="barcode in $ctrl.barcodes track by $index">
|
||||||
|
<vn-textfield vn-three label="code" model="barcode.code"></vn-textfield>
|
||||||
|
<vn-one pad-medium-top>
|
||||||
|
<vn-icon
|
||||||
|
pointer
|
||||||
|
medium-grey
|
||||||
|
icon="remove_circle_outline"
|
||||||
|
ng-click="$ctrl.removeBarcode($index)">
|
||||||
|
</vn-icon>
|
||||||
|
<vn-icon
|
||||||
|
pointer
|
||||||
|
margin-medium-left
|
||||||
|
orange
|
||||||
|
icon="add_circle"
|
||||||
|
ng-if = "barcode.showAddIcon"
|
||||||
|
ng-click="$ctrl.addBarcode()"
|
||||||
|
></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>
|
||||||
|
|
|
@ -1,5 +1,127 @@
|
||||||
import ngModule from '../module';
|
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.barcodes = [];
|
||||||
|
this.removedBarcodes = [];
|
||||||
|
this.oldBarcodes = {};
|
||||||
|
}
|
||||||
|
_setIconAdd() {
|
||||||
|
if (this.barcodes.length) {
|
||||||
|
this.barcodes.map(element => {
|
||||||
|
element.showAddIcon = false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
this.barcodes[this.barcodes.length - 1].showAddIcon = true;
|
||||||
|
} else {
|
||||||
|
this.addBarcode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_setDirtyForm() {
|
||||||
|
if (this.$scope.form) {
|
||||||
|
this.$scope.form.$setDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_unsetDirtyForm() {
|
||||||
|
if (this.$scope.form) {
|
||||||
|
this.$scope.form.$setPristine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_equalBarcodes(oldBarcode, newBarcode) {
|
||||||
|
return oldBarcode.id === newBarcode.id && oldBarcode.code === newBarcode.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
addBarcode() {
|
||||||
|
this.barcodes.push({code: null, itemFk: this.$state.params.id, showAddIcon: true});
|
||||||
|
this._setIconAdd();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeBarcode(index) {
|
||||||
|
let item = this.barcodes[index];
|
||||||
|
if (item) {
|
||||||
|
this.barcodes.splice(index, 1);
|
||||||
|
this._setIconAdd();
|
||||||
|
if (item.id) {
|
||||||
|
this.removedBarcodes.push(item.id);
|
||||||
|
this._setDirtyForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submit() {
|
||||||
|
let codes = [];
|
||||||
|
let repeatedBarcodes = false;
|
||||||
|
let canSubmit;
|
||||||
|
let barcodesObj = {
|
||||||
|
delete: this.removedBarcodes,
|
||||||
|
create: [],
|
||||||
|
update: []
|
||||||
|
};
|
||||||
|
for (let i = 0; i < this.barcodes.length; i++) {
|
||||||
|
let barcode = this.barcodes[i];
|
||||||
|
let isNewBarcode = barcode.id === undefined;
|
||||||
|
|
||||||
|
if (barcode.code && codes.indexOf(barcode.code) !== -1) {
|
||||||
|
repeatedBarcodes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (barcode.code) codes.push(barcode.code);
|
||||||
|
|
||||||
|
if (isNewBarcode && barcode.code) {
|
||||||
|
barcodesObj.create.push(barcode);
|
||||||
|
} else if (!isNewBarcode && !this._equalBarcodes(this.oldBarcodes[barcode.id], barcode)) {
|
||||||
|
barcodesObj.update.push(barcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repeatedBarcodes) {
|
||||||
|
return this.vnApp.showMessage(this.$translate.instant('The barcode must be unique'));
|
||||||
|
}
|
||||||
|
|
||||||
|
canSubmit = barcodesObj.update.length > 0 || barcodesObj.create.length > 0 || barcodesObj.delete.length > 0;
|
||||||
|
|
||||||
|
if (canSubmit) {
|
||||||
|
return this.$http.post(`/item/api/ItemBarcodes/crudItemBarcodes`, barcodesObj).then(() => {
|
||||||
|
this.getBarcodes();
|
||||||
|
this._unsetDirtyForm();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.vnApp.showMessage(this.$translate.instant('No changes to save'));
|
||||||
|
}
|
||||||
|
|
||||||
|
setOldBarcodes(response) {
|
||||||
|
this._setIconAdd();
|
||||||
|
response.data.forEach(barcode => {
|
||||||
|
this.oldBarcodes[barcode.id] = Object.assign({}, barcode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getBarcodes() {
|
||||||
|
let filter = {
|
||||||
|
where: {itemFk: this.$state.params.id}
|
||||||
|
};
|
||||||
|
this.$http.get(`/item/api/ItemBarcodes?filter=${JSON.stringify(filter)}`).then(response => {
|
||||||
|
this.barcodes = response.data;
|
||||||
|
this.setOldBarcodes(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$onInit() {
|
||||||
|
this.getBarcodes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.$inject = ['$state', '$scope', '$http', '$q', '$translate', 'vnApp'];
|
||||||
|
|
||||||
ngModule.component('vnItemBarcode', {
|
ngModule.component('vnItemBarcode', {
|
||||||
template: require('./item-barcode.html')
|
template: require('./item-barcode.html'),
|
||||||
|
controller: Controller
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
import './item-barcode.js';
|
||||||
|
|
||||||
|
describe('Item', () => {
|
||||||
|
describe('Component vnItemBarcode', () => {
|
||||||
|
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_;
|
||||||
|
$state.params.id = '1';
|
||||||
|
controller = $componentController('vnItemBarcode', {$state: $state});
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('add / remove barcode()', () => {
|
||||||
|
it('should add one empty barcode into controller barcodes collection and call _setIconAdd()', () => {
|
||||||
|
controller.barcodes = [];
|
||||||
|
spyOn(controller, '_setIconAdd').and.callThrough();
|
||||||
|
controller.addBarcode();
|
||||||
|
|
||||||
|
expect(controller._setIconAdd).toHaveBeenCalledWith();
|
||||||
|
expect(controller.barcodes.length).toEqual(1);
|
||||||
|
expect(controller.barcodes[0].id).toBe(undefined);
|
||||||
|
expect(controller.barcodes[0].showAddIcon).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove a barcode that occupies the position in the index given and call _setIconAdd()', () => {
|
||||||
|
let index = 2;
|
||||||
|
controller.barcodes = [
|
||||||
|
{id: 1, code: '1111', showAddIcon: false},
|
||||||
|
{id: 2, code: '2222', showAddIcon: false},
|
||||||
|
{id: 3, code: '3333', showAddIcon: true}
|
||||||
|
];
|
||||||
|
|
||||||
|
spyOn(controller, '_setIconAdd').and.callThrough();
|
||||||
|
|
||||||
|
controller.removeBarcode(index);
|
||||||
|
|
||||||
|
expect(controller._setIconAdd).toHaveBeenCalledWith();
|
||||||
|
expect(controller.barcodes.length).toEqual(2);
|
||||||
|
expect(controller.barcodes[0].showAddIcon).toBeFalsy();
|
||||||
|
expect(controller.barcodes[1].showAddIcon).toBeTruthy();
|
||||||
|
expect(controller.barcodes[index]).toBe(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('_equalBarcodes()', () => {
|
||||||
|
it('should return true if two barcodes are equals independent of control attributes', () => {
|
||||||
|
let code1 = {id: 1, code: '1111', showAddIcon: true};
|
||||||
|
let code2 = {id: 1, code: '1111', showAddIcon: false};
|
||||||
|
let equals = controller._equalBarcodes(code2, code1);
|
||||||
|
|
||||||
|
expect(equals).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if two barcodes aint equals independent of control attributes', () => {
|
||||||
|
let code1 = {id: 1, code: '1111', showAddIcon: true};
|
||||||
|
let code2 = {id: 1, code: '2222', showAddIcon: true};
|
||||||
|
let equals = controller._equalBarcodes(code2, code1);
|
||||||
|
|
||||||
|
expect(equals).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getBarcodes()', () => {
|
||||||
|
it('should perform a GET query to receive the item barcodes', () => {
|
||||||
|
let filter = {
|
||||||
|
where: {itemFk: '1'}
|
||||||
|
};
|
||||||
|
let res = [{id: 1, code: '1111'}];
|
||||||
|
$httpBackend.when('GET', `/item/api/ItemBarcodes?filter=${JSON.stringify(filter)}`).respond(res);
|
||||||
|
$httpBackend.expectGET(`/item/api/ItemBarcodes?filter=${JSON.stringify(filter)}`);
|
||||||
|
controller.getBarcodes();
|
||||||
|
$httpBackend.flush();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('submit()', () => {
|
||||||
|
it("should return an error message 'The barcode must be unique' when the code isnt unique", () => {
|
||||||
|
spyOn(controller.vnApp, 'showMessage').and.callThrough();
|
||||||
|
controller.barcodes = [
|
||||||
|
{code: 123454, itemFk: 1, id: 1},
|
||||||
|
{code: 123454, itemFk: 1}
|
||||||
|
];
|
||||||
|
controller.oldBarcodes = {1: {id: 1, code: 123454, itemFk: 1}};
|
||||||
|
controller.submit();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The barcode must be unique');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should perfom a query to delete barcodes", () => {
|
||||||
|
controller.removedBarcodes = [1, 2, 3];
|
||||||
|
|
||||||
|
$httpBackend.when('GET', `/item/api/ItemBarcodes?filter={"where":{"itemFk":"1"}}`).respond([{code: 123454, itemFk: 1, id: 1}]);
|
||||||
|
$httpBackend.when('POST', `/item/api/ItemBarcodes/crudItemBarcodes`).respond('ok!');
|
||||||
|
$httpBackend.expectPOST(`/item/api/ItemBarcodes/crudItemBarcodes`);
|
||||||
|
controller.submit();
|
||||||
|
$httpBackend.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should perfom a query to update barcodes", () => {
|
||||||
|
controller.barcodes = [{code: 2222, itemFk: 1, id: 1}];
|
||||||
|
controller.oldBarcodes = {1: {id: 1, code: 1111, itemFk: 1}};
|
||||||
|
|
||||||
|
$httpBackend.when('GET', `/item/api/ItemBarcodes?filter={"where":{"itemFk":"1"}}`).respond([{}]);
|
||||||
|
$httpBackend.when('POST', `/item/api/ItemBarcodes/crudItemBarcodes`).respond('ok!');
|
||||||
|
$httpBackend.expectPOST(`/item/api/ItemBarcodes/crudItemBarcodes`);
|
||||||
|
controller.submit();
|
||||||
|
$httpBackend.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should perfom a query to create new barcode", () => {
|
||||||
|
controller.barcodes = [{code: 1111, itemFk: 1}];
|
||||||
|
|
||||||
|
$httpBackend.when('GET', `/item/api/ItemBarcodes?filter={"where":{"itemFk":"1"}}`).respond([{}]);
|
||||||
|
$httpBackend.when('POST', `/item/api/ItemBarcodes/crudItemBarcodes`).respond('ok!');
|
||||||
|
$httpBackend.expectPOST(`/item/api/ItemBarcodes/crudItemBarcodes`);
|
||||||
|
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.oldBarcodes = [
|
||||||
|
{code: 1, itemFk: 1, id: 1},
|
||||||
|
{code: 2, itemFk: 1, id: 2}
|
||||||
|
];
|
||||||
|
controller.barcodes = [];
|
||||||
|
controller.submit();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No changes to save');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,36 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('crudItemBarcodes', {
|
||||||
|
description: 'create, update or delete barcodes',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'barcodes',
|
||||||
|
type: 'Object',
|
||||||
|
require: true,
|
||||||
|
description: 'object with barcodes to create, update or delete, Example: {create: [], update: [], delete: []}',
|
||||||
|
http: {source: 'body'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/crudItemBarcodes`,
|
||||||
|
verb: 'post'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.crudItemBarcodes = barcodes => {
|
||||||
|
let promises = [];
|
||||||
|
|
||||||
|
if (barcodes.delete && barcodes.delete.length) {
|
||||||
|
promises.push(Self.destroyAll({id: {inq: barcodes.delete}}));
|
||||||
|
}
|
||||||
|
if (barcodes.create.length) {
|
||||||
|
promises.push(Self.create(barcodes.create));
|
||||||
|
}
|
||||||
|
if (barcodes.update.length) {
|
||||||
|
barcodes.update.forEach(barcode => {
|
||||||
|
promises.push(Self.upsert(barcode));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.all(promises);
|
||||||
|
};
|
||||||
|
};
|
|
@ -84,6 +84,11 @@
|
||||||
"type": "hasMany",
|
"type": "hasMany",
|
||||||
"model": "ItemTag",
|
"model": "ItemTag",
|
||||||
"foreignKey": "itemFk"
|
"foreignKey": "itemFk"
|
||||||
|
},
|
||||||
|
"itemBarcode": {
|
||||||
|
"type": "hasMany",
|
||||||
|
"model": "ItemBarcode",
|
||||||
|
"foreignKey": "itemFk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = function(Self) {
|
||||||
|
require('../methods/item/crudItemBarcodes.js')(Self);
|
||||||
|
};
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"name": "ItemBarcode",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "itemBarcode",
|
||||||
|
"database": "vn"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "Number",
|
||||||
|
"id": true,
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"code": {
|
||||||
|
"type": "String",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"item": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Item",
|
||||||
|
"foreignKey": "itemFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,5 +37,8 @@
|
||||||
},
|
},
|
||||||
"ItemLog": {
|
"ItemLog": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"ItemBarcode": {
|
||||||
|
"dataSource": "vn"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue