2502-supplier_summary + e2e #425

Merged
joan merged 15 commits from 2502-supplier_summary into dev 2020-10-29 07:55:38 +00:00
32 changed files with 724 additions and 40 deletions

View File

@ -1208,11 +1208,11 @@ INSERT INTO `vn`.`annualAverageInvoiced`(`clientFk`, `invoiced`)
(104, 500),
(105, 5000);
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`retAccount`,`commission`, `created`, `postcodeFk`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `phone`, `payDay`)
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`retAccount`,`commission`, `created`, `postcodeFk`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`)
VALUES
(1, 'Plants SL', 'Plants nick', 4000000001, 1, 'A11111111', 0, NULL, 0, CURDATE(), 1111, 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 123456789, 15),
(2, 'Flower King', 'The king', 4000000002, 1, 'B22222222', 0, NULL, 0, CURDATE(), 2222, 1, 'supplier address 2', 'LONDON', 2, 45671, 1, 2, 987654321, 10),
(442, 'Verdnatura Levante SL', 'Verdnatura', 4000000442, 1, 'C33333333', 0, NULL, 0, CURDATE(), 3333, 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 987123654, 15);
(1, 'Plants SL', 'Plants nick', 4000000001, 1, '06089160W', 0, NULL, 0, CURDATE(), 1111, 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15),
(2, 'Flower King', 'The king', 4000000002, 1, 'B22222222', 0, NULL, 0, CURDATE(), 2222, 1, 'supplier address 2', 'LONDON', 2, 45671, 1, 2, 10),
(442, 'Verdnatura Levante SL', 'Verdnatura', 4000000442, 1, 'C33333333', 0, NULL, 0, CURDATE(), 3333, 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15);
INSERT INTO `cache`.`cache_calc`(`id`, `cache_id`, `cacheName`, `params`, `last_refresh`, `expires`, `created`, `connection_id`)
VALUES

View File

@ -901,5 +901,16 @@ export default {
newEntryTravel: 'vn-entry-create vn-autocomplete[ng-model="$ctrl.entry.travelFk"]',
newEntryCompany: 'vn-entry-create vn-autocomplete[ng-model="$ctrl.entry.companyFk"]',
saveNewEntry: 'vn-entry-create button[type="submit"]'
},
supplierSummary: {
header: 'vn-supplier-summary > vn-card > h5',
basicDataId: 'vn-supplier-summary vn-label-value[label="Id"]',
fiscalAddressTaxNumber: 'vn-supplier-summary vn-label-value[label="Tax number"]',
billingDataPayMethod: 'vn-supplier-summary vn-label-value[label="Pay method"]'
},
supplierDescriptor: {
alias: 'vn-supplier-descriptor vn-label-value[label="Alias"]',
clientButton: 'vn-supplier-descriptor vn-icon[icon="person"]',
entriesButton: 'vn-supplier-descriptor vn-icon[icon="icon-entry"]',
}
};

View File

@ -0,0 +1,84 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Supplier descriptor path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('administrative', 'supplier');
await page.accessToSearchResult('1');
});
afterAll(async() => {
await browser.close();
});
// summary
it('should reach the second entry summary section', async() => {
await page.waitForState('supplier.card.summary');
});
it(`should confirm there's data on the summary header`, async() => {
const result = await page.waitToGetProperty(selectors.supplierSummary.header, 'innerText');
expect(result).toContain('Plants SL - 1');
});
it(`should confirm there's data on the summary basic data`, async() => {
const result = await page.waitToGetProperty(selectors.supplierSummary.basicDataId, 'innerText');
expect(result).toContain('Id 1');
});
it(`should confirm there's data on the summary fiscal address`, async() => {
const result = await page.waitToGetProperty(selectors.supplierSummary.fiscalAddressTaxNumber, 'innerText');
expect(result).toContain('Tax number 06089160W');
});
it(`should confirm there's data on the summary fiscal pay method`, async() => {
const result = await page.waitToGetProperty(selectors.supplierSummary.billingDataPayMethod, 'innerText');
expect(result).toContain('Pay method PayMethod one');
});
// descriptor
it(`should confirm there's data on the descriptor`, async() => {
const result = await page.waitToGetProperty(selectors.supplierDescriptor.alias, 'innerText');
expect(result).toContain('Plants nick');
});
it(`should navigate to the supplier's client summary using the icon client button`, async() => {
await page.waitToClick(selectors.supplierDescriptor.clientButton);
await page.waitForState('client.card.summary');
});
it(`should navigate back to the supplier`, async() => {
await page.waitToClick(selectors.globalItems.homeButton);
await page.waitForState('home');
await page.selectModule('supplier');
await page.accessToSearchResult('1');
await page.waitForState('supplier.card.summary');
});
it(`should navigate to the supplier's entries`, async() => {
await page.waitToClick(selectors.supplierDescriptor.entriesButton);
await page.waitForState('entry.index');
});
it(`should navigate back to suppliers but a different one this time`, async() => {
await page.waitToClick(selectors.globalItems.homeButton);
await page.waitForState('home');
await page.selectModule('supplier');
await page.accessToSearchResult('2');
await page.waitForState('supplier.card.summary');
});
it(`should check the client button isn't present since this supplier should not be a client`, async() => {
await page.waitForSelector(selectors.supplierDescriptor.clientButton, {hidden: true});
});
});

View File

@ -162,6 +162,9 @@ function e2eSingleRun() {
`${__dirname}/e2e/paths/08*/*[sS]pec.js`,
`${__dirname}/e2e/paths/09*/*[sS]pec.js`,
`${__dirname}/e2e/paths/10*/*[sS]pec.js`,
`${__dirname}/e2e/paths/11*/*[sS]pec.js`,
`${__dirname}/e2e/paths/12*/*[sS]pec.js`,
`${__dirname}/e2e/paths/13*/*[sS]pec.js`,
`${__dirname}/e2e/paths/**/*[sS]pec.js`
];

View File

@ -1,12 +1,12 @@
module.exports = Self => {
Self.remoteMethod('getSummary', {
description: 'Updates the item taxes',
description: 'Return the claim summary',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The item id',
description: 'The claim id',
http: {source: 'path'}
}],
returns: {

View File

@ -56,7 +56,7 @@ describe('Update Claim', () => {
hasToPickUp: false
}
};
await app.models.Claim.updateClaim(ctx, newClaim.id,);
await app.models.Claim.updateClaim(ctx, newClaim.id);
let updatedClaim = await app.models.Claim.findById(newClaim.id);

View File

@ -51,7 +51,7 @@ describe('Client', () => {
}
};
const serializedParams = $httpParamSerializer({filter});
$httpBackend.expect('GET', `ClientRisks?${serializedParams}`,).respond([{amount: 20}]);
$httpBackend.expect('GET', `ClientRisks?${serializedParams}`).respond([{amount: 20}]);
controller.getAmountPaid();
$httpBackend.flush();
@ -65,7 +65,7 @@ describe('Client', () => {
controller.$params = {id: 101};
$httpBackend.expect('POST', `Receipts`,).respond({id: 1});
$httpBackend.expect('POST', `Receipts`).respond({id: 1});
controller.responseHandler('accept');
$httpBackend.flush();

View File

@ -18,7 +18,7 @@ describe('Component vnOrderIndex', () => {
beforeEach(ngModule('order'));
beforeEach(inject(($componentController, _$window_,) => {
beforeEach(inject(($componentController, _$window_) => {
$window = _$window_;
const $element = angular.element('<vn-order-index></vn-order-index>');
controller = $componentController('vnOrderIndex', {$element});

View File

@ -60,7 +60,11 @@ module.exports = Self => {
let where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'search':
return {'s.id': value};
return {or: [
{'s.id': value},
{'s.name': {like: `%${value}%`}},
{'s.nickname': {like: `%${value}%`}}
]};
case 'nickname':

remove comments

remove comments
param = `s.${param}`;
return {[param]: {like: `%${value}%`}};

View File

@ -0,0 +1,74 @@
module.exports = Self => {
Self.remoteMethod('getSummary', {
description: 'Returns the supplier summary',
accessType: 'READ',
accepts: {
arg: 'id',
type: 'number',
required: true,
description: 'The supplier id',
http: {source: 'path'}
},
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/getSummary`,
verb: 'GET'
}
});
Self.getSummary = async id => {
let filter = {
where: {id: id},
fields: [
'id',
'name',
'nickname',
'isOfficial',
'isActive',
'note',
'nif',
'street',
'city',
'postCode',
'provinceFk',
'countryFk',
'payMethodFk',
'payDemFk',
'payDay',
'account',
'isFarmer',
],
include: [
{
relation: 'province',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'country',
scope: {
fields: ['id', 'country', 'code']
}
},
{
relation: 'payMethod',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'payDem',
scope: {
fields: ['id', 'payDem']
}
}
]
};
let supplier = await Self.app.models.Supplier.findOne(filter);
return supplier;
};
};

View File

@ -0,0 +1,28 @@
const app = require('vn-loopback/server/server');
describe('Supplier getSummary()', () => {
it('should return a summary object containing data from one supplier', async() => {
const supplier = await app.models.Supplier.getSummary(1);
expect(supplier.id).toEqual(1);
expect(supplier.name).toEqual('Plants SL');
expect(supplier.nif).toEqual('06089160W');
expect(supplier.account).toEqual(4000000001);
expect(supplier.payDay).toEqual(15);
});
joan marked this conversation as resolved
Review

containing it's supplier country relation

containing it's supplier country relation
it(`should return a summary object containing it's supplier country relation`, async() => {
const supplier = await app.models.Supplier.getSummary(1);
const country = supplier.country();
expect(country.id).toEqual(1);
expect(country.code).toEqual('ES');
});
it(`should return a summary object containing it's billing data relation`, async() => {
const supplier = await app.models.Supplier.getSummary(1);
const payMethod = supplier.payMethod();
expect(payMethod.name).toEqual('PayMethod one');
});
});

View File

@ -1,5 +1,8 @@
{
"Supplier": {
"dataSource": "vn"
},
"PayDem": {
"dataSource": "vn"
}
}

View File

@ -0,0 +1,19 @@
{
"name": "PayDem",
"base": "VnModel",
"options": {
"mysql": {
"table": "payDem"
}
},
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"payDem": {
"type": "Number"
}
}
}

View File

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

View File

@ -45,6 +45,12 @@
"isActive": {
"type": "Boolean"
},
"isOfficial": {
"type": "Boolean"
},
"note": {
"type": "String"
},
"street": {
"type": "String"
},
@ -63,10 +69,41 @@
"payDemFk": {
"type": "Number"
},
"payDay": {
"type": "Number"
},
"nickname": {
"type": "String"
}
},
"relations": {
"payMethod": {
"type": "belongsTo",
"model": "PayMethod",
"foreignKey": "payMethodFk"
},
"payDem": {
"type": "belongsTo",
"model": "PayDem",
"foreignKey": "payDemFk"
},
"province": {
"type": "belongsTo",
"model": "Province",
"foreignKey": "provinceFk"
},
"country": {
"type": "belongsTo",
"model": "Country",
"foreignKey": "countryFk"
},
"client": {
"type": "belongsTo",
"model": "Client",
"foreignKey": "nif",
"primaryKey": "fi"
}
},
"acls": [
{
"accessType": "READ",

View File

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

View File

@ -0,0 +1,48 @@
import ngModule from '../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
let filter = {
include: [
{
relation: 'province',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'country',
scope: {
fields: ['id', 'name', 'code']
}
},
{
relation: 'payMethod',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'payDem',
scope: {
fields: ['id', 'payDem']
}
},
{
relation: 'client',
scope: {
fields: ['id', 'fi']
}
}
]
};
this.$http.get(`Suppliers/${this.$params.id}`, {filter})
.then(response => this.supplier = response.data);
}
}
ngModule.vnComponent('vnSupplierCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,57 @@
<vn-descriptor-content
module="supplier"
description="$ctrl.supplier.name">
<slot-body>
<div class="attributes">
<vn-label-value label="Tax number"
value="{{$ctrl.supplier.nif}}">
</vn-label-value>
<vn-label-value label="Alias"
value="{{$ctrl.supplier.nickname}}">
</vn-label-value>
<vn-label-value label="Pay method"
value="{{$ctrl.supplier.payMethod.name}}">
</vn-label-value>
<vn-label-value label="Payment deadline"
value="{{$ctrl.supplier.payDem.payDem}}">
</vn-label-value>
<vn-label-value label="Pay day"
value="{{$ctrl.supplier.payDay}}">
</vn-label-value>
<vn-label-value label="Account"
value="{{$ctrl.supplier.account}}">
</vn-label-value>
</div>
<div class="icons">
<vn-icon
vn-tooltip="Inactive supplier"
icon="icon-disabled"
ng-class="{bright: $ctrl.supplier.isActive == false}">
</vn-icon>
<vn-icon
vn-tooltip="Official supplier"

official supplier

official supplier
icon="icon-net"
ng-class="{bright: $ctrl.supplier.isOfficial == false}">
</vn-icon>
</div>
<div class="quicklinks">
<div ng-transclude="btnOne">
<vn-quick-link
tooltip="All entries with current supplier"
state="['entry.index', {q: $ctrl.entryFilter}]"
icon="icon-entry">
</vn-quick-link>
</div>
<div ng-transclude="btnTwo">
<vn-quick-link
ng-if="$ctrl.supplier.client.fi"
tooltip="Go to client"
state="['client.card.summary', {id: $ctrl.supplier.client.id}]"
icon="person">
</vn-quick-link>
</div>
<div ng-transclude="btnThree">
</div>
</div>
</slot-body>
</vn-descriptor-content>

View File

@ -0,0 +1,79 @@
import ngModule from '../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get supplier() {
return this.entity;
}
set supplier(value) {
this.entity = value;
}
get entryFilter() {
if (!this.supplier) return null;
const date = new Date();
date.setHours(0, 0, 0, 0);
const from = new Date(date.getTime());
from.setDate(from.getDate() - 10);
const to = new Date(date.getTime());
to.setDate(to.getDate() + 10);
return JSON.stringify({
supplierFk: this.supplier.id,
from,
to
});
}
loadData() {
const filter = {
fields: [
'id',
'name',
'nickname',
'nif',
'payMethodFk',
'payDemFk',
'payDay',
'isActive',
'isOfficial',
'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.supplier.id}`, {filter})
.then(res => this.supplier = res.data);
}
}
ngModule.vnComponent('vnSupplierDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
supplier: '<'
}
});

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',
'isOfficial',
'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,5 @@
Tax number: NIF / CIF
All entries with current supplier: Todas las entradas con el proveedor actual
Go to client: Ir al cliente
Official supplier: Proveedor oficial

official supplier

official supplier
Inactive supplier: Proveedor inactivo

inactive supplier

inactive supplier

View File

@ -3,3 +3,6 @@ export * from './module';
import './main';
import './index/';
import './search-panel';
import './summary';
import './card';
import './descriptor';

View File

@ -8,7 +8,7 @@
<vn-searchbar
vn-focus
panel="vn-supplier-search-panel"
info="Search suppliers by id"
info="Search suppliers by id, name or alias"
model="model">
</vn-searchbar>
</vn-portal>

View File

@ -2,6 +2,7 @@
"module": "supplier",
"name": "Suppliers",
"icon" : "icon-supplier",
"dependencies": ["client", "item"],
"validations" : true,
"menus": {
"main": [
@ -22,6 +23,19 @@
"state": "supplier.index",
"component": "vn-supplier-index",
"description": "Suppliers"
}, {
"url": "/:id",
"state": "supplier.card",
"abstract": true,
"component": "vn-supplier-card"
}, {
"url": "/summary",
"state": "supplier.card.summary",
"component": "vn-supplier-summary",
"description": "Summary",
"params": {
"supplier": "$ctrl.supplier"
}
}
]
}

View File

@ -5,7 +5,7 @@
vn-one
label="General search"
ng-model="filter.search"
info="Search suppliers by id"
info="Search suppliers by id, name or alias"
vn-focus>
</vn-textfield>
</vn-horizontal>

View File

@ -1,3 +1,4 @@
Province: Provincia
Country: País
Tax number: Nif
Tax number: NIF / CIF

NIF

NIF
Search suppliers by id, name or alias: Busca proveedores por id, nombre o alias

Busca proveedores por id, nombre o alias

Busca proveedores por id, nombre o alias

View File

@ -0,0 +1,74 @@
<vn-card class="summary">
<h5>{{$ctrl.summary.name}} - {{$ctrl.summary.id}}</h5>
<vn-horizontal>
<vn-one>
<h4 translate>Basic data</h4>
<vn-label-value label="Id"
value="{{$ctrl.summary.id}}">
</vn-label-value>
<vn-label-value label="Alias"
value="{{$ctrl.summary.nickname}}">
</vn-label-value>
<vn-check
label="Is official"
ng-model="$ctrl.summary.isOfficial"
disabled="true">
</vn-check>
<vn-check
label="Is active"
ng-model="$ctrl.summary.isActive"
disabled="true">
</vn-check>
<vn-label-value label="Notes"
value="{{$ctrl.summary.note}}">
</vn-label-value>
</vn-one>
<vn-one>
<h4 translate>Fiscal address</h4>
<vn-label-value label="Social name"
value="{{$ctrl.summary.name}}">
</vn-label-value>
<vn-label-value label="Tax number"
value="{{$ctrl.summary.nif}}">
</vn-label-value>
<vn-label-value label="Street" ellipsize="false"
value="{{$ctrl.summary.street}}">
</vn-label-value>
<vn-label-value label="City"
value="{{$ctrl.summary.city}}">
</vn-label-value>
<vn-label-value label="Postcode"
value="{{$ctrl.summary.postCode}}">
</vn-label-value>
<vn-label-value label="Province"
value="{{$ctrl.summary.province.name}}">
</vn-label-value>
<vn-label-value label="Country"
value="{{$ctrl.summary.country.country}}">
</vn-label-value>
</vn-one>
<vn-one>
<h4 translate>Billing data</h4>
<vn-label-value label="Pay method"
value="{{$ctrl.summary.payMethod.name}}">
</vn-label-value>
<vn-label-value label="Payment deadline"
value="{{$ctrl.summary.payDem.payDem}}">
</vn-label-value>
<vn-label-value label="Pay day"
value="{{$ctrl.summary.payDay}}">
</vn-label-value>
<vn-label-value label="Account"
value="{{$ctrl.summary.account}}">
</vn-label-value>
<vn-check
label="Is Farmer"
ng-model="$ctrl.summary.isFarmer"
disabled="true">
</vn-check>
</vn-one>
</vn-horizontal>
</vn-card>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>

View File

@ -0,0 +1,26 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
$onChanges() {
if (!this.supplier)
return;
this.getSummary();
}
getSummary() {
return this.$http.get(`Suppliers/${this.supplier.id}/getSummary`).then(response => {
this.summary = response.data;
});
}
}
ngModule.vnComponent('vnSupplierSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
supplier: '<'
}
});

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,5 @@
Is official: Es oficial
Country: País
Tax number: NIF / CIF

NIF y CIF?

NIF y CIF?
Search suppliers by id, name or alias: Busca proveedores por id, nombre o alias

-el

-el
Is Farmer: Es agrícola

View File

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

is !important that important?

is !important that important?
}
}