Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2729-prices_popover

This commit is contained in:
Carlos Jimenez Ruiz 2021-04-16 10:07:18 +02:00
commit 9a4c056059
56 changed files with 667 additions and 161 deletions

View File

@ -30193,7 +30193,7 @@ BEGIN
NEW.currencyFk != OLD.currencyFk
THEN
CALL vn2008.recibidaIvaDivisaUpdate(NEW.id);
-- CALL vn2008.recibidaIvaDivisaUpdate(NEW.id);
END IF;

View File

@ -31,7 +31,7 @@ describe('Client create path', () => {
await page.write(selectors.createClientView.taxNumber, '74451390E');
await page.write(selectors.createClientView.userName, 'CaptainMarvel');
await page.write(selectors.createClientView.email, 'CarolDanvers@verdnatura.es');
await page.autocompleteSearch(selectors.createClientView.salesPerson, 'replenisher');
await page.autocompleteSearch(selectors.createClientView.salesPerson, 'salesPerson');
await page.waitToClick(selectors.createClientView.createButton);
const message = await page.waitForSnackbar();

View File

@ -96,7 +96,7 @@ describe('Client Edit basicData path', () => {
await page.write(selectors.clientBasicData.phone, '333333333');
await page.clearInput(selectors.clientBasicData.mobile);
await page.write(selectors.clientBasicData.mobile, '444444444');
await page.autocompleteSearch(selectors.clientBasicData.salesPerson, 'replenisherNick');
await page.autocompleteSearch(selectors.clientBasicData.salesPerson, 'salesPerson');
await page.autocompleteSearch(selectors.clientBasicData.channel, 'Metropolis newspaper');
await page.waitToClick(selectors.clientBasicData.saveButton);
const message = await page.waitForSnackbar();
@ -143,7 +143,7 @@ describe('Client Edit basicData path', () => {
const result = await page
.waitToGetProperty(selectors.clientBasicData.salesPerson, 'value');
expect(result).toEqual('replenisherNick');
expect(result).toEqual('salesPersonNick');
});
it('should now confirm the channel have been selected', async() => {

View File

@ -28,10 +28,10 @@
<vn-autocomplete
disabled="false"
ng-model="$ctrl.claim.workerFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithRole"
show-field="nickname"
search-function="{firstName: $search}"
where="{role: 'employee'}"
where="{role: 'salesPerson'}"
label="Attended by">
</vn-autocomplete>
<vn-autocomplete

View File

@ -68,7 +68,7 @@
</vn-autocomplete>
<vn-autocomplete
ng-model="claimDevelopment.workerFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithInheritedRole"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"

View File

@ -25,20 +25,20 @@
<vn-autocomplete
vn-one
ng-model="filter.salesPersonFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithRole"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
where="{role: 'salesPerson'}"
label="Salesperson">
<tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete
vn-one
ng-model="filter.attenderFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithRole"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
where="{role: 'salesPerson'}"
label="Attended by">
<tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete>

View File

@ -1,23 +0,0 @@
const app = require('vn-loopback/server/server');
describe('Client activeWorkersWithRole', () => {
it('should return the sales people as result', async() => {
let filter = {where: {role: 'salesPerson'}};
let result = await app.models.Client.activeWorkersWithRole(filter);
let isSalesPerson = await app.models.Account.hasRole(result[0].id, 'salesPerson');
expect(result.length).toEqual(19);
expect(isSalesPerson).toBeTruthy();
});
it('should return the buyers as result', async() => {
let filter = {where: {role: 'buyer'}};
let result = await app.models.Client.activeWorkersWithRole(filter);
let isBuyer = await app.models.Account.hasRole(result[0].id, 'buyer');
expect(result.length).toEqual(17);
expect(isBuyer).toBeTruthy();
});
});

View File

@ -8,7 +8,6 @@ const LoopBackContext = require('loopback-context');
module.exports = Self => {
// Methods
require('../methods/client/activeWorkersWithRole')(Self);
require('../methods/client/getCard')(Self);
require('../methods/client/createWithUser')(Self);
require('../methods/client/listWorkers')(Self);

View File

@ -54,11 +54,11 @@
<vn-autocomplete
vn-one
ng-model="$ctrl.client.salesPersonFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithInheritedRole"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
where="{role: 'salesPerson'}"
label="Salesperson"
vn-acl="salesAssistant">
</vn-autocomplete>

View File

@ -17,10 +17,10 @@
<vn-autocomplete
vn-one
ng-model="filter.buyerId"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithRole"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
where="{role: 'buyer'}"
label="Buyer">
<tpl-item>{{nickname}}</tpl-item>
</vn-autocomplete>

View File

@ -18,10 +18,10 @@
<vn-autocomplete
label="Salesperson"
ng-model="$ctrl.client.salesPersonFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithInheritedRole"
search-function="{firstName: $search}"
show-field="firstName"
where="{role: 'employee'}">
where="{role: 'salesPerson'}">
<tpl-item>{{firstName}} {{lastName}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>

View File

@ -17,7 +17,7 @@
<vn-autocomplete
vn-one
ng-model="filter.salesPersonFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithInheritedRole"
search-function="{firstName: $search}"
show-field="firstName"
value-field="id"

View File

@ -10,76 +10,81 @@ module.exports = Self => {
accepts: [
{
arg: 'filter',
type: 'Object',
type: 'object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
http: {source: 'query'}
}, {
arg: 'search',
type: 'String',
type: 'string',
description: 'Searchs the entry by id',
http: {source: 'query'}
}, {
arg: 'id',
type: 'Integer',
type: 'integer',
description: 'The entry id',
http: {source: 'query'}
}, {
arg: 'created',
type: 'Date',
type: 'date',
description: 'The created date to filter',
http: {source: 'query'}
}, {
arg: 'travelFk',
type: 'Number',
type: 'number',
description: 'The travel id to filter',
http: {source: 'query'}
}, {
arg: 'companyFk',
type: 'Number',
type: 'number',
description: 'The company to filter',
http: {source: 'query'}
}, {
arg: 'isBooked',
type: 'Boolean',
type: 'boolean',
description: 'The isBokked filter',
http: {source: 'query'}
}, {
arg: 'isConfirmed',
type: 'Boolean',
type: 'boolean',
description: 'The isConfirmed filter',
http: {source: 'query'}
}, {
arg: 'isOrdered',
type: 'Boolean',
type: 'boolean',
description: 'The isOrdered filter',
http: {source: 'query'}
}, {
arg: 'ref',
type: 'String',
type: 'string',
description: 'The ref filter',
http: {source: 'query'}
}, {
arg: 'supplierFk',
type: 'Number',
type: 'number',
description: 'The supplier id to filter',
http: {source: 'query'}
}, {
arg: 'invoiceInFk',
type: 'number',
description: 'The invoiceIn id to filter',
http: {source: 'query'}
}, {
arg: 'currencyFk',
type: 'Number',
type: 'number',
description: 'The currency id to filter',
http: {source: 'query'}
}, {
arg: 'from',
type: 'Date',
type: 'date',
description: `The from date filter`
}, {
arg: 'to',
type: 'Date',
type: 'date',
description: `The to date filter`
}
],
returns: {
type: ['Object'],
type: ['object'],
root: true
},
http: {
@ -116,6 +121,7 @@ module.exports = Self => {
case 'travelFk':
case 'currencyFk':
case 'supplierFk':
case 'invoiceInFk':
param = `e.${param}`;
return {[param]: value};
}

View File

@ -39,11 +39,11 @@
<vn-autocomplete
disabled="false"
ng-model="filter.salesPersonFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithRole"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
where="{role: 'buyer'}"
label="Buyer">
</vn-autocomplete>
</vn-horizontal>

View File

@ -17,7 +17,7 @@ module.exports = Self => {
{
arg: 'search',
type: 'string',
description: 'Searchs the invoiceOut by id',
description: 'Searchs the invoiceIn by id',
http: {source: 'query'}
},
{
@ -25,6 +25,11 @@ module.exports = Self => {
type: 'string',
description: 'The supplier reference'
},
{
arg: 'supplierFk',
type: 'number',
description: 'The supplier id'
},
{
arg: 'fi',
type: 'string',
@ -108,6 +113,7 @@ module.exports = Self => {
case 'fi':
return {[`s.${param}`]: value};
case 'supplierRef':
case 'supplierFk':
case 'serialNumber':
case 'serial':
case 'issued':
@ -132,7 +138,7 @@ module.exports = Self => {
ii.isBooked,
ii.supplierRef,
ii.docFk AS dmsFk,
s.id AS supplierFk,
ii.supplierFk,
s.name AS supplierName,
s.account,
SUM(iid.amount) AS amount,

View File

@ -0,0 +1,9 @@
const app = require('vn-loopback/server/server');
describe('invoiceIn summary()', () => {
it('should return a summary object containing data from one invoiceIn', async() => {
const summary = await app.models.InvoiceIn.summary(1);
expect(summary.supplierRef).toEqual('1234');
});
});

View File

@ -0,0 +1,48 @@
module.exports = Self => {
Self.remoteMethod('summary', {
description: 'The invoiceIn summary',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The invoiceIn id',
http: {source: 'path'}
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/summary`,
verb: 'GET'
}
});
Self.summary = async id => {
const filter = {
include: [
{
relation: 'company',
scope: {
fields: ['id', 'code']
}
},
{
relation: 'supplier',
scope: {
fields: ['id', 'name']
}
},
{
relation: 'sageWithholding',
scope: {
fields: ['withholding']
}
}
]
};
return Self.app.models.InvoiceIn.findById(id, filter);
};
};

View File

@ -1,5 +1,8 @@
{
"InvoiceIn": {
"dataSource": "vn"
},
"InvoiceInDueDay": {
"dataSource": "vn"
}
}

View File

@ -0,0 +1,33 @@
{
"name": "InvoiceInDueDay",
"base": "VnModel",
"options": {
"mysql": {
"table": "invoiceInDueDay"
}
},
"properties": {
"id": {
"id": true,
"type": "number",
"description": "Identifier"
},
"invoiceInFk": {
"type": "number"
},
"dueDated": {
"type": "date"
},
"bankFk": {
"type": "number"
},
"amount": {
"type": "number"
},
"created": {
"type": "date"
}
}
}

View File

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

View File

@ -18,6 +18,9 @@
"serial": {
"type": "string"
},
"supplierRef": {
"type": "string"
},
"issued": {
"type": "date"
},
@ -27,12 +30,18 @@
"isBooked": {
"type": "boolean"
},
"isVatDeductible": {
"type": "boolean"
},
"booked": {
"type": "date"
},
"operated": {
"type": "date"
},
"bookEntried": {
"type": "date"
},
"dmsFk": {
"type": "number",
"mysql": {
@ -41,6 +50,16 @@
}
},
"relations": {
"invoiceInDueDay": {
"type": "hasMany",
"model": "InvoiceInDueDay",
"foreignKey": "invoiceInFk"
},
"sageWithholding": {
"type": "belongsTo",
"model": "SageWithholding",
"foreignKey": "withholdingSageFk"
},
"company": {
"type": "belongsTo",
"model": "Company",

View File

@ -0,0 +1,93 @@
<mg-ajax path="InvoiceIns/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.invoiceIn"
form="form"
save="patch">
</vn-watcher>
<form name="form" ng-submit="watcher.submit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-date-picker
vn-one
label="Expedition date"
ng-model="$ctrl.invoiceIn.issued"
vn-focus
rule>
</vn-date-picker>
<vn-date-picker
vn-one
label="Operation date"
ng-model="$ctrl.invoiceIn.operated"
rule>
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
ng-model="$ctrl.invoiceIn.supplierFk"
url="Suppliers"
show-field="nickname"
search-function="{or: [{id: $search}, {nickname: {like: '%'+ $search +'%'}}]}"
value-field="id"
order="nickname"
label="Supplier"
required="true"
rule>
<tpl-item>
{{::id}} - {{::nickname}}
</tpl-item>
</vn-autocomplete>
<vn-textfield
label="Supplier ref"
ng-model="$ctrl.invoiceIn.supplierRef"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
vn-one
label="Entry date"
ng-model="$ctrl.invoiceIn.bookEntried"
rule>
</vn-date-picker>
<vn-date-picker
vn-one
label="Accounted date"
ng-model="$ctrl.invoiceIn.booked"
rule>
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
label="Currency"
ng-model="$ctrl.invoiceIn.currencyFk"
url="Currencies"
show-field="name"
value-field="id"
rule>
</vn-autocomplete>
<vn-autocomplete
url="Companies"
label="Company"
show-field="code"
value-field="id"
ng-model="$ctrl.invoiceIn.companyFk"
rule>
</vn-autocomplete>
</vn-horizontal>
</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('vnInvoiceInBasicData', {
template: require('./index.html'),
controller: Section,
bindings: {
invoiceIn: '<'
}
});

View File

@ -3,10 +3,27 @@ import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
const filter = {};
const filter = {
include: [
{
relation: 'supplier'
},
{
relation: 'invoiceInDueDay'
},
{
relation: 'company'
}
]};
this.$http.get(`InvoiceIns/${this.$params.id}`, {filter})
.then(res => this.invoiceIn = res.data);
.then(res => {
this.invoiceIn = res.data;
this.invoiceIn.amount = res.data.invoiceInDueDay.reduce(
(accumulator, currentValue) => {
return accumulator + (currentValue['amount'] || 0);
}, 0);
});
}
}

View File

@ -3,7 +3,12 @@ import './index.js';
describe('vnInvoiceIn', () => {
let controller;
let $httpBackend;
let data = {id: 1, name: 'fooName'};
const expectedAmount = 99;
const data = {
id: 1,
name: 'fooName',
invoiceInDueDay: [{amount: expectedAmount}]
};
beforeEach(ngModule('invoiceIn'));
@ -21,7 +26,8 @@ describe('vnInvoiceIn', () => {
controller.reload();
$httpBackend.flush();
expect(controller.invoiceIn).toEqual(data);
expect(controller.invoiceIn).toBeDefined();
expect(controller.invoiceIn.amount).toEqual(expectedAmount);
});
});

View File

@ -0,0 +1,49 @@
<vn-descriptor-content module="invoiceIn" description="$ctrl.invoiceIn.supplierRef">
<slot-menu>
<vn-item
ng-click="deleteConfirmation.show()"
vn-acl="invoicing"
vn-acl-action="remove"
name="deleteInvoice"
translate>
Delete Invoice
</vn-item>
</slot-menu>
<slot-body>
<div class="attributes">
<vn-label-value label="Date" value="{{$ctrl.invoiceIn.issued | date: 'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value label="Booked" value="{{$ctrl.invoiceIn.booked | date: 'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value label="Import" value="{{$ctrl.invoiceIn.amount | currency: 'EUR': 2}}">
</vn-label-value>
<vn-label-value label="Supplier">
<span ng-click="supplierDescriptor.show($event, $ctrl.invoiceIn.supplier.id)" class="link">
{{$ctrl.invoiceIn.supplier.nickname}}
</span>
</vn-label-value>
</div>
<div class="quicklinks">
<div ng-transclude="btnOne">
<vn-quick-link tooltip="Supplier" state="['supplier.card.summary', {id: $ctrl.invoiceIn.supplier.id}]"
icon="icon-supplier">
</vn-quick-link>
</div>
<div ng-transclude="btnTwo">
<vn-quick-link tooltip="Entries list" state="['entry.index', {q: $ctrl.entryFilter}]"
icon="icon-entry">
</vn-quick-link>
</div>
<div ng-transclude="btnThree">
<vn-quick-link tooltip="Invoice list" state="['invoiceIn.index', {q: $ctrl.invoiceInFilter}]"
icon="icon-invoiceIn">
</vn-quick-link>
</div>
</div>
</slot-body>
</vn-descriptor-content>
<vn-confirm vn-id="deleteConfirmation" on-accept="$ctrl.deleteInvoiceIn()"
question="Are you sure you want to delete this invoice?">
</vn-confirm>
<vn-supplier-descriptor-popover vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>

View File

@ -10,6 +10,26 @@ class Controller extends Descriptor {
this.entity = value;
}
get entryFilter() {
if (this.invoiceIn)
return JSON.stringify({invoiceInFk: this.invoiceIn.id});
return null;
}
get invoiceInFilter() {
if (this.invoiceIn)
return JSON.stringify({supplierFk: this.invoiceIn.supplierFk});
return null;
}
deleteInvoiceIn() {
return this.$http.delete(`InvoiceIns/${this.id}`)
.then(() => this.$state.go('invoiceIn.index'))
.then(() => this.vnApp.showSuccess(this.$t('InvoiceIn deleted')));
}
loadData() {
const filter = {
include: [

View File

@ -6,3 +6,5 @@ import './search-panel';
import './card';
import './descriptor';
import './descriptor-popover';
import './summary';
import './basic-data';

View File

@ -22,26 +22,28 @@
</vn-tr>
</vn-thead>
<vn-tbody>
<a ng-repeat="invoiceIn in model.data"
class="clickable vn-tr search-result">
<a
ng-repeat="invoiceIn in model.data"
class="clickable vn-tr search-result"
ui-sref="invoiceIn.card.summary({id: {{::invoiceIn.id}}})">
<vn-td>{{::invoiceIn.id}}</vn-td>
<vn-td>
<span
class="link"
class="link"
vn-click-stop="supplierDescriptor.show($event, invoiceIn.supplierFk)">
{{::invoiceIn.supplierName}}
</span>
</vn-td>
</vn-td>
<vn-td>{{::invoiceIn.supplierRef | dashIfEmpty}}</vn-td>
<vn-td>{{::invoiceIn.serialNumber}}</vn-td>
<vn-td>{{::invoiceIn.serial}}</vn-td>
<vn-td>{{::invoiceIn.account}}</vn-td>
<vn-td>{{::invoiceIn.serialNumber}}</vn-td>
<vn-td>{{::invoiceIn.serial}}</vn-td>
<vn-td>{{::invoiceIn.account}}</vn-td>
<vn-td expand>{{::invoiceIn.issued | date:'dd/MM/yyyy' | dashIfEmpty}}</vn-td>
<vn-td center>
<vn-check disabled="true"
ng-model="invoiceIn.isBooked">
</vn-check>
</vn-td>
</vn-td>
<vn-td>{{::invoiceIn.awbCode}}</vn-td>
<vn-td>{{::invoiceIn.amount | currency:'EUR'}}</vn-td>
<vn-td shrink>
@ -67,38 +69,6 @@
</vn-data-viewer>
<vn-popup vn-id="summary">
<vn-invoice-in-summary
invoice-in="$ctrl.selectedinvoiceIn">
invoice-in="$ctrl.selectedInvoiceIn">
</vn-invoice-in-summary>
</vn-popup>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>
<vn-contextmenu vn-id="contextmenu" targets="['vn-data-viewer']" model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-menu>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.filterBySelection()">
Filter by selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.excludeSelection()">
Exclude selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.removeFilter()">
Remove filter
</vn-item>
<vn-item translate
ng-click="contextmenu.removeAllFilters()">
Remove all filters
</vn-item>
<vn-item translate
ng-if="contextmenu.isActionAllowed()"
ng-click="contextmenu.copyValue()">
Copy value
</vn-item>
</slot-menu>
</vn-contextmenu>
</vn-popup>

View File

@ -34,6 +34,11 @@ export default class Controller extends Section {
return [minHour, maxHour];
}
preview(invoiceIn) {
this.selectedInvoiceIn = invoiceIn;
this.$.summary.show();
}
}
ngModule.vnComponent('vnInvoiceInIndex', {

View File

@ -1,2 +1,5 @@
InvoiceIn: Facturas recibidas
Search invoices in by reference: Buscar facturas recibidas por referencia
Search invoices in by reference: Buscar facturas recibidas por referencia
Entries list: Listado de entradas
Invoice list: Listado de entradas
InvoiceIn deleted: Factura eliminada

View File

@ -7,6 +7,12 @@
"menus": {
"main": [
{"state": "invoiceIn.index", "icon": "icon-invoiceIn"}
],
"card": [
{
"state": "invoiceIn.card.basicData",
"icon": "settings"
}
]
},
"routes": [
@ -36,9 +42,18 @@
"component": "vn-invoice-in-summary",
"description": "Summary",
"params": {
"invoice-In": "$ctrl.invoiceIn"
"invoice-in": "$ctrl.invoiceIn"
},
"acl": ["developer"]
"acl": ["administrative"]
},
{
"url": "/basic-data",
"state": "invoiceIn.card.basicData",
"component": "vn-invoice-in-basic-data",
"description": "Basic data",
"params": {
"invoice-in": "$ctrl.invoiceIn"
}
}
]
}

View File

@ -0,0 +1,52 @@
<vn-card class="summary">
<h5>
<a
ng-if="::$ctrl.summary.id"
vn-tooltip="Go to the Invoice In"
ui-sref="invoiceIn.card.summary({id: {{::$ctrl.summary.id}}})"
name="goToSummary">
<vn-icon-button icon="launch"></vn-icon-button>
</a>
<span>{{$ctrl.summary.id}} - {{$ctrl.summary.supplier.name}}</span>
</h5>
<vn-horizontal>
<vn-auto>
<h4 translate>Basic data</h4>
<vn-horizontal>
<vn-one>
<vn-label-value label="Supplier" value="{{$ctrl.summary.supplier.name}}">
</vn-label-value>
<vn-label-value label="Supplier ref" value="{{$ctrl.summary.supplierRef}}">
</vn-label-value>
<vn-label-value label="Doc number" value="{{$ctrl.summary.serial}}/{{$ctrl.summary.serialNumber}}">
</vn-label-value>
</vn-one>
<vn-one>
<vn-label-value label="Expedition date" value="{{$ctrl.summary.issued | date: 'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value label="Operation date" value="{{$ctrl.summary.operated | date: 'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value label="Entry date" value="{{$ctrl.summary.bookEntried | date: 'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value label="Booked date" value="{{$ctrl.summary.booked | date: 'dd/MM/yyyy'}}">
</vn-label-value>
</vn-one>
<vn-one>
<vn-label-value label="Sage withholding" value="{{$ctrl.summary.sageWithholding.withholding}}">
</vn-label-value>
<vn-label-value label="Company" value="{{$ctrl.summary.company.code}}">
</vn-label-value>
<vn-vertical>
<vn-check label="Deductible" ng-model="$ctrl.summary.isVatDeductible" disabled="true">
</vn-check>
<vn-check label="Booked" ng-model="$ctrl.summary.isBooked" disabled="true">
</vn-check>
</vn-vertical>
</vn-one>
</vn-horizontal>
</vn-auto>
</vn-horizontal>
</vn-card>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>

View File

@ -0,0 +1,28 @@
import ngModule from '../module';
import Summary from 'salix/components/summary';
import './style.scss';
class Controller extends Summary {
set invoiceIn(value) {
this._invoiceIn = value;
if (value && value.id)
this.getSummary();
}
get invoiceIn() {
return this._invoiceIn;
}
getSummary() {
return this.$http.get(`InvoiceIns/${this.invoiceIn.id}/summary`)
.then(res => this.summary = res.data);
}
}
ngModule.vnComponent('vnInvoiceInSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
invoiceIn: '<'
}
});

View File

@ -0,0 +1,29 @@
import './index.js';
describe('InvoiceIn', () => {
describe('Component summary', () => {
let controller;
let $httpBackend;
let $scope;
beforeEach(ngModule('invoiceIn'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element('<vn-invoice-in-summary></vn-invoice-in-summary>');
controller = $componentController('vnInvoiceInSummary', {$element, $scope});
controller.invoiceIn = {id: 1};
}));
describe('getSummary()', () => {
it('should perform a query to set summary', () => {
$httpBackend.when('GET', `InvoiceIns/1/summary`).respond(200, 'the data you are looking for');
controller.getSummary();
$httpBackend.flush();
expect(controller.summary).toEqual('the data you are looking for');
});
});
});
});

View File

@ -0,0 +1,10 @@
Go to the Invoice In: Ir a la factura recibida
Expedition date: Fecha expedición
Operation date: Fecha operación
Supplier ref: Ref. proveedor
Entry date: Fecha asiento
Booked date: Fecha contable
Accounted date: Fecha contable
Doc number: Numero documento
Sage withholding: Retención sage
Deductible: Deducible

View File

@ -0,0 +1,5 @@
@import "variables";
vn-invoice-in-summary .summary {
width: $width-lg;
}

View File

@ -44,7 +44,7 @@
<vn-autocomplete
vn-one
ng-model="filter.buyerFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithRolee"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"

View File

@ -17,10 +17,10 @@
<vn-autocomplete
vn-one
ng-model="filter.attenderFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithRole"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
where="{role: 'buyer'}"
label="Atender">
<tpl-item>{{nickname}}</tpl-item>
</vn-autocomplete>

View File

@ -42,11 +42,11 @@
vn-one
disabled="false"
ng-model="filter.salesPersonFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithRole"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
where="{role: 'buyer'}"
label="Buyer">
</vn-autocomplete>
</vn-horizontal>

View File

@ -28,11 +28,11 @@
<vn-autocomplete
vn-one
ng-model="filter.workerFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithInheritedRole"
search-function="{firstName: $search}"
show-field="nickname"
value-field="id"
where="{role: 'employee'}"
where="{role: 'salesPerson'}"
label="Sales person">
</vn-autocomplete>
</vn-horizontal>

View File

@ -11,7 +11,7 @@
<vn-autocomplete
vn-one
ng-model="$ctrl.route.workerFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithInheritedRole"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"

View File

@ -11,7 +11,7 @@
<vn-autocomplete
label="Worker"
ng-model="$ctrl.route.workerFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithInheritedRole"
show-field="nickname"
search-function="{firstName: $search}"
where="{role: 'employee'}">

View File

@ -13,7 +13,7 @@
<vn-autocomplete
vn-one
ng-model="filter.workerFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithInheritedRole"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"

View File

@ -18,7 +18,7 @@
<vn-autocomplete
vn-one
ng-model="$ctrl.supplier.workerFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithInheritedRole"
search-function="{firstName: $search}"
show-field="nickname"
value-field="id"

View File

@ -17,10 +17,10 @@
<vn-autocomplete
vn-one
ng-model="filter.buyerId"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithRole"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
where="{role: 'buyer'}"
label="Buyer">
<tpl-item>{{nickname}}</tpl-item>
</vn-autocomplete>

View File

@ -18,7 +18,7 @@
<vn-autocomplete
label="Buyer"
ng-model="$ctrl.ticketRequest.attenderFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithRole"
show-field="nickname"
where="{role: 'buyer'}"
search-function="{firstName: $search}">

View File

@ -60,7 +60,7 @@
<vn-autocomplete
vn-one
ng-model="filter.salesPersonFk"
url="Clients/activeWorkersWithRole"
url="Workers/activeWithInheritedRole"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"

View File

@ -21,7 +21,7 @@
</vn-autocomplete>
<vn-autocomplete
vn-one
url="Clients/activeWorkersWithRole"
url="Workers/activeWithInheritedRole"
ng-if="$ctrl.isPickerDesignedState"
ng-model="$ctrl.workerFk"
show-field="nickname"

View File

@ -3,26 +3,7 @@ const buildFilter = require('vn-loopback/util/filter').buildFilter;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => {
Self.remoteMethod('activeWorkersWithRole', {
description: 'Returns actives workers with salesperson role',
accessType: 'READ',
accepts: [{
arg: 'filter',
type: 'Object',
description: 'Filter defining where and paginated data',
required: true
}],
returns: {
type: 'Worker',
root: true
},
http: {
path: `/activeWorkersWithRole`,
verb: 'get'
}
});
Self.activeWorkersWithRole = async filter => {
Self.activeWorkers = async(query, filter) => {
let conn = Self.dataSource.connector;
if (filter.where && filter.where.and && Array.isArray(filter.where.and)) {
let where = {};
@ -54,14 +35,8 @@ module.exports = Self => {
myFilter = mergeFilters(myFilter, clientFilter);
let stmt = new ParameterizedSQL(
`SELECT DISTINCT w.id, w.firstName, w.lastName, u.name, u.nickname
FROM worker w
JOIN account.user u ON u.id = w.userFk
JOIN account.roleRole i ON i.role = u.role
JOIN account.role r ON r.id = i.inheritsFrom`
);
let stmt = new ParameterizedSQL(query);
stmt.merge(conn.makeSuffix(myFilter));
return await conn.executeStmt(stmt);
return conn.executeStmt(stmt);
};
};

View File

@ -0,0 +1,32 @@
module.exports = Self => {
Self.remoteMethod('activeWithInheritedRole', {
description: 'Returns active workers with a role',
accessType: 'READ',
accepts: [{
arg: 'filter',
type: 'Object',
description: 'Filter defining where and paginated data',
required: true
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/activeWithInheritedRole`,
verb: 'GET'
}
});
Self.activeWithInheritedRole = async filter => {
const query =
`SELECT DISTINCT w.id, w.firstName, w.lastName, u.name, u.nickname
FROM worker w
JOIN account.user u ON u.id = w.userFk
JOIN account.roleRole i ON i.role = u.role
JOIN account.role r ON r.id = i.inheritsFrom`;
return Self.activeWorkers(query, filter);
};
};

View File

@ -0,0 +1,31 @@
module.exports = Self => {
Self.remoteMethod('activeWithRole', {
description: 'Returns active workers with an inherited role',
accessType: 'READ',
accepts: [{
arg: 'filter',
type: 'Object',
description: 'Filter defining where and paginated data',
required: true
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/activeWithRole`,
verb: 'GET'
}
});
Self.activeWithRole = async filter => {
const query =
`SELECT DISTINCT w.id, w.firstName, w.lastName, u.name, u.nickname
FROM worker w
JOIN account.user u ON u.id = w.id
JOIN account.role r ON r.id = u.role`;
return Self.activeWorkers(query, filter);
};
};

View File

@ -0,0 +1,29 @@
const app = require('vn-loopback/server/server');
describe('Worker activeWithInheritedRole', () => {
it('should return the workers with an inherited role of salesPerson', async() => {
const filter = {where: {role: 'salesPerson'}};
const result = await app.models.Worker.activeWithInheritedRole(filter);
const randomIndex = Math.floor(Math.random() * result.length);
const worker = result[randomIndex];
const isSalesPerson = await app.models.Account.hasRole(worker.id, 'salesPerson');
expect(result.length).toEqual(19);
expect(isSalesPerson).toBe(true);
});
it('should return the workers with an inherited role of buyer', async() => {
const filter = {where: {role: 'buyer'}};
const result = await app.models.Worker.activeWithInheritedRole(filter);
const randomIndex = Math.floor(Math.random() * result.length);
const worker = result[randomIndex];
const isBuyer = await app.models.Account.hasRole(worker.id, 'buyer');
expect(result.length).toEqual(17);
expect(isBuyer).toBe(true);
});
});

View File

@ -0,0 +1,21 @@
const app = require('vn-loopback/server/server');
describe('Worker activeWithRole', () => {
it('should return the sales people as result', async() => {
const filter = {where: {role: 'salesPerson'}};
const result = await app.models.Worker.activeWithRole(filter);
const firstWorker = result[0];
expect(result.length).toEqual(1);
expect(firstWorker.nickname).toEqual('salesPersonNick');
});
it('should return the buyers as result', async() => {
const filter = {where: {role: 'buyer'}};
const result = await app.models.Worker.activeWithRole(filter);
const firstWorker = result[0];
expect(result.length).toEqual(1);
expect(firstWorker.nickname).toEqual('buyerNick');
});
});

View File

@ -7,4 +7,7 @@ module.exports = Self => {
require('../methods/worker/createAbsence')(Self);
require('../methods/worker/deleteAbsence')(Self);
require('../methods/worker/updateAbsence')(Self);
require('../methods/worker/active')(Self);
require('../methods/worker/activeWithRole')(Self);
require('../methods/worker/activeWithInheritedRole')(Self);
};

View File

@ -30,13 +30,13 @@
rule>
</vn-textfield>
<vn-autocomplete
disabled="false"
ng-model="$ctrl.worker.bossFk"
url="Clients/activeWorkersWithRole"
show-field="nickname"
search-function="{firstName: $search}"
where="{role: 'employee'}"
label="Boss">
disabled="false"
ng-model="$ctrl.worker.bossFk"
url="Clients/activeWorkersWithInheritRole"
show-field="nickname"
search-function="{firstName: $search}"
where="{role: 'employee'}"
label="Boss">
</vn-autocomplete>
</vn-horizontal>
</vn-vertical>