Merge remote-tracking branch 'upstream/dev' into jgallego

This commit is contained in:
Gerard 2019-02-19 16:12:01 +01:00
commit 927957aed8
50 changed files with 667 additions and 203 deletions

17
Jenkinsfile vendored
View File

@ -17,10 +17,15 @@ pipeline {
steps { steps {
script { script {
env.COMPOSE_PROJECT_NAME = "${env.PROJECT_NAME}-${env.BRANCH_NAME}" env.COMPOSE_PROJECT_NAME = "${env.PROJECT_NAME}-${env.BRANCH_NAME}"
env.GIT_COMMITTER_EMAIL = sh(
script: 'git --no-pager show -s --format="%ae"', if (!env.GIT_COMMITTER_EMAIL) {
returnStdout: true env.COMMITTER_EMAIL = sh(
).trim() script: 'git --no-pager show -s --format="%ae"',
returnStdout: true
).trim()
} else {
env.COMMITTER_EMAIL = env.GIT_COMMITTER_EMAIL;
}
switch (env.BRANCH_NAME) { switch (env.BRANCH_NAME) {
case 'master': case 'master':
@ -39,7 +44,7 @@ pipeline {
break break
} }
} }
echo "Committer: ${env.GIT_COMMITTER_EMAIL}" sh 'printenv'
} }
} }
stage('Install') { stage('Install') {
@ -143,7 +148,7 @@ pipeline {
} }
} }
if (!env.GIT_COMMITTER_EMAIL) return if (!env.COMMITTER_EMAIL) return
try { try {
mail( mail(
to: env.GIT_COMMITTER_EMAIL, to: env.GIT_COMMITTER_EMAIL,

View File

@ -1,7 +1,8 @@
import selectors from '../../helpers/selectors.js'; import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/nightmare'; import createNightmare from '../../helpers/nightmare';
describe('Ticket Edit sale path', () => { // #1152 refactor ticket.sale, update price no funciona correctamente.
xdescribe('Ticket Edit sale path', () => {
const nightmare = createNightmare(); const nightmare = createNightmare();
beforeAll(() => { beforeAll(() => {
@ -156,8 +157,7 @@ describe('Ticket Edit sale path', () => {
it('should update the price', async() => { it('should update the price', async() => {
const result = await nightmare const result = await nightmare
.waitToClick(selectors.ticketSales.firstSalePrice) .waitToClick(selectors.ticketSales.firstSalePrice)
.write(selectors.ticketSales.firstSalePriceInput, 5) .write(selectors.ticketSales.firstSalePriceInput, '5\u000d')
.write('body', '\u000d') // simulates enter
.waitForLastSnackbar(); .waitForLastSnackbar();
expect(result).toEqual('Data saved!'); expect(result).toEqual('Data saved!');
@ -181,8 +181,8 @@ describe('Ticket Edit sale path', () => {
const result = await nightmare const result = await nightmare
.waitToClick(selectors.ticketSales.firstSaleDiscount) .waitToClick(selectors.ticketSales.firstSaleDiscount)
.wait('vn-textfield[label="Discount"] > div[class="container selected"]') // a function selects the text after it's loaded .wait('vn-textfield[label="Discount"] > div[class="container selected"]') // a function selects the text after it's loaded
.write(selectors.ticketSales.firstSaleDiscountInput, 50) .write(selectors.ticketSales.firstSaleDiscountInput, '50\u000d')
.write('body', '\u000d') // simulates enter // .write('body', '\u000d') // simulates enter
.waitForLastSnackbar(); .waitForLastSnackbar();
expect(result).toEqual('Data saved!'); expect(result).toEqual('Data saved!');
@ -318,7 +318,7 @@ describe('Ticket Edit sale path', () => {
it('should go back to the original ticket sales section', async() => { it('should go back to the original ticket sales section', async() => {
const url = await nightmare const url = await nightmare
.waitToClick(selectors.itemsIndex.goBackToModuleIndexButton) .waitToClick(selectors.itemsIndex.goBackToModuleIndexButton)
.write(selectors.ticketsIndex.searchTicketInput, 'id:16') .write(selectors.ticketsIndex.searchTicketInput, '16')
.waitToClick(selectors.ticketsIndex.searchButton) .waitToClick(selectors.ticketsIndex.searchButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1) .waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
.waitForTextInElement(selectors.ticketsIndex.searchResult, 'address 21') .waitForTextInElement(selectors.ticketsIndex.searchResult, 'address 21')
@ -488,7 +488,7 @@ describe('Ticket Edit sale path', () => {
it('should now search for a specific ticket', async() => { it('should now search for a specific ticket', async() => {
const result = await nightmare const result = await nightmare
.write(selectors.ticketsIndex.searchTicketInput, 'id:16') .write(selectors.ticketsIndex.searchTicketInput, '16')
.waitToClick(selectors.ticketsIndex.searchButton) .waitToClick(selectors.ticketsIndex.searchButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1) .waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
.countElement(selectors.ticketsIndex.searchResult); .countElement(selectors.ticketsIndex.searchResult);
@ -574,7 +574,7 @@ describe('Ticket Edit sale path', () => {
it('should once again search for a specific ticket', async() => { it('should once again search for a specific ticket', async() => {
const result = await nightmare const result = await nightmare
.write(selectors.ticketsIndex.searchTicketInput, 'id:16') .write(selectors.ticketsIndex.searchTicketInput, '16')
.waitToClick(selectors.ticketsIndex.searchButton) .waitToClick(selectors.ticketsIndex.searchButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1) .waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
.countElement(selectors.ticketsIndex.searchResult); .countElement(selectors.ticketsIndex.searchResult);

View File

@ -75,9 +75,9 @@ export default class ArrayModel extends ModelProxy {
}); });
} }
data.sort((a, b) => this.sortFunc(a, b, orderComp)); data = data.sort((a, b) => this.sortFunc(a, b, orderComp));
} else if (typeof order === 'function') } else if (typeof order === 'function')
data.sort(order); data = data.sort(order);
this.skip = skip; this.skip = skip;

View File

@ -1,13 +1,13 @@
<md-checkbox <md-checkbox
aria-label="::$ctrl.label" aria-label="::$ctrl.label"
md-indeterminate="$ctrl.isIntermediate" md-indeterminate="$ctrl.isIntermediate"
ng-disabled="$ctrl.disabled" ng-disabled="::$ctrl.disabled"
ng-checked="$ctrl.isChecked" ng-checked="$ctrl.isChecked"
ng-model="$ctrl.model"> ng-model="$ctrl.model">
<span translate>{{::$ctrl.label}}</span> <span translate>{{::$ctrl.label}}</span>
</md-checkbox> </md-checkbox>
<i class="material-icons" <i class="material-icons"
ng-if="$ctrl.hasInfo" ng-if="::$ctrl.hasInfo"
vn-tooltip="{{$ctrl.info}}"> vn-tooltip="{{::$ctrl.info}}">
info_outline info_outline
</i> </i>

View File

@ -14,4 +14,8 @@ vn-check {
md-checkbox.md-checked .md-icon { md-checkbox.md-checked .md-icon {
background-color: $color-main; background-color: $color-main;
} }
md-checkbox {
margin-bottom: 0
}
} }

View File

@ -4,8 +4,8 @@
<section <section
class="inline-tag ellipsize" class="inline-tag ellipsize"
ng-class="::{empty: !fetchedTag.value}" ng-class="::{empty: !fetchedTag.value}"
ng-repeat="fetchedTag in $ctrl.tags track by $index" ng-repeat="fetchedTag in $ctrl.tags track by $index"
vn-tooltip="{{::fetchedTag.name}}: {{::fetchedTag.value}}"> title="{{::fetchedTag.name}}: {{::fetchedTag.value}}">
{{::fetchedTag.value}} {{::fetchedTag.value}}
</section> </section>
</vn-auto> </vn-auto>

View File

@ -155,7 +155,6 @@ vn-table {
padding: 1.5em; padding: 1.5em;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
color: $color-font-secondary;
} }
} }
vn-autocomplete { vn-autocomplete {

View File

@ -19,7 +19,7 @@ input[type=reset]::-moz-focus-inner {
user-select: none; user-select: none;
} }
a , .link{ a, .link{
color: $color-font-link; color: $color-font-link;
text-decoration: none; text-decoration: none;
} }
@ -153,7 +153,7 @@ a.vn-list-item {
} }
.vn-list-item { .vn-list-item {
padding: $pad-medium; padding: $pad-medium;
border-bottom: $border-thin solid $color-spacer-light; border-bottom: $border-thin solid $color-spacer;
display: block; display: block;
text-decoration: none; text-decoration: none;
@ -184,7 +184,6 @@ vn-empty-rows.vn-list-item {
text-align: center; text-align: center;
padding: 1.5em; padding: 1.5em;
box-sizing: border-box; box-sizing: border-box;
color: $color-font-secondary;
} }
/** START - FORM ELEMENTS DISABLED **/ /** START - FORM ELEMENTS DISABLED **/

View File

@ -89,12 +89,33 @@ module.exports = Self => {
filter = { filter = {
where: {claimFk: id}, where: {claimFk: id},
include: [ include: [
{relation: 'claimResponsible'}, {
{relation: 'worker'}, relation: 'claimResponsible'
{relation: 'claimDestination'}, },
{relation: 'claimReason'}, {
{relation: 'claimResult'}, relation: 'worker',
{relation: 'claimRedelivery'} scope: {
fields: ['userFk'],
include: {
relation: 'user',
scope: {
fields: ['nickname']
}
}
}
},
{
relation: 'claimDestination'
},
{
relation: 'claimReason'
},
{
relation: 'claimResult'
},
{
relation: 'claimRedelivery'
}
] ]
}; };
promises.push(Self.app.models.ClaimDevelopment.find(filter)); promises.push(Self.app.models.ClaimDevelopment.find(filter));

View File

@ -37,12 +37,18 @@
ui-sref="claim.card.summary({id: claim.id})"> ui-sref="claim.card.summary({id: claim.id})">
<vn-td number>{{::claim.id}}</vn-td> <vn-td number>{{::claim.id}}</vn-td>
<vn-td expand> <vn-td expand>
<span class="link" ng-click="$ctrl.showDescriptor($event, claim.client.id)"> <span class="link" ng-click="$ctrl.showClientDescriptor($event, claim.client.id)">
{{::claim.client.name}} {{::claim.client.name}}
</span> </span>
</vn-td> </vn-td>
<vn-td center>{{::claim.created | date:'dd/MM/yyyy'}}</vn-td> <vn-td center>{{::claim.created | date:'dd/MM/yyyy'}}</vn-td>
<vn-td expand>{{::claim.worker.user.nickname}}</vn-td> <vn-td expand>
<span
class="link"
ng-click="$ctrl.showWorkerDescriptor($event, claim.worker.user.id)">
{{::claim.worker.user.nickname}}
</span>
</vn-td>
<vn-td> <vn-td>
<span class="chip {{::$ctrl.stateColor(claim)}}"> <span class="chip {{::$ctrl.stateColor(claim)}}">
{{::claim.claimState.description}} {{::claim.claimState.description}}
@ -61,7 +67,11 @@
</vn-card> </vn-card>
<vn-pagination model="model"></vn-pagination> <vn-pagination model="model"></vn-pagination>
</div> </div>
<vn-client-descriptor-popover vn-id="descriptor"></vn-client-descriptor-popover> <vn-client-descriptor-popover vn-id="clientDescriptor"></vn-client-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker">
</vn-worker-descriptor-popover>
<vn-dialog class="dialog-summary" <vn-dialog class="dialog-summary"
vn-id="dialog-summary-claim"> vn-id="dialog-summary-claim">
<tpl-body> <tpl-body>

View File

@ -65,14 +65,22 @@ export default class Controller {
} }
} }
showDescriptor(event, clientFk) { showClientDescriptor(event, clientFk) {
this.$.descriptor.clientFk = clientFk; this.$.clientDescriptor.clientFk = clientFk;
this.$.descriptor.parent = event.target; this.$.clientDescriptor.parent = event.target;
this.$.descriptor.show(); this.$.clientDescriptor.show();
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
} }
showWorkerDescriptor(event, userId) {
event.preventDefault();
event.stopImmediatePropagation();
this.selectedWorker = userId;
this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show();
}
preview(event, claim) { preview(event, claim) {
this.claimSelected = claim; this.claimSelected = claim;
this.$.dialogSummaryClaim.show(); this.$.dialogSummaryClaim.show();

View File

@ -3,7 +3,7 @@
"name": "Claims", "name": "Claims",
"icon": "icon-claims", "icon": "icon-claims",
"validations": true, "validations": true,
"dependencies": ["item", "client"], "dependencies": ["worker", "item", "client"],
"menu": [ "menu": [
{"state": "claim.card.basicData", "icon": "settings"}, {"state": "claim.card.basicData", "icon": "settings"},
{"state": "claim.card.detail", "icon": "icon-details"}, {"state": "claim.card.detail", "icon": "icon-details"},

View File

@ -54,7 +54,7 @@
<vn-tr ng-repeat="saleClaimed in $ctrl.summary.salesClaimed"> <vn-tr ng-repeat="saleClaimed in $ctrl.summary.salesClaimed">
<vn-td number> <vn-td number>
<span <span
ng-click="$ctrl.showDescriptor($event, saleClaimed.sale.itemFk)" ng-click="$ctrl.showItemDescriptor($event, saleClaimed.sale.itemFk)"
class="link"> class="link">
{{saleClaimed.sale.itemFk | zeroFill:6}} {{saleClaimed.sale.itemFk | zeroFill:6}}
</span> </span>
@ -90,7 +90,13 @@
<vn-td>{{development.claimReason.description}}</vn-td> <vn-td>{{development.claimReason.description}}</vn-td>
<vn-td>{{development.claimResult.description}}</vn-td> <vn-td>{{development.claimResult.description}}</vn-td>
<vn-td>{{development.claimResponsible.description}}</vn-td> <vn-td>{{development.claimResponsible.description}}</vn-td>
<vn-td>{{development.worker.firstName}}</vn-td> <vn-td expand>
<span
class="link"
ng-click="$ctrl.showWorkerDescriptor($event, development.worker.user.id)">
{{::development.worker.user.nickname}}
</span>
</vn-td>
<vn-td>{{development.claimRedelivery.description}}</vn-td> <vn-td>{{development.claimRedelivery.description}}</vn-td>
</vn-tr> </vn-tr>
</vn-tbody> </vn-tbody>
@ -115,7 +121,7 @@
<vn-tr ng-repeat="action in $ctrl.summary.actions"> <vn-tr ng-repeat="action in $ctrl.summary.actions">
<vn-td number> <vn-td number>
<span <span
ng-click="$ctrl.showDescriptor($event, action.sale.itemFk)" ng-click="$ctrl.showItemDescriptor($event, action.sale.itemFk)"
class="link"> class="link">
{{action.sale.itemFk | zeroFill:6}} {{action.sale.itemFk | zeroFill:6}}
</span> </span>
@ -138,6 +144,10 @@
</vn-horizontal> </vn-horizontal>
</vn-card> </vn-card>
<vn-item-descriptor-popover <vn-item-descriptor-popover
vn-id="descriptor" vn-id="itemDescriptor"
quicklinks="$ctrl.quicklinks"> quicklinks="$ctrl.quicklinks">
</vn-item-descriptor-popover> </vn-item-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker">
</vn-worker-descriptor-popover>

View File

@ -1,7 +1,7 @@
import ngModule from '../module'; import ngModule from '../module';
class Controller { class Controller {
constructor($http, $scope) { constructor($scope, $http) {
this.$http = $http; this.$http = $http;
this.$ = $scope; this.$ = $scope;
} }
@ -17,8 +17,7 @@ class Controller {
this.getSummary(); this.getSummary();
} }
// Item Descriptor showItemDescriptor(event, itemFk) {
showDescriptor(event, itemFk) {
this.quicklinks = { this.quicklinks = {
btnThree: { btnThree: {
icon: 'icon-transaction', icon: 'icon-transaction',
@ -28,13 +27,21 @@ class Controller {
tooltip: 'Item diary' tooltip: 'Item diary'
} }
}; };
this.$.descriptor.itemFk = itemFk; this.$.itemDescriptor.itemFk = itemFk;
this.$.descriptor.parent = event.target; this.$.itemDescriptor.parent = event.target;
this.$.descriptor.show(); this.$.itemDescriptor.show();
}
showWorkerDescriptor(event, userId) {
event.preventDefault();
event.stopImmediatePropagation();
this.selectedWorker = userId;
this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show();
} }
} }
Controller.$inject = ['$http', '$scope']; Controller.$inject = ['$scope', '$http'];
ngModule.component('vnClaimSummary', { ngModule.component('vnClaimSummary', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -1,8 +1,10 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const buildFilter = require('vn-loopback/util/filter').buildFilter;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('filter', { Self.remoteMethodCtx('filter', {
description: 'Find all instances of the model matched by filter from the data source.', description: 'Find all instances of the model matched by filter from the data source.',
accessType: 'READ', accessType: 'READ',
accepts: [ accepts: [
@ -16,6 +18,21 @@ module.exports = Self => {
type: ['Object'], type: ['Object'],
description: 'List of tags to filter with', description: 'List of tags to filter with',
http: {source: 'query'} http: {source: 'query'}
}, {
arg: 'search',
type: 'String',
description: `If it's and integer searchs by id, otherwise it searchs by name`,
http: {source: 'query'}
}, {
arg: 'categoryFk',
type: 'Integer',
description: 'Category id',
http: {source: 'query'}
}, {
arg: 'typeFk',
type: 'Integer',
description: 'Type id',
http: {source: 'query'}
} }
], ],
returns: { returns: {
@ -28,12 +45,34 @@ module.exports = Self => {
} }
}); });
Self.filter = async(filter, tags) => { Self.filter = async(ctx, filter, tags) => {
let conn = Self.dataSource.connector;
let where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {'i.id': {inq: value}}
: {'i.name': {like: `%${value}%`}};
case 'id':
return {'i.id': value};
case 'description':
return {'i.description': {like: `%${value}%`}};
case 'categoryFk':
return {'ic.id': value};
case 'typeFk':
return {'t.id': value};
}
});
filter = mergeFilters(ctx.args.filter, {where});
let stmt = new ParameterizedSQL( let stmt = new ParameterizedSQL(
`SELECT i.id, i.image, i.name, i.description, `SELECT i.id, i.image, i.name, i.description,
i.size, i.tag5, i.value5, i.tag6, i.value6, i.size, i.tag5, i.value5, i.tag6, i.value6,
i.tag7, i.value7, i.tag8, i.value8, i.isActive, i.tag7, i.value7, i.tag8, i.value8, i.isActive,
t.name type, u.nickname userNickname, t.name type, u.nickname userNickname,
t.name type, u.id userId,
intr.description AS intrastat, i.stems, intr.description AS intrastat, i.stems,
ori.code AS origin, t.name AS type, ori.code AS origin, t.name AS type,
ic.name AS category, i.density, tc.description AS taxClass ic.name AS category, i.density, tc.description AS taxClass
@ -48,9 +87,9 @@ module.exports = Self => {
LEFT JOIN taxClass tc ON tc.id = i.taxClassFk` LEFT JOIN taxClass tc ON tc.id = i.taxClassFk`
); );
if (tags) { if (ctx.args.tags) {
let i = 1; let i = 1;
for (let tag of tags) { for (let tag of ctx.args.tags) {
if (tag.value == null) continue; if (tag.value == null) continue;
let tAlias = `it${i++}`; let tAlias = `it${i++}`;
stmt.merge({ stmt.merge({
@ -61,8 +100,7 @@ module.exports = Self => {
}); });
} }
} }
stmt.merge(conn.makeSuffix(filter));
stmt.merge(Self.buildSuffix(filter, 'i'));
return Self.rawStmt(stmt); return Self.rawStmt(stmt);
}; };
}; };

View File

@ -4,11 +4,10 @@ describe('item filter()', () => {
it('should return 1 result using filter and tags', async() => { it('should return 1 result using filter and tags', async() => {
let filter = { let filter = {
order: 'isActive ASC, name', order: 'isActive ASC, name',
limit: 8, limit: 8
where: {and: [{typeFk: 2}]}
}; };
let tags = [{value: 'Gem2', tagFk: 58}]; let tags = [{value: 'Gem2', tagFk: 58}];
let result = await app.models.Item.filter(filter, tags); let result = await app.models.Item.filter({args: {filter: filter, typeFk: 2, tags: tags}});
expect(result.length).toEqual(1); expect(result.length).toEqual(1);
expect(result[0].id).toEqual(2); expect(result[0].id).toEqual(2);

View File

@ -23,7 +23,7 @@
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th>Date</vn-th> <vn-th>Date</vn-th>
<vn-th number order="DESC">Id</vn-th> <vn-th number order="DESC">Ticket/Entry</vn-th>
<vn-th>State</vn-th> <vn-th>State</vn-th>
<vn-th>Reference</vn-th> <vn-th>Reference</vn-th>
<vn-th>Client</vn-th> <vn-th>Client</vn-th>

View File

@ -1,3 +1,4 @@
In: Entrada In: Entrada
Out: Salida Out: Salida
Visible quantity: Cantidad visible Visible quantity: Cantidad visible
Ticket/Entry: Ticket/Entrada

View File

@ -4,22 +4,20 @@
limit="12" limit="12"
order="isActive DESC, name, id" order="isActive DESC, name, id"
data="items" data="items"
auto-load="true"> auto-load="false">
</vn-crud-model> </vn-crud-model>
<div class="content-block"> <div class="content-block">
<div class="vn-list"> <div class="vn-list">
<vn-card pad-medium-h> <vn-card pad-medium-h>
<vn-searchbar <vn-searchbar
panel="vn-item-search-panel" panel="vn-item-search-panel"
model="model" on-search="$ctrl.onSearch($params)"
expr-builder="$ctrl.exprBuilder(param, value)"
param-builder="$ctrl.paramBuilder(param, value)"
vn-focus> vn-focus>
</vn-searchbar> </vn-searchbar>
</vn-card> </vn-card>
</div> </div>
<vn-card margin-medium-v> <vn-card margin-medium-v>
<vn-table model="model" show-fields="$ctrl.showFields" vn-uvc="itemIndex"> <vn-table model="model" auto-load="false" show-fields="$ctrl.showFields" vn-uvc="itemIndex">
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th th-id="picture"></vn-th> <vn-th th-id="picture"></vn-th>
@ -45,14 +43,21 @@
<img <img
ng-src="{{::$ctrl.imagesPath}}/50x50/{{::item.image}}" ng-src="{{::$ctrl.imagesPath}}/50x50/{{::item.image}}"
zoom-image="{{::$ctrl.imagesPath}}/1600x900/{{::item.image}}" zoom-image="{{::$ctrl.imagesPath}}/1600x900/{{::item.image}}"
ng-click="$ctrl.stopEvent($event)"
on-error-src/> on-error-src/>
</vn-td> </vn-td>
<vn-td number>{{::item.id | zeroFill:6}}</vn-td> <vn-td expand>
<span
class="link"
ng-click="$ctrl.showItemDescriptor($event, item.id)">
{{::item.id | zeroFill:6}}
</span>
</vn-td>
<vn-td expand> <vn-td expand>
<vn-fetched-tags <vn-fetched-tags
max-length="6" max-length="6"
item="item" item="::item"
title="item.name"> title="::item.name">
</vn-fetched-tags> </vn-fetched-tags>
</vn-td> </vn-td>
<vn-td number>{{::item.stems}}</vn-td> <vn-td number>{{::item.stems}}</vn-td>
@ -60,13 +65,20 @@
<vn-td>{{::item.category}}</vn-td> <vn-td>{{::item.category}}</vn-td>
<vn-td>{{::item.intrastat}}</vn-td> <vn-td>{{::item.intrastat}}</vn-td>
<vn-td>{{::item.origin}}</vn-td> <vn-td>{{::item.origin}}</vn-td>
<vn-td>{{::item.userNickname}}</vn-td> <vn-td expand>
<span
class="link"
ng-click="$ctrl.showWorkerDescriptor($event, item.userId)">
{{::item.userNickname}}
</span>
</vn-td>
<vn-td>{{::item.density}}</vn-td>
<vn-td number>{{::item.density}}</vn-td> <vn-td number>{{::item.density}}</vn-td>
<vn-td>{{::item.taxClass}}</vn-td> <vn-td>{{::item.taxClass}}</vn-td>
<vn-td> <vn-td>
<vn-check <vn-check
disabled="true" disabled="true"
field="item.isActive"> field="::item.isActive">
</vn-check> </vn-check>
</vn-td> </vn-td>
<vn-td shrink> <vn-td shrink>
@ -102,4 +114,10 @@
on-response="$ctrl.onCloneAccept(response)" on-response="$ctrl.onCloneAccept(response)"
question="Do you want to clone this item?" question="Do you want to clone this item?"
message="All it's properties will be copied"> message="All it's properties will be copied">
</vn-confirm> </vn-confirm>
<vn-item-descriptor-popover vn-id="itemDescriptor">
</vn-item-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker">
</vn-worker-descriptor-popover>

View File

@ -16,48 +16,52 @@ class Controller {
}; };
} }
exprBuilder(param, value) { stopEvent(event) {
switch (param) { event.preventDefault();
case 'search': event.stopImmediatePropagation();
return /^\d+$/.test(value)
? {id: value}
: {name: {like: `%${value}%`}};
case 'name':
case 'description':
return {[param]: {like: `%${value}%`}};
case 'id':
case 'typeFk':
return {[param]: value};
}
} }
showDescriptor(event, itemFk) { onSearch(params) {
if (params)
this.$.model.applyFilter(null, params);
else
this.$.model.clear();
}
showItemDescriptor(event, itemFk) {
if (event.defaultPrevented) return;
event.preventDefault();
event.stopPropagation();
this.quicklinks = { this.quicklinks = {
btnThree: { btnThree: {
icon: 'icon-transaction', icon: 'icon-transaction',
state: `item.card.diary({ state: `item.card.diary({
id: ${itemFk}, id: ${itemFk},
warehouseFk: ${this.ticket.warehouseFk},
ticketFk: ${this.ticket.id}
})`, })`,
tooltip: 'Item diary' tooltip: 'Item diary'
} }
}; };
this.$scope.descriptor.itemFk = itemFk; this.$.itemDescriptor.itemFk = itemFk;
this.$scope.descriptor.parent = event.target; this.$.itemDescriptor.parent = event.target;
this.$scope.descriptor.show(); this.$.itemDescriptor.show();
} }
paramBuilder(param, value) {
switch (param) { showWorkerDescriptor(event, userId) {
case 'tags': if (event.defaultPrevented) return;
return {[param]: value};
} event.preventDefault();
event.stopPropagation();
this.selectedWorker = userId;
this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show();
} }
cloneItem(event, item) { cloneItem(event, item) {
event.preventDefault(); this.stopEvent(event);
event.stopImmediatePropagation();
this.itemSelected = item; this.itemSelected = item;
this.$.clone.show(); this.$.clone.show();
} }
@ -75,8 +79,7 @@ class Controller {
} }
preview(event, item) { preview(event, item) {
event.preventDefault(); this.stopEvent(event);
event.stopImmediatePropagation();
this.itemSelected = item; this.itemSelected = item;
this.$.preview.show(); this.$.preview.show();
} }

View File

@ -3,7 +3,7 @@
"name": "Items", "name": "Items",
"icon": "inbox", "icon": "inbox",
"validations" : true, "validations" : true,
"dependencies": ["client", "ticket"], "dependencies": ["worker", "client", "ticket"],
"menu": [ "menu": [
{"state": "item.card.data", "icon": "settings"}, {"state": "item.card.data", "icon": "settings"},
{"state": "item.card.tags", "icon": "icon-tags"}, {"state": "item.card.tags", "icon": "icon-tags"},

View File

@ -40,6 +40,13 @@
field="filter.typeFk"> field="filter.typeFk">
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Description"
model="filter.description">
</vn-textfield>
</vn-horizontal>
<vn-horizontal ng-repeat="itemTag in filter.tags"> <vn-horizontal ng-repeat="itemTag in filter.tags">
<vn-autocomplete <vn-autocomplete
vn-id="tag" vn-id="tag"
@ -74,13 +81,6 @@
tabindex="-1"> tabindex="-1">
</vn-icon-button> </vn-icon-button>
</vn-horizontal> </vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Description"
model="filter.description">
</vn-textfield>
</vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-icon-button <vn-icon-button
vn-bind="+" vn-bind="+"

View File

@ -111,6 +111,7 @@ module.exports = Self => {
FROM tmp.ticketCalculateItem tci FROM tmp.ticketCalculateItem tci
JOIN vn.item i ON i.id = tci.itemFk JOIN vn.item i ON i.id = tci.itemFk
JOIN vn.itemType it ON it.id = i.typeFk JOIN vn.itemType it ON it.id = i.typeFk
JOIN vn.ink ON ink.id = i.inkFk
JOIN vn.worker w on w.id = it.workerFk`); JOIN vn.worker w on w.id = it.workerFk`);
// Apply order by tag // Apply order by tag

View File

@ -19,6 +19,7 @@
initial-data="$ctrl.field" initial-data="$ctrl.field"
field="$ctrl.field" field="$ctrl.field"
translate-fields="['name']" translate-fields="['name']"
order="name"
show-field="name" show-field="name"
value-field="field" value-field="field"
label="Order by"> label="Order by">

View File

@ -12,7 +12,9 @@ class Controller {
{way: 'DESC', name: 'Descendant'}, {way: 'DESC', name: 'Descendant'},
]; ];
this.defaultFieldList = [ this.defaultFieldList = [
{field: 'relevancy DESC, name', name: 'Name'}, {field: 'relevancy DESC, name', name: 'Default'},
{field: 'showOrder, price', name: 'Color'},
{field: 'name', name: 'Name'},
{field: 'price', name: 'Price'} {field: 'price', name: 'Price'}
]; ];
this.fieldList = []; this.fieldList = [];
@ -27,20 +29,18 @@ class Controller {
*/ */
onDataChange() { onDataChange() {
const items = this.$scope.model.data; const items = this.$scope.model.data;
const newFilterList = [];
if (!items) return; if (!items) return;
this.fieldList = [];
this.fieldList = this.fieldList.concat(this.defaultFieldList);
items.forEach(item => { items.forEach(item => {
// Add new tag filters
item.tags.forEach(itemTag => { item.tags.forEach(itemTag => {
const alreadyAdded = this.fieldList.find(order => { const alreadyAdded = newFilterList.findIndex(filter => {
return order.field == itemTag.tagFk; return filter.field == itemTag.tagFk;
}); });
if (!alreadyAdded) { if (alreadyAdded == -1) {
this.fieldList.push({ newFilterList.push({
name: itemTag.name, name: itemTag.name,
field: itemTag.tagFk, field: itemTag.tagFk,
isTag: true isTag: true
@ -48,6 +48,20 @@ class Controller {
} }
}); });
}); });
// Add default filters - Replaces tags with same name
this.defaultFieldList.forEach(defaultField => {
const index = newFilterList.findIndex(newfield => {
return newfield.name == defaultField.name;
});
if (index > -1)
newFilterList[index] = defaultField;
else
newFilterList.push(defaultField);
});
this.fieldList = newFilterList;
} }
/** /**

View File

@ -17,18 +17,17 @@ describe('Order', () => {
describe('onDataChange()', () => { describe('onDataChange()', () => {
it(`should return an object with order params`, () => { it(`should return an object with order params`, () => {
let expectedList = [
{field: 'relevancy DESC, name', name: 'Name'},
{field: 'price', name: 'Price'},
{field: 4, name: 'Length', isTag: true}
];
$scope.model.data = [{id: 1, name: 'My Item', tags: [ $scope.model.data = [{id: 1, name: 'My Item', tags: [
{tagFk: 4, name: 'Length'} {tagFk: 4, name: 'Length'},
{tagFk: 5, name: 'Color'}
]}]; ]}];
let expectedResult = [{field: 'showOrder, price', name: 'Color'}];
let unexpectedResult = [{tagFk: 5, name: 'Color'}];
controller.onDataChange(); controller.onDataChange();
expect(controller.fieldList).toEqual(expectedList); expect(controller.fieldList.length).toEqual(5);
expect(controller.fieldList).toEqual(jasmine.arrayContaining(expectedResult));
expect(controller.fieldList).not.toEqual(jasmine.arrayContaining(unexpectedResult));
}); });
}); });

View File

@ -36,11 +36,17 @@
ui-sref="order.card.summary({id: {{::order.id}}})"> ui-sref="order.card.summary({id: {{::order.id}}})">
<vn-td number>{{::order.id}}</vn-td> <vn-td number>{{::order.id}}</vn-td>
<vn-td expand> <vn-td expand>
<span class="link" ng-click="$ctrl.showDescriptor($event, order.clientFk)"> <span class="link" ng-click="$ctrl.showClientDescriptor($event, order.clientFk)">
{{::order.client.name}} {{::order.client.name}}
</span> </span>
</vn-td> </vn-td>
<vn-td>{{::order.client.salesPerson.user.nickname}}</vn-td> <vn-td expand>
<span
class="link"
ng-click="$ctrl.showWorkerDescriptor($event, order.client.salesPerson.user.id)">
{{::order.client.salesPerson.user.nickname | dashIfEmpty}}
</span>
</vn-td>
<vn-td center> <vn-td center>
<vn-check <vn-check
field="order.isConfirmed" field="order.isConfirmed"
@ -68,8 +74,12 @@
<vn-float-button icon="add"></vn-float-button> <vn-float-button icon="add"></vn-float-button>
</a> </a>
<vn-client-descriptor-popover <vn-client-descriptor-popover
vn-id="descriptor"> vn-id="clientDescriptor">
</vn-client-descriptor-popover> </vn-client-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker">
</vn-worker-descriptor-popover>
<vn-dialog <vn-dialog
vn-id="summary" vn-id="summary"
class="dialog-summary"> class="dialog-summary">

View File

@ -2,7 +2,7 @@ import ngModule from '../module';
export default class Controller { export default class Controller {
constructor($scope) { constructor($scope) {
this.$scope = $scope; this.$ = $scope;
this.ticketSelected = null; this.ticketSelected = null;
this.filter = { this.filter = {
include: [ include: [
@ -51,23 +51,31 @@ export default class Controller {
} }
} }
showDescriptor(event, clientFk) { showClientDescriptor(event, clientFk) {
this.$scope.descriptor.clientFk = clientFk; this.$.clientDescriptor.clientFk = clientFk;
this.$scope.descriptor.parent = event.target; this.$.clientDescriptor.parent = event.target;
this.$scope.descriptor.show(); this.$.clientDescriptor.show();
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
} }
showWorkerDescriptor(event, userId) {
event.preventDefault();
event.stopImmediatePropagation();
this.selectedWorker = userId;
this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show();
}
onDescriptorLoad() { onDescriptorLoad() {
this.$scope.popover.relocate(); this.$.popover.relocate();
} }
preview(event, order) { preview(event, order) {
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
this.selectedOrder = order; this.selectedOrder = order;
this.$scope.summary.show(); this.$.summary.show();
} }
} }

View File

@ -3,7 +3,7 @@
"name": "Orders", "name": "Orders",
"icon": "shopping_cart", "icon": "shopping_cart",
"validations": true, "validations": true,
"dependencies": ["item", "ticket"], "dependencies": ["worker", "item", "ticket"],
"menu": [ "menu": [
{"state": "order.card.basicData", "icon": "settings"}, {"state": "order.card.basicData", "icon": "settings"},
{"state": "order.card.catalog", "icon": "shopping_cart"}, {"state": "order.card.catalog", "icon": "shopping_cart"},

View File

@ -33,14 +33,15 @@ module.exports = Self => {
e.checked, e.checked,
i2.name nameBox, i2.name nameBox,
e.itemFk, e.itemFk,
w.name nameWorker, u.nickname userNickname,
w.firstName, u.id userId,
e.created e.created
FROM FROM
vn.expedition e vn.expedition e
LEFT JOIN vn.item i2 ON i2.id = e.itemFk LEFT JOIN vn.item i2 ON i2.id = e.itemFk
INNER JOIN vn.item i1 ON i1.id = e.isBox INNER JOIN vn.item i1 ON i1.id = e.isBox
LEFT JOIN vn.worker w ON w.id = e.workerFk LEFT JOIN vn.worker w ON w.id = e.workerFk
JOIN account.user u ON u.id = w.id
`); `);
stmt.merge(Self.buildSuffix(filter, 'e')); stmt.merge(Self.buildSuffix(filter, 'e'));

View File

@ -32,6 +32,7 @@ module.exports = Self => {
st.created, st.created,
st.workerFk, st.workerFk,
u.nickname userNickname, u.nickname userNickname,
u.id userId,
ste.name AS state ste.name AS state
FROM saleTracking st FROM saleTracking st
JOIN sale s ON s.id = st.saleFk JOIN sale s ON s.id = st.saleFk

View File

@ -183,7 +183,8 @@ module.exports = Self => {
ts.stateFk as stateFk, ts.stateFk as stateFk,
ts.alertLevel as alertLevel, ts.alertLevel as alertLevel,
ts.code as alertLevelCode, ts.code as alertLevelCode,
u.nickname userNickname u.nickname userNickname,
u.id userId
FROM ticket t FROM ticket t
LEFT JOIN address a ON a.id = t.addressFk LEFT JOIN address a ON a.id = t.addressFk
LEFT JOIN province p ON p.id = a.provinceFk LEFT JOIN province p ON p.id = a.provinceFk

View File

@ -33,7 +33,7 @@
<vn-td number> <vn-td number>
<span <span
ng-class="{link: expedition.itemFk}" ng-class="{link: expedition.itemFk}"
ng-click="$ctrl.showDescriptor($event, expedition.itemFk)"> ng-click="$ctrl.showItemDescriptor($event, expedition.itemFk)">
{{expedition.itemFk | zeroFill:6}} {{expedition.itemFk | zeroFill:6}}
</span> </span>
</vn-td> </vn-td>
@ -41,7 +41,14 @@
<vn-td>{{::expedition.nameBox}}</vn-td> <vn-td>{{::expedition.nameBox}}</vn-td>
<vn-td number>{{::expedition.counter}}</vn-td> <vn-td number>{{::expedition.counter}}</vn-td>
<vn-td number>{{::expedition.checked}}</vn-td> <vn-td number>{{::expedition.checked}}</vn-td>
<vn-td>{{::expedition.firstName}} {{::expedition.nameWorker}}</vn-td> <vn-td>{{::expedition.userNickname}}</vn-td>
<vn-td expand>
<span
class="link"
ng-click="$ctrl.showWorkerDescriptor($event, expedition.userId)">
{{::expedition.userNickname | dashIfEmpty}}
</span>
</vn-td>
<vn-td>{{::expedition.created | date:'dd/MM/yyyy HH:mm'}}</vn-td> <vn-td>{{::expedition.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr> </vn-tr>
</vn-tbody> </vn-tbody>
@ -51,9 +58,14 @@
</vn-card> </vn-card>
</vn-vertical> </vn-vertical>
<vn-item-descriptor-popover vn-id="descriptor" <vn-item-descriptor-popover
vn-id="itemDescriptor"
quicklinks="$ctrl.quicklinks"> quicklinks="$ctrl.quicklinks">
</vn-item-descriptor-popover> </vn-item-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker">
</vn-worker-descriptor-popover>
<vn-confirm <vn-confirm
vn-id="delete-expedition" vn-id="delete-expedition"
on-response="$ctrl.returnDialog(response)" on-response="$ctrl.returnDialog(response)"

View File

@ -2,25 +2,25 @@ import ngModule from '../module';
class Controller { class Controller {
constructor($scope, $stateParams, $http) { constructor($scope, $stateParams, $http) {
this.$scope = $scope; this.$ = $scope;
this.$stateParams = $stateParams; this.$stateParams = $stateParams;
this.$http = $http; this.$http = $http;
} }
deleteExpedition(expedition) { deleteExpedition(expedition) {
this.expeditionId = expedition.id; this.expeditionId = expedition.id;
this.$scope.deleteExpedition.show(); this.$.deleteExpedition.show();
} }
returnDialog(response) { returnDialog(response) {
if (response === 'ACCEPT') { if (response === 'ACCEPT') {
this.$http.delete(`/ticket/api/Expeditions/${this.expeditionId}`).then( this.$http.delete(`/ticket/api/Expeditions/${this.expeditionId}`).then(
() => this.$scope.model.refresh() () => this.$.model.refresh()
); );
} }
} }
showDescriptor(event, itemFk) { showItemDescriptor(event, itemFk) {
if (!itemFk) return; if (!itemFk) return;
this.quicklinks = { this.quicklinks = {
btnThree: { btnThree: {
@ -33,13 +33,21 @@ class Controller {
tooltip: 'Item diary', tooltip: 'Item diary',
}, },
}; };
this.$scope.descriptor.itemFk = itemFk; this.$.itemDescriptor.itemFk = itemFk;
this.$scope.descriptor.parent = event.target; this.$.itemDescriptor.parent = event.target;
this.$scope.descriptor.show(); this.$.itemDescriptor.show();
}
showWorkerDescriptor(event, userId) {
event.preventDefault();
event.stopImmediatePropagation();
this.selectedWorker = userId;
this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show();
} }
onDescriptorLoad() { onDescriptorLoad() {
this.$scope.popover.relocate(); this.$.popover.relocate();
} }
} }

View File

@ -59,7 +59,13 @@
</vn-icon> </vn-icon>
</vn-td> </vn-td>
<vn-td number>{{::ticket.id}}</vn-td> <vn-td number>{{::ticket.id}}</vn-td>
<vn-td>{{::ticket.userNickname | dashIfEmpty}}</vn-td> <vn-td expand>
<span
class="link"
ng-click="$ctrl.showWorkerDescriptor($event, ticket.userId)">
{{::ticket.userNickname | dashIfEmpty}}
</span>
</vn-td>
<vn-td> <vn-td>
<span class="chip {{$ctrl.compareDate(ticket.shipped)}}"> <span class="chip {{$ctrl.compareDate(ticket.shipped)}}">
{{::ticket.shipped | dateTime: 'dd/MM/yyyy'}} {{::ticket.shipped | dateTime: 'dd/MM/yyyy'}}
@ -69,7 +75,7 @@
<vn-td expand> <vn-td expand>
<span <span
class="link" class="link"
ng-click="$ctrl.showDescriptor($event, ticket.clientFk)"> ng-click="$ctrl.showClientDescriptor($event, ticket.clientFk)">
{{::ticket.nickname}} {{::ticket.nickname}}
</span> </span>
</vn-td> </vn-td>
@ -110,5 +116,9 @@
<vn-ticket-summary ticket="$ctrl.selectedTicket"></vn-ticket-summary> <vn-ticket-summary ticket="$ctrl.selectedTicket"></vn-ticket-summary>
</tpl-body> </tpl-body>
</vn-dialog> </vn-dialog>
<vn-client-descriptor-popover vn-id="descriptor"> <vn-client-descriptor-popover vn-id="clientDescriptor">
</vn-client-descriptor-popover> </vn-client-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker">
</vn-worker-descriptor-popover>

View File

@ -88,12 +88,20 @@ export default class Controller {
return 'alert'; return 'alert';
} }
showDescriptor(event, clientFk) { showClientDescriptor(event, clientFk) {
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
this.$.descriptor.clientFk = clientFk; this.$.clientDescriptor.clientFk = clientFk;
this.$.descriptor.parent = event.target; this.$.clientDescriptor.parent = event.target;
this.$.descriptor.show(); this.$.clientDescriptor.show();
}
showWorkerDescriptor(event, userId) {
event.preventDefault();
event.stopImmediatePropagation();
this.selectedWorker = userId;
this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show();
} }
preview(event, ticket) { preview(event, ticket) {

View File

@ -14,12 +14,13 @@ describe('Component vnTicketIndex', () => {
}]; }];
beforeEach(() => { beforeEach(() => {
ngModule('worker');
ngModule('client'); ngModule('client');
ngModule('item'); ngModule('item');
ngModule('ticket'); ngModule('ticket');
}); });
beforeEach(inject(($compile, $rootScope, $httpBackend, _$window_) => { beforeEach(inject(($compile, $rootScope, _$window_) => {
$window = _$window_; $window = _$window_;
$element = $compile('<vn-ticket-index></vn-ticket-index>')($rootScope); $element = $compile('<vn-ticket-index></vn-ticket-index>')($rootScope);
controller = $element.controller('vnTicketIndex'); controller = $element.controller('vnTicketIndex');
@ -54,18 +55,18 @@ describe('Component vnTicketIndex', () => {
}); });
}); });
describe('showDescriptor()', () => { describe('showClientDescriptor()', () => {
it('should show the descriptor popover', () => { it('should show the client descriptor popover', () => {
spyOn(controller.$.descriptor, 'show'); spyOn(controller.$.clientDescriptor, 'show');
let event = new MouseEvent('click', { let event = new MouseEvent('click', {
view: $window, view: $window,
bubbles: true, bubbles: true,
cancelable: true cancelable: true
}); });
controller.showDescriptor(event, tickets[0].clientFk); controller.showClientDescriptor(event, tickets[0].clientFk);
expect(controller.$.descriptor.show).toHaveBeenCalledWith(); expect(controller.$.clientDescriptor.show).toHaveBeenCalledWith();
}); });
}); });

View File

@ -35,14 +35,26 @@
<vn-td number>{{::request.id}}</vn-td> <vn-td number>{{::request.id}}</vn-td>
<vn-td expand>{{::request.description}}</vn-td> <vn-td expand>{{::request.description}}</vn-td>
<vn-td number>{{::request.created | dateTime: 'dd/MM/yyyy'}}</vn-td> <vn-td number>{{::request.created | dateTime: 'dd/MM/yyyy'}}</vn-td>
<vn-td>{{::request.requester.user.nickname}}</vn-td> <vn-td expand>
<vn-td>{{::request.atender.user.nickname}}</vn-td> <span
class="link"
ng-click="$ctrl.showWorkerDescriptor($event, request.requester.user.id)">
{{::request.requester.user.nickname | dashIfEmpty}}
</span>
</vn-td>
<vn-td expand>
<span
class="link"
ng-click="$ctrl.showWorkerDescriptor($event, request.atender.user.id)">
{{::request.atender.user.nickname | dashIfEmpty}}
</span>
</vn-td>
<vn-td number>{{::request.quantity}}</vn-td> <vn-td number>{{::request.quantity}}</vn-td>
<vn-td number>{{::request.price | currency: 'EUR': 2}}</vn-td> <vn-td number>{{::request.price | currency: 'EUR': 2}}</vn-td>
<vn-td number> <vn-td number>
<span <span
ng-show="::request.saleFk" ng-show="::request.saleFk"
ng-click="$ctrl.showDescriptor($event, request.sale)" ng-click="$ctrl.showItemDescriptor($event, request.sale)"
class="link"> class="link">
{{request.saleFk | zeroFill:6}} {{request.saleFk | zeroFill:6}}
</span> </span>
@ -69,9 +81,13 @@
</vn-card> </vn-card>
</form> </form>
<vn-item-descriptor-popover <vn-item-descriptor-popover
vn-id="descriptor" vn-id="itemDescriptor"
quicklinks="$ctrl.quicklinks"> quicklinks="$ctrl.quicklinks">
</vn-item-descriptor-popover> </vn-item-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker">
</vn-worker-descriptor-popover>
<a ui-sref="ticket.card.request.create" <a ui-sref="ticket.card.request.create"
vn-tooltip="New request" vn-tooltip="New request"
vn-bind="+" vn-bind="+"

View File

@ -1,9 +1,9 @@
import ngModule from '../../module'; import ngModule from '../../module';
class Controller { class Controller {
constructor($stateParams, $scope) { constructor($scope, $stateParams) {
this.$stateParams = $stateParams; this.$stateParams = $stateParams;
this.$scope = $scope; this.$ = $scope;
this.filter = { this.filter = {
include: [ include: [
{ {
@ -36,15 +36,15 @@ class Controller {
} }
removeLine(index) { removeLine(index) {
this.$scope.model.remove(index); this.$.model.remove(index);
this.$scope.watcher.check(); this.$.watcher.check();
this.$scope.model.save().then(() => { this.$.model.save().then(() => {
this.$scope.watcher.notifySaved(); this.$.watcher.notifySaved();
this.$scope.watcher.updateOriginalData(); this.$.watcher.updateOriginalData();
}); });
} }
// Item Descriptor
showDescriptor(event, sale) { showItemDescriptor(event, sale) {
this.quicklinks = { this.quicklinks = {
btnThree: { btnThree: {
icon: 'icon-transaction', icon: 'icon-transaction',
@ -55,13 +55,21 @@ class Controller {
tooltip: 'Item diary' tooltip: 'Item diary'
} }
}; };
this.$scope.descriptor.itemFk = sale.itemFk; this.$.itemDescriptor.itemFk = sale.itemFk;
this.$scope.descriptor.parent = event.target; this.$.itemDescriptor.parent = event.target;
this.$scope.descriptor.show(); this.$.itemDescriptor.show();
}
showWorkerDescriptor(event, userId) {
event.preventDefault();
event.stopImmediatePropagation();
this.selectedWorker = userId;
this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show();
} }
} }
Controller.$inject = ['$stateParams', '$scope']; Controller.$inject = ['$scope', '$stateParams'];
ngModule.component('vnTicketRequestIndex', { ngModule.component('vnTicketRequestIndex', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -3,7 +3,7 @@
"name": "Tickets", "name": "Tickets",
"icon": "icon-ticket", "icon": "icon-ticket",
"validations": true, "validations": true,
"dependencies": ["item", "client"], "dependencies": ["worker", "item", "client"],
"menu": [ "menu": [
{"state": "ticket.card.data.stepOne", "icon": "settings"}, {"state": "ticket.card.data.stepOne", "icon": "settings"},
{"state": "ticket.card.sale", "icon": "icon-lines"}, {"state": "ticket.card.sale", "icon": "icon-lines"},

View File

@ -32,7 +32,7 @@
</vn-td> </vn-td>
<vn-td number> <vn-td number>
<span <span
ng-click="$ctrl.showDescriptor($event, sale.itemFk)" ng-click="$ctrl.showItemDescriptor($event, sale.itemFk)"
class="link"> class="link">
{{sale.itemFk | zeroFill:6}} {{sale.itemFk | zeroFill:6}}
</span> </span>
@ -40,9 +40,12 @@
<vn-td><vn-fetched-tags max-length="6" item="sale.item"/></vn-td> <vn-td><vn-fetched-tags max-length="6" item="sale.item"/></vn-td>
<vn-td>{{::sale.quantity}}</vn-td> <vn-td>{{::sale.quantity}}</vn-td>
<vn-td>{{::sale.originalQuantity}}</vn-td> <vn-td>{{::sale.originalQuantity}}</vn-td>
<vn-td title="{{::sale.firstName}} {{::sale.name}}" <vn-td expand>
class="ellipsize" style="max-width: 5em"> <span
{{::sale.userNickname}} class="link"
ng-click="$ctrl.showWorkerDescriptor($event, sale.userId)">
{{::sale.userNickname | dashIfEmpty}}
</span>
</vn-td> </vn-td>
<vn-td>{{::sale.state}}</vn-td> <vn-td>{{::sale.state}}</vn-td>
<vn-td>{{::sale.created | date: 'dd/MM/yyyy HH:mm'}}</vn-td> <vn-td>{{::sale.created | date: 'dd/MM/yyyy HH:mm'}}</vn-td>
@ -53,6 +56,10 @@
<vn-pagination model="model"></vn-pagination> <vn-pagination model="model"></vn-pagination>
</vn-card> </vn-card>
</vn-vertical> </vn-vertical>
<vn-item-descriptor-popover vn-id="descriptor" <vn-item-descriptor-popover vn-id="itemDescriptor"
quicklinks="$ctrl.quicklinks"> quicklinks="$ctrl.quicklinks">
</vn-item-descriptor-popover> </vn-item-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker">
</vn-worker-descriptor-popover>

View File

@ -2,11 +2,11 @@ import ngModule from '../module';
class Controller { class Controller {
constructor($scope, $stateParams) { constructor($scope, $stateParams) {
this.$scope = $scope; this.$ = $scope;
this.$stateParams = $stateParams; this.$stateParams = $stateParams;
} }
showDescriptor(event, itemFk) { showItemDescriptor(event, itemFk) {
this.quicklinks = { this.quicklinks = {
btnThree: { btnThree: {
icon: 'icon-transaction', icon: 'icon-transaction',
@ -18,13 +18,21 @@ class Controller {
tooltip: 'Item diary', tooltip: 'Item diary',
}, },
}; };
this.$scope.descriptor.itemFk = itemFk; this.$.itemDescriptor.itemFk = itemFk;
this.$scope.descriptor.parent = event.target; this.$.itemDescriptor.parent = event.target;
this.$scope.descriptor.show(); this.$.itemDescriptor.show();
}
showWorkerDescriptor(event, userId) {
event.preventDefault();
event.stopImmediatePropagation();
this.selectedWorker = userId;
this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show();
} }
onDescriptorLoad() { onDescriptorLoad() {
this.$scope.popover.relocate(); this.$.popover.relocate();
} }
} }

View File

@ -6,7 +6,6 @@
limit="20" limit="20"
data="trackings"> data="trackings">
</vn-crud-model> </vn-crud-model>
<vn-vertical compact> <vn-vertical compact>
<vn-card pad-large> <vn-card pad-large>
<vn-vertical> <vn-vertical>
@ -22,7 +21,13 @@
<vn-tbody> <vn-tbody>
<vn-tr ng-repeat="tracking in trackings"> <vn-tr ng-repeat="tracking in trackings">
<vn-td>{{::tracking.state.name}}</vn-td> <vn-td>{{::tracking.state.name}}</vn-td>
<vn-td>{{::tracking.worker.firstName}} {{ticket.worker.name}}</vn-td> <vn-td expand>
<span
class="link"
ng-click="$ctrl.showWorkerDescriptor($event, tracking.worker.user.id)">
{{::tracking.worker.user.nickname | dashIfEmpty}}
</span>
</vn-td>
<vn-td>{{::tracking.created | date:'dd/MM/yyyy HH:mm'}}</vn-td> <vn-td>{{::tracking.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr> </vn-tr>
</vn-tbody> </vn-tbody>
@ -33,4 +38,8 @@
</vn-vertical> </vn-vertical>
<a ui-sref="ticket.card.tracking.edit" vn-bind="+" fixed-bottom-right> <a ui-sref="ticket.card.tracking.edit" vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button> <vn-float-button icon="add"></vn-float-button>
</a> </a>
<vn-worker-descriptor-popover
vn-id="workerDescriptor"
user-id="$ctrl.selectedWorker">
</vn-worker-descriptor-popover>

View File

@ -1,14 +1,21 @@
import ngModule from '../../module'; import ngModule from '../../module';
class Controller { class Controller {
constructor($stateParams) { constructor($scope, $stateParams) {
this.$ = $scope;
this.$stateParams = $stateParams; this.$stateParams = $stateParams;
this.filter = { this.filter = {
include: [ include: [
{ {
relation: 'worker', relation: 'worker',
scope: { scope: {
fields: ['firstName', 'name'] fields: ['userFk'],
include: {
relation: 'user',
scope: {
fields: ['nickname']
}
}
} }
}, },
{ {
@ -20,9 +27,17 @@ class Controller {
] ]
}; };
} }
showWorkerDescriptor(event, userId) {
event.preventDefault();
event.stopImmediatePropagation();
this.selectedWorker = userId;
this.$.workerDescriptor.parent = event.target;
this.$.workerDescriptor.show();
}
} }
Controller.$inject = ['$stateParams']; Controller.$inject = ['$scope', '$stateParams'];
ngModule.component('vnTicketTrackingIndex', { ngModule.component('vnTicketTrackingIndex', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -0,0 +1,12 @@
<vn-popover vn-id="popover">
<vn-spinner
ng-if="$ctrl.worker == null"
style="padding: 1em;"
enable="true">
</vn-spinner>
<vn-worker-descriptor
ng-if="$ctrl.worker"
worker="$ctrl.worker"
quicklinks="$ctrl.quicklinks">
</vn-worker-descriptor>
</vn-popover>

View File

@ -0,0 +1,83 @@
import ngModule from '../module';
import Component from 'core/lib/component';
import './style.scss';
class Controller extends Component {
constructor($element, $scope, $http, $timeout, $q) {
super($element, $scope);
this.$timeout = $timeout;
this.$http = $http;
this.$q = $q;
this.worker = null;
}
set userId(id) {
if (id == this._userId) return;
this._userId = id;
this.worker = null;
this.loadData();
}
get userId() {
return this._userId;
}
set quicklinks(value = {}) {
this._quicklinks = Object.assign(value, this._quicklinks);
}
get quicklinks() {
return this._quicklinks;
}
show() {
this.$.popover.parent = this.parent;
this.$.popover.show();
}
loadData() {
let query = `api/Workers/findOne`;
let filter = {
where: {
userFk: this._userId
},
include: [
{
relation: 'user',
scope: {fields: ['name', 'email']}
}, {
relation: 'client',
scope: {fields: ['fi']}
}, {
relation: 'sip',
scope: {fields: ['extension']}
}, {
relation: 'department',
scope: {
include: {
relation: 'department'
}
}
}
]
};
this.$http.get(query, {params: {filter}}).then(res => {
this.worker = res.data;
this.$.$applyAsync(() => {
this.$.popover.relocate();
});
});
}
}
Controller.$inject = ['$element', '$scope', '$http', '$timeout', '$q'];
ngModule.component('vnWorkerDescriptorPopover', {
template: require('./index.html'),
controller: Controller,
bindings: {
userId: '<',
quicklinks: '<'
}
});

View File

@ -0,0 +1,97 @@
import './index.js';
describe('worker Component vnWorkerDescriptorPopover', () => {
let $httpBackend;
let $httpParamSerializer;
let $scope;
let controller;
let $element;
beforeEach(ngModule('worker'));
beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
$element = angular.element(`<div></div>`);
$scope = $rootScope.$new();
$scope.popover = {relocate: () => {}, show: () => {}};
controller = $componentController('vnWorkerDescriptorPopover', {$scope, $element});
}));
describe('workerFk()', () => {
it(`should not apply any changes if the received id is the same stored in _workerFk`, () => {
controller.worker = 'I exist!';
controller._userId = 1;
spyOn(controller, 'loadData');
controller.userId = 1;
expect(controller.worker).toEqual('I exist!');
expect(controller._userId).toEqual(1);
expect(controller.loadData).not.toHaveBeenCalled();
});
it(`should set the received id into _workerFk, set the worker to null and then call loadData()`, () => {
controller.worker = `Please don't`;
controller._userId = 1;
spyOn(controller, 'loadData');
controller.userId = 999;
expect(controller.worker).toBeNull();
expect(controller._userId).toEqual(999);
expect(controller.loadData).toHaveBeenCalledWith();
});
});
describe('show()', () => {
it(`should call the show()`, () => {
spyOn(controller.$.popover, 'show');
controller.show();
expect(controller.$.popover.show).toHaveBeenCalledWith();
});
});
describe('loadData()', () => {
it(`should perform a get query to store the worker data into the controller`, () => {
controller.userId = 1;
controller.canceler = null;
let response = {};
let config = {
filter: {
where: {
userFk: controller.userId
},
include: [
{
relation: 'user',
scope: {fields: ['name', 'email']}
}, {
relation: 'client',
scope: {fields: ['fi']}
}, {
relation: 'sip',
scope: {fields: ['extension']}
}, {
relation: 'department',
scope: {
include: {
relation: 'department'
}
}
}
]
}
};
let json = $httpParamSerializer(config);
$httpBackend.whenGET(`api/Workers/findOne?${json}`).respond(response);
$httpBackend.expectGET(`api/Workers/findOne?${json}`);
controller.loadData();
$httpBackend.flush();
expect(controller.worker).toEqual(response);
});
});
});

View File

@ -0,0 +1,11 @@
vn-ticket-descriptor-popover {
vn-ticket-descriptor {
display: block;
width: 16em;
max-height: 28em;
& > vn-card {
margin: 0!important;
}
}
}

View File

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

View File

@ -657,8 +657,8 @@ INSERT INTO `vn`.`saleComponent`(`saleFk`, `componentFk`, `value`)
INSERT INTO `vn`.`saleTracking`(`saleFk`, `isChecked`, `created`, `originalQuantity`, `workerFk`, `actionFk`, `id`, `stateFk`) INSERT INTO `vn`.`saleTracking`(`saleFk`, `isChecked`, `created`, `originalQuantity`, `workerFk`, `actionFk`, `id`, `stateFk`)
VALUES VALUES
( 1, 0, CURDATE(), 5, 40, 3, 1, 14), ( 1, 0, CURDATE(), 5, 55, 3, 1, 14),
( 1, 1, CURDATE(), 5, 40, 3, 2, 8), ( 1, 1, CURDATE(), 5, 54, 3, 2, 8),
( 2, 1, CURDATE(), 10, 40, 4, 3, 8), ( 2, 1, CURDATE(), 10, 40, 4, 3, 8),
( 3, 1, CURDATE(), 2, 40, 4, 4, 8); ( 3, 1, CURDATE(), 2, 40, 4, 4, 8);