Merge pull request 'fixes #6153 desplegar test-master' (!1721) from test into master
gitea/salix/pipeline/head This commit looks good Details

Reviewed-on: #1721
Reviewed-by: Javi Gallego <jgallego@verdnatura.es>
This commit is contained in:
Javi Gallego 2023-08-29 07:26:31 +00:00
commit 28be9ec6a6
42 changed files with 260 additions and 90 deletions

View File

@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2334.01] - 2023-08-24
### Added
- (General -> Errores) Botón para enviar cau con los datos del error
### Changed
### Fixed
## [2332.01] - 2023-08-10 ## [2332.01] - 2023-08-10
### Added ### Added

View File

@ -0,0 +1,63 @@
const smtp = require('vn-print/core/smtp');
const config = require('vn-print/core/config');
module.exports = Self => {
Self.remoteMethodCtx('sendToSupport', {
description: 'Send mail to support',
accessType: 'WRITE',
accepts: [
{
arg: 'reason',
type: 'string',
description: 'The reason'
},
{
arg: 'additionalData',
type: 'object',
required: true,
description: 'The additional data'
}
],
returns: {
type: 'object',
root: true
},
http: {
path: `/send-to-support`,
verb: 'POST'
}
});
Self.sendToSupport = async(ctx, reason, additionalData) => {
const emailUser =
await Self.app.models.EmailUser.findById(ctx.req.accessToken.userId, {fields: ['email']});
let html = `<strong>Motivo</strong>:<br/>${reason}<br/>`;
html += `<strong>Usuario</strong>:<br/>${ctx.req.accessToken.userId} ${emailUser.email}<br/>`;
for (const data in additionalData)
html += `<strong>${data}</strong>:<br/>${tryParse(additionalData[data])}<br/>`;
const subjectReason = JSON.parse(additionalData?.httpRequest)?.data?.error;
smtp.send({
to: `${config.app.reportEmail}, ${emailUser.email}`,
subject:
'[Support-Salix] ' +
additionalData?.frontPath + ' ' +
subjectReason?.name + ':' +
subjectReason?.message,
html
});
};
function tryParse(value) {
try {
try {
value = JSON.parse(value);
} catch {}
return JSON.stringify(value, null, '&nbsp;').split('\n').join('<br>');
} catch {
return value;
}
}
};

View File

@ -7,6 +7,11 @@ module.exports = Self => {
type: 'string', type: 'string',
description: 'The user name or email', description: 'The user name or email',
required: true required: true
},
{
arg: 'app',
type: 'string',
description: 'The directory for mail'
} }
], ],
http: { http: {
@ -15,7 +20,7 @@ module.exports = Self => {
} }
}); });
Self.recoverPassword = async function(user) { Self.recoverPassword = async function(user, app) {
const models = Self.app.models; const models = Self.app.models;
const usesEmail = user.indexOf('@') !== -1; const usesEmail = user.indexOf('@') !== -1;
@ -29,7 +34,7 @@ module.exports = Self => {
} }
try { try {
await Self.resetPassword({email: user, emailTemplate: 'recover-password'}); await Self.resetPassword({email: user, emailTemplate: 'recover-password', app});
} catch (err) { } catch (err) {
if (err.code === 'EMAIL_NOT_FOUND') if (err.code === 'EMAIL_NOT_FOUND')
return; return;

View File

@ -53,19 +53,13 @@ module.exports = Self => {
return Self.validateLogin(user, password); return Self.validateLogin(user, password);
}; };
Self.passExpired = async(vnUser, myOptions) => { Self.passExpired = async vnUser => {
const today = Date.vnNew(); const today = Date.vnNew();
today.setHours(0, 0, 0, 0); today.setHours(0, 0, 0, 0);
if (vnUser.passExpired && vnUser.passExpired.getTime() <= today.getTime()) { if (vnUser.passExpired && vnUser.passExpired.getTime() <= today.getTime()) {
const $ = Self.app.models;
const changePasswordToken = await $.AccessToken.create({
scopes: ['changePassword'],
userId: vnUser.id
}, myOptions);
const err = new UserError('Pass expired', 'passExpired'); const err = new UserError('Pass expired', 'passExpired');
changePasswordToken.twoFactor = vnUser.twoFactor ? true : false; err.details = {userId: vnUser.id, twoFactor: vnUser.twoFactor ? true : false};
err.details = {token: changePasswordToken};
throw err; throw err;
} }
}; };

View File

@ -1,4 +1,5 @@
module.exports = Self => { module.exports = Self => {
require('../methods/osticket/osTicketReportEmail')(Self); require('../methods/osticket/osTicketReportEmail')(Self);
require('../methods/osticket/closeTicket')(Self); require('../methods/osticket/closeTicket')(Self);
require('../methods/osticket/sendToSupport')(Self);
}; };

View File

@ -96,11 +96,21 @@ module.exports = function(Self) {
const headers = httpRequest.headers; const headers = httpRequest.headers;
const origin = headers.origin; const origin = headers.origin;
const defaultHash = '/reset-password?access_token=$token$';
const recoverHashes = {
hedera: 'verificationToken=$token$'
};
const app = info.options?.app;
let recoverHash = app ? recoverHashes[app] : defaultHash;
recoverHash = recoverHash.replace('$token$', info.accessToken.id);
const user = await Self.app.models.VnUser.findById(info.user.id); const user = await Self.app.models.VnUser.findById(info.user.id);
const params = { const params = {
recipient: info.email, recipient: info.email,
lang: user.lang, lang: user.lang,
url: `${origin}/#!/reset-password?access_token=${info.accessToken.id}` url: origin + '/#!' + recoverHash
}; };
const options = Object.assign({}, info.options); const options = Object.assign({}, info.options);

View File

@ -31,7 +31,6 @@ RUN sed -i -e 's/@mockDate/'"$MOCKDATE"'/g' mockDate.sql \
&& gosu mysql docker-structure.sh && gosu mysql docker-structure.sh
COPY changes ./changes COPY changes ./changes
COPY dump/fixtures.sql ./ COPY dump/fixtures.sql ./
ARG STAMP=unknown
RUN gosu mysql docker-fixtures.sh RUN gosu mysql docker-fixtures.sh
RUN echo "[INFO] -> Import finished" \ RUN echo "[INFO] -> Import finished" \

View File

@ -1,4 +0,0 @@
/**
* Hay una versión en salix que machacará toda esta función/procedimiento avisa
* a ___ de los cambios que quieres hacer.
*/

View File

View File

@ -0,0 +1,6 @@
UPDATE `salix`.`ACL`
SET principalId='salesPerson'
WHERE
model='Ticket'
AND property='setDeleted'
AND accessType='WRITE';

View File

@ -22,12 +22,8 @@ module.exports = class Docker {
* @param {String} networkName Name of the container network * @param {String} networkName Name of the container network
*/ */
async run(ci, networkName = 'jenkins') { async run(ci, networkName = 'jenkins') {
let d = new Date();
let pad = v => v < 10 ? '0' + v : v;
let stamp = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
log('Building container image...'); log('Building container image...');
await this.execP(`docker build --build-arg STAMP=${stamp} -t salix-db ./db`); await this.execP(`docker build -t salix-db ./db`);
log('Image built.'); log('Image built.');
let dockerArgs; let dockerArgs;

File diff suppressed because one or more lines are too long

View File

@ -362,16 +362,16 @@ INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city
VALUES VALUES
(1101, 'Bruce Wayne', '84612325V', 'BATMAN', 'Alfred', '1007 MOUNTAIN DRIVE, GOTHAM', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'), (1101, 'Bruce Wayne', '84612325V', 'BATMAN', 'Alfred', '1007 MOUNTAIN DRIVE, GOTHAM', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1102, 'Petter Parker', '87945234L', 'SPIDER MAN', 'Aunt May', '20 INGRAM STREET, QUEENS, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'PetterParker@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'), (1102, 'Petter Parker', '87945234L', 'SPIDER MAN', 'Aunt May', '20 INGRAM STREET, QUEENS, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'PetterParker@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1103, 'Clark Kent', '06815934E', 'Super man', 'lois lane', '344 Clinton Street, Apartament 3-D', 'Gotham', 46460, 1111111111, 222222222, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 0, 19, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'), (1103, 'Clark Kent', '06815934E', 'SUPER MAN', 'lois lane', '344 CLINTON STREET, APARTAMENT 3-D', 'Gotham', 46460, 1111111111, 222222222, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 0, 19, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1104, 'Tony Stark', '06089160W', 'Iron man', 'Pepper Potts', '10880 Malibu Point, 90265', 'Gotham', 46460, 1111111111, 222222222, 1, 'TonyStark@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'), (1104, 'Tony Stark', '06089160W', 'IRON MAN', 'Pepper Potts', '10880 MALIBU POINT, 90265', 'Gotham', 46460, 1111111111, 222222222, 1, 'TonyStark@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1105, 'Max Eisenhardt', '251628698', 'Magneto', 'Rogue', 'Unknown Whereabouts', 'Gotham', 46460, 1111111111, 222222222, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 300, 8, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1, 'florist'), (1105, 'Max Eisenhardt', '251628698', 'MAGNETO', 'Rogue', 'UNKNOWN WHEREABOUTS', 'Gotham', 46460, 1111111111, 222222222, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 300, 8, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1, 'florist'),
(1106, 'DavidCharlesHaller', '53136686Q', 'LEGION', 'Charles Xavier', 'CITY OF NEW YORK, NEW YORK, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'DavidCharlesHaller@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1, 'florist'), (1106, 'DavidCharlesHaller', '53136686Q', 'LEGION', 'Charles Xavier', 'CITY OF NEW YORK, NEW YORK, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'DavidCharlesHaller@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1, 'florist'),
(1107, 'Hank Pym', '09854837G', 'ANT MAN', 'Hawk', 'ANTHILL, SAN FRANCISCO, CALIFORNIA', 'Gotham', 46460, 1111111111, 222222222, 1, 'HankPym@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 19, 0, 1, 'florist'), (1107, 'Hank Pym', '09854837G', 'ANT MAN', 'Hawk', 'ANTHILL, SAN FRANCISCO, CALIFORNIA', 'Gotham', 46460, 1111111111, 222222222, 1, 'HankPym@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 19, 0, 1, 'florist'),
(1108, 'Charles Xavier', '22641921P', 'Professor X', 'Beast', '3800 Victory Pkwy, Cincinnati, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist'), (1108, 'Charles Xavier', '22641921P', 'PROFESSOR X', 'Beast', '3800 VICTORY PKWY, CINCINNATI, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist'),
(1109, 'Bruce Banner', '16104829E', 'Hulk', 'Black widow', 'Somewhere in New York', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist'), (1109, 'Bruce Banner', '16104829E', 'HULK', 'Black widow', 'SOMEWHERE IN NEW YORK', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist'),
(1110, 'Jessica Jones', '58282869H', 'Jessica Jones', 'Luke Cage', 'NYCC 2015 Poster', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist'), (1110, 'Jessica Jones', '58282869H', 'JESSICA JONES', 'Luke Cage', 'NYCC 2015 POSTER', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist'),
(1111, 'Missing', NULL, 'Missing man', 'Anton', 'The space, Universe far away', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others'), (1111, 'Missing', NULL, 'MISSING MAN', 'Anton', 'THE SPACE, UNIVERSE FAR AWAY', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others'),
(1112, 'Trash', NULL, 'Garbage man', 'Unknown name', 'New York city, Underground', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others'); (1112, 'Trash', NULL, 'GARBAGE MAN', 'Unknown name', 'NEW YORK CITY, UNDERGROUND', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others');
INSERT INTO `vn`.`client`(`id`, `name`, `fi`, `socialName`, `contact`, `street`, `city`, `postcode`, `isRelevant`, `email`, `iban`,`dueDay`,`accountingAccount`, `isEqualizated`, `provinceFk`, `hasToInvoice`, `credit`, `countryFk`, `isActive`, `gestdocFk`, `quality`, `payMethodFk`,`created`, `isTaxDataChecked`) INSERT INTO `vn`.`client`(`id`, `name`, `fi`, `socialName`, `contact`, `street`, `city`, `postcode`, `isRelevant`, `email`, `iban`,`dueDay`,`accountingAccount`, `isEqualizated`, `provinceFk`, `hasToInvoice`, `credit`, `countryFk`, `isActive`, `gestdocFk`, `quality`, `payMethodFk`,`created`, `isTaxDataChecked`)
SELECT id, name, CONCAT(RPAD(CONCAT(id,9),8,id),'A'), CONCAT(name, 'Social'), CONCAT(name, 'Contact'), CONCAT(name, 'Street'), 'GOTHAM', 46460, 1, CONCAT(name,'@mydomain.com'), NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5, util.VN_CURDATE(), 1 SELECT id, name, CONCAT(RPAD(CONCAT(id,9),8,id),'A'), CONCAT(name, 'Social'), CONCAT(name, 'Contact'), CONCAT(name, 'Street'), 'GOTHAM', 46460, 1, CONCAT(name,'@mydomain.com'), NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5, util.VN_CURDATE(), 1

View File

@ -65,7 +65,6 @@ TABLES=(
sample sample
state state
ticketUpdateAction ticketUpdateAction
time
volumeConfig volumeConfig
workCenter workCenter
companyI18n companyI18n

View File

@ -10,7 +10,7 @@ describe('Ticket create path', () => {
beforeAll(async() => { beforeAll(async() => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('employee', 'ticket'); await page.loginAndModule('salesPerson', 'ticket');
}); });
afterAll(async() => { afterAll(async() => {

View File

@ -55,3 +55,4 @@ import './datalist';
import './contextmenu'; import './contextmenu';
import './rating'; import './rating';
import './smart-table'; import './smart-table';
import './support-dialog';

View File

@ -1 +1,5 @@
<div id="shapes"></div> <div id="shapes"></div>
<vn-support-dialog
vn-id="support-dialog"
additional-data="$ctrl.additionalData">
</vn-support-dialog>

View File

@ -27,6 +27,18 @@ export default class Controller extends Component {
setTimeout(() => element.classList.add('shown'), 30); setTimeout(() => element.classList.add('shown'), 30);
shape.element = element; shape.element = element;
if (data.additionalData && this.vnToken.token) {
this.additionalData = data.additionalData;
let supportButton = document.createElement('i');
supportButton.setAttribute('class', 'material-icons clickable');
supportButton.addEventListener('click', () => this.$.supportDialog.show());
element.appendChild(supportButton);
let buttonIcon = 'support_agent';
buttonIcon = document.createTextNode(buttonIcon);
supportButton.appendChild(buttonIcon);
}
if (shape.type) if (shape.type)
element.classList.add(shape.type); element.classList.add(shape.type);
@ -95,7 +107,7 @@ export default class Controller extends Component {
clearTimeout(shape.hideTimeout); clearTimeout(shape.hideTimeout);
shape.hideTimeout = setTimeout( shape.hideTimeout = setTimeout(
() => this.hide(shape), shape.timeout || 3000); () => this.hide(shape), shape.timeout || 5000);
this.lastShape = shape; this.lastShape = shape;
} }

View File

@ -20,6 +20,10 @@ vn-snackbar .shape {
margin-bottom: 15px; margin-bottom: 15px;
color: white; color: white;
padding: 12px 25px 12px 12px; padding: 12px 25px 12px 12px;
display: flex ;
flex-direction: row;
justify-content: center;
align-items: center;
& > .text { & > .text {
text-align: center; text-align: center;
@ -64,4 +68,12 @@ vn-snackbar .shape {
top: 0; top: 0;
right: 0 right: 0
} }
.clickable{
background-color: $color-main;
padding: 6px;
border-radius: 50%;
cursor: pointer;
margin-right: 7px;
}
} }

View File

@ -0,0 +1,22 @@
<tpl-body>
<section>
<h5 class="vn-py-sm" translate>Send cau</h5>
<vn-horizontal>
<vn-textarea vn-one
label="ExplainReason"
ng-model="$ctrl.reason"
rows="2"
required="true">
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<span>
{{'By sending this ticket, all the data related to the error, the section, the user, etc., are already sent.' | translate}}
</span>
</vn-horizontal>
</section>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Send</button>
</tpl-buttons>

View File

@ -0,0 +1,30 @@
import ngModule from '../../module';
import Dialog from '../dialog';
export default class Controller extends Dialog {
constructor($element, $, $transclude) {
super($element, $, $transclude);
}
responseHandler(response) {
if (response !== 'accept')
return super.responseHandler(response);
this.$http.post('Ostickets/send-to-support', {
reason: this.reason,
additionalData: this.additionalData
})
.then(() => super.responseHandler(response))
.then(() => this.vnApp.showSuccess(this.$t('Email sended!')));
}
}
Controller.$inject = ['$element', '$scope', '$transclude'];
ngModule.vnComponent('vnSupportDialog', {
slotTemplate: require('./index.html'),
controller: Controller,
bindings: {
additionalData: '<?'
}
});

View File

@ -14,3 +14,4 @@ Previous: Back
Load more: Load more Load more: Load more
Auto-scroll interrupted, please adjust the search: Auto-scroll interrupted, please adjust the search Auto-scroll interrupted, please adjust the search: Auto-scroll interrupted, please adjust the search
General search: General search General search: General search
ExplainReason: Explain the reason why this error should not occur

View File

@ -64,3 +64,6 @@ No results found: Sin resultados
No data: Sin datos No data: Sin datos
Undo changes: Deshacer cambios Undo changes: Deshacer cambios
Load more results: Cargar más resultados Load more results: Cargar más resultados
Send cau: Enviar cau
By sending this ticket, all the data related to the error, the section, the user, etc., are already sent.: Al enviar este cau ya se envían todos los datos relacionados con el error, la sección, el usuario, etc
ExplainReason: Explique el motivo por el que no deberia aparecer este fallo

View File

@ -23,9 +23,9 @@ export default class App {
this.logger.showSuccess(message); this.logger.showSuccess(message);
} }
showError(message) { showError(message, additionalData) {
if (this.logger) if (this.logger)
this.logger.showError(message); this.logger.showError(message, additionalData);
} }
pushLoader() { pushLoader() {

View File

@ -60,7 +60,7 @@ export default class Token {
if (!this.token) return; if (!this.token) return;
const created = storage.getItem('vnTokenCreated'); const created = storage.getItem('vnTokenCreated');
this.created = created && new Date(created); this.created = created && new Date(created);
this.renewPeriod = storage.getItem('vnTokenRenewPeriod'); this.ttl = storage.getItem('vnTokenTtl');
} }
setStorage(storage, token, created, ttl) { setStorage(storage, token, created, ttl) {

View File

@ -25,15 +25,15 @@ export default class App extends Component {
} }
showMessage(message) { showMessage(message) {
this.$.snackbar.show({message: message}); this.$.snackbar.show({message});
} }
showSuccess(message) { showSuccess(message) {
this.$.snackbar.showSuccess({message: message}); this.$.snackbar.showSuccess({message});
} }
showError(message) { showError(message, additionalData) {
this.$.snackbar.showError({message: message}); this.$.snackbar.showError({message, additionalData});
} }
} }

View File

@ -15,9 +15,6 @@ export default class Controller {
} }
$onInit() { $onInit() {
if (!this.$state.params.id)
this.$state.go('login');
this.$http.get('UserPasswords/findOne') this.$http.get('UserPasswords/findOne')
.then(res => { .then(res => {
this.passRequirements = res.data; this.passRequirements = res.data;
@ -25,7 +22,7 @@ export default class Controller {
} }
submit() { submit() {
const userId = this.$state.params.userId; const userId = parseInt(this.$state.params.userId);
const oldPassword = this.oldPassword; const oldPassword = this.oldPassword;
const newPassword = this.newPassword; const newPassword = this.newPassword;
const repeatPassword = this.repeatPassword; const repeatPassword = this.repeatPassword;
@ -36,18 +33,13 @@ export default class Controller {
if (newPassword != this.repeatPassword) if (newPassword != this.repeatPassword)
throw new UserError(`Passwords don't match`); throw new UserError(`Passwords don't match`);
const headers = {
Authorization: this.$state.params.id
};
this.$http.patch('Accounts/change-password', this.$http.patch('Accounts/change-password',
{ {
id: userId, userId,
oldPassword, oldPassword,
newPassword, newPassword,
code code
}, }
{headers}
).then(() => { ).then(() => {
this.vnApp.showSuccess(this.$translate.instant('Password updated!')); this.vnApp.showSuccess(this.$translate.instant('Password updated!'));
this.$state.go('login'); this.$state.go('login');

View File

@ -36,7 +36,7 @@ export default class Controller {
const err = req.data?.error; const err = req.data?.error;
if (err?.code == 'passExpired') if (err?.code == 'passExpired')
this.$state.go('change-password', err.details.token); this.$state.go('change-password', err.details);
this.loading = false; this.loading = false;
this.password = ''; this.password = '';

View File

@ -148,7 +148,13 @@ function $exceptionHandler(vnApp, $window, $state, $injector) {
if (messageT) if (messageT)
message = $translate.instant(messageT); message = $translate.instant(messageT);
vnApp.showError(message);
const additonalData = {
frontPath: $state.current.name,
httpRequest: cause?.replace('Possibly unhandled rejection: ', ''),
backError: exception
};
vnApp.showError(message, additonalData);
}; };
} }
ngModule.factory('$exceptionHandler', $exceptionHandler); ngModule.factory('$exceptionHandler', $exceptionHandler);

View File

@ -45,7 +45,7 @@ function config($stateProvider, $urlRouterProvider) {
}) })
.state('change-password', { .state('change-password', {
parent: 'outLayout', parent: 'outLayout',
url: '/change-password?id&userId&twoFactor', url: '/change-password?userId&twoFactor',
description: 'Change password', description: 'Change password',
template: '<vn-change-password></vn-change-password>' template: '<vn-change-password></vn-change-password>'
}) })

View File

@ -179,6 +179,7 @@
"You can not use the same password": "You can not use the same password", "You can not use the same password": "You can not use the same password",
"Valid priorities": "Valid priorities: %d", "Valid priorities": "Valid priorities: %d",
"Negative basis of tickets": "Negative basis of tickets: {{ticketsIds}}", "Negative basis of tickets": "Negative basis of tickets: {{ticketsIds}}",
"This ticket cannot be left empty.": "This ticket cannot be left empty. %s",
"Social name should be uppercase": "Social name should be uppercase", "Social name should be uppercase": "Social name should be uppercase",
"Street should be uppercase": "Street should be uppercase", "Street should be uppercase": "Street should be uppercase",
"You don't have enough privileges.": "You don't have enough privileges.", "You don't have enough privileges.": "You don't have enough privileges.",

View File

@ -306,6 +306,7 @@
"Valid priorities": "Prioridades válidas: %d", "Valid priorities": "Prioridades válidas: %d",
"Negative basis of tickets": "Base negativa para los tickets: {{ticketsIds}}", "Negative basis of tickets": "Base negativa para los tickets: {{ticketsIds}}",
"You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado", "You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado",
"This ticket cannot be left empty.": "Este ticket no se puede dejar vacío. %s",
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias", "The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
"You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado", "You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado",
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado", "This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado",

View File

@ -1,12 +1,15 @@
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('changePassword', { Self.remoteMethod('changePassword', {
description: 'Changes the user password', description: 'Changes the user password',
accessType: 'WRITE',
accessScopes: ['changePassword'],
accepts: [ accepts: [
{ {
arg: 'userId',
type: 'integer',
description: 'The user id',
required: true
}, {
arg: 'oldPassword', arg: 'oldPassword',
type: 'string', type: 'string',
description: 'The old password', description: 'The old password',
@ -28,9 +31,7 @@ module.exports = Self => {
} }
}); });
Self.changePassword = async function(ctx, oldPassword, newPassword, code, options) { Self.changePassword = async function(userId, oldPassword, newPassword, code, options) {
const userId = ctx.req.accessToken.userId;
const myOptions = {}; const myOptions = {};
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);

View File

@ -1,7 +1,7 @@
const {models} = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('account changePassword()', () => { describe('account changePassword()', () => {
const ctx = {req: {accessToken: {userId: 70}}}; const userId = 70;
const unauthCtx = { const unauthCtx = {
req: { req: {
headers: {}, headers: {},
@ -20,7 +20,7 @@ describe('account changePassword()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await models.Account.changePassword(ctx, 'wrongPassword', 'nightmare.9999', null, options); await models.Account.changePassword(userId, 'wrongPassword', 'nightmare.9999', null, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
await tx.rollback(); await tx.rollback();
@ -37,8 +37,8 @@ describe('account changePassword()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await models.Account.changePassword(ctx, 'nightmare', 'nightmare.9999', null, options); await models.Account.changePassword(userId, 'nightmare', 'nightmare.9999', null, options);
await models.Account.changePassword(ctx, 'nightmare.9999', 'nightmare.9999', null, options); await models.Account.changePassword(userId, 'nightmare.9999', 'nightmare.9999', null, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
await tx.rollback(); await tx.rollback();
@ -54,7 +54,7 @@ describe('account changePassword()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await models.Account.changePassword(ctx, 'nightmare', 'nightmare.9999', null, options); await models.Account.changePassword(userId, 'nightmare', 'nightmare.9999', null, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
await tx.rollback(); await tx.rollback();
@ -86,8 +86,8 @@ describe('account changePassword()', () => {
} }
try { try {
const authCode = await models.AuthCode.findOne({where: {userFk: 70}}, options); const authCode = await models.AuthCode.findOne({where: {userFk: userId}}, options);
await models.Account.changePassword(ctx, 'nightmare', 'nightmare.9999', authCode.code, options); await models.Account.changePassword(userId, 'nightmare', 'nightmare.9999', authCode.code, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
await tx.rollback(); await tx.rollback();

View File

@ -1,7 +1,7 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
describe('Client last active tickets', () => { describe('Client last active tickets', () => {
it('should receive an array of last active tickets of Bruce Wayne', async() => { it('should receive an array of last active tickets of BRUCE WAYNE', async() => {
const tx = await models.Client.beginTransaction({}); const tx = await models.Client.beginTransaction({});
try { try {

View File

@ -1,7 +1,7 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
describe('Client transactions', () => { describe('Client transactions', () => {
it('should call transactions() method to receive a list of Web Payments from Bruce Wayne', async() => { it('should call transactions() method to receive a list of Web Payments from BRUCE WAYNE', async() => {
const tx = await models.Client.beginTransaction({}); const tx = await models.Client.beginTransaction({});
try { try {

View File

@ -46,8 +46,6 @@ class Controller extends Section {
} }
deleteRoadmaps() { deleteRoadmaps() {
console.log(this.checked);
for (const roadmap of this.checked) { for (const roadmap of this.checked) {
this.$http.delete(`Roadmaps/${roadmap.id}`) this.$http.delete(`Roadmaps/${roadmap.id}`)
.then(() => this.$.model.refresh()) .then(() => this.$.model.refresh())

View File

@ -248,6 +248,7 @@ module.exports = Self => {
am.name AS agencyMode, am.name AS agencyMode,
am.id AS agencyModeFk, am.id AS agencyModeFk,
st.name AS state, st.name AS state,
st.classColor,
wk.lastName AS salesPerson, wk.lastName AS salesPerson,
ts.stateFk AS stateFk, ts.stateFk AS stateFk,
ts.alertLevel AS alertLevel, ts.alertLevel AS alertLevel,

View File

@ -5,6 +5,8 @@ describe('sale transferSales()', () => {
const userId = 1101; const userId = 1101;
const activeCtx = { const activeCtx = {
accessToken: {userId: userId}, accessToken: {userId: userId},
headers: {origin: ''},
__: value => value
}; };
const ctx = {req: activeCtx}; const ctx = {req: activeCtx};

View File

@ -37,6 +37,7 @@ module.exports = Self => {
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const models = Self.app.models; const models = Self.app.models;
const myOptions = {userId}; const myOptions = {userId};
const $t = ctx.req.__; // $translate
let tx; let tx;
if (typeof options == 'object') if (typeof options == 'object')
@ -95,9 +96,18 @@ module.exports = Self => {
const isTicketEmpty = await models.Ticket.isEmpty(id, myOptions); const isTicketEmpty = await models.Ticket.isEmpty(id, myOptions);
if (isTicketEmpty) { if (isTicketEmpty) {
await originalTicket.updateAttributes({ try {
isDeleted: true await models.Ticket.setDeleted(ctx, id, myOptions);
}, myOptions); } catch (e) {
if (e.statusCode === 400) {
throw new UserError(
`This ticket cannot be left empty.`,
'TRANSFER_SET_DELETED',
$t(e.message, ...e.translateArgs)
);
}
throw e;
}
} }
if (tx) await tx.commit(); if (tx) await tx.commit();

View File

@ -27,6 +27,9 @@
"code": { "code": {
"type": "string", "type": "string",
"required": false "required": false
},
"classColor": {
"type": "string"
} }
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "salix-back", "name": "salix-back",
"version": "23.32.02", "version": "23.34.01",
"author": "Verdnatura Levante SL", "author": "Verdnatura Levante SL",
"description": "Salix backend", "description": "Salix backend",
"license": "GPL-3.0", "license": "GPL-3.0",