feat: add new module shelving
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Vicent Llopis 2022-05-24 12:50:22 +02:00
parent 99c7ad1c1b
commit f00fb0dc3f
38 changed files with 1006 additions and 0 deletions

View File

@ -18,6 +18,7 @@
"modules/supplier/front/**/*",
"modules/ticket/front/**/*",
"modules/travel/front/**/*",
"modules/shelving/front/**/*",
"modules/worker/front/**/*",
"modules/zone/front/**/*"
]

View File

@ -21,6 +21,7 @@ export default function moduleImport(moduleName) {
case 'entry' : return import('entry/front');
case 'account' : return import('account/front');
case 'supplier' : return import('supplier/front');
case 'shelving' : return import('shelving/front');
case 'monitor' : return import('monitor/front');
}
}

View File

@ -13,6 +13,7 @@ import './modules/route/front/module.js';
import './modules/ticket/front/module.js';
import './modules/travel/front/module.js';
import './modules/worker/front/module.js';
import './modules/shelving/front/module.js';
core.run(vnInterceptor => {
vnInterceptor.setApiPath(null);

View File

@ -0,0 +1,52 @@
module.exports = Self => {
Self.remoteMethod('getSummary', {
description: 'Returns the shelving summary',
accessType: 'READ',
accepts: {
arg: 'code',
type: 'string',
required: true,
description: 'The shelving code',
http: {source: 'path'}
},
returns: {
type: 'object',
root: true
},
http: {
path: `/:code/getSummary`,
verb: 'GET'
}
});
Self.getSummary = async code => {
let filter = {
where: {code: code},
fields: [
'code',
'parkingFk',
'priority',
'userFk',
'isRecyclable'
],
include: [
{
relation: 'parking'
},
{
relation: 'worker',
scope: {
fields: ['id', 'userFk'],
include: {
relation: 'user',
scope: {
fields: ['id', 'nickname']
}
}
}
}
]
};
return Self.app.models.Shelving.findOne(filter);
};
};

View File

@ -0,0 +1,8 @@
{
"Parking": {
"dataSource": "vn"
},
"Shelving": {
"dataSource": "vn"
}
}

View File

@ -0,0 +1,33 @@
{
"name": "Parking",
"base": "VnModel",
"options": {
"mysql": {
"table": "parking"
}
},
"properties": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
},
"column": {
"type": "string",
"required": true
},
"row": {
"type": "string",
"required": true
},
"sectorFk": {
"type": "number"
},
"code": {
"type": "string"
},
"pickingOrder": {
"type": "number"
}
}
}

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/shelving/getSummary')(Self);
};

View File

@ -0,0 +1,48 @@
{
"name": "Shelving",
"base": "VnModel",
"options": {
"mysql": {
"table": "shelving"
}
},
"properties": {
"id": {
"type": "string",
"id": true,
"description": "Identifier",
"mysql": {
"columnName": "code"
}
},
"parkingFk": {
"type": "number",
"required": true
},
"isPrinted": {
"type": "boolean",
"required": true
},
"priority": {
"type": "number"
},
"userFk": {
"type": "number"
},
"isRecyclable": {
"type": "boolean"
}
},
"relations": {
"parking": {
"type": "belongsTo",
"model": "Parking",
"foreignKey": "parkingFk"
},
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "userFk"
}
}
}

View File

@ -0,0 +1,52 @@
<mg-ajax path="Shelvings/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.shelving"
form="form"
save="patch">
</vn-watcher>
<form name="form" ng-submit="watcher.submit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-horizontal>
<vn-textfield
label="Code"
ng-model="$ctrl.shelving.id"
rule
vn-focus>
</vn-textfield>
<vn-autocomplete
vn-one
url="Parkings"
label="Parking"
show-field="id"
value-field="id"
ng-model="$ctrl.shelving.parkingFk">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
label="Priority"
ng-model="$ctrl.shelving.priority"
rule>
</vn-textfield>
<vn-check
label="Recyclable"
ng-model="$ctrl.shelving.isRecyclable">
</vn-check>
</vn-horizontal>
</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,10 @@
import ngModule from '../module';
import Section from 'salix/components/section';
ngModule.vnComponent('vnShelvingBasicData', {
template: require('./index.html'),
controller: Section,
bindings: {
shelving: '<'
}
});

View File

@ -0,0 +1,5 @@
Notes: Notas
Active: Activo
Verified: Verificado
PayMethodChecked: Método de pago validado
Responsible for approving invoices: Responsable de aprobar las facturas

View File

@ -0,0 +1,8 @@
<vn-portal slot="menu">
<vn-shelving-descriptor
shelving="$ctrl.shelving"
on-change="$ctrl.reload()">
</vn-shelving-descriptor>
<vn-left-menu source="card"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -0,0 +1,29 @@
import ngModule from '../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
const filter = {
include: [
{relation: 'worker',
scope: {
fields: ['userFk'],
include: {
relation: 'user',
scope: {
fields: ['nickname']
}
}
}},
{relation: 'parking'}
]
};
this.$http.get(`Shelvings/${this.$params.id}`, {filter})
.then(res => this.shelving = res.data);
}
}
ngModule.vnComponent('vnShelvingCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,30 @@
<vn-watcher
vn-id="watcher"
url="Shelvings"
data="$ctrl.shelving"
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.shelving.id"
rule
vn-focus>
</vn-textfield>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="shelving.index">
</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('shelving.card.basicData', {id: res.data.id})
);
}
}
ngModule.vnComponent('vnShelvingCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,122 @@
import './index';
describe('Client', () => {
describe('Component vnClientCreate', () => {
let $scope;
let $state;
let controller;
beforeEach(ngModule('client'));
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-client-create></vn-client-create>');
controller = $componentController('vnClientCreate', {$element, $scope});
}));
it('should define and set scope, state and client properties', () => {
expect(controller.$).toBe($scope);
expect(controller.$state).toBe($state);
expect(controller.client.active).toBe(true);
});
describe('onSubmit()', () => {
it(`should call submit() on the watcher then expect a callback`, () => {
jest.spyOn($state, 'go');
controller.onSubmit();
expect(controller.$state.go).toHaveBeenCalledWith('client.card.basicData', {id: '1234'});
});
});
describe('province() setter', () => {
it(`should set countryFk property`, () => {
controller.client.countryFk = null;
controller.province = {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
};
expect(controller.client.countryFk).toEqual(2);
});
});
describe('town() setter', () => {
it(`should set provinceFk property`, () => {
controller.town = {
provinceFk: 1,
code: 46001,
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
},
postcodes: []
};
expect(controller.client.provinceFk).toEqual(1);
});
it(`should set provinceFk property and fill the postalCode if there's just one`, () => {
controller.town = {
provinceFk: 1,
code: 46001,
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
},
postcodes: [{code: '46001'}]
};
expect(controller.client.provinceFk).toEqual(1);
expect(controller.client.postcode).toEqual('46001');
});
});
describe('postcode() setter', () => {
it(`should set the town, provinceFk and contryFk properties`, () => {
controller.postcode = {
townFk: 1,
code: 46001,
town: {
id: 1,
name: 'New York',
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
}
}
};
expect(controller.client.city).toEqual('New York');
expect(controller.client.provinceFk).toEqual(1);
expect(controller.client.countryFk).toEqual(2);
});
});
});
});

View File

@ -0,0 +1,11 @@
Name: Nombre
Tax number: NIF/CIF
Business name: Razón social
Web user: Usuario Web
Email: E-mail
Create and edit: Crear y editar
You can save multiple emails: >-
Puede guardar varios correos electrónicos encadenándolos mediante comas
sin espacios, ejemplo: user@dominio.com, user2@dominio.com siendo el primer
correo electrónico el principal
The type of business must be filled in basic data: El tipo de negocio debe estar rellenado en datos básicos

View File

@ -0,0 +1,45 @@
<vn-descriptor-content
module="shelving"
description="$ctrl.shelving.id"
summary="$ctrl.$.summary">
<slot-menu>
<vn-item
ng-click="deleteShelving.show()"
name="deleteShelving"
translate>
Delete
</vn-item>
</slot-menu>
<slot-body>
<div class="attributes">
<vn-label-value
label="Code"
value="{{$ctrl.shelving.id}}">
</vn-label-value>
<vn-label-value
label="Parking"
value="{{$ctrl.shelving.parking.code}}">
</vn-label-value>
<vn-label-value
label="Worker">
<span
ng-click="workerDescriptor.show($event, $ctrl.shelving.userFk)"
class="link">
{{::$ctrl.shelving.worker.user.nickname}}
</span>
</vn-label-value>
</div>
</slot-body>
</vn-descriptor-content>
<vn-confirm
vn-id="delete-shelving"
on-accept="$ctrl.onDelete()"
question="Are you sure you want to continue?"
message="Shelving will be removed">
</vn-confirm>
<vn-popup vn-id="summary">
<vn-shelving-summary shelving="$ctrl.shelving"></vn-shelving-summary>
</vn-popup>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>

View File

@ -0,0 +1,66 @@
import ngModule from '../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get shelving() {
return this.entity;
}
set shelving(value) {
this.entity = value;
}
onDelete() {
return this.$http.delete(`Shelvings/${this.shelving.id}`)
.then(() => this.$state.go('shelving.index'))
.then(() => this.vnApp.showSuccess(this.$t('Shelving removed')));
}
// loadData() {
// const filter = {
// fields: [
// 'id',
// 'name',
// 'nickname',
// 'nif',
// 'payMethodFk',
// 'payDemFk',
// 'payDay',
// 'isActive',
// 'isSerious',
// 'account'
// ],
// include: [
// {
// relation: 'payMethod',
// scope: {
// fields: ['id', 'name']
// }
// },
// {
// relation: 'payDem',
// scope: {
// fields: ['id', 'payDem']
// }
// },
// {
// relation: 'client',
// scope: {
// fields: ['id', 'fi']
// }
// }
// ]
// };
// return this.getData(`Suppliers/${this.id}`, {filter})
// .then(res => this.supplier = res.data);
// }
}
ngModule.vnComponent('vnShelvingDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
shelving: '<'
}
});

View File

@ -0,0 +1,64 @@
import './index.js';
describe('Supplier Component vnSupplierDescriptor', () => {
let $httpBackend;
let controller;
let $httpParamSerializer;
const supplier = {id: 1};
beforeEach(ngModule('supplier'));
beforeEach(inject(($componentController, _$httpBackend_, _$httpParamSerializer_) => {
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
controller = $componentController('vnSupplierDescriptor', {$element: null}, {supplier});
}));
describe('loadData()', () => {
it('should perform ask for the supplier', () => {
const filter = {
fields: [
'id',
'name',
'nickname',
'nif',
'payMethodFk',
'payDemFk',
'payDay',
'isActive',
'isSerious',
'account'
],
include: [
{
relation: 'payMethod',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'payDem',
scope: {
fields: ['id', 'payDem']
}
},
{
relation: 'client',
scope: {
fields: ['id', 'fi']
}
}
]
};
const serializedParams = $httpParamSerializer({filter});
let query = `Suppliers/${controller.supplier.id}?${serializedParams}`;
jest.spyOn(controller, 'getData');
$httpBackend.expect('GET', query).respond({id: 1});
controller.loadData();
$httpBackend.flush();
expect(controller.getData).toHaveBeenCalledTimes(1);
});
});
});

View File

@ -0,0 +1,7 @@
Tax number: NIF / CIF
All entries with current supplier: Todas las entradas con el proveedor actual
Go to client: Ir al cliente
Verified supplier: Proveedor verificado
Unverified supplier: Proveedor no verificado
Inactive supplier: Proveedor inactivo
Create invoiceIn: Crear factura recibida

View File

@ -0,0 +1,10 @@
export * from './module';
import './basic-data';
import './card';
import './create';
import './descriptor';
import './index/';
import './main';
import './search-panel';
import './summary';

View File

@ -0,0 +1,43 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<div class="vn-list separated">
<a
ng-repeat="shelving in model.data track by shelving.id"
ui-sref="shelving.card.summary(::{id: shelving.id})"
translate-attr="{title: 'View shelving'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::shelving.id}}</h6>
<div>{{::shelving.parking.code}}</div>
<div>{{::shelving.priority}}</div>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
vn-click-stop="$ctrl.preview(shelving)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-item-section>
</a>
</div>
</vn-card>
</vn-data-viewer>
<vn-popup vn-id="summary">
<vn-shelving-summary
shelving="$ctrl.selectedShelving">
</vn-shelving-summary>
</vn-popup>
<a
ui-sref="shelving.create"
vn-tooltip="New shelving"
vn-bind="+"
vn-acl-action="remove"
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(shelving) {
this.selectedShelving = shelving;
this.$.summary.show();
}
}
ngModule.vnComponent('vnShelvingIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,5 @@
Payment deadline: Plazo de pago
Pay day: Dia de pago
Account: Cuenta
Pay method: Metodo de pago
Tax number: Nif

View File

@ -0,0 +1 @@
Accounts: Cuentas

View File

@ -0,0 +1,18 @@
<vn-crud-model
vn-id="model"
url="Shelvings"
filter="$ctrl.filter"
limit="20">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
panel="vn-shelving-search-panel"
info="Search shelving by code"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
</vn-searchbar>
</vn-portal>
<vn-portal slot="menu">
<vn-left-menu></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -0,0 +1,33 @@
import ngModule from '../module';
import ModuleMain from 'salix/components/module-main';
export default class Shelving extends ModuleMain {
constructor($element, $) {
super($element, $);
this.filter = {
include: [
{relation: 'parking'}
],
};
}
$onInit() {
console.log(this.$params);
}
exprBuilder(param, value) {
switch (param) {
case 'search':
return {id: {like: `%${value}%`}};
case 'parkingFk':
case 'userFk':
case 'isRecyclable':
return {[param]: value};
}
}
}
ngModule.vnComponent('vnShelving', {
controller: Shelving,
template: require('./index.html')
});

View File

@ -0,0 +1,3 @@
import {ng} from 'core/vendor';
export default ng.module('shelving', ['salix']);

View File

@ -0,0 +1,71 @@
{
"module": "shelving",
"name": "Shelvings",
"icon" : "contact_support",
"dependencies": [],
"validations" : true,
"menus": {
"main": [
{"state": "shelving.index", "icon": "contact_support"}
],
"card": [
{"state": "shelving.card.basicData", "icon": "settings"},
{"state": "shelving.card.log", "icon": "history"}
]
},
"keybindings": [
{"key": "s", "state": "shelving.index"}
],
"routes": [
{
"url": "/shelving",
"state": "shelving",
"abstract": true,
"component": "vn-shelving",
"description": "Shelvings"
},
{
"url": "/index?q",
"state": "shelving.index",
"component": "vn-shelving-index",
"description": "Shelvings"
},
{
"url": "/create",
"state": "shelving.create",
"component": "vn-shelving-create",
"description": "New shelving"
},
{
"url": "/:id",
"state": "shelving.card",
"abstract": true,
"component": "vn-shelving-card"
},
{
"url": "/summary",
"state": "shelving.card.summary",
"component": "vn-shelving-summary",
"description": "Summary",
"params": {
"shelving": "$ctrl.shelving"
}
},
{
"url": "/basic-data",
"state": "shelving.card.basicData",
"component": "vn-shelving-basic-data",
"description": "Basic data",
"acl": ["administrative"],
"params": {
"shelving": "$ctrl.shelving"
}
},
{
"url" : "/log",
"state": "shelving.card.log",
"component": "vn-shelving-log",
"description": "Log"
}
]
}

View File

@ -0,0 +1,43 @@
<div class="search-panel">
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield
vn-one
label="General search"
ng-model="filter.search"
info="Search shelvings by code"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
url="Parkings"
label="Parking"
show-field="code"
value-field="id"
ng-model="filter.parkingFk">
</vn-autocomplete>
<vn-autocomplete
vn-one
url="Workers"
label="Worker"
search-function="{or: [{id: $search}, {firstName: {like: $search}}]}"
show-field="firstName"
value-field="id"
ng-model="filter.userFk">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-check
vn-one
label="Recyclable"
ng-model="filter.isRecyclable"
triple-state="true">
</vn-check>
</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.vnComponent('vnShelvingSearchPanel', {
template: require('./index.html'),
controller: SearchPanel
});

View File

@ -0,0 +1,4 @@
Province: Provincia
Country: País
Tax number: NIF / CIF
Search suppliers by id, name or alias: Busca proveedores por id, nombre o alias

View File

@ -0,0 +1,51 @@
<vn-card class="summary">
<h5>
<a ng-if="::$ctrl.summary.id"
vn-tooltip="Go to the shelving"
ui-sref="shelving.card.summary({id: {{::$ctrl.summary.id}}})"
name="goToSummary">
<vn-icon-button icon="launch"></vn-icon-button>
</a>
<span>{{::$ctrl.summary.id}}</span>
</h5>
<vn-horizontal>
<vn-one>
<h4>
<a
ui-sref="shelving.card.basicData({id: $ctrl.summary.id})"
target="_self">
<span translate vn-tooltip="Go to">Basic data</span>
</a>
</h4>
<vn-vertical>
<vn-label-value
label="Code"
value="{{::$ctrl.summary.id}}">
</vn-label-value>
<vn-label-value
label="Parking"
value="{{::$ctrl.summary.parking.code}}">
</vn-label-value>
<vn-label-value
label="Priority"
value="{{::$ctrl.summary.priority}}">
</vn-label-value>
<vn-label-value label="Worker">
<span
ng-click="workerDescriptor.show($event, $ctrl.summary.userFk)"
class="link">
{{$ctrl.summary.worker.user.nickname}}
</span>
</vn-label-value>
<vn-check
label="Recyclable"
ng-model="$ctrl.summary.isRecyclable"
disabled="true">
</vn-check>
</vn-vertical>
</vn-one>
</vn-horizontal>
</vn-card>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>

View File

@ -0,0 +1,41 @@
import ngModule from '../module';
import Summary from 'salix/components/summary';
import './style.scss';
class Controller extends Summary {
set shelving(value) {
this._shelving = value;
this.summary = null;
if (!value) return;
const filter = {
include: [
{relation: 'worker',
scope: {
fields: ['userFk'],
include: {
relation: 'user',
scope: {
fields: ['nickname']
}
}
}},
{relation: 'parking'}
]
};
this.$http.get(`Shelvings/${value.id}`, {filter})
.then(res => this.summary = res.data);
}
get shelving() {
return this._shelving;
}
}
ngModule.vnComponent('vnShelvingSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
shelving: '<'
}
});

View File

@ -0,0 +1,32 @@
import './index';
describe('Supplier', () => {
describe('Component vnSupplierSummary', () => {
let controller;
let $httpBackend;
let $scope;
beforeEach(ngModule('supplier'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element('<vn-supplier-summary></vn-supplier-summary>');
controller = $componentController('vnSupplierSummary', {$element, $scope});
}));
describe('getSummary()', () => {
it('should perform a get asking for the supplier data', () => {
controller.supplier = {id: 1};
const query = `Suppliers/${controller.supplier.id}/getSummary`;
$httpBackend.expectGET(query).respond({id: 1});
controller.getSummary();
$httpBackend.flush();
expect(controller.summary).toEqual({id: 1});
});
});
});
});

View File

@ -0,0 +1,12 @@
Verified: Verificado
Country: País
Tax number: NIF / CIF
Search suppliers by id, name or alias: Busca proveedores por id, nombre o alias
Is Farmer: Es agrícola
Sage tax type: Tipo de impuesto Sage
Sage transaction type: Tipo de transacción Sage
Sage withholding: Retencion Sage
Go to the supplier: Ir al proveedor
Responsible: Responsable
Supplier activity: Actividad proveedor
Healt register: Pasaporte sanitario

View File

@ -0,0 +1,7 @@
@import "variables";
vn-client-summary {
.alert span {
color: $color-alert
}
}