Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2587-supplier_minor_changes
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Bernat Exposito 2020-11-18 11:47:37 +01:00
commit 781766a155
45 changed files with 555 additions and 66 deletions

View File

@ -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}"`];
};
};

View File

@ -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();
});
});

View File

@ -45,6 +45,9 @@
},
"updated": {
"type": "date"
},
"image": {
"type": "String"
}
},
"relations": {

View File

@ -43,7 +43,12 @@
"model": "ImageCollectionSize",
"foreignKey": "collectionFk",
"property": "id"
}
},
"readRole": {
"type": "belongsTo",
"model": "Role",
"foreignKey": "readRoleFk"
}
},
"acls": [
{

View File

@ -3,6 +3,8 @@ const sharp = require('sharp');
const path = require('path');
module.exports = Self => {
require('../methods/image/download')(Self);
Self.getPath = function() {
return '/var/lib/salix/image';
};

View File

@ -29,6 +29,14 @@
"default": 0
}
},
"relations": {
"collection": {
"type": "belongsTo",
"model": "ImageCollection",
"foreignKey": "collectionFk",
"primaryKey": "name"
}
},
"acls": [
{
"accessType": "READ",

View File

@ -1,3 +1,11 @@
UPDATE `salix`.`ACL` SET `principalId` = 'deliveryBoss' WHERE (`id` = '194');
UPDATE `salix`.`ACL` SET `principalId` = 'claimManager' WHERE (`id` = '97');
UPDATE `salix`.`ACL` SET `principalId` = 'claimManager' WHERE (`id` = '100');
UPDATE `salix`.`ACL` SET `principalId` = 'claimManager' WHERE (`id` = '103');
UPDATE `salix`.`ACL` SET `principalId` = 'claimManager' WHERE (`id` = '202');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Town', '*', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Province', '*', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Supplier', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Supplier', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('SupplierLog', '*', 'READ', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,5 @@
ALTER TABLE `hedera`.`imageCollection`
ADD COLUMN `readRoleFk` VARCHAR(45) NULL DEFAULT NULL AFTER `column`;
update `hedera`.`imageCollection` set `readRoleFk` = 1;

View File

@ -0,0 +1,2 @@
ALTER TABLE `account`.`user`
ADD COLUMN `image` VARCHAR(255) NULL AFTER `password`;

View File

@ -0,0 +1,20 @@
CREATE TABLE `vn`.supplierFreighter
(
supplierFk INT NOT NULL,
CONSTRAINT supplierFreighter_pk
PRIMARY KEY (supplierFk),
CONSTRAINT supplier_id_fk
FOREIGN KEY (supplierFk) REFERENCES supplier (id)
ON UPDATE CASCADE ON DELETE CASCADE
);
INSERT IGNORE INTO `vn`.supplierFreighter (supplierFk) VALUES (286);
INSERT IGNORE INTO `vn`.supplierFreighter (supplierFk) VALUES (454);
INSERT IGNORE INTO `vn`.supplierFreighter (supplierFk) VALUES (582);
INSERT IGNORE INTO `vn`.supplierFreighter (supplierFk) VALUES (470);
INSERT IGNORE INTO `vn`.supplierFreighter (supplierFk) VALUES (775);
INSERT IGNORE INTO `vn`.supplierFreighter (supplierFk) VALUES (812);
INSERT IGNORE INTO `vn`.supplierFreighter (supplierFk) VALUES (1112);
INSERT IGNORE INTO `vn`.supplierFreighter (supplierFk) VALUES (1242);
INSERT IGNORE INTO `vn`.supplierFreighter (supplierFk) VALUES (1281);
INSERT IGNORE INTO `vn`.supplierFreighter (supplierFk) VALUES (1765);

View File

@ -0,0 +1,7 @@
ALTER TABLE `vn`.travel
DROP FOREIGN KEY travel_ibfk_4;
ALTER TABLE `vn`.travel
ADD CONSTRAINT supplierFreighter_fk_4
FOREIGN KEY (cargoSupplierFk) REFERENCES supplierFreighter (supplierFk)
ON UPDATE CASCADE ON DELETE SET NULL;

View File

@ -1,10 +0,0 @@
UPDATE `salix`.`ACL` SET `principalId` = 'deliveryBoss' WHERE (`id` = '194');
UPDATE `salix`.`ACL` SET `principalId` = 'claimManager' WHERE (`id` = '97');
UPDATE `salix`.`ACL` SET `principalId` = 'claimManager' WHERE (`id` = '100');
UPDATE `salix`.`ACL` SET `principalId` = 'claimManager' WHERE (`id` = '103');
UPDATE `salix`.`ACL` SET `principalId` = 'claimManager' WHERE (`id` = '202');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Town', '*', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Province', '*', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('supplier', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('SupplierContact', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');

View File

@ -443,7 +443,7 @@ USE `hedera`;
LOCK TABLES `imageCollection` WRITE;
/*!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 */;
UNLOCK TABLES;

View File

@ -29,8 +29,8 @@ INSERT INTO `vn`.`packagingConfig`(`upperGap`)
UPDATE `account`.`role` SET id = 100 WHERE id = 0;
CALL `account`.`role_sync`;
INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `password`,`role`,`active`,`email`, `lang`)
SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@mydomain.com'), 'en'
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', 'e7723f0b24ff05b32ed09d95196f2f29'
FROM `account`.`role` WHERE id <> 20
ORDER BY id;
@ -51,20 +51,20 @@ INSERT INTO `hedera`.`tpvConfig`(`id`, `currency`, `terminal`, `transactionType`
VALUES
(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
(101, 'BruceWayne', 'Bruce Wayne', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es'),
(102, 'PetterParker', 'Petter Parker', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en'),
(103, 'ClarkKent', 'Clark Kent', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr'),
(104, 'TonyStark', 'Tony Stark', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es'),
(105, 'MaxEisenhardt', 'Max Eisenhardt', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt'),
(106, 'DavidCharlesHaller', 'David Charles Haller', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en'),
(107, 'HankPym', 'Hank Pym', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en'),
(108, 'CharlesXavier', 'Charles Xavier', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en'),
(109, 'BruceBanner', 'Bruce Banner', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en'),
(110, 'JessicaJones', 'Jessica Jones', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en'),
(111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en'),
(112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en');
(101, 'BruceWayne', 'Bruce Wayne', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
(102, 'PetterParker', 'Petter Parker', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(103, 'ClarkKent', 'Clark Kent', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr', 'e7723f0b24ff05b32ed09d95196f2f29'),
(104, 'TonyStark', 'Tony Stark', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
(105, 'MaxEisenhardt', 'Max Eisenhardt', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt', 'e7723f0b24ff05b32ed09d95196f2f29'),
(106, 'DavidCharlesHaller', 'David Charles Haller', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(107, 'HankPym', 'Hank Pym', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(108, 'CharlesXavier', 'Charles Xavier', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(109, 'BruceBanner', 'Bruce Banner', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(110, 'JessicaJones', 'Jessica Jones', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en', NULL),
(111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL),
(112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL);
INSERT INTO `account`.`mailAlias`(`id`, `alias`, `description`, `isPublic`)
VALUES
@ -439,7 +439,7 @@ INSERT INTO `vn`.`bankEntity`(`id`, `countryFk`, `name`, `bic`)
(2100, 1, 'Caixa Bank', 'CAIXESBB');
INSERT INTO `vn`.`supplierAccount`(`id`, `supplierFk`, `iban`, `bankEntityFk`)
VALUES
VALUES
(241, 442, 'ES111122333344111122221111', 128);
INSERT INTO `vn`.`company`(`id`, `code`, `supplierAccountFk`, `workerManagerFk`, `companyCode`, `sage200Company`, `expired`)
@ -1226,6 +1226,11 @@ INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email
(3, 2, 321654987, NULL, 'supplier2@email.es', NULL, NULL),
(4, 442, 321654987, NULL, NULL, 'observation442', NULL);
INSERT INTO `vn`.`supplierFreighter` (`supplierFk`)
VALUES
(1),
(2);
INSERT INTO `cache`.`cache_calc`(`id`, `cache_id`, `cacheName`, `params`, `last_refresh`, `expires`, `created`, `connection_id`)
VALUES
(1, 2, 'available', CONCAT_WS('/',1,CURDATE()), CURRENT_TIMESTAMP(), DATE_ADD(CURRENT_TIMESTAMP(),INTERVAL 15 MINUTE), CURDATE(), NULL),
@ -1240,16 +1245,16 @@ INSERT INTO `vn`.`ticketWeekly`(`ticketFk`, `weekDay`)
(4, 4),
(5, 6);
INSERT INTO `vn`.`travel`(`id`,`shipped`, `landed`, `warehouseInFk`, `warehouseOutFk`, `agencyFk`, `m3`, `kg`,`ref`, `totalEntries`)
INSERT INTO `vn`.`travel`(`id`,`shipped`, `landed`, `warehouseInFk`, `warehouseOutFk`, `agencyFk`, `m3`, `kg`,`ref`, `totalEntries`, `cargoSupplierFk`)
VALUES
(1, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 1, 2, 1, 100.00, 1000, 'first travel', 1),
(2, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 150, 2000, 'second travel', 2),
(3, CURDATE(), CURDATE(), 1, 2, 1, 0.00, 0.00, 'third travel', 1),
(4, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 50.00, 500, 'fourth travel', 0),
(5, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 3, 2, 1, 50.00, 500, 'fifth travel', 1),
(6, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 4, 2, 1, 50.00, 500, 'sixth travel', 1),
(7, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 5, 2, 1, 50.00, 500, 'seventh travel', 2),
(8, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 5, 2, 1, 50.00, 500, 'eight travel', 1);
(1, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 1, 2, 1, 100.00, 1000, 'first travel', 1, 1),
(2, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 150, 2000, 'second travel', 2, 2),
(3, CURDATE(), CURDATE(), 1, 2, 1, 0.00, 0.00, 'third travel', 1, 1),
(4, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 50.00, 500, 'fourth travel', 0, 2),
(5, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 3, 2, 1, 50.00, 500, 'fifth travel', 1, 1),
(6, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 4, 2, 1, 50.00, 500, 'sixth travel', 1, 2),
(7, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 5, 2, 1, 50.00, 500, 'seventh travel', 2, 1),
(8, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 5, 2, 1, 50.00, 500, 'eight travel', 1, 2);
INSERT INTO `vn`.`entry`(`id`, `supplierFk`, `created`, `travelFk`, `isConfirmed`, `companyFk`, `ref`,`isInventory`, `isRaid`, `notes`, `evaNotes`)
VALUES
@ -2143,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 -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);

View File

@ -815,6 +815,10 @@ export default {
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)'
},
travelIndex: {
anySearchResult: 'vn-travel-index vn-tbody > a',
firstSearchResult: 'vn-travel-index vn-tbody > a:nth-child(1)'
},
travelBasicDada: {
reference: 'vn-travel-basic-data vn-textfield[ng-model="$ctrl.travel.ref"]',
agency: 'vn-travel-basic-data vn-autocomplete[ng-model="$ctrl.travel.agencyModeFk"]',
@ -841,6 +845,11 @@ export default {
createdThermograph: 'vn-travel-thermograph-index vn-tbody > vn-tr',
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: {
searchResult: 'vn-zone-index a.vn-tr',
},

View File

@ -41,7 +41,6 @@ describe('Client balance path', () => {
it('should click the new payment button', async() => {
await page.closePopup();
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() => {

View File

@ -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');
});
});

View File

@ -31,12 +31,15 @@
ng-if="$ctrl.rightMenu"
ng-click="$ctrl.rightMenu.show()">
</vn-icon-button>
<vn-icon-button
<button class="buttonAccount">
<img
id="user"
icon="account_circle"
ng-src="{{$ctrl.getImageUrl()}}"
ng-click="userPopover.show($event)"
translate-attr="{title: 'Account'}">
</vn-icon-button>
translate-attr="{title: 'Account'}"
on-error-src/>
</button>
</div>
<vn-menu vn-id="apps-menu">
<vn-list class="modules-menu">

View File

@ -18,6 +18,14 @@ export class Layout extends Component {
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'];

View File

@ -22,4 +22,19 @@ describe('Component vnLayout', () => {
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();
});
});
});

View File

@ -109,6 +109,14 @@ vn-layout {
}
}
}
img {
width: 40px;
border-radius: 50%;
}
.buttonAccount {
background: none;
border: none;
}
@media screen and (max-width: $mobile-width) {
& > vn-topbar {
& > .start > .logo {
@ -147,3 +155,4 @@ vn-layout {
font-size: 1.5rem;
height: auto;
}

View File

@ -13,7 +13,9 @@
<vn-popover vn-id="popover">
<vn-vertical class="user-popover vn-pa-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>
<div class="user">

View File

@ -3,12 +3,13 @@ import './style.scss';
import config from '../../config.json';
class Controller {
constructor($, $translate, vnConfig, vnAuth) {
constructor($, $translate, vnConfig, vnAuth, vnToken) {
Object.assign(this, {
$,
$translate,
vnConfig,
vnAuth,
vnToken,
lang: $translate.use(),
langs: []
});
@ -77,8 +78,12 @@ class Controller {
this.$.companies.refresh();
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', {
template: require('./index.html'),

View File

@ -57,5 +57,13 @@ describe('Salix', () => {
expect(controller.companyFk).toBe(4);
});
});
describe('getImageUrl()', () => {
it('should return de url image', () => {
const url = controller.getImageUrl();
expect(url).toBeDefined();
});
});
});
});

View File

@ -11,6 +11,10 @@
font-size: 5rem;
color: $color-font-bg-marginal;
}
img {
width: 80px;
border-radius: 50%;
}
& > div {
display: flex;
flex-direction: column;

View File

@ -55,8 +55,8 @@
</vn-td>
<vn-td number>{{::saleClaimed.sale.price | currency: 'EUR':2}}</vn-td>
<vn-td number>
<span ng-class="{'link': $ctrl.isEditable}"
title="{{$ctrl.isEditable ? 'Edit discount' : ''}}"
<span ng-class="{'link': $ctrl.isRewritable && $ctrl.isClaimManager}"
translate-attr="{title: $ctrl.isRewritable && $ctrl.isClaimManager ? 'Edit discount' : ''}"
ng-click="$ctrl.showEditPopover($event, saleClaimed)">
{{saleClaimed.sale.discount}} %
</span>

View File

@ -54,6 +54,10 @@ class Controller extends Section {
this.updateNewPrice();
}
get isClaimManager() {
return this.aclService.hasAny(['claimManager']);
}
openAddSalesDialog() {
this.getClaimableFromTicket();
this.$.addSales.show();
@ -131,17 +135,6 @@ class Controller extends Section {
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() {
this.$http.get(`Tickets/${this.claim.ticketFk}/getSalesPersonMana`).then(res => {
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() {
const claimedSale = this.saleClaimed.sale;
if (this.newDiscount != claimedSale.discount) {
@ -176,8 +177,6 @@ class Controller extends Section {
this.clearDiscount();
this.vnApp.showSuccess(this.$t('Data saved!'));
}).catch(err => {
this.vnApp.showError(err.message);
});
}

View File

@ -50,7 +50,13 @@
</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 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.price3 | dashIfEmpty}}</vn-td>
<vn-td number class="expendable">{{entry.stickers | dashIfEmpty}}</vn-td>
@ -77,6 +83,10 @@
</vn-card>
</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"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-menu>

View File

@ -70,13 +70,17 @@ module.exports = Self => {
const userId = ctx.req.accessToken.userId;
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({
where: {ticketFk: id}
});
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`);
const usesMana = await models.WorkerMana.findOne({

View File

@ -11,11 +11,10 @@
"type": "string"
},
"ticketFk": {
"id": 1,
"id": true,
"type": "Number"
},
"ticketTrackingFk": {
"id": 2,
"type": "Number"
}
},

View File

@ -37,6 +37,9 @@
"m3": {
"type": "Number"
},
"cargoSupplierFk": {
"type": "Number"
},
"agencyModeFk": {
"type": "Number",
"mysql": {

View File

@ -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>

View File

@ -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: '<',
}
});

View File

@ -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});
});
});
});

View File

@ -0,0 +1 @@
Clone travel: Clonar envío

View File

@ -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;
}
}
}

View File

@ -1,6 +1,9 @@
<vn-descriptor-content
module="travel"
description="$ctrl.travel.ref">
<slot-dot-menu>
<vn-travel-descriptor-menu travel-id="$ctrl.travel.id"/>
</slot-dot-menu>
<slot-body>
<div class="attributes">
<vn-label-value
@ -24,5 +27,18 @@
value="{{$ctrl.travel.totalEntries}}">
</vn-label-value>
</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>
</vn-descriptor-content>

View File

@ -10,6 +10,18 @@ class Controller extends Descriptor {
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() {
const filter = {
fields: [
@ -19,7 +31,8 @@ class Controller extends Descriptor {
'landed',
'totalEntries',
'warehouseInFk',
'warehouseOutFk'
'warehouseOutFk',
'cargoSupplierFk'
],
include: [
{

View File

@ -13,3 +13,4 @@ import './thermograph/index/';
import './thermograph/create/';
import './thermograph/edit/';
import './descriptor-popover';
import './descriptor-menu';

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB