4002-item.itemType #974

Merged
carlosjr merged 10 commits from 4002-item.itemType into dev 2022-05-23 12:31:24 +00:00
30 changed files with 654 additions and 17 deletions

View File

@ -0,0 +1,4 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
vicent marked this conversation as resolved Outdated

you are inserting in the same table using the INSERT INTO twice...

you can wrap both in the same insert ie:

INSERT INTO salix.ACL(model, property, accessType, permission, principalType, principalId)
VALUES
('ItemType', '', 'READ', 'ALLOW', 'ROLE', 'employee'),
('ItemType', '
', 'WRITE', 'ALLOW', 'ROLE', 'buyer');

you are inserting in the same table using the INSERT INTO twice... you can wrap both in the same insert ie: INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('ItemType', '*', 'READ', 'ALLOW', 'ROLE', 'employee'), ('ItemType', '*', 'WRITE', 'ALLOW', 'ROLE', 'buyer');
VALUES
('ItemType', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
('ItemType', '*', 'WRITE', 'ALLOW', 'ROLE', 'buyer');

View File

@ -744,14 +744,19 @@ INSERT INTO `vn`.`itemCategory`(`id`, `name`, `display`, `color`, `icon`, `code`
(7, 'Accessories', 1, NULL, 'icon-accessory', 'accessory'),
(8, 'Fruit', 1, NULL, 'icon-fruit', 'fruit');
INSERT INTO `vn`.`itemType`(`id`, `code`, `name`, `categoryFk`, `warehouseFk`, `life`,`workerFk`, `isPackaging`)
INSERT INTO `vn`.`temperature`(`code`, `name`, `description`)
VALUES
(1, 'CRI', 'Crisantemo', 2, 1, 31, 35, 0),
(2, 'ITG', 'Anthurium', 1, 1, 31, 35, 0),
(3, 'WPN', 'Paniculata', 2, 1, 31, 35, 0),
(4, 'PRT', 'Delivery ports', 3, 1, NULL, 35, 1),
(5, 'CON', 'Container', 3, 1, NULL, 35, 1),
(6, 'ALS', 'Alstroemeria', 1, 1, 31, 16, 0);
('warm', 'Warm', 'Warm'),
('cool', 'Cool', 'Cool');
INSERT INTO `vn`.`itemType`(`id`, `code`, `name`, `categoryFk`, `warehouseFk`, `life`,`workerFk`, `isPackaging`, `temperatureFk`)
VALUES
(1, 'CRI', 'Crisantemo', 2, 1, 31, 35, 0, 'cool'),
(2, 'ITG', 'Anthurium', 1, 1, 31, 35, 0, 'cool'),
(3, 'WPN', 'Paniculata', 2, 1, 31, 35, 0, 'cool'),
(4, 'PRT', 'Delivery ports', 3, 1, NULL, 35, 1, 'warm'),
(5, 'CON', 'Container', 3, 1, NULL, 35, 1, 'warm'),
(6, 'ALS', 'Alstroemeria', 1, 1, 31, 16, 0, 'warm');
INSERT INTO `vn`.`ink`(`id`, `name`, `picture`, `showOrder`, `hex`)
VALUES
@ -2290,11 +2295,6 @@ INSERT INTO `vn`.`workerTimeControlParams` (`id`, `dayBreak`, `weekBreak`, `week
(1, 43200, 129600, 734400, 43200, 50400, 259200, 1296000, 36000);
INSERT IGNORE INTO `vn`.`greugeConfig` (`id`, `freightPickUpPrice`) VALUES ('1', '11');
INSERT INTO `vn`.`temperature`(`code`, `name`, `description`)
VALUES
('warm', 'Warm', 'Warm'),
('cool', 'Cool', 'Cool');
INSERT INTO `vn`.`thermograph`(`id`, `model`)
VALUES

View File

@ -6,7 +6,6 @@ class Controller extends Component {
this._role = value;
this.$.summary = null;
if (!value) return;
this.$http.get(`Roles/${value.id}`)
.then(res => this.$.summary = res.data);
}

View File

@ -21,8 +21,11 @@
"life": {
vicent marked this conversation as resolved Outdated

relation still missing

relation still missing
"type": "number"
},
"isPackaging": {
"type": "boolean"
"promo": {
vicent marked this conversation as resolved Outdated

it's important to add the relations to any foreign key instead of a siemple field (just the foreign key)

it's important to add the relations to any foreign key instead of a siemple field (just the foreign key)
"type": "number"
},
"isUnconventionalSize": {
"type": "number"
}
},
vicent marked this conversation as resolved Outdated

relation still missing

relation still missing
"relations": {
@ -40,6 +43,16 @@
"type": "belongsTo",
"model": "ItemCategory",
"foreignKey": "categoryFk"
},
"itemPackingType": {
"type": "belongsTo",
"model": "ItemPackingType",
"foreignKey": "itemPackingTypeFk"
},
"temperature": {
"type": "belongsTo",
"model": "Temperature",
"foreignKey": "temperatureFk"
}
},
"acls": [

View File

@ -23,4 +23,4 @@ import './waste/index/';
import './waste/detail';
import './fixed-price';
import './fixed-price-search-panel';
import './item-type';

View File

@ -0,0 +1,62 @@
<vn-watcher
vn-id="watcher"
url="ItemTypes"
data="$ctrl.itemType"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="Code"
ng-model="$ctrl.itemType.code"
rule
vn-focus>
</vn-textfield>
<vn-textfield
label="Name"
ng-model="$ctrl.itemType.name"
rule>
</vn-textfield>
<vn-autocomplete
label="Worker"
ng-model="$ctrl.itemType.workerFk"
url="Workers"
show-field="firstName"
value-field="id"
rule>
</vn-autocomplete>
<vn-autocomplete
label="Category"
ng-model="$ctrl.itemType.categoryFk"
url="ItemCategories"
show-field="name"
value-field="id"
rule>
</vn-autocomplete>
<vn-autocomplete
label="Temperature"
ng-model="$ctrl.itemType.temperatureFk"
url="Temperatures"
show-field="name"
value-field="code"
rule>
</vn-autocomplete>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -0,0 +1,12 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {}
ngModule.component('vnItemTypeBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
itemType: '<'
}
});

View File

@ -0,0 +1,5 @@
<vn-portal slot="menu">
<vn-item-type-descriptor item-type="$ctrl.itemType"></vn-item-type-descriptor>
<vn-left-menu source="itemType"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -0,0 +1,23 @@
import ngModule from '../../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
const filter = {
include: [
{relation: 'worker'},
{relation: 'category'},
{relation: 'itemPackingType'},
{relation: 'temperature'}
]
};
this.$http.get(`ItemTypes/${this.$params.id}`, {filter})
.then(res => this.itemType = res.data);
}
}
ngModule.vnComponent('vnItemTypeCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,27 @@
import './index';
describe('component vnItemTypeCard', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnItemTypeCard', {$element: null});
}));
describe('reload()', () => {
it('should reload the controller data', () => {
controller.$params.id = 1;
const itemType = {id: 1};
vicent marked this conversation as resolved Outdated

"foo" doesn't give context, therefore not making code maintenance easier.

"foo" doesn't give context, therefore not making code maintenance easier.
$httpBackend.expectGET('ItemTypes/1').respond(itemType);
controller.reload();
$httpBackend.flush();
expect(controller.itemType).toEqual(itemType);
});
});
});

View File

@ -0,0 +1,62 @@
<vn-watcher
vn-id="watcher"
url="ItemTypes"
data="$ctrl.itemType"
insert-mode="true"
form="form">
</vn-watcher>
<form
name="form"
vn-http-submit="$ctrl.onSubmit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="Code"
ng-model="$ctrl.itemType.code"
rule
vn-focus>
</vn-textfield>
<vn-textfield
label="Name"
ng-model="$ctrl.itemType.name"
rule>
</vn-textfield>
<vn-autocomplete
label="Worker"
ng-model="$ctrl.itemType.workerFk"
url="Workers"
show-field="firstName"
value-field="id"
rule>
</vn-autocomplete>
<vn-autocomplete
label="Category"
ng-model="$ctrl.itemType.categoryFk"
url="ItemCategories"
show-field="name"
value-field="id"
rule>
</vn-autocomplete>
<vn-autocomplete
label="Temperature"
ng-model="$ctrl.itemType.temperatureFk"
url="Temperatures"
show-field="name"
value-field="code"
rule>
</vn-autocomplete>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="item.itemType">
</vn-button>
</vn-button-bar>
</form>

View File

@ -0,0 +1,15 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onSubmit() {
return this.$.watcher.submit().then(res =>
this.$state.go('item.itemType.card.basicData', {id: res.data.id})
);
}
}
ngModule.component('vnItemTypeCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,34 @@
import './index';
describe('component vnItemTypeCreate', () => {
let $scope;
let $state;
let controller;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, $rootScope, _$state_) => {
$scope = $rootScope.$new();
$state = _$state_;
$scope.watcher = {
submit: () => {
return {
then: callback => {
callback({data: {id: '1234'}});
}
};
}
};
const $element = angular.element('<vn-item-type-create></vn-item-type-create>');
controller = $componentController('vnItemTypeCreate', {$element, $scope});
}));
describe('onSubmit()', () => {
it(`should call submit() on the watcher then expect a callback`, () => {
jest.spyOn($state, 'go');
controller.onSubmit();
expect(controller.$state.go).toHaveBeenCalledWith('item.itemType.card.basicData', {id: '1234'});
});
});
});

View File

@ -0,0 +1,25 @@
<vn-descriptor-content
module="item"
base-state="item.itemType"
description="$ctrl.itemType.code">
<slot-body>
<div class="attributes">
<vn-label-value
label="Code"
value="{{$ctrl.itemType.code}}">
</vn-label-value>
<vn-label-value
label="Name"
value="{{$ctrl.itemType.name}}">
</vn-label-value>
<vn-label-value
label="Worker"
value="{{$ctrl.itemType.worker.firstName}} {{$ctrl.itemType.worker.lastName}}">
</vn-label-value>
<vn-label-value
label="Category"
value="{{$ctrl.itemType.category.name}}">
</vn-label-value>
</div>
</slot-body>
</vn-descriptor-content>

View File

@ -0,0 +1,20 @@
import ngModule from '../../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get itemType() {
return this.entity;
}
set itemType(value) {
this.entity = value;
}
}
ngModule.component('vnItemTypeDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
itemType: '<'
}
});

View File

@ -0,0 +1,8 @@
import './main';
import './index/';
import './summary';
import './card';
import './descriptor';
import './create';
import './basic-data';
import './search-panel';

View File

@ -0,0 +1,43 @@
<vn-crud-model
vn-id="model"
auto-load="true"
url="ItemTypes"
data="itemTypes"
order="name">
</vn-crud-model>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<div class="vn-list separated">
<a
ng-repeat="itemType in itemTypes track by itemType.id"
ui-sref="item.itemType.card.summary(::{id: itemType.id})"
ui-sref-opts="{inherit: false}"
translate-attr="{title: 'View itemType'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::itemType.code}}</h6>
<div>{{::itemType.name}}</div>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
vn-click-stop="$ctrl.preview(itemType)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-item-section>
</a>
</div>
</vn-card>
</vn-data-viewer>
<vn-popup vn-id="summary">
<vn-item-type-summary item-type="$ctrl.selectedItemType"></vn-item-type-summary>
</vn-popup>
<a ui-sref="item.itemType.create"
vn-tooltip="New itemType"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -0,0 +1,14 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
preview(itemType) {
this.selectedItemType = itemType;
this.$.summary.show();
}
}
ngModule.component('vnItemTypeIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,34 @@
import './index';
describe('Item', () => {
describe('Component vnItemTypeIndex', () => {
let controller;
let $window;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, _$window_) => {
$window = _$window_;
const $element = angular.element('<vn-item-type-summary></vn-item-type-summary>');
controller = $componentController('vnItemTypeIndex', {$element});
}));
describe('preview()', () => {
it('should show the dialog summary', () => {
controller.$.summary = {show: () => {}};
jest.spyOn(controller.$.summary, 'show');
const itemType = {id: 1};
const event = new MouseEvent('click', {
view: $window,
bubbles: true,
cancelable: true
});
controller.preview(event, itemType);
expect(controller.$.summary.show).toHaveBeenCalledWith();
});
});
});
});

View File

@ -0,0 +1,2 @@
Item Type: Familia
New itemType: Nueva familia

View File

@ -0,0 +1,18 @@
<vn-crud-model
vn-id="model"
url="ItemTypes"
filter="::$ctrl.filter"
limit="20">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
info="Search itemType by id, name or code"
panel="vn-item-type-search-panel"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)"
base-state="item.itemType">
</vn-searchbar>
</vn-portal>
<ui-view>
<vn-item-type-index></vn-item-type-index>
</ui-view>

View File

@ -0,0 +1,24 @@
import ngModule from '../../module';
import ModuleMain from 'salix/components/module-main';
export default class ItemType extends ModuleMain {
exprBuilder(param, value) {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {id: value}
: {or: [
{name: {like: `%${value}%`}},
{code: {like: `%${value}%`}}
]};
case 'name':
case 'code':
return {[param]: {like: `%${value}%`}};
}
}
}
ngModule.vnComponent('vnItemType', {
controller: ItemType,
template: require('./index.html')
});

View File

@ -0,0 +1,31 @@
import './index';
describe('Item', () => {
describe('Component vnItemType', () => {
let controller;
beforeEach(ngModule('item'));
beforeEach(inject($componentController => {
const $element = angular.element('<vn-item-type></vn-item-type>');
controller = $componentController('vnItemType', {$element});
}));
describe('exprBuilder()', () => {
it('should return a filter based on a search by id', () => {
const filter = controller.exprBuilder('search', '123');
expect(filter).toEqual({id: '123'});
});
it('should return a filter based on a search by name or code', () => {
const filter = controller.exprBuilder('search', 'Alstroemeria');
expect(filter).toEqual({or: [
{name: {like: '%Alstroemeria%'}},
{code: {like: '%Alstroemeria%'}},
]});
});
});
});
});

View File

@ -0,0 +1 @@
Search itemType by id, name or code: Buscar familia por id, nombre o código

View File

@ -0,0 +1,22 @@
<div class="search-panel">
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield
vicent marked this conversation as resolved
Review

this textfield may want to be the focus

this textfield may want to be the focus
vn-one
label="Name"
ng-model="filter.name"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Code"
ng-model="filter.code">
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -0,0 +1,7 @@
import ngModule from '../../module';
import SearchPanel from 'core/components/searchbar/search-panel';
ngModule.component('vnItemTypeSearchPanel', {
template: require('./index.html'),
controller: SearchPanel
});

View File

@ -0,0 +1,50 @@
<vn-card class="summary">
<h5>
<span>{{summary.id}} - {{summary.name}} - {{summary.worker.firstName}} {{summary.worker.lastName}}</span>
</h5>
<vn-horizontal class="vn-pa-md">
<vn-one>
<h4 translate>Basic data</h4>
<vn-label-value
label="Id"
value="{{summary.id}}">
</vn-label-value>
<vn-label-value
label="Code"
value="{{summary.code}}">
</vn-label-value>
<vn-label-value
label="Name"
value="{{summary.name}}">
</vn-label-value>
<vn-label-value
label="Worker"
value="{{summary.worker.firstName}} {{summary.worker.lastName}}">
</vn-label-value>
<vn-label-value
label="Category"
value="{{summary.category.name}}">
</vn-label-value>
<vn-label-value
label="Temperature"
value="{{summary.temperature.name}}">
</vn-label-value>
<vn-label-value
label="Life"
value="{{summary.life}}">
</vn-label-value>
<vn-label-value
label="Promo"
value="{{summary.promo}}">
</vn-label-value>
<vn-label-value
label="Item packing type"
value="{{summary.itemPackingType.description}}">
</vn-label-value>
<vn-label-value
label="Is unconventional size"
value="{{summary.isUnconventionalSize}}">
</vn-label-value>
</vn-one>
</vn-horizontal>
</vn-card>

View File

@ -0,0 +1,33 @@
import ngModule from '../../module';
import Component from 'core/lib/component';
class Controller extends Component {
set itemType(value) {
this._itemType = value;
this.$.summary = null;
if (!value) return;
const filter = {
include: [
{relation: 'worker'},
{relation: 'category'},
{relation: 'itemPackingType'},
{relation: 'temperature'}
]
};
this.$http.get(`ItemTypes/${value.id}`, {filter})
.then(res => this.$.summary = res.data);
}
get itemType() {
return this._itemType;
}
}
ngModule.component('vnItemTypeSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
itemType: '<'
}
});

View File

@ -0,0 +1,4 @@
Life: Vida
Promo: Promoción
Item packing type: Tipo de embalaje
Is unconventional size: Es de tamaño poco convencional

View File

@ -9,7 +9,8 @@
{"state": "item.index", "icon": "icon-item"},
{"state": "item.request", "icon": "icon-buyrequest"},
{"state": "item.waste.index", "icon": "icon-claims"},
{"state": "item.fixedPrice", "icon": "icon-fixedPrice"}
{"state": "item.fixedPrice", "icon": "icon-fixedPrice"},
{"state": "item.itemType", "icon": "contact_support"}
],
"card": [
{"state": "item.card.basicData", "icon": "settings"},
@ -20,6 +21,9 @@
{"state": "item.card.diary", "icon": "icon-transaction"},
{"state": "item.card.last-entries", "icon": "icon-regentry"},
{"state": "item.card.log", "icon": "history"}
],
"itemType": [
{"state": "item.itemType.card.basicData", "icon": "settings"}
]
},
"keybindings": [
@ -169,6 +173,47 @@
"component": "vn-fixed-price",
"description": "Fixed prices",
"acl": ["buyer"]
},
{
"url" : "/item-type?q",
"state": "item.itemType",
"component": "vn-item-type",
"description": "Item Type",
"acl": ["buyer"]
},
{
"url": "/create",
"state": "item.itemType.create",
"component": "vn-item-type-create",
"description": "New itemType",
"acl": ["buyer"]
},
{
"url": "/:id",
"state": "item.itemType.card",
"component": "vn-item-type-card",
"abstract": true,
"description": "Detail"
},
{
"url": "/summary",
"state": "item.itemType.card.summary",
"component": "vn-item-type-summary",
"description": "Summary",
"params": {
"item-type": "$ctrl.itemType"
},
"acl": ["buyer"]
},
{
"url": "/basic-data",
"state": "item.itemType.card.basicData",
"component": "vn-item-type-basic-data",
"description": "Basic data",
"params": {
"item-type": "$ctrl.itemType"
},
"acl": ["buyer"]
}
]
}