Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2569-travel_extra_community
gitea/salix/pipeline/head There was a failure building this commit
Details
gitea/salix/pipeline/head There was a failure building this commit
Details
This commit is contained in:
commit
d40ea52560
|
@ -0,0 +1,96 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('download', {
|
||||||
|
description: 'Get the user image',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'collection',
|
||||||
|
type: 'String',
|
||||||
|
description: 'The image collection',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'size',
|
||||||
|
type: 'String',
|
||||||
|
description: 'The image size',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'Number',
|
||||||
|
description: 'The user id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}
|
||||||
|
|
||||||
|
],
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
arg: 'body',
|
||||||
|
type: 'file',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'Content-Type',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'Content-Disposition',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/:collection/:size/:id/download`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.download = async function(collection, size, id) {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const filter = {
|
||||||
|
where: {
|
||||||
|
name: collection},
|
||||||
|
include: {
|
||||||
|
relation: 'readRole'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const imageCollection = await models.ImageCollection.findOne(filter);
|
||||||
|
const entity = await models[imageCollection.model].findById(id, {
|
||||||
|
fields: ['id', imageCollection.property]
|
||||||
|
});
|
||||||
|
const image = await models.Image.findOne({where: {
|
||||||
|
collectionFk: collection,
|
||||||
|
name: entity[imageCollection.property]}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!image) return false;
|
||||||
|
|
||||||
|
const imageRole = imageCollection.readRole().name;
|
||||||
|
const hasRole = await models.Account.hasRole(id, imageRole);
|
||||||
|
if (!hasRole)
|
||||||
|
throw new UserError(`You don't have enough privileges`);
|
||||||
|
|
||||||
|
let file;
|
||||||
|
let env = process.env.NODE_ENV;
|
||||||
|
if (env && env != 'development') {
|
||||||
|
file = {
|
||||||
|
path: `/var/lib/salix/image/${collection}/${size}/${image.name}.png`,
|
||||||
|
contentType: 'image/png',
|
||||||
|
name: `${image.name}.png`
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
file = {
|
||||||
|
path: `${process.cwd()}/storage/image/${collection}/${size}/${image.name}.png`,
|
||||||
|
contentType: 'image/png',
|
||||||
|
name: `${image.name}.png`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
await fs.access(file.path);
|
||||||
|
let stream = fs.createReadStream(file.path);
|
||||||
|
return [stream, file.contentType, `filename="${file.name}"`];
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,21 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
|
describe('image download()', () => {
|
||||||
|
const collection = 'user';
|
||||||
|
const size = '160x160';
|
||||||
|
|
||||||
|
it('should return the image content-type of the user', async() => {
|
||||||
|
const userId = 9;
|
||||||
|
const image = await app.models.Image.download(collection, size, userId);
|
||||||
|
const contentType = image[1];
|
||||||
|
|
||||||
|
expect(contentType).toEqual('image/png');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should return false if the user doesn't have image`, async() => {
|
||||||
|
const userId = 110;
|
||||||
|
const image = await app.models.Image.download(collection, size, userId);
|
||||||
|
|
||||||
|
expect(image).toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
|
@ -45,6 +45,9 @@
|
||||||
},
|
},
|
||||||
"updated": {
|
"updated": {
|
||||||
"type": "date"
|
"type": "date"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"type": "String"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -43,7 +43,12 @@
|
||||||
"model": "ImageCollectionSize",
|
"model": "ImageCollectionSize",
|
||||||
"foreignKey": "collectionFk",
|
"foreignKey": "collectionFk",
|
||||||
"property": "id"
|
"property": "id"
|
||||||
}
|
},
|
||||||
|
"readRole": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Role",
|
||||||
|
"foreignKey": "readRoleFk"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"acls": [
|
"acls": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,6 +3,8 @@ const sharp = require('sharp');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
|
require('../methods/image/download')(Self);
|
||||||
|
|
||||||
Self.getPath = function() {
|
Self.getPath = function() {
|
||||||
return '/var/lib/salix/image';
|
return '/var/lib/salix/image';
|
||||||
};
|
};
|
||||||
|
|
|
@ -29,6 +29,14 @@
|
||||||
"default": 0
|
"default": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"relations": {
|
||||||
|
"collection": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "ImageCollection",
|
||||||
|
"foreignKey": "collectionFk",
|
||||||
|
"primaryKey": "name"
|
||||||
|
}
|
||||||
|
},
|
||||||
"acls": [
|
"acls": [
|
||||||
{
|
{
|
||||||
"accessType": "READ",
|
"accessType": "READ",
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
ALTER TABLE `hedera`.`imageCollection`
|
||||||
|
ADD COLUMN `readRoleFk` VARCHAR(45) NULL DEFAULT NULL AFTER `column`;
|
||||||
|
|
||||||
|
update `hedera`.`imageCollection` set `readRoleFk` = 1;
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE `account`.`user`
|
||||||
|
ADD COLUMN `image` VARCHAR(255) NULL AFTER `password`;
|
|
@ -443,7 +443,7 @@ USE `hedera`;
|
||||||
|
|
||||||
LOCK TABLES `imageCollection` WRITE;
|
LOCK TABLES `imageCollection` WRITE;
|
||||||
/*!40000 ALTER TABLE `imageCollection` DISABLE KEYS */;
|
/*!40000 ALTER TABLE `imageCollection` DISABLE KEYS */;
|
||||||
INSERT INTO `imageCollection` VALUES (1,'catalog','Artículo',3840,2160,'Item','image','vn','item','image'),(4,'link','Enlace',200,200,'Link','image','hedera','link','image'),(5,'news','Noticias',800,1200,'New','image','hedera','news','image');
|
INSERT INTO `imageCollection` VALUES (1,'catalog','Artículo',3840,2160,'Item','image','vn','item','image'),(4,'link','Enlace',200,200,'Link','image','hedera','link','image'),(5,'news','Noticias',800,1200,'New','image','hedera','news','image'),('6','user','Usuario','800','1200','Account','image','account','user','image');
|
||||||
/*!40000 ALTER TABLE `imageCollection` ENABLE KEYS */;
|
/*!40000 ALTER TABLE `imageCollection` ENABLE KEYS */;
|
||||||
UNLOCK TABLES;
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,8 @@ INSERT INTO `vn`.`packagingConfig`(`upperGap`)
|
||||||
UPDATE `account`.`role` SET id = 100 WHERE id = 0;
|
UPDATE `account`.`role` SET id = 100 WHERE id = 0;
|
||||||
CALL `account`.`role_sync`;
|
CALL `account`.`role_sync`;
|
||||||
|
|
||||||
INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `password`,`role`,`active`,`email`, `lang`)
|
INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `password`,`role`,`active`,`email`, `lang`, `image`)
|
||||||
SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@mydomain.com'), 'en'
|
SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@mydomain.com'), 'en', 'e7723f0b24ff05b32ed09d95196f2f29'
|
||||||
FROM `account`.`role` WHERE id <> 20
|
FROM `account`.`role` WHERE id <> 20
|
||||||
ORDER BY id;
|
ORDER BY id;
|
||||||
|
|
||||||
|
@ -51,20 +51,20 @@ INSERT INTO `hedera`.`tpvConfig`(`id`, `currency`, `terminal`, `transactionType`
|
||||||
VALUES
|
VALUES
|
||||||
(1, 978, 1, 0, 2000, 9, 0);
|
(1, 978, 1, 0, 2000, 9, 0);
|
||||||
|
|
||||||
INSERT INTO `account`.`user`(`id`,`name`,`nickname`, `password`,`role`,`active`,`email`,`lang`)
|
INSERT INTO `account`.`user`(`id`,`name`,`nickname`, `password`,`role`,`active`,`email`,`lang`, `image`)
|
||||||
VALUES
|
VALUES
|
||||||
(101, 'BruceWayne', 'Bruce Wayne', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es'),
|
(101, 'BruceWayne', 'Bruce Wayne', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||||
(102, 'PetterParker', 'Petter Parker', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en'),
|
(102, 'PetterParker', 'Petter Parker', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||||
(103, 'ClarkKent', 'Clark Kent', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr'),
|
(103, 'ClarkKent', 'Clark Kent', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||||
(104, 'TonyStark', 'Tony Stark', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es'),
|
(104, 'TonyStark', 'Tony Stark', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||||
(105, 'MaxEisenhardt', 'Max Eisenhardt', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt'),
|
(105, 'MaxEisenhardt', 'Max Eisenhardt', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||||
(106, 'DavidCharlesHaller', 'David Charles Haller', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en'),
|
(106, 'DavidCharlesHaller', 'David Charles Haller', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||||
(107, 'HankPym', 'Hank Pym', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en'),
|
(107, 'HankPym', 'Hank Pym', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||||
(108, 'CharlesXavier', 'Charles Xavier', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en'),
|
(108, 'CharlesXavier', 'Charles Xavier', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||||
(109, 'BruceBanner', 'Bruce Banner', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en'),
|
(109, 'BruceBanner', 'Bruce Banner', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||||
(110, 'JessicaJones', 'Jessica Jones', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en'),
|
(110, 'JessicaJones', 'Jessica Jones', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en', NULL),
|
||||||
(111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en'),
|
(111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL),
|
||||||
(112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en');
|
(112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL);
|
||||||
|
|
||||||
INSERT INTO `account`.`mailAlias`(`id`, `alias`, `description`, `isPublic`)
|
INSERT INTO `account`.`mailAlias`(`id`, `alias`, `description`, `isPublic`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2148,3 +2148,10 @@ INSERT INTO `vn`.`campaign`(`code`, `dated`)
|
||||||
('allSaints', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -2 YEAR)), '-11-01')),
|
('allSaints', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -2 YEAR)), '-11-01')),
|
||||||
('allSaints', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -3 YEAR)), '-11-01'));
|
('allSaints', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -3 YEAR)), '-11-01'));
|
||||||
|
|
||||||
|
INSERT INTO `hedera`.`image`(`collectionFk`, `name`)
|
||||||
|
VALUES
|
||||||
|
('user', 'e7723f0b24ff05b32ed09d95196f2f29');
|
||||||
|
|
||||||
|
INSERT INTO `hedera`.`imageCollectionSize`(`id`, `collectionFk`,`width`, `height`)
|
||||||
|
VALUES
|
||||||
|
(1, 4, 160, 160);
|
|
@ -421,6 +421,10 @@ export default {
|
||||||
moreMenuRestoreTicket: '.vn-menu [name="restoreTicket"]',
|
moreMenuRestoreTicket: '.vn-menu [name="restoreTicket"]',
|
||||||
moreMenuMakeInvoice: '.vn-menu [name="makeInvoice"]',
|
moreMenuMakeInvoice: '.vn-menu [name="makeInvoice"]',
|
||||||
moreMenuChangeShippedHour: '.vn-menu [name="changeShipped"]',
|
moreMenuChangeShippedHour: '.vn-menu [name="changeShipped"]',
|
||||||
|
moreMenuPaymentSMS: '.vn-menu [name="sendPaymentSms"]',
|
||||||
|
moreMenuSendImportSms: '.vn-menu [name="sendImportSms"]',
|
||||||
|
SMStext: 'textarea[name="message"]',
|
||||||
|
sendSMSbutton: 'button[response="accept"]',
|
||||||
changeShippedHourDialog: '.vn-dialog.shown',
|
changeShippedHourDialog: '.vn-dialog.shown',
|
||||||
changeShippedHour: '.vn-dialog.shown vn-input-time[ng-model="$ctrl.newShipped"]',
|
changeShippedHour: '.vn-dialog.shown vn-input-time[ng-model="$ctrl.newShipped"]',
|
||||||
addStowawayDialogFirstTicket: '.vn-dialog.shown vn-table vn-tbody vn-tr',
|
addStowawayDialogFirstTicket: '.vn-dialog.shown vn-table vn-tbody vn-tr',
|
||||||
|
@ -815,6 +819,10 @@ export default {
|
||||||
ticketOne: 'vn-invoice-out-summary > vn-card > vn-horizontal > vn-auto > vn-table > div > vn-tbody > vn-tr:nth-child(1)',
|
ticketOne: 'vn-invoice-out-summary > vn-card > vn-horizontal > vn-auto > vn-table > div > vn-tbody > vn-tr:nth-child(1)',
|
||||||
ticketTwo: 'vn-invoice-out-summary > vn-card > vn-horizontal > vn-auto > vn-table > div > vn-tbody > vn-tr:nth-child(2)'
|
ticketTwo: 'vn-invoice-out-summary > vn-card > vn-horizontal > vn-auto > vn-table > div > vn-tbody > vn-tr:nth-child(2)'
|
||||||
},
|
},
|
||||||
|
travelIndex: {
|
||||||
|
anySearchResult: 'vn-travel-index vn-tbody > a',
|
||||||
|
firstSearchResult: 'vn-travel-index vn-tbody > a:nth-child(1)'
|
||||||
|
},
|
||||||
travelBasicDada: {
|
travelBasicDada: {
|
||||||
reference: 'vn-travel-basic-data vn-textfield[ng-model="$ctrl.travel.ref"]',
|
reference: 'vn-travel-basic-data vn-textfield[ng-model="$ctrl.travel.ref"]',
|
||||||
agency: 'vn-travel-basic-data vn-autocomplete[ng-model="$ctrl.travel.agencyModeFk"]',
|
agency: 'vn-travel-basic-data vn-autocomplete[ng-model="$ctrl.travel.agencyModeFk"]',
|
||||||
|
@ -841,6 +849,11 @@ export default {
|
||||||
createdThermograph: 'vn-travel-thermograph-index vn-tbody > vn-tr',
|
createdThermograph: 'vn-travel-thermograph-index vn-tbody > vn-tr',
|
||||||
upload: 'vn-travel-thermograph-create button[type=submit]'
|
upload: 'vn-travel-thermograph-create button[type=submit]'
|
||||||
},
|
},
|
||||||
|
travelDescriptor: {
|
||||||
|
filterByAgencyButton: 'vn-descriptor-content .quicklinks > div:nth-child(1) > vn-quick-link > a[vn-tooltip="All travels with current agency"]',
|
||||||
|
dotMenu: 'vn-travel-descriptor vn-icon-button[icon="more_vert"]',
|
||||||
|
dotMenuClone: '#clone'
|
||||||
|
},
|
||||||
zoneIndex: {
|
zoneIndex: {
|
||||||
searchResult: 'vn-zone-index a.vn-tr',
|
searchResult: 'vn-zone-index a.vn-tr',
|
||||||
},
|
},
|
||||||
|
@ -927,7 +940,7 @@ export default {
|
||||||
},
|
},
|
||||||
supplierBasicData: {
|
supplierBasicData: {
|
||||||
alias: 'vn-supplier-basic-data vn-textfield[ng-model="$ctrl.supplier.nickname"]',
|
alias: 'vn-supplier-basic-data vn-textfield[ng-model="$ctrl.supplier.nickname"]',
|
||||||
isOfficial: 'vn-supplier-basic-data vn-check[ng-model="$ctrl.supplier.isOfficial"]',
|
isSerious: 'vn-supplier-basic-data vn-check[ng-model="$ctrl.supplier.isSerious"]',
|
||||||
isActive: 'vn-supplier-basic-data vn-check[ng-model="$ctrl.supplier.isActive"]',
|
isActive: 'vn-supplier-basic-data vn-check[ng-model="$ctrl.supplier.isActive"]',
|
||||||
notes: 'vn-supplier-basic-data vn-textarea[ng-model="$ctrl.supplier.note"]',
|
notes: 'vn-supplier-basic-data vn-textarea[ng-model="$ctrl.supplier.note"]',
|
||||||
saveButton: 'vn-supplier-basic-data button[type="submit"]',
|
saveButton: 'vn-supplier-basic-data button[type="submit"]',
|
||||||
|
|
|
@ -41,7 +41,6 @@ describe('Client balance path', () => {
|
||||||
it('should click the new payment button', async() => {
|
it('should click the new payment button', async() => {
|
||||||
await page.closePopup();
|
await page.closePopup();
|
||||||
await page.reloadSection('client.card.balance.index');
|
await page.reloadSection('client.card.balance.index');
|
||||||
await page.waitForState('client.card.balance.index');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a new payment that clears the debt', async() => {
|
it('should create a new payment that clears the debt', async() => {
|
||||||
|
|
|
@ -39,7 +39,7 @@ describe('Ticket descriptor path', () => {
|
||||||
expect(result).toContain('08:15');
|
expect(result).toContain('08:15');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete the ticket using the descriptor more menu', async() => {
|
it('should delete the ticket using the descriptor menu', async() => {
|
||||||
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
|
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
|
||||||
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
|
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
|
||||||
await page.waitToClick(selectors.ticketDescriptor.acceptDialog);
|
await page.waitToClick(selectors.ticketDescriptor.acceptDialog);
|
||||||
|
@ -64,7 +64,7 @@ describe('Ticket descriptor path', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Restore ticket', () => {
|
describe('Restore ticket', () => {
|
||||||
it('should restore the ticket using the descriptor more menu', async() => {
|
it('should restore the ticket using the descriptor menu', async() => {
|
||||||
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
|
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
|
||||||
await page.waitToClick(selectors.ticketDescriptor.moreMenuRestoreTicket);
|
await page.waitToClick(selectors.ticketDescriptor.moreMenuRestoreTicket);
|
||||||
await page.waitToClick(selectors.ticketDescriptor.acceptDialog);
|
await page.waitToClick(selectors.ticketDescriptor.acceptDialog);
|
||||||
|
@ -148,7 +148,7 @@ describe('Ticket descriptor path', () => {
|
||||||
expect(result).toEqual('-');
|
expect(result).toEqual('-');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should invoice the ticket using the descriptor more menu', async() => {
|
it('should invoice the ticket using the descriptor menu', async() => {
|
||||||
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
|
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
|
||||||
await page.waitForContentLoaded();
|
await page.waitForContentLoaded();
|
||||||
await page.waitToClick(selectors.ticketDescriptor.moreMenuMakeInvoice);
|
await page.waitToClick(selectors.ticketDescriptor.moreMenuMakeInvoice);
|
||||||
|
@ -165,4 +165,30 @@ describe('Ticket descriptor path', () => {
|
||||||
expect(result).toEqual('T4444445');
|
expect(result).toEqual('T4444445');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('SMS', () => {
|
||||||
|
it('should send the payment SMS using the descriptor menu', async() => {
|
||||||
|
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
|
||||||
|
await page.waitForContentLoaded();
|
||||||
|
await page.waitToClick(selectors.ticketDescriptor.moreMenuPaymentSMS);
|
||||||
|
await page.waitForSelector(selectors.ticketDescriptor.SMStext);
|
||||||
|
await page.waitPropertyLength(selectors.ticketDescriptor.SMStext, 'value', 128);
|
||||||
|
await page.waitToClick(selectors.ticketDescriptor.sendSMSbutton);
|
||||||
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.text).toContain('SMS sent!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send the import SMS using the descriptor menu', async() => {
|
||||||
|
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
|
||||||
|
await page.waitForContentLoaded();
|
||||||
|
await page.waitToClick(selectors.ticketDescriptor.moreMenuSendImportSms);
|
||||||
|
await page.waitForSelector(selectors.ticketDescriptor.SMStext);
|
||||||
|
await page.waitPropertyLength(selectors.ticketDescriptor.SMStext, 'value', 144);
|
||||||
|
await page.waitToClick(selectors.ticketDescriptor.sendSMSbutton);
|
||||||
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.text).toContain('SMS sent!');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import selectors from '../../helpers/selectors.js';
|
||||||
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
|
describe('Travel descriptor path', () => {
|
||||||
|
let browser;
|
||||||
|
let page;
|
||||||
|
|
||||||
|
beforeAll(async() => {
|
||||||
|
browser = await getBrowser();
|
||||||
|
page = browser.page;
|
||||||
|
await page.loginAndModule('buyer', 'travel');
|
||||||
|
await page.accessToSearchResult('1');
|
||||||
|
await page.waitForState('travel.card.summary');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async() => {
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should click the descriptor button to navigate to the travel index showing all travels with current agency', async() => {
|
||||||
|
await page.waitToClick(selectors.travelDescriptor.filterByAgencyButton);
|
||||||
|
await page.waitForState('travel.index');
|
||||||
|
const result = await page.countElement(selectors.travelIndex.anySearchResult);
|
||||||
|
|
||||||
|
expect(result).toBeGreaterThanOrEqual(7);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate to the first search result', async() => {
|
||||||
|
await page.waitToClick(selectors.travelIndex.firstSearchResult);
|
||||||
|
await page.waitForState('travel.card.summary');
|
||||||
|
const state = await page.getState();
|
||||||
|
|
||||||
|
expect(state).toBe('travel.card.summary');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be redirected to the create travel when using the clone option of the dot menu', async() => {
|
||||||
|
await page.waitToClick(selectors.travelDescriptor.dotMenu);
|
||||||
|
await page.waitToClick(selectors.travelDescriptor.dotMenuClone);
|
||||||
|
await page.respondToDialog('accept');
|
||||||
|
await page.waitForState('travel.create');
|
||||||
|
const state = await page.getState();
|
||||||
|
|
||||||
|
expect(state).toBe('travel.create');
|
||||||
|
});
|
||||||
|
});
|
|
@ -20,7 +20,7 @@ describe('Supplier basic data path', () => {
|
||||||
it('should edit the basic data', async() => {
|
it('should edit the basic data', async() => {
|
||||||
await page.clearInput(selectors.supplierBasicData.alias);
|
await page.clearInput(selectors.supplierBasicData.alias);
|
||||||
await page.write(selectors.supplierBasicData.alias, 'Plants Nick SL');
|
await page.write(selectors.supplierBasicData.alias, 'Plants Nick SL');
|
||||||
await page.waitToClick(selectors.supplierBasicData.isOfficial);
|
await page.waitToClick(selectors.supplierBasicData.isSerious);
|
||||||
await page.waitToClick(selectors.supplierBasicData.isActive);
|
await page.waitToClick(selectors.supplierBasicData.isActive);
|
||||||
await page.write(selectors.supplierBasicData.notes, 'Some notes');
|
await page.write(selectors.supplierBasicData.notes, 'Some notes');
|
||||||
|
|
||||||
|
@ -40,8 +40,8 @@ describe('Supplier basic data path', () => {
|
||||||
expect(result).toEqual('Plants Nick SL');
|
expect(result).toEqual('Plants Nick SL');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check the isOffical checkbox is now unchecked', async() => {
|
it('should check the isSerious checkbox is now unchecked', async() => {
|
||||||
const result = await page.checkboxState(selectors.supplierBasicData.isOfficial);
|
const result = await page.checkboxState(selectors.supplierBasicData.isSerious);
|
||||||
|
|
||||||
expect(result).toBe('unchecked');
|
expect(result).toBe('unchecked');
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,12 +31,15 @@
|
||||||
ng-if="$ctrl.rightMenu"
|
ng-if="$ctrl.rightMenu"
|
||||||
ng-click="$ctrl.rightMenu.show()">
|
ng-click="$ctrl.rightMenu.show()">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
<vn-icon-button
|
<button class="buttonAccount">
|
||||||
|
<img
|
||||||
id="user"
|
id="user"
|
||||||
icon="account_circle"
|
ng-src="{{$ctrl.getImageUrl()}}"
|
||||||
ng-click="userPopover.show($event)"
|
ng-click="userPopover.show($event)"
|
||||||
translate-attr="{title: 'Account'}">
|
translate-attr="{title: 'Account'}"
|
||||||
</vn-icon-button>
|
on-error-src/>
|
||||||
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<vn-menu vn-id="apps-menu">
|
<vn-menu vn-id="apps-menu">
|
||||||
<vn-list class="modules-menu">
|
<vn-list class="modules-menu">
|
||||||
|
|
|
@ -18,6 +18,14 @@ export class Layout extends Component {
|
||||||
window.localStorage.currentUserWorkerId = json.data.id;
|
window.localStorage.currentUserWorkerId = json.data.id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getImageUrl() {
|
||||||
|
if (!this.$.$root.user) return;
|
||||||
|
|
||||||
|
const userId = this.$.$root.user.id;
|
||||||
|
const token = this.vnToken.token;
|
||||||
|
return `/api/Images/user/160x160/${userId}/download?access_token=${token}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Layout.$inject = ['$element', '$scope', 'vnModules'];
|
Layout.$inject = ['$element', '$scope', 'vnModules'];
|
||||||
|
|
||||||
|
|
|
@ -22,4 +22,19 @@ describe('Component vnLayout', () => {
|
||||||
expect(controller.$.$root.user.name).toEqual('batman');
|
expect(controller.$.$root.user.name).toEqual('batman');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getImageUrl()', () => {
|
||||||
|
it('should return the url image if the user is defined', () => {
|
||||||
|
controller.$.$root.user = {id: 1};
|
||||||
|
const url = controller.getImageUrl();
|
||||||
|
|
||||||
|
expect(url).not.toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined if the user is not defined', () => {
|
||||||
|
const url = controller.getImageUrl();
|
||||||
|
|
||||||
|
expect(url).not.toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -109,6 +109,14 @@ vn-layout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
img {
|
||||||
|
width: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.buttonAccount {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
@media screen and (max-width: $mobile-width) {
|
@media screen and (max-width: $mobile-width) {
|
||||||
& > vn-topbar {
|
& > vn-topbar {
|
||||||
& > .start > .logo {
|
& > .start > .logo {
|
||||||
|
@ -147,3 +155,4 @@ vn-layout {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
<vn-popover vn-id="popover">
|
<vn-popover vn-id="popover">
|
||||||
<vn-vertical class="user-popover vn-pa-md">
|
<vn-vertical class="user-popover vn-pa-md">
|
||||||
<div class="profile-card vn-pb-md">
|
<div class="profile-card vn-pb-md">
|
||||||
<vn-icon icon="account_circle"></vn-icon>
|
<img
|
||||||
|
ng-src="{{$ctrl.getImageUrl($root.user.id)}}"
|
||||||
|
on-error-src/>
|
||||||
<div class="vn-pl-sm">
|
<div class="vn-pl-sm">
|
||||||
<div>
|
<div>
|
||||||
<div class="user">
|
<div class="user">
|
||||||
|
|
|
@ -3,12 +3,13 @@ import './style.scss';
|
||||||
import config from '../../config.json';
|
import config from '../../config.json';
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
constructor($, $translate, vnConfig, vnAuth) {
|
constructor($, $translate, vnConfig, vnAuth, vnToken) {
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
$,
|
$,
|
||||||
$translate,
|
$translate,
|
||||||
vnConfig,
|
vnConfig,
|
||||||
vnAuth,
|
vnAuth,
|
||||||
|
vnToken,
|
||||||
lang: $translate.use(),
|
lang: $translate.use(),
|
||||||
langs: []
|
langs: []
|
||||||
});
|
});
|
||||||
|
@ -77,8 +78,12 @@ class Controller {
|
||||||
this.$.companies.refresh();
|
this.$.companies.refresh();
|
||||||
this.$.popover.show(event.target);
|
this.$.popover.show(event.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getImageUrl(userId) {
|
||||||
|
return '/api/Images/user/160x160/' + userId + '/download?access_token=' + this.vnToken.token;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Controller.$inject = ['$scope', '$translate', 'vnConfig', 'vnAuth'];
|
Controller.$inject = ['$scope', '$translate', 'vnConfig', 'vnAuth', 'vnToken'];
|
||||||
|
|
||||||
ngModule.vnComponent('vnUserPopover', {
|
ngModule.vnComponent('vnUserPopover', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -57,5 +57,13 @@ describe('Salix', () => {
|
||||||
expect(controller.companyFk).toBe(4);
|
expect(controller.companyFk).toBe(4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getImageUrl()', () => {
|
||||||
|
it('should return de url image', () => {
|
||||||
|
const url = controller.getImageUrl();
|
||||||
|
|
||||||
|
expect(url).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,6 +11,10 @@
|
||||||
font-size: 5rem;
|
font-size: 5rem;
|
||||||
color: $color-font-bg-marginal;
|
color: $color-font-bg-marginal;
|
||||||
}
|
}
|
||||||
|
img {
|
||||||
|
width: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
& > div {
|
& > div {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -55,8 +55,8 @@
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>{{::saleClaimed.sale.price | currency: 'EUR':2}}</vn-td>
|
<vn-td number>{{::saleClaimed.sale.price | currency: 'EUR':2}}</vn-td>
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
<span ng-class="{'link': $ctrl.isEditable}"
|
<span ng-class="{'link': $ctrl.isRewritable && $ctrl.isClaimManager}"
|
||||||
title="{{$ctrl.isEditable ? 'Edit discount' : ''}}"
|
translate-attr="{title: $ctrl.isRewritable && $ctrl.isClaimManager ? 'Edit discount' : ''}"
|
||||||
ng-click="$ctrl.showEditPopover($event, saleClaimed)">
|
ng-click="$ctrl.showEditPopover($event, saleClaimed)">
|
||||||
{{saleClaimed.sale.discount}} %
|
{{saleClaimed.sale.discount}} %
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -54,6 +54,10 @@ class Controller extends Section {
|
||||||
this.updateNewPrice();
|
this.updateNewPrice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isClaimManager() {
|
||||||
|
return this.aclService.hasAny(['claimManager']);
|
||||||
|
}
|
||||||
|
|
||||||
openAddSalesDialog() {
|
openAddSalesDialog() {
|
||||||
this.getClaimableFromTicket();
|
this.getClaimableFromTicket();
|
||||||
this.$.addSales.show();
|
this.$.addSales.show();
|
||||||
|
@ -131,17 +135,6 @@ class Controller extends Section {
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
showEditPopover(event, saleClaimed) {
|
|
||||||
if (this.isEditable) {
|
|
||||||
if (!this.aclService.hasAny(['claimManager']))
|
|
||||||
return this.vnApp.showError(this.$t('Insuficient permisos'));
|
|
||||||
|
|
||||||
this.saleClaimed = saleClaimed;
|
|
||||||
this.$.editPopover.parent = event.target;
|
|
||||||
this.$.editPopover.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSalespersonMana() {
|
getSalespersonMana() {
|
||||||
this.$http.get(`Tickets/${this.claim.ticketFk}/getSalesPersonMana`).then(res => {
|
this.$http.get(`Tickets/${this.claim.ticketFk}/getSalesPersonMana`).then(res => {
|
||||||
this.mana = res.data;
|
this.mana = res.data;
|
||||||
|
@ -164,6 +157,14 @@ class Controller extends Section {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showEditPopover(event, saleClaimed) {
|
||||||
|
if (this.aclService.hasAny(['claimManager'])) {
|
||||||
|
this.saleClaimed = saleClaimed;
|
||||||
|
this.$.editPopover.parent = event.target;
|
||||||
|
this.$.editPopover.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateDiscount() {
|
updateDiscount() {
|
||||||
const claimedSale = this.saleClaimed.sale;
|
const claimedSale = this.saleClaimed.sale;
|
||||||
if (this.newDiscount != claimedSale.discount) {
|
if (this.newDiscount != claimedSale.discount) {
|
||||||
|
@ -176,8 +177,6 @@ class Controller extends Section {
|
||||||
this.clearDiscount();
|
this.clearDiscount();
|
||||||
|
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
}).catch(err => {
|
|
||||||
this.vnApp.showError(err.message);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,13 +79,20 @@
|
||||||
</vn-quick-link>
|
</vn-quick-link>
|
||||||
</div>
|
</div>
|
||||||
<div ng-transclude="btnTwo">
|
<div ng-transclude="btnTwo">
|
||||||
|
<vn-quick-link
|
||||||
|
tooltip="Client invoices list"
|
||||||
|
state="['invoiceOut.index', {q: $ctrl.filter}]"
|
||||||
|
icon="icon-invoices">
|
||||||
|
</vn-quick-link>
|
||||||
|
</div>
|
||||||
|
<div ng-transclude="btnThree">
|
||||||
<vn-quick-link
|
<vn-quick-link
|
||||||
tooltip="New order"
|
tooltip="New order"
|
||||||
state="['order.create', {clientFk: $ctrl.id}]"
|
state="['order.create', {clientFk: $ctrl.id}]"
|
||||||
icon="icon-basketadd">
|
icon="icon-basketadd">
|
||||||
</vn-quick-link>
|
</vn-quick-link>
|
||||||
</div>
|
</div>
|
||||||
<div ng-transclude="btnThree">
|
<div ng-transclude="btnFour">
|
||||||
<vn-quick-link
|
<vn-quick-link
|
||||||
tooltip="Go to user"
|
tooltip="Go to user"
|
||||||
state="['account.card.summary', {id: $ctrl.id}]"
|
state="['account.card.summary', {id: $ctrl.id}]"
|
||||||
|
|
|
@ -2,4 +2,5 @@ Simple ticket: Ticket simple
|
||||||
View consumer report: Ver informe de consumo
|
View consumer report: Ver informe de consumo
|
||||||
From date: Fecha desde
|
From date: Fecha desde
|
||||||
To date: Fecha hasta
|
To date: Fecha hasta
|
||||||
Go to user: Ir al usuario
|
Go to user: Ir al usuario
|
||||||
|
Client invoices list: Listado de facturas del cliente
|
|
@ -8,6 +8,14 @@ export default class Controller extends Section {
|
||||||
|
|
||||||
const hasContactData = this.client.email || this.client.phone || this.client.mobile;
|
const hasContactData = this.client.email || this.client.phone || this.client.mobile;
|
||||||
const hasChangedTaxData = !orgData.isTaxDataChecked && this.client.isTaxDataChecked;
|
const hasChangedTaxData = !orgData.isTaxDataChecked && this.client.isTaxDataChecked;
|
||||||
|
|
||||||
|
const shouldInvoice = this.client.isActive && !this.client.hasToInvoice;
|
||||||
|
const clientActivation = this.client.isActive && (orgData.isActive != this.client.isActive);
|
||||||
|
if (shouldInvoice && clientActivation) {
|
||||||
|
this.client.hasToInvoice = true;
|
||||||
|
this.vnApp.showMessage(this.$t('Client invoices enabled'));
|
||||||
|
}
|
||||||
|
|
||||||
if (hasChangedTaxData && hasContactData)
|
if (hasChangedTaxData && hasContactData)
|
||||||
this.checkExistingClient();
|
this.checkExistingClient();
|
||||||
else this.save();
|
else this.save();
|
||||||
|
|
|
@ -50,6 +50,16 @@ describe('Client', () => {
|
||||||
expect(controller.save).not.toHaveBeenCalledWith();
|
expect(controller.save).not.toHaveBeenCalledWith();
|
||||||
expect(controller.checkExistingClient).toHaveBeenCalledWith();
|
expect(controller.checkExistingClient).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should enable the hasToInvoice property any time the form activates the client with isActive', () => {
|
||||||
|
$scope.watcher.orgData.isActive = false;
|
||||||
|
controller.client.isActive = true;
|
||||||
|
controller.client.hasToInvoice = false;
|
||||||
|
|
||||||
|
controller.onSubmit();
|
||||||
|
|
||||||
|
expect(controller.client.hasToInvoice).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('checkExistingClient()', () => {
|
describe('checkExistingClient()', () => {
|
||||||
|
|
|
@ -50,7 +50,13 @@
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td title="{{::entry.warehouse| dashIfEmpty}}">{{::entry.warehouse| dashIfEmpty}}</vn-td>
|
<vn-td title="{{::entry.warehouse| dashIfEmpty}}">{{::entry.warehouse| dashIfEmpty}}</vn-td>
|
||||||
<vn-td expand>{{::entry.landed | date:'dd/MM/yyyy HH:mm'}}</vn-td>
|
<vn-td expand>{{::entry.landed | date:'dd/MM/yyyy HH:mm'}}</vn-td>
|
||||||
<vn-td number>{{::entry.entryFk | dashIfEmpty}}</vn-td>
|
<vn-td shrink>
|
||||||
|
<span
|
||||||
|
vn-click-stop="entryDescriptor.show($event, entry.entryFk)"
|
||||||
|
class="link">
|
||||||
|
{{::entry.entryFk | dashIfEmpty}}
|
||||||
|
</span>
|
||||||
|
</vn-td>
|
||||||
<vn-td number>{{::entry.price2 | dashIfEmpty}}</vn-td>
|
<vn-td number>{{::entry.price2 | dashIfEmpty}}</vn-td>
|
||||||
<vn-td number>{{::entry.price3 | dashIfEmpty}}</vn-td>
|
<vn-td number>{{::entry.price3 | dashIfEmpty}}</vn-td>
|
||||||
<vn-td number class="expendable">{{entry.stickers | dashIfEmpty}}</vn-td>
|
<vn-td number class="expendable">{{entry.stickers | dashIfEmpty}}</vn-td>
|
||||||
|
@ -77,6 +83,10 @@
|
||||||
</vn-card>
|
</vn-card>
|
||||||
</vn-data-viewer>
|
</vn-data-viewer>
|
||||||
|
|
||||||
|
<vn-entry-descriptor-popover
|
||||||
|
vn-id="entryDescriptor">
|
||||||
|
</vn-entry-descriptor-popover>
|
||||||
|
|
||||||
<vn-contextmenu vn-id="contextmenu" targets="['vn-data-viewer']" model="model"
|
<vn-contextmenu vn-id="contextmenu" targets="['vn-data-viewer']" model="model"
|
||||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||||
<slot-menu>
|
<slot-menu>
|
||||||
|
|
|
@ -25,7 +25,7 @@ module.exports = Self => {
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name',
|
||||||
'nickname',
|
'nickname',
|
||||||
'isOfficial',
|
'isSerious',
|
||||||
'isActive',
|
'isActive',
|
||||||
'note',
|
'note',
|
||||||
'nif',
|
'nif',
|
||||||
|
@ -39,6 +39,9 @@ module.exports = Self => {
|
||||||
'payDay',
|
'payDay',
|
||||||
'account',
|
'account',
|
||||||
'isFarmer',
|
'isFarmer',
|
||||||
|
'sageTaxTypeFk',
|
||||||
|
'sageTransactionTypeFk',
|
||||||
|
'sageWithholdingFk',
|
||||||
],
|
],
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
|
@ -64,10 +67,27 @@ module.exports = Self => {
|
||||||
scope: {
|
scope: {
|
||||||
fields: ['id', 'payDem']
|
fields: ['id', 'payDem']
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'sageTaxType',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'vat']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'sageTransactionType',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'transaction']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'sageWithholding',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'withholding']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
let supplier = await Self.app.models.Supplier.findOne(filter);
|
let supplier = await Self.app.models.Supplier.findOne(filter);
|
||||||
return supplier;
|
return supplier;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "SupplierContact",
|
"name": "SupplierContact",
|
||||||
"base": "VnModel",
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model":"SupplierLog",
|
||||||
|
"relation": "supplier"
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "supplierContact"
|
"table": "supplierContact"
|
||||||
|
|
|
@ -51,6 +51,9 @@
|
||||||
"isOfficial": {
|
"isOfficial": {
|
||||||
"type": "Boolean"
|
"type": "Boolean"
|
||||||
},
|
},
|
||||||
|
"isSerious": {
|
||||||
|
"type": "Boolean"
|
||||||
|
},
|
||||||
"note": {
|
"note": {
|
||||||
"type": "String"
|
"type": "String"
|
||||||
},
|
},
|
||||||
|
@ -123,7 +126,22 @@
|
||||||
"model": "Client",
|
"model": "Client",
|
||||||
"foreignKey": "nif",
|
"foreignKey": "nif",
|
||||||
"primaryKey": "fi"
|
"primaryKey": "fi"
|
||||||
}
|
},
|
||||||
|
"sageTaxType": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "SageTaxType",
|
||||||
|
"foreignKey": "sageTaxTypeFk"
|
||||||
|
},
|
||||||
|
"sageTransactionType": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "SageTransactionType",
|
||||||
|
"foreignKey": "sageTransactionTypeFk"
|
||||||
|
},
|
||||||
|
"sageWithholding": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "SageWithholding",
|
||||||
|
"foreignKey": "sageWithholdingFk"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"acls": [
|
"acls": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Official"
|
label="Verified"
|
||||||
ng-model="$ctrl.supplier.isOfficial">
|
ng-model="$ctrl.supplier.isSerious">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Active"
|
label="Active"
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
Notes: Notas
|
Notes: Notas
|
||||||
Active: Activo
|
Active: Activo
|
||||||
Official: Oficial
|
Verified: Verificado
|
|
@ -34,7 +34,7 @@
|
||||||
rule="SupplierContact">
|
rule="SupplierContact">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-two
|
||||||
label="Email"
|
label="Email"
|
||||||
ng-model="contact.email"
|
ng-model="contact.email"
|
||||||
rule="SupplierContact">
|
rule="SupplierContact">
|
||||||
|
|
|
@ -29,9 +29,9 @@
|
||||||
ng-class="{bright: $ctrl.supplier.isActive == false}">
|
ng-class="{bright: $ctrl.supplier.isActive == false}">
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
<vn-icon
|
<vn-icon
|
||||||
vn-tooltip="Official supplier"
|
vn-tooltip="Verified supplier"
|
||||||
icon="icon-net"
|
icon="verified_user"
|
||||||
ng-class="{bright: $ctrl.supplier.isOfficial == false}">
|
ng-class="{bright: $ctrl.supplier.isSerious == true}">
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="quicklinks">
|
<div class="quicklinks">
|
||||||
|
|
|
@ -40,7 +40,7 @@ class Controller extends Descriptor {
|
||||||
'payDemFk',
|
'payDemFk',
|
||||||
'payDay',
|
'payDay',
|
||||||
'isActive',
|
'isActive',
|
||||||
'isOfficial',
|
'isSerious',
|
||||||
'account'
|
'account'
|
||||||
],
|
],
|
||||||
include: [
|
include: [
|
||||||
|
|
|
@ -26,7 +26,7 @@ describe('Supplier Component vnSupplierDescriptor', () => {
|
||||||
'payDemFk',
|
'payDemFk',
|
||||||
'payDay',
|
'payDay',
|
||||||
'isActive',
|
'isActive',
|
||||||
'isOfficial',
|
'isSerious',
|
||||||
'account'
|
'account'
|
||||||
],
|
],
|
||||||
include: [
|
include: [
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Tax number: NIF / CIF
|
Tax number: NIF / CIF
|
||||||
All entries with current supplier: Todas las entradas con el proveedor actual
|
All entries with current supplier: Todas las entradas con el proveedor actual
|
||||||
Go to client: Ir al cliente
|
Go to client: Ir al cliente
|
||||||
Official supplier: Proveedor oficial
|
Verified supplier: Proveedor verificado
|
||||||
Inactive supplier: Proveedor inactivo
|
Inactive supplier: Proveedor inactivo
|
|
@ -90,6 +90,15 @@
|
||||||
rule>
|
rule>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield
|
||||||
|
vn-one
|
||||||
|
label="Street"
|
||||||
|
ng-model="$ctrl.supplier.street"
|
||||||
|
rule
|
||||||
|
vn-focus>
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-datalist vn-one
|
<vn-datalist vn-one
|
||||||
label="Postcode"
|
label="Postcode"
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
</vn-data-viewer>
|
</vn-data-viewer>
|
||||||
<vn-popup vn-id="dialog-summary-client">
|
<vn-popup vn-id="dialog-summary-supplier">
|
||||||
<vn-supplier-summary
|
<vn-supplier-summary
|
||||||
supplier="$ctrl.supplierSelected">
|
supplier="$ctrl.supplierSelected">
|
||||||
</vn-supplier-summary>
|
</vn-supplier-summary>
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
|
|
||||||
export default class Controller extends Section {}
|
export default class Controller extends Section {
|
||||||
|
openSummary(supplier, event) {
|
||||||
|
if (event.defaultPrevented) return;
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
this.supplierSelected = supplier;
|
||||||
|
this.$.dialogSummarySupplier.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnSupplierIndex', {
|
ngModule.vnComponent('vnSupplierIndex', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
url="Suppliers/filter"
|
url="Suppliers/filter"
|
||||||
limit="20"
|
limit="20">
|
||||||
auto-load="true">
|
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-portal slot="topbar">
|
<vn-portal slot="topbar">
|
||||||
<vn-searchbar
|
<vn-searchbar
|
||||||
|
|
|
@ -1,65 +1,50 @@
|
||||||
<vn-card class="summary">
|
<vn-card class="summary">
|
||||||
<h5>{{$ctrl.summary.name}} - {{$ctrl.summary.id}}</h5>
|
<h5>{{::$ctrl.summary.name}} - {{::$ctrl.summary.id}}</h5>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<h4 translate>Basic data</h4>
|
<h4 translate>Basic data</h4>
|
||||||
<vn-label-value label="Id"
|
<vn-vertical>
|
||||||
value="{{$ctrl.summary.id}}">
|
<vn-label-value
|
||||||
</vn-label-value>
|
label="Id"
|
||||||
<vn-label-value label="Alias"
|
value="{{::$ctrl.summary.id}}">
|
||||||
value="{{$ctrl.summary.nickname}}">
|
</vn-label-value>
|
||||||
</vn-label-value>
|
<vn-label-value
|
||||||
<vn-check
|
label="Alias"
|
||||||
label="Is official"
|
value="{{::$ctrl.summary.nickname}}">
|
||||||
ng-model="$ctrl.summary.isOfficial"
|
</vn-label-value>
|
||||||
disabled="true">
|
<vn-check
|
||||||
</vn-check>
|
label="Verified"
|
||||||
<vn-check
|
ng-model="$ctrl.summary.isSerious"
|
||||||
label="Is active"
|
disabled="true">
|
||||||
ng-model="$ctrl.summary.isActive"
|
</vn-check>
|
||||||
disabled="true">
|
<vn-check
|
||||||
</vn-check>
|
label="Is active"
|
||||||
<vn-label-value label="Notes"
|
ng-model="$ctrl.summary.isActive"
|
||||||
value="{{$ctrl.summary.note}}">
|
disabled="true">
|
||||||
</vn-label-value>
|
</vn-check>
|
||||||
</vn-one>
|
<vn-label-value
|
||||||
<vn-one>
|
label="Notes"
|
||||||
<h4 translate>Fiscal address</h4>
|
value="{{::$ctrl.summary.note}}">
|
||||||
<vn-label-value label="Social name"
|
</vn-label-value>
|
||||||
value="{{$ctrl.summary.name}}">
|
</vn-vertical>
|
||||||
</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>
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<h4 translate>Billing data</h4>
|
<h4 translate>Billing data</h4>
|
||||||
<vn-label-value label="Pay method"
|
<vn-label-value
|
||||||
value="{{$ctrl.summary.payMethod.name}}">
|
label="Pay method"
|
||||||
|
value="{{::$ctrl.summary.payMethod.name}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Payment deadline"
|
<vn-label-value
|
||||||
value="{{$ctrl.summary.payDem.payDem}}">
|
label="Payment deadline"
|
||||||
|
value="{{::$ctrl.summary.payDem.payDem}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Pay day"
|
<vn-label-value
|
||||||
value="{{$ctrl.summary.payDay}}">
|
label="Pay day"
|
||||||
|
value="{{::$ctrl.summary.payDay}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-label-value label="Account"
|
<vn-label-value
|
||||||
value="{{$ctrl.summary.account}}">
|
label="Account"
|
||||||
|
value="{{::$ctrl.summary.account}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Is Farmer"
|
label="Is Farmer"
|
||||||
|
@ -68,6 +53,58 @@
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-one>
|
||||||
|
<h4 translate>Fiscal data</h4>
|
||||||
|
<vn-label-value
|
||||||
|
label="Sage tax type"
|
||||||
|
value="{{::$ctrl.summary.sageTaxType.vat}}">
|
||||||
|
</vn-label-value>
|
||||||
|
<vn-label-value
|
||||||
|
title="{{::$ctrl.summary.sageTransactionType.transaction}}"
|
||||||
|
label="Sage transaction type"
|
||||||
|
value="{{::$ctrl.summary.sageTransactionType.transaction}}">
|
||||||
|
</vn-label-value>
|
||||||
|
<vn-label-value
|
||||||
|
title="{{::$ctrl.summary.sageWithholding.withholding}}"
|
||||||
|
label="Sage withholding"
|
||||||
|
value="{{::$ctrl.summary.sageWithholding.withholding}}">
|
||||||
|
</vn-label-value>
|
||||||
|
</vn-one>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<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"
|
||||||
|
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-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-worker-descriptor-popover
|
<vn-worker-descriptor-popover
|
||||||
vn-id="workerDescriptor">
|
vn-id="workerDescriptor">
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
Is official: Es oficial
|
Verified: Verificado
|
||||||
Country: País
|
Country: País
|
||||||
Tax number: NIF / CIF
|
Tax number: NIF / CIF
|
||||||
Search suppliers by id, name or alias: Busca proveedores por id, nombre o alias
|
Search suppliers by id, name or alias: Busca proveedores por id, nombre o alias
|
||||||
Is Farmer: Es agrícola
|
Is Farmer: Es agrícola
|
||||||
|
Sage tax type: Tipo de impuesto Sage
|
||||||
|
Sage transaction type: Tipo de transacción Sage
|
||||||
|
Sage withholding: Retencion Sage
|
|
@ -70,13 +70,17 @@ module.exports = Self => {
|
||||||
|
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
const isLocked = await models.Ticket.isLocked(id);
|
const isLocked = await models.Ticket.isLocked(id);
|
||||||
const isSalesPerson = await models.Account.hasRole(userId, 'salesPerson');
|
const roles = await models.Account.getRoles(userId);
|
||||||
|
const hasAllowedRoles = roles.filter(role =>
|
||||||
|
role == 'salesPerson' || role == 'claimManager'
|
||||||
|
);
|
||||||
|
|
||||||
const state = await Self.app.models.TicketState.findOne({
|
const state = await Self.app.models.TicketState.findOne({
|
||||||
where: {ticketFk: id}
|
where: {ticketFk: id}
|
||||||
});
|
});
|
||||||
const alertLevel = state ? state.alertLevel : null;
|
const alertLevel = state ? state.alertLevel : null;
|
||||||
|
|
||||||
if (isLocked || (!isSalesPerson && alertLevel > 0))
|
if (isLocked || (!hasAllowedRoles && alertLevel > 0))
|
||||||
throw new UserError(`The sales of this ticket can't be modified`);
|
throw new UserError(`The sales of this ticket can't be modified`);
|
||||||
|
|
||||||
const usesMana = await models.WorkerMana.findOne({
|
const usesMana = await models.WorkerMana.findOne({
|
||||||
|
|
|
@ -11,11 +11,10 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"ticketFk": {
|
"ticketFk": {
|
||||||
"id": 1,
|
"id": true,
|
||||||
"type": "Number"
|
"type": "Number"
|
||||||
},
|
},
|
||||||
"ticketTrackingFk": {
|
"ticketTrackingFk": {
|
||||||
"id": 2,
|
|
||||||
"type": "Number"
|
"type": "Number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,11 +46,13 @@
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item
|
||||||
ng-click="$ctrl.sendPaymentSms()"
|
ng-click="$ctrl.sendPaymentSms()"
|
||||||
|
name="sendPaymentSms"
|
||||||
translate>
|
translate>
|
||||||
SMS Pending payment
|
SMS Pending payment
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item
|
||||||
ng-click="$ctrl.sendImportSms()"
|
ng-click="$ctrl.sendImportSms()"
|
||||||
|
name="sendImportSms"
|
||||||
translate>
|
translate>
|
||||||
SMS Minimum import
|
SMS Minimum import
|
||||||
</vn-item>
|
</vn-item>
|
||||||
|
|
|
@ -37,6 +37,9 @@
|
||||||
"m3": {
|
"m3": {
|
||||||
"type": "Number"
|
"type": "Number"
|
||||||
},
|
},
|
||||||
|
"cargoSupplierFk": {
|
||||||
|
"type": "Number"
|
||||||
|
},
|
||||||
"agencyModeFk": {
|
"agencyModeFk": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"mysql": {
|
"mysql": {
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<vn-icon-button
|
||||||
|
icon="more_vert"
|
||||||
|
vn-popover="menu">
|
||||||
|
</vn-icon-button>
|
||||||
|
<vn-menu vn-id="menu">
|
||||||
|
<vn-list>
|
||||||
|
<vn-item
|
||||||
|
id="clone"
|
||||||
|
ng-click="clone.show()"
|
||||||
|
translate>
|
||||||
|
Clone travel
|
||||||
|
</vn-item>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
||||||
|
|
||||||
|
<!-- Clone travel popup -->
|
||||||
|
<vn-confirm
|
||||||
|
vn-id="clone"
|
||||||
|
on-accept="$ctrl.onCloneAccept()"
|
||||||
|
question="Do you want to clone this travel?"
|
||||||
|
message="All it's properties will be copied">
|
||||||
|
</vn-confirm>
|
|
@ -0,0 +1,72 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class Controller extends Section {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
}
|
||||||
|
|
||||||
|
get travelId() {
|
||||||
|
return this._travelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
set travelId(value) {
|
||||||
|
this._travelId = value;
|
||||||
|
|
||||||
|
if (value) this.loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadData() {
|
||||||
|
const filter = {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'ref',
|
||||||
|
'shipped',
|
||||||
|
'landed',
|
||||||
|
'totalEntries',
|
||||||
|
'agencyFk',
|
||||||
|
'warehouseInFk',
|
||||||
|
'warehouseOutFk',
|
||||||
|
'cargoSupplierFk'
|
||||||
|
],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'warehouseIn',
|
||||||
|
scope: {
|
||||||
|
fields: ['name']
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
relation: 'warehouseOut',
|
||||||
|
scope: {
|
||||||
|
fields: ['name']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
return this.$http.get(`Travels/${this.travelId}`, {filter})
|
||||||
|
.then(res => this.travel = res.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCloneAccept() {
|
||||||
|
const params = JSON.stringify({
|
||||||
|
ref: this.travel.ref,
|
||||||
|
agencyModeFk: this.travel.agencyFk,
|
||||||
|
shipped: this.travel.shipped,
|
||||||
|
landed: this.travel.landed,
|
||||||
|
warehouseInFk: this.travel.warehouseInFk,
|
||||||
|
warehouseOutFk: this.travel.warehouseOutFk
|
||||||
|
});
|
||||||
|
this.$state.go('travel.create', {q: params});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.$inject = ['$element', '$scope'];
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnTravelDescriptorMenu', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
travelId: '<',
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,39 @@
|
||||||
|
import './index.js';
|
||||||
|
|
||||||
|
describe('Travel Component vnTravelDescriptorMenu', () => {
|
||||||
|
let controller;
|
||||||
|
beforeEach(ngModule('travel'));
|
||||||
|
|
||||||
|
beforeEach(inject(($componentController, $state,) => {
|
||||||
|
const $element = angular.element('<vn-travel-descriptor-menu></vn-travel-descriptor-menu>');
|
||||||
|
controller = $componentController('vnTravelDescriptorMenu', {$element});
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('onCloneAccept()', () => {
|
||||||
|
it('should call state.go with the travel data', () => {
|
||||||
|
jest.spyOn(controller.$state, 'go').mockReturnValue('ok');
|
||||||
|
|
||||||
|
controller.travel = {
|
||||||
|
ref: 'the ref',
|
||||||
|
agencyFk: 'the agency',
|
||||||
|
shipped: 'the shipped date',
|
||||||
|
landed: 'the landing date',
|
||||||
|
warehouseInFk: 'the receiver warehouse',
|
||||||
|
warehouseOutFk: 'the sender warehouse'
|
||||||
|
};
|
||||||
|
|
||||||
|
controller.onCloneAccept();
|
||||||
|
|
||||||
|
const params = JSON.stringify({
|
||||||
|
ref: controller.travel.ref,
|
||||||
|
agencyModeFk: controller.travel.agencyFk,
|
||||||
|
shipped: controller.travel.shipped,
|
||||||
|
landed: controller.travel.landed,
|
||||||
|
warehouseInFk: controller.travel.warehouseInFk,
|
||||||
|
warehouseOutFk: controller.travel.warehouseOutFk
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(controller.$state.go).toHaveBeenCalledWith('travel.create', {'q': params});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1 @@
|
||||||
|
Clone travel: Clonar envío
|
|
@ -0,0 +1,24 @@
|
||||||
|
@import "./effects";
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
vn-travel-descriptor-menu {
|
||||||
|
& > vn-icon-button[icon="more_vert"] {
|
||||||
|
display: flex;
|
||||||
|
min-width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
& > vn-icon-button[icon="more_vert"] {
|
||||||
|
@extend %clickable;
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
|
& > vn-icon {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
vn-icon {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
<vn-descriptor-content
|
<vn-descriptor-content
|
||||||
module="travel"
|
module="travel"
|
||||||
description="$ctrl.travel.ref">
|
description="$ctrl.travel.ref">
|
||||||
|
<slot-dot-menu>
|
||||||
|
<vn-travel-descriptor-menu travel-id="$ctrl.travel.id"/>
|
||||||
|
</slot-dot-menu>
|
||||||
<slot-body>
|
<slot-body>
|
||||||
<div class="attributes">
|
<div class="attributes">
|
||||||
<vn-label-value
|
<vn-label-value
|
||||||
|
@ -24,5 +27,18 @@
|
||||||
value="{{$ctrl.travel.totalEntries}}">
|
value="{{$ctrl.travel.totalEntries}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="quicklinks">
|
||||||
|
<div ng-transclude="btnOne">
|
||||||
|
<vn-quick-link
|
||||||
|
tooltip="All travels with current agency"
|
||||||
|
state="['travel.index', {q: $ctrl.travelFilter}]"
|
||||||
|
icon="local_airport">
|
||||||
|
</vn-quick-link>
|
||||||
|
</div>
|
||||||
|
<div ng-transclude="btnTwo">
|
||||||
|
</div>
|
||||||
|
<div ng-transclude="btnThree">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</slot-body>
|
</slot-body>
|
||||||
</vn-descriptor-content>
|
</vn-descriptor-content>
|
||||||
|
|
|
@ -10,6 +10,18 @@ class Controller extends Descriptor {
|
||||||
this.entity = value;
|
this.entity = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get travelFilter() {
|
||||||
|
let travelFilter;
|
||||||
|
const travel = this.travel;
|
||||||
|
|
||||||
|
if (travel && travel.agencyFk) {
|
||||||
|
travelFilter = this.travel && JSON.stringify({
|
||||||
|
agencyFk: this.travel.agencyFk
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return travelFilter;
|
||||||
|
}
|
||||||
|
|
||||||
loadData() {
|
loadData() {
|
||||||
const filter = {
|
const filter = {
|
||||||
fields: [
|
fields: [
|
||||||
|
@ -19,7 +31,8 @@ class Controller extends Descriptor {
|
||||||
'landed',
|
'landed',
|
||||||
'totalEntries',
|
'totalEntries',
|
||||||
'warehouseInFk',
|
'warehouseInFk',
|
||||||
'warehouseOutFk'
|
'warehouseOutFk',
|
||||||
|
'cargoSupplierFk'
|
||||||
],
|
],
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,5 +13,6 @@ import './thermograph/index/';
|
||||||
import './thermograph/create/';
|
import './thermograph/create/';
|
||||||
import './thermograph/edit/';
|
import './thermograph/edit/';
|
||||||
import './descriptor-popover';
|
import './descriptor-popover';
|
||||||
|
import './descriptor-menu';
|
||||||
import './extra-community';
|
import './extra-community';
|
||||||
import './extra-community-search-panel';
|
import './extra-community-search-panel';
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
Loading…
Reference in New Issue