Merge branch 'dev' into 5347-clientCreateRefactor
gitea/salix/pipeline/head There was a failure building this commit Details

This commit is contained in:
Pablo Natek 2023-07-03 11:35:50 +00:00
commit 8ef6f0dd17
64 changed files with 2162 additions and 3033 deletions

View File

@ -5,21 +5,13 @@ All notable changes to this project will be documented in this file.
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).
## [2328.01] - 2023-07-13
### Added
### Changed
### Fixed
## [2326.01] - 2023-06-29
### Added
- (Entradas -> Correo) Al cambiar el tipo de cambio enviará un correo a las personas designadas
- (General -> Históricos) Botón para ver el estado del registro en cada punto
- (General -> Históricos) Al filtar por registro se muestra todo el histórial desde que fue creado
- (Tickets -> Índice) Permite enviar varios albaranes a Docuware
### Changed
- (General -> Históricos) Los registros se muestran agrupados por usuario y entidad

View File

@ -17,23 +17,22 @@ module.exports = Self => {
Self.renewToken = async function(ctx) {
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const created = ctx.req.accessToken.created;
const tokenId = ctx.req.accessToken.id;
const token = ctx.req.accessToken;
const now = new Date();
const differenceMilliseconds = now - created;
const differenceMilliseconds = now - token.created;
const differenceSeconds = Math.floor(differenceMilliseconds / 1000);
const accessTokenConfig = await models.AccessTokenConfig.findOne({fields: ['renewPeriod']});
const fields = ['renewPeriod', 'courtesyTime'];
const accessTokenConfig = await models.AccessTokenConfig.findOne({fields});
if (differenceSeconds <= accessTokenConfig.renewPeriod)
throw new UserError(`The renew period has not been exceeded`);
if (differenceSeconds < accessTokenConfig.renewPeriod - accessTokenConfig.courtesyTime)
throw new UserError(`The renew period has not been exceeded`, 'periodNotExceeded');
await Self.logout(tokenId);
const user = await Self.findById(userId);
await Self.logout(token.id);
const user = await Self.findById(token.userId);
const accessToken = await user.createAccessToken();
return {token: accessToken.id, created: accessToken.created};
return {id: accessToken.id, ttl: accessToken.ttl};
};
};

View File

@ -76,6 +76,6 @@ module.exports = Self => {
let loginInfo = Object.assign({password}, userInfo);
token = await Self.login(loginInfo, 'user');
return {token: token.id, created: token.created};
return {token: token.id, ttl: token.ttl};
};
};

View File

@ -16,6 +16,10 @@
"type": "number",
"required": true
},
"courtesyTime": {
"type": "number",
"required": true
},
"renewInterval": {
"type": "number",
"required": true

View File

@ -1,10 +1,11 @@
CREATE TABLE `salix`.`accessTokenConfig` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`renewPeriod` int(10) unsigned DEFAULT NULL,
`courtesyTime` int(10) unsigned DEFAULT NULL,
`renewInterval` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT IGNORE INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `renewInterval`)
INSERT IGNORE INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `courtesyTime`, `renewInterval`)
VALUES
(1, 21600, 300);
(1, 21600, 5, 300);

View File

@ -1,9 +1,9 @@
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
VALUES
('InvoiceOut','makePdfAndNotify','WRITE','ALLOW','ROLE','invoicing'),
('InvoiceOutConfig','*','READ','ALLOW','ROLE','invoicing');
CREATE OR REPLACE TABLE vn.invoiceOutConfig (
CREATE OR REPLACE TABLE `vn`.`invoiceOutConfig` (
id INT UNSIGNED auto_increment NOT NULL,
parallelism int UNSIGNED DEFAULT 1 NOT NULL,
PRIMARY KEY (id)

View File

@ -0,0 +1,13 @@
UPDATE `salix`.`ACL`
SET principalId='financialBoss'
WHERE
model = 'Client'
AND property = 'editCredit';
UPDATE `salix`.`ACL`
SET property='zeroCreditEditor'
WHERE
model = 'Client'
AND property = 'isNotEditableCredit';

File diff suppressed because one or more lines are too long

View File

@ -144,17 +144,17 @@ INSERT INTO `vn`.`currency`(`id`, `code`, `name`, `ratio`)
(3, 'GBP', 'Libra', 1),
(4, 'JPY', 'Yen Japones', 1);
INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`, `continentFk`, `hasDailyInvoice`, `CEE`, `politicalCountryFk`)
INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`, `continentFk`, `hasDailyInvoice`, `CEE`)
VALUES
(1, 'España', 1, 'ES', 1, 24, 4, 0, 1, 1),
(2, 'Italia', 1, 'IT', 1, 27, 4, 0, 1, 2),
(3, 'Alemania', 1, 'DE', 1, 22, 4, 0, 1, 3),
(4, 'Rumania', 1, 'RO', 1, 24, 4, 0, 1, 4),
(5, 'Holanda', 1, 'NL', 1, 18, 4, 0, 1, 5),
(8, 'Portugal', 1, 'PT', 1, 27, 4, 0, 1, 8),
(13,'Ecuador', 0, 'EC', 1, 24, 2, 1, 2, 13),
(19,'Francia', 1, 'FR', 1, 27, 4, 0, 1, 19),
(30,'Canarias', 1, 'IC', 1, 24, 4, 1, 2, 30);
(1, 'España', 1, 'ES', 1, 24, 4, 0, 1),
(2, 'Italia', 1, 'IT', 1, 27, 4, 0, 1),
(3, 'Alemania', 1, 'DE', 1, 22, 4, 0, 1),
(4, 'Rumania', 1, 'RO', 1, 24, 4, 0, 1),
(5, 'Holanda', 1, 'NL', 1, 18, 4, 0, 1),
(8, 'Portugal', 1, 'PT', 1, 27, 4, 0, 1),
(13,'Ecuador', 0, 'EC', 1, 24, 2, 1, 2),
(19,'Francia', 1, 'FR', 1, 27, 4, 0, 1),
(30,'Canarias', 1, 'IC', 1, 24, 4, 1, 2);
INSERT INTO `vn`.`warehouseAlias`(`id`, `name`)
VALUES
@ -552,8 +552,11 @@ INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`
VALUES
(1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, util.VN_CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'),
(2, 'Farmer King', 'The farmer', 4000020002, 1, '87945234L', 0, util.VN_CURDATE(), 1, 'supplier address 2', 'GOTHAM', 2, 43022, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'),
(69, 'Packaging', 'Packaging nick', 4100000069, 1, '94935005K', 0, util.VN_CURDATE(), 1, 'supplier address 5', 'ASGARD', 3, 46600, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'),
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, util.VN_CURDATE(), 1, 'supplier address 3', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'),
(1381, 'Ornamentales', 'Ornamentales', 7185000440, 1, '03815934E', 0, util.VN_CURDATE(), 1, 'supplier address 4', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V');
(567, 'Holland', 'Holland nick', 4000020567, 1, '14364089Z', 0, util.VN_CURDATE(), 1, 'supplier address 6', 'ASGARD', 3, 46600, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'),
(791, 'Bros SL', 'Bros nick', 5115000791, 1, '37718083S', 0, util.VN_CURDATE(), 1, 'supplier address 7', 'ASGARD', 3, 46600, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'),
(1381, 'Ornamentales', 'Ornamentales', 7185001381, 1, '07972486L', 0, util.VN_CURDATE(), 1, 'supplier address 4', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V');
INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`)
VALUES
@ -699,12 +702,12 @@ INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agen
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`)
VALUES
(1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, 'T1111111', 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
(2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T1111111', 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
(3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T2222222', 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)),
(4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T3333333', 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH)),
(5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T4444444', 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH)),
(6 , 1, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Mountain Drive Gotham', 1, 'A1111111', 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
(1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
(2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
(3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)),
(4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH)),
(5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH)),
(6 , 1, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
(7 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
(8 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Bat cave', 121, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
(9 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
@ -2572,6 +2575,26 @@ INSERT INTO `vn`.`ticketRecalc`(`ticketFk`)
CALL `vn`.`ticket_doRecalc`();
UPDATE `vn`.`ticket`
SET refFk = 'T1111111'
WHERE id IN (1,2);
UPDATE `vn`.`ticket`
SET refFk = 'T2222222'
WHERE id = 3;
UPDATE `vn`.`ticket`
SET refFk = 'T3333333'
WHERE id = 4;
UPDATE `vn`.`ticket`
SET refFk = 'T4444444'
WHERE id = 5;
UPDATE `vn`.`ticket`
SET refFk = 'A1111111'
WHERE id = 6;
INSERT INTO `vn`.`zoneAgencyMode`(`id`, `agencyModeFk`, `zoneFk`)
VALUES
(1, 1, 1),
@ -2579,7 +2602,7 @@ INSERT INTO `vn`.`zoneAgencyMode`(`id`, `agencyModeFk`, `zoneFk`)
(3, 6, 5),
(4, 7, 1);
INSERT INTO `vn`.`expeditionTruck` (`id`, `ETD`, `description`)
INSERT INTO `vn`.`expeditionTruck` (`id`, `eta`, `description`)
VALUES
(1, CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL +3 YEAR))), 'Best truck in fleet');

File diff suppressed because it is too large Load Diff

View File

@ -312,8 +312,8 @@ export default {
clientDefaulter: {
anyClient: 'vn-client-defaulter tbody > tr',
firstClientName: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(2) > span',
firstSalesPersonName: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(3) > span',
firstObservation: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(8) > vn-textarea[ng-model="defaulter.observation"]',
firstSalesPersonName: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(4) > span',
firstObservation: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(9) > vn-textarea[ng-model="defaulter.observation"]',
allDefaulterCheckbox: 'vn-client-defaulter thead vn-multi-check',
addObservationButton: 'vn-client-defaulter vn-button[icon="icon-notes"]',
observation: '.vn-dialog.shown vn-textarea[ng-model="$ctrl.defaulter.observation"]',

View File

@ -39,9 +39,9 @@ describe('Client web access path', () => {
const userName = await page.getValue($.userName);
const email = await page.getValue($.email);
await page.accessToSection('client.card.log');
const logName = await page.innerText($.nameValue);
const logActive = await page.innerText($.activeValue);
// await page.accessToSection('client.card.log');
// const logName = await page.innerText($.nameValue);
// const logActive = await page.innerText($.activeValue);
expect(enableMessage.type).toBe('success');
expect(modifyMessage.type).toBe('success');
@ -50,7 +50,7 @@ describe('Client web access path', () => {
expect(userName).toEqual('Legion');
expect(email).toEqual('legion@marvel.com');
expect(logName).toEqual('Legion');
expect(logActive).toEqual('✗');
// expect(logName).toEqual('Legion');
// expect(logActive).toEqual('✗');
});
});

View File

@ -19,7 +19,6 @@ describe('Item edit tax path', () => {
it(`should add the item tax to all countries`, async() => {
await page.autocompleteSearch(selectors.itemTax.firstClass, 'General VAT');
await page.autocompleteSearch(selectors.itemTax.secondClass, 'General VAT');
await page.autocompleteSearch(selectors.itemTax.thirdClass, 'General VAT');
await page.waitToClick(selectors.itemTax.submitTaxButton);
const message = await page.waitForSnackbar();
@ -40,13 +39,6 @@ describe('Item edit tax path', () => {
expect(secondVatType).toEqual('General VAT');
});
it(`should confirm the third item tax class was edited`, async() => {
const thirdVatType = await page
.waitToGetProperty(selectors.itemTax.thirdClass, 'value');
expect(thirdVatType).toEqual('General VAT');
});
it(`should edit the first class without saving the form`, async() => {
await page.autocompleteSearch(selectors.itemTax.firstClass, 'Reduced VAT');
const firstVatType = await page.waitToGetProperty(selectors.itemTax.firstClass, 'value');

View File

@ -9,7 +9,7 @@ describe('Travel descriptor path', () => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'travel');
await page.write(selectors.travelIndex.generalSearchFilter, '1');
await page.write(selectors.travelIndex.generalSearchFilter, '3');
await page.keyboard.press('Enter');
await page.waitForState('travel.card.summary');
});
@ -23,7 +23,7 @@ describe('Travel descriptor path', () => {
await page.waitForState('travel.index');
const result = await page.countElement(selectors.travelIndex.anySearchResult);
expect(result).toBeGreaterThanOrEqual(7);
expect(result).toBeGreaterThanOrEqual(1);
});
it('should navigate to the first search result', async() => {

View File

@ -81,6 +81,6 @@ describe('Account Role create and basic data path', () => {
await page.accessToSection('account.role.card.inherited');
const rolesCount = await page.countElement(selectors.accountRoleInheritance.anyResult);
expect(rolesCount).toEqual(6);
expect(rolesCount).toEqual(7);
});
});

View File

@ -59,12 +59,13 @@ export default class Auth {
password: password || undefined
};
const now = new Date();
return this.$http.post('VnUsers/signIn', params)
.then(json => this.onLoginOk(json, remember));
.then(json => this.onLoginOk(json, now, remember));
}
onLoginOk(json, remember) {
this.vnToken.set(json.data.token, json.data.created, remember);
onLoginOk(json, now, remember) {
this.vnToken.set(json.data.token, now, json.data.ttl, remember);
return this.loadAcls().then(() => {
let continueHash = this.$state.params.continue;

View File

@ -1,11 +1,16 @@
import ngModule from '../module';
import HttpError from 'core/lib/http-error';
interceptor.$inject = ['$q', 'vnApp', 'vnToken', '$translate'];
function interceptor($q, vnApp, vnToken, $translate) {
interceptor.$inject = ['$q', 'vnApp', '$translate'];
function interceptor($q, vnApp, $translate) {
let apiPath = 'api/';
let token = sessionStorage.getItem('vnToken')
?? localStorage.getItem('vnToken');
return {
setToken(newToken) {
token = newToken;
},
setApiPath(path) {
apiPath = path;
},
@ -14,8 +19,8 @@ function interceptor($q, vnApp, vnToken, $translate) {
if (config.url.charAt(0) !== '/' && apiPath)
config.url = `${apiPath}${config.url}`;
if (vnToken.token)
config.headers.Authorization = vnToken.token;
if (token)
config.headers.Authorization = token;
if ($translate.use())
config.headers['Accept-Language'] = $translate.use();
if (config.filter) {

View File

@ -6,37 +6,118 @@ import ngModule from '../module';
* @property {String} token The current login token or %null
*/
export default class Token {
constructor() {
try {
this.token = sessionStorage.getItem('vnToken');
this.created = sessionStorage.getItem('vnTokenCreated');
if (!this.token) {
this.token = localStorage.getItem('vnToken');
this.created = localStorage.getItem('vnTokenCreated');
}
} catch (e) {}
}
set(token, created, remember) {
this.unset();
try {
if (remember) {
localStorage.setItem('vnToken', token);
localStorage.setItem('vnTokenCreated', created);
} else {
sessionStorage.setItem('vnToken', token);
sessionStorage.setItem('vnTokenCreated', created);
}
} catch (e) {}
constructor(vnInterceptor, $http, $rootScope) {
Object.assign(this, {
vnInterceptor,
$http,
$rootScope
});
this.token = token;
this.created = created;
try {
this.getStorage(sessionStorage);
this.remember = true;
if (!this.token) {
this.getStorage(localStorage);
this.remember = false;
}
} catch (e) {}
}
set(token, created, ttl, remember) {
this.unset();
Object.assign(this, {
token,
created,
ttl,
remember
});
this.vnInterceptor.setToken(token);
try {
if (remember)
this.setStorage(localStorage, token, created, ttl);
else
this.setStorage(sessionStorage, token, created, ttl);
} catch (err) {
console.error(err);
}
}
unset() {
localStorage.removeItem('vnToken');
sessionStorage.removeItem('vnToken');
this.token = null;
this.created = null;
}
this.ttl = null;
this.remember = null;
this.vnInterceptor.setToken(null);
this.removeStorage(localStorage);
this.removeStorage(sessionStorage);
}
getStorage(storage) {
this.token = storage.getItem('vnToken');
if (!this.token) return;
const created = storage.getItem('vnTokenCreated');
this.created = created && new Date(created);
this.renewPeriod = storage.getItem('vnTokenRenewPeriod');
}
setStorage(storage, token, created, ttl) {
storage.setItem('vnToken', token);
storage.setItem('vnTokenCreated', created.toJSON());
storage.setItem('vnTokenTtl', ttl);
}
removeStorage(storage) {
storage.removeItem('vnToken');
storage.removeItem('vnTokenCreated');
storage.removeItem('vnTokenTtl');
}
fetchConfig() {
const filter = {fields: ['renewInterval', 'renewPeriod']};
this.$http.get('AccessTokenConfigs/findOne', {filter}).then(res => {
const data = res.data;
if (!data) return;
this.renewPeriod = data.renewPeriod;
this.stopRenewer();
this.inservalId = setInterval(() => this.checkValidity(), data.renewInterval * 1000);
this.checkValidity();
});
}
checkValidity() {
if (this.checking || !this.created) return;
this.checking = true;
const renewPeriod = Math.min(this.ttl, this.renewPeriod) * 1000;
const maxDate = this.created.getTime() + renewPeriod;
const now = new Date();
if (now.getTime() <= maxDate) {
this.checking = false;
return;
}
this.$http.post('VnUsers/renewToken')
.then(res => {
const token = res.data;
this.set(token.id, now, token.ttl, this.remember);
})
.catch(res => {
if (res.data?.error?.code !== 'periodNotExceeded')
throw res;
})
.finally(() => {
this.checking = false;
});
}
stopRenewer() {
clearInterval(this.inservalId);
}
}
Token.$inject = ['vnInterceptor', '$http', '$rootScope'];
ngModule.service('vnToken', Token);

View File

@ -42,7 +42,7 @@
<button class="buttonAccount">
<img
id="user"
ng-src="{{$ctrl.getImageUrl()}}"
ng-src="{{::$ctrl.getImageUrl()}}"
ng-click="userPopover.show($event)"
translate-attr="{title: 'Account'}"
on-error-src/>

View File

@ -3,14 +3,13 @@ import Component from 'core/lib/component';
import './style.scss';
export class Layout extends Component {
constructor($element, $, vnModules, vnToken) {
constructor($element, $, vnModules) {
super($element, $);
this.modules = vnModules.get();
}
$onInit() {
this.getUserData();
this.getAccessTokenConfig();
}
getUserData() {
@ -32,41 +31,11 @@ export class Layout extends Component {
window.location.reload();
}
getAccessTokenConfig() {
this.$http.get('AccessTokenConfigs').then(json => {
const firtsResult = json.data[0];
if (!firtsResult) return;
this.renewPeriod = firtsResult.renewPeriod;
this.renewInterval = firtsResult.renewInterval;
const intervalMilliseconds = firtsResult.renewInterval * 1000;
this.inservalId = setInterval(this.checkTokenValidity.bind(this), intervalMilliseconds);
});
}
checkTokenValidity() {
const now = new Date();
const differenceMilliseconds = now - new Date(this.vnToken.created);
const differenceSeconds = Math.floor(differenceMilliseconds / 1000);
if (differenceSeconds > this.renewPeriod) {
this.$http.post('VnUsers/renewToken')
.then(json => {
if (json.data.token) {
let remember = true;
if (window.sessionStorage.vnToken) remember = false;
this.vnToken.set(json.data.token, json.data.created, remember);
}
});
}
}
$onDestroy() {
clearInterval(this.inservalId);
this.vnToken.stopRenewer();
}
}
Layout.$inject = ['$element', '$scope', 'vnModules', 'vnToken'];
Layout.$inject = ['$element', '$scope', 'vnModules'];
ngModule.vnComponent('vnLayout', {
template: require('./index.html'),

View File

@ -37,49 +37,4 @@ describe('Component vnLayout', () => {
expect(url).not.toBeDefined();
});
});
describe('getAccessTokenConfig()', () => {
it(`should set the renewPeriod and renewInterval properties in localStorage`, () => {
const response = [{
renewPeriod: 100,
renewInterval: 5
}];
$httpBackend.expect('GET', `AccessTokenConfigs`).respond(response);
controller.getAccessTokenConfig();
$httpBackend.flush();
expect(controller.renewPeriod).toBe(100);
expect(controller.renewInterval).toBe(5);
expect(controller.inservalId).toBeDefined();
});
});
describe('checkTokenValidity()', () => {
it(`should not call renewToken and not set vnToken in the controller`, () => {
controller.renewPeriod = 100;
controller.vnToken.created = new Date();
controller.checkTokenValidity();
expect(controller.vnToken.token).toBeNull();
});
it(`should call renewToken and set vnToken properties in the controller`, () => {
const response = {
token: 999,
created: new Date()
};
controller.renewPeriod = 100;
const oneHourBefore = new Date(Date.now() - (60 * 60 * 1000));
controller.vnToken.created = oneHourBefore;
$httpBackend.expect('POST', `VnUsers/renewToken`).respond(response);
controller.checkTokenValidity();
$httpBackend.flush();
expect(controller.vnToken.token).toBe(999);
expect(controller.vnToken.created).toEqual(response.created);
});
});
});

View File

@ -24,6 +24,7 @@
</div>
<div class="user-log vn-mb-sm" ng-repeat="userLog in ::originLog.logs">
<div class="timeline">
<div class="user-avatar">
<vn-avatar
ng-class="::{system: !userLog.user}"
val="{{::userLog.user ? userLog.user.nickname : $ctrl.$t('System')}}"
@ -33,6 +34,7 @@
ng-src="/api/Images/user/160x160/{{::userLog.userFk}}/download?access_token={{::$ctrl.vnToken.token}}">
</img>
</vn-avatar>
</div>
<div class="arrow bg-panel" ng-if="::$ctrl.byRecord"></div>
<div class="line"></div>
</div>

View File

@ -115,23 +115,21 @@ export default class Controller extends Section {
// User
const userChanged = originChanged
|| log.userFk != prevLog.userFk
|| nLogs >= 5;
|| log.userFk != prevLog.userFk;
if (userChanged) {
originLog.logs.push(userLog = {
user: log.user,
userFk: log.userFk,
logs: []
});
nLogs = 0;
}
nLogs++;
// Model
const modelChanged = userChanged
|| log.changedModel != prevLog.changedModel
|| log.changedModelId != prevLog.changedModelId;
|| log.changedModelId != prevLog.changedModelId
|| nLogs >= 6;
if (modelChanged) {
userLog.logs.push(modelLog = {
model: log.changedModel,
@ -140,7 +138,9 @@ export default class Controller extends Section {
showValue: log.changedModelValue,
logs: []
});
nLogs = 0;
}
nLogs++;
modelLog.logs.push(log);

View File

@ -44,21 +44,30 @@ vn-log {
right: -4px;
z-index: 1;
}
& > .user-avatar {
background-color: $color-bg;
padding: $spacing-sm 0;
margin-top: -$spacing-sm;
position: sticky;
top: 64px;
& > vn-avatar {
cursor: pointer;
display: block;
&.system {
background-color: $color-main !important;
}
}
}
& > .line {
position: absolute;
background-color: $color-main;
width: 2px;
left: 18px;
z-index: -1;
top: 44px;
bottom: -2px;
top: 0;
bottom: -$spacing-sm;
}
}
&:last-child > .timeline > .line {

View File

@ -11,7 +11,8 @@ function config($stateProvider, $urlRouterProvider) {
abstract: true,
template: '<vn-layout></vn-layout>',
resolve: {
config: ['vnConfig', vnConfig => vnConfig.initialize()]
config: ['vnConfig', vnConfig => vnConfig.initialize()],
token: ['vnToken', vnToken => vnToken.fetchConfig()]
}
})
.state('outLayout', {

View File

@ -176,5 +176,6 @@
"Can't transfer claimed sales": "Can't transfer claimed sales",
"Invalid quantity": "Invalid quantity",
"Failed to upload delivery note": "Error to upload delivery note {{id}}",
"Mail not sent": "There has been an error sending the invoice to the client [{{clientId}}]({{{clientUrl}}}), please check the email address"
"Mail not sent": "There has been an error sending the invoice to the client [{{clientId}}]({{{clientUrl}}}), please check the email address",
"The renew period has not been exceeded": "The renew period has not been exceeded"
}

View File

@ -177,7 +177,6 @@
"You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria",
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
"Sorts whole route": "Reordena ruta entera",
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*",
"New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*",
"Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",

View File

@ -77,14 +77,6 @@ module.exports = Self => {
if (salesPersonId)
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
await models.ClaimLog.create({
originFk: args.id,
userFk: userId,
action: 'insert',
description: 'Claim-pickup-order sent',
changedModel: 'Mail'
});
const email = new Email('claim-pickup-order', params);
return email.send();

View File

@ -19,9 +19,6 @@ module.exports = Self => {
});
Self.confirmTransaction = async(ctx, id, options) => {
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
let tx;
const myOptions = {userId: ctx.req.accessToken.userId};
@ -34,29 +31,8 @@ module.exports = Self => {
}
try {
const oldTpvTransaction = await models.TpvTransaction.findById(id, null, myOptions);
const confirm = await Self.rawSql('CALL hedera.tpvTransaction_confirmById(?)', [id], myOptions);
const tpvTransaction = await models.TpvTransaction.findById(id, null, myOptions);
const oldInstance = {status: oldTpvTransaction.status};
const newInstance = {status: tpvTransaction.status};
const logRecord = {
originFk: tpvTransaction.clientFk,
userFk: userId,
action: 'update',
changedModel: 'TpvTransaction',
changedModelId: id,
oldInstance: oldInstance,
newInstance: newInstance
};
await models.ClientLog.create(logRecord, myOptions);
if (tx) await tx.commit();
return confirm;
} catch (e) {
if (tx) await tx.rollback();

View File

@ -30,34 +30,9 @@ module.exports = Self => {
}
});
Self.sendSms = async(ctx, id, destination, message, options) => {
Self.sendSms = async(ctx, id, destination, message) => {
const models = Self.app.models;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const userId = ctx.req.accessToken.userId;
const sms = await models.Sms.send(ctx, destination, message);
const logRecord = {
originFk: id,
userFk: userId,
action: 'insert',
changedModel: 'sms',
newInstance: {
destinationFk: id,
destination: destination,
message: message,
statusCode: sms.statusCode,
status: sms.status
}
};
const clientLog = await models.ClientLog.create(logRecord, myOptions);
sms.logId = clientLog.id;
return sms;
};
};

View File

@ -13,10 +13,7 @@ describe('client sendSms()', () => {
const sms = await models.Client.sendSms(ctx, id, destination, message, options);
const createdLog = await models.ClientLog.findById(sms.logId, null, options);
const json = JSON.parse(JSON.stringify(createdLog.newInstance));
expect(json.message).toEqual(message);
expect(sms).toBeDefined();
await tx.rollback();
} catch (e) {

View File

@ -144,20 +144,7 @@ module.exports = Self => {
if (taxDataChecked && !hasSageData)
throw new UserError(`You need to fill sage information before you check verified data`);
if (args.despiteOfClient) {
const logRecord = {
originFk: clientId,
userFk: userId,
action: 'update',
changedModel: 'Client',
changedModelId: clientId,
description: $t(`Client checked as validated despite of duplication`, {
clientId: args.despiteOfClient
})
};
await models.ClientLog.create(logRecord, myOptions);
}
// Remove unwanted properties
delete args.ctx;
delete args.id;

View File

@ -60,6 +60,7 @@ module.exports = Self => {
DISTINCT c.id clientFk,
c.name clientName,
c.salesPersonFk,
c.businessTypeFk,
u.name salesPersonName,
d.amount,
co.created,

View File

@ -406,17 +406,27 @@ module.exports = Self => {
const canEditCredit = await models.ACL.checkAccessAcl(accessToken, 'Client', 'editCredit', 'WRITE');
if (!canEditCredit) {
const lastCredit = await models.ClientCredit.findOne({
field: ['workerFk', 'amount'],
where: {
clientFk: finalState.id
},
order: 'id DESC'
}, ctx.options);
const lastAmount = lastCredit && lastCredit.amount;
const lastCreditIsNotEditable = !await models.ACL.checkAccessAcl(accessToken, 'Client', 'isNotEditableCredit', 'WRITE');
if (lastCredit && lastCredit.amount == 0) {
const zeroCreditEditor =
await models.ACL.checkAccessAcl(accessToken, 'Client', 'zeroCreditEditor', 'WRITE');
const lastCreditIsNotEditable =
await models.ACL.checkAccessAcl(
{req: {accessToken: {userId: lastCredit.workerFk}}},
'Client',
'zeroCreditEditor',
'WRITE'
);
if (lastAmount == 0 && lastCreditIsNotEditable)
if (lastCreditIsNotEditable && !zeroCreditEditor)
throw new UserError(`You can't change the credit set to zero from a financialBoss`);
}
const creditLimits = await models.ClientCreditLimit.find({
fields: ['roleFk'],
@ -479,21 +489,6 @@ module.exports = Self => {
hasChanges = oldData.name != changes.name || oldData.active != changes.active;
if (!hasChanges) return;
const isClient = await Self.app.models.Client.count({id: oldData.id});
if (isClient) {
const loopBackContext = LoopBackContext.getCurrentContext();
const userId = loopBackContext.active.accessToken.userId;
const logRecord = {
originFk: oldData.id,
userFk: userId,
action: 'update',
changedModel: 'VnUser',
oldInstance: {name: oldData.name, active: oldData.active},
newInstance: {name: changes.name, active: changes.active}
};
await Self.app.models.ClientLog.create(logRecord);
}
}
});
});

View File

@ -60,22 +60,22 @@ describe('Client Model', () => {
try {
const options = {transaction: tx};
const context = {options};
const ctx = {options};
// Set credit to zero by a financialBoss
const financialBoss = await models.VnUser.findOne({
where: {name: 'financialBoss'}
}, options);
context.options.accessToken = {userId: financialBoss.id};
ctx.options.accessToken = {userId: financialBoss.id};
await models.Client.changeCredit(context, instance, {credit: 0});
await models.Client.changeCredit(ctx, instance, {credit: 0});
const salesAssistant = await models.VnUser.findOne({
where: {name: 'salesAssistant'}
}, options);
context.options.accessToken = {userId: salesAssistant.id};
ctx.options.accessToken = {userId: salesAssistant.id};
await models.Client.changeCredit(context, instance, {credit: 300});
await models.Client.changeCredit(ctx, instance, {credit: 300});
await tx.rollback();
} catch (e) {
@ -93,14 +93,14 @@ describe('Client Model', () => {
try {
const options = {transaction: tx};
const context = {options};
const ctx = {options};
const salesAssistant = await models.VnUser.findOne({
where: {name: 'salesAssistant'}
}, options);
context.options.accessToken = {userId: salesAssistant.id};
ctx.options.accessToken = {userId: salesAssistant.id};
await models.Client.changeCredit(context, instance, {credit: 99999});
await models.Client.changeCredit(ctx, instance, {credit: 99999});
await tx.rollback();
} catch (e) {

View File

@ -4,7 +4,7 @@
filter="::$ctrl.filter"
limit="20"
order="amount DESC"
data="defaulters"
data="$ctrl.defaulters"
on-data-change="$ctrl.reCheck()"
auto-load="true">
</vn-crud-model>
@ -34,7 +34,7 @@
</div>
<div class="vn-pa-md">
<vn-button
ng-show="$ctrl.checked.length > 0"
disabled="$ctrl.checked.length == 0"
ng-click="notesDialog.show()"
name="notesDialog"
vn-tooltip="Add observation"
@ -54,6 +54,9 @@
<th field="clientFk">
<span translate>Client</span>
</th>
<th>
<span translate>Es trabajador</span>
</th>
<th field="salesPersonFk">
<span translate>Comercial</span>
</th>
@ -94,7 +97,7 @@
</tr>
</thead>
<tbody>
<tr ng-repeat="defaulter in defaulters">
<tr ng-repeat="defaulter in $ctrl.defaulters">
<td shrink>
<vn-check
ng-model="defaulter.checked"
@ -110,6 +113,12 @@
{{::defaulter.clientName}}
</span>
</td>
<td>
<vn-check
ng-model="defaulter.isWorker"
disabled="true">
</vn-check>
</td>
<td>
<span
title="{{::defaulter.salesPersonName}}"

View File

@ -6,6 +6,7 @@ export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.defaulter = {};
this.defaulters = [];
this.checkedDefaulers = [];
this.smartTableOptions = {
@ -69,6 +70,18 @@ export default class Controller extends Section {
this.getBalanceDueTotal();
}
set defaulters(value) {
if (!value || !value.length) return;
for (let defaulter of value)
defaulter.isWorker = defaulter.businessTypeFk === 'worker';
this._defaulters = value;
}
get defaulters() {
return this._defaulters;
}
get checked() {
const clients = this.$.model.data || [];
const checkedLines = [];

View File

@ -17,6 +17,7 @@ describe('InvoiceOut refund()', () => {
const options = {transaction: tx};
try {
await models.TicketRefund.destroyAll(null, options);
const result = await models.InvoiceOut.refund(ctx, 'T1111111', withWarehouse, options);
expect(result).toBeDefined();

View File

@ -64,7 +64,7 @@ describe('item getBalance()', () => {
const secondItemBalance = await models.Item.getBalance(ctx, secondFilter, options);
expect(firstItemBalance[9].claimFk).toEqual(null);
expect(secondItemBalance[5].claimFk).toEqual(2);
expect(secondItemBalance[4].claimFk).toEqual(2);
await tx.rollback();
} catch (e) {

View File

@ -42,8 +42,9 @@ module.exports = Self => {
promises.push(Self.app.models.ItemTaxCountry.updateAll(
{id: tax.id},
{taxClassFk: tax.taxClassFk}
), myOptions);
{taxClassFk: tax.taxClassFk},
myOptions
));
}
await Promise.all(promises);

View File

@ -21,7 +21,6 @@ module.exports = Self => {
Self.guessPriority = async(ctx, id) => {
const userId = ctx.req.accessToken.userId;
const $t = ctx.req.__; // $translate
const query = `CALL vn.routeGuessPriority(?)`;
const tx = await Self.beginTransaction({});
@ -30,16 +29,6 @@ module.exports = Self => {
const priority = await Self.rawSql(query, [id], options);
let logRecord = {
originFk: id,
userFk: userId,
action: 'update',
changedModel: 'Route',
description: $t('Sorts whole route')
};
await Self.app.models.RouteLog.create(logRecord, options);
await tx.commit();
return priority;
} catch (e) {

View File

@ -31,18 +31,6 @@ describe('route updateVolume()', () => {
const updatedRoute = await app.models.Route.findById(routeId, null, options);
expect(updatedRoute.m3).not.toEqual(route.m3);
const logs = await app.models.RouteLog.find({
fields: ['id', 'newInstance']
}, options);
const m3Log = logs.filter(log => {
if (log.newInstance)
return log.newInstance.m3 === updatedRoute.m3;
});
expect(m3Log.length).toEqual(1);
await tx.rollback();
} catch (e) {
await tx.rollback();

View File

@ -35,24 +35,9 @@ module.exports = Self => {
}
try {
const originalRoute = await models.Route.findById(id, null, myOptions);
await Self.rawSql(`CALL vn.routeUpdateM3(?)`, [id], myOptions);
const updatedRoute = await models.Route.findById(id, null, myOptions);
await models.RouteLog.create({
originFk: id,
userFk: userId,
action: 'update',
changedModel: 'Route',
changedModelId: id,
oldInstance: {m3: originalRoute.m3},
newInstance: {m3: updatedRoute.m3}
}, myOptions);
if (tx) await tx.commit();
return updatedRoute;
} catch (e) {
if (tx) await tx.rollback();

View File

@ -106,7 +106,7 @@ module.exports = Self => {
}
}
const query = `CALL vn.ticket_recalc(?)`;
const query = `CALL vn.ticket_recalc(?, NULL)`;
await Self.rawSql(query, [refundTicket.id], myOptions);
if (tx) await tx.commit();

View File

@ -96,7 +96,7 @@ module.exports = Self => {
await sale.updateAttributes({price: newPrice}, myOptions);
await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions);
await Self.rawSql('CALL vn.ticket_recalc(?)', [sale.ticketFk], myOptions);
await Self.rawSql('CALL vn.ticket_recalc(?, NULL)', [sale.ticketFk], myOptions);
const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) {

View File

@ -97,22 +97,6 @@ module.exports = Self => {
});
await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions);
const logRecord = {
originFk: sale.ticketFk,
userFk: userId,
action: 'update',
changedModel: 'ticketRequest',
newInstance: {
destinationFk: sale.ticketFk,
quantity: sale.quantity,
concept: sale.concept,
itemId: sale.itemFk,
ticketId: sale.ticketFk,
}
};
await Self.app.models.TicketLog.create(logRecord, myOptions);
if (tx) await tx.commit();
return sale;

View File

@ -85,7 +85,7 @@ module.exports = Self => {
}, myOptions);
await Self.rawSql('CALL vn.sale_calculateComponent(?, NULL)', [newSale.id], myOptions);
await Self.rawSql('CALL vn.ticket_recalc(?)', [id], myOptions);
await Self.rawSql('CALL vn.ticket_recalc(?, NULL)', [id], myOptions);
const sale = await models.Sale.findById(newSale.id, {
include: {

View File

@ -242,16 +242,6 @@ module.exports = Self => {
const oldProperties = await loggable.translateValues(Self, changes.old);
const newProperties = await loggable.translateValues(Self, changes.new);
await models.TicketLog.create({
originFk: args.id,
userFk: userId,
action: 'update',
changedModel: 'Ticket',
changedModelId: args.id,
oldInstance: oldProperties,
newInstance: newProperties
}, myOptions);
const salesPersonId = originalTicket.client().salesPersonFk;
if (salesPersonId) {
const origin = ctx.req.headers.origin;

View File

@ -52,23 +52,10 @@ module.exports = Self => {
});
if (!ticket.originId || !ticket.destinationId) continue;
const ticketDestinationLogRecord = {
originFk: ticket.destinationId,
userFk: ctx.req.accessToken.userId,
action: 'update',
changedModel: 'Ticket',
changedModelId: ticket.destinationId,
changedModelValue: ticket.destinationId,
oldInstance: {},
newInstance: {mergedTicket: ticket.originId}
};
await models.Sale.updateAll({ticketFk: ticket.originId}, {ticketFk: ticket.destinationId}, myOptions);
if (await models.Ticket.setDeleted(ctx, ticket.originId, myOptions)) {
await models.TicketLog.create(ticketDestinationLogRecord, myOptions);
if (await models.Ticket.setDeleted(ctx, ticket.originId, myOptions))
await models.Chat.sendCheckingPresence(ctx, ticket.workerFk, message);
}
}
if (tx)
await tx.commit();
} catch (e) {

View File

@ -30,54 +30,12 @@ module.exports = Self => {
}
});
Self.sendSms = async(ctx, id, destination, message, options) => {
Self.sendSms = async(ctx, id, destination, message) => {
const models = Self.app.models;
const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
const userId = ctx.req.accessToken.userId;
try {
const sms = await models.Sms.send(ctx, destination, message);
const newTicketSms = {
await models.TicketSms.create({
ticketFk: id,
smsFk: sms.id
};
await models.TicketSms.create(newTicketSms);
const logRecord = {
originFk: id,
userFk: userId,
action: 'insert',
changedModel: 'sms',
newInstance: {
destinationFk: id,
destination: destination,
message: message,
statusCode: sms.statusCode,
status: sms.status
}
};
const ticketLog = await models.TicketLog.create(logRecord, myOptions);
sms.logId = ticketLog.id;
if (tx) await tx.commit();
return sms;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
});
};
};

View File

@ -43,12 +43,10 @@ describe('ticket merge()', () => {
await models.Ticket.merge(ctx, [tickets], options);
const createdTicketLog = await models.TicketLog.find({where: {originFk: tickets.destinationId}}, options);
const deletedTicket = await models.Ticket.findOne({where: {id: tickets.originId}}, options);
const salesTicketFuture = await models.Sale.find({where: {ticketFk: tickets.destinationId}}, options);
const chatNotificationAfterMerge = await models.Chat.find(null, options);
expect(createdTicketLog.length).toEqual(1);
expect(deletedTicket.isDeleted).toEqual(true);
expect(salesTicketFuture.length).toEqual(2);
expect(chatNotificationBeforeMerge.length).toEqual(chatNotificationAfterMerge.length - 2);

View File

@ -12,19 +12,14 @@ describe('ticket sendSms()', () => {
const destination = 222222222;
const message = 'this is the message created in a test';
const sms = await models.Ticket.sendSms(ctx, id, destination, message, options);
const createdLog = await models.TicketLog.findById(sms.logId, null, options);
await models.Ticket.sendSms(ctx, id, destination, message, options);
const filter = {
ticketFk: createdLog.originFk
ticketFk: id
};
const ticketSms = await models.TicketSms.findOne(filter, options);
const json = JSON.parse(JSON.stringify(createdLog.newInstance));
expect(json.message).toEqual(message);
expect(ticketSms.ticketFk).toEqual(createdLog.originFk);
expect(ticketSms.ticketFk).toEqual(id);
await tx.rollback();
} catch (e) {

View File

@ -84,13 +84,6 @@ module.exports = Self => {
for (const sale of sales) {
const originalSale = map.get(sale.id);
const originalSaleData = { // <-- Loopback modifies original instance on save
itemFk: originalSale.itemFk,
quantity: originalSale.quantity,
concept: originalSale.concept,
ticketFk: originalSale.ticketFk
};
if (sale.quantity == originalSale.quantity) {
query = `UPDATE sale
SET ticketFk = ?
@ -100,48 +93,6 @@ module.exports = Self => {
await transferPartialSale(
ticketId, originalSale, sale, myOptions);
}
// Log to original ticket
await models.TicketLog.create({
originFk: id,
userFk: userId,
action: 'update',
changedModel: 'Sale',
changedModelId: sale.id,
oldInstance: {
item: originalSaleData.itemFk,
quantity: originalSaleData.quantity,
concept: originalSaleData.concept,
ticket: originalSaleData.ticketFk
},
newInstance: {
item: sale.itemFk,
quantity: sale.quantity,
concept: sale.concept,
ticket: ticketId
}
}, myOptions);
// Log to destination ticket
await models.TicketLog.create({
originFk: ticketId,
userFk: userId,
action: 'update',
changedModel: 'Sale',
changedModelId: sale.id,
oldInstance: {
item: originalSaleData.itemFk,
quantity: originalSaleData.quantity,
concept: originalSaleData.concept,
ticket: originalSaleData.ticketFk
},
newInstance: {
item: sale.itemFk,
quantity: sale.quantity,
concept: sale.concept,
ticket: ticketId
}
}, myOptions);
}
const isTicketEmpty = await models.Ticket.isEmpty(id, myOptions);

View File

@ -147,7 +147,7 @@ module.exports = Self => {
await Promise.all(promises);
await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions);
await Self.rawSql('CALL vn.ticket_recalc(?)', [id], myOptions);
await Self.rawSql('CALL vn.ticket_recalc(?, NULL)', [id], myOptions);
const ticket = await models.Ticket.findById(id, {
include: {

View File

@ -1,30 +1,4 @@
const LoopBackContext = require('loopback-context');
module.exports = Self => {
// Methods
require('./ticket-methods')(Self);
Self.observe('before save', async function(ctx) {
const loopBackContext = LoopBackContext.getCurrentContext();
const httpCtx = loopBackContext.active;
if (ctx.isNewInstance) return;
let changes = ctx.data || ctx.instance;
if (changes.routeFk === null && ctx.currentInstance.routeFk != null) {
let instance = JSON.parse(JSON.stringify(ctx.currentInstance));
let userId = httpCtx.accessToken.userId;
let logRecord = {
originFk: ctx.currentInstance.routeFk,
userFk: userId,
action: 'delete',
changedModel: 'Route',
oldInstance: {ticket: instance.id},
newInstance: null
};
await Self.app.models.RouteLog.create(logRecord);
}
});
};

View File

@ -156,7 +156,7 @@
icon="install_mobile"
ng-show="$ctrl.totalChecked > 0"
ng-click="$ctrl.sendDocuware()"
vn-tooltip="Set as delivered and open delivery note(s)"
vn-tooltip="Set as delivered and send delivery note(s) to the tablet"
tooltip-position="left">
</vn-button>
<vn-button class="round vn-mb-sm"

View File

@ -3,7 +3,7 @@ Go to lines: Ir a lineas
Not available: No disponible
Not visible: No visible
Payment on account...: Pago a cuenta...
Set as delivered and open delivery note(s): Marcar como servido/s y abrir albarán/es
Set as delivered and send delivery note(s) to the tablet: Marcar como servido/s y enviar albarán/es a la tablet
Closure: Cierre
You cannot make a payment on account from multiple clients: No puedes realizar un pago a cuenta de clientes diferentes
Filter by selection: Filtro por selección

View File

@ -25,9 +25,7 @@ module.exports = Self => {
});
Self.cloneWithEntries = async(ctx, id) => {
const userId = ctx.req.accessToken.userId;
const conn = Self.dataSource.connector;
const models = Self.app.models;
const travel = await Self.findById(id, {
fields: [
'id',
@ -81,18 +79,6 @@ module.exports = Self => {
]
});
const oldProperties = await loggable.translateValues(Self, travel);
const newProperties = await loggable.translateValues(Self, newTravel);
await models.TravelLog.create({
originFk: newTravel.id,
userFk: userId,
action: 'insert',
changedModel: 'Travel',
changedModelId: newTravel.id,
oldInstance: oldProperties,
newInstance: newProperties
});
return newTravel.id;
};
};

View File

@ -30,24 +30,8 @@ module.exports = Self => {
SET travelFk = NULL, dmsFk = NULL
WHERE id = ?`, [id], {userId});
const oldInstance = {
travelFk: travelThermograph.travelFk,
dmsFk: travelThermograph.dmsFk
};
await models.TravelLog.create({
originFk: travelThermograph.travelFk,
userFk: userId,
action: 'delete',
changedModel: 'TravelThermograph',
changedModelId: id,
oldInstance: oldInstance,
newInstance: {}
});
travelThermograph.travelFk = null;
travelThermograph.dmsFk = null;
return travelThermograph;
};
};

View File

@ -37,6 +37,16 @@ class Controller extends SearchPanel {
}
applyFilters(param) {
if (typeof this.filter.scopeDays === 'number') {
const shippedFrom = Date.vnNew();
shippedFrom.setHours(0, 0, 0, 0);
const shippedTo = new Date(shippedFrom.getTime());
shippedTo.setDate(shippedTo.getDate() + this.filter.scopeDays);
shippedTo.setHours(23, 59, 59, 999);
Object.assign(this.filter, {shippedFrom, shippedTo});
}
this.model.applyFilter({}, this.filter)
.then(() => {
if (param && this.model._orgData.length === 1)

View File

@ -92,7 +92,7 @@
</span>
</vn-td>
<vn-td expand>{{entry.supplierName}}</vn-td>
<vn-td shrink>{{entry.ref}}</vn-td>
<vn-td shrink>{{entry.reference}}</vn-td>
<vn-td shrink>{{entry.hb}}</vn-td>
<vn-td shrink>{{entry.freightValue | currency: 'EUR': 2}}</vn-td>
<vn-td shrink>{{entry.packageValue | currency: 'EUR': 2}}</vn-td>

View File

@ -40,7 +40,6 @@ module.exports = Self => {
Self.updateWorkerTimeControlMail = async(ctx, options) => {
const models = Self.app.models;
const args = ctx.args;
const userId = ctx.req.accessToken.userId;
const myOptions = {};
@ -57,9 +56,6 @@ module.exports = Self => {
if (!workerTimeControlMail) throw new UserError(`There aren't records for this week`);
const oldState = workerTimeControlMail.state;
const oldReason = workerTimeControlMail.reason;
await workerTimeControlMail.updateAttributes({
state: args.state,
reason: args.reason || null
@ -70,22 +66,5 @@ module.exports = Self => {
sendedCounter: workerTimeControlMail.sendedCounter + 1
}, myOptions);
}
const logRecord = {
originFk: args.workerId,
userFk: userId,
action: 'update',
changedModel: 'WorkerTimeControlMail',
oldInstance: {
state: oldState,
reason: oldReason
},
newInstance: {
state: args.state,
reason: args.reason
}
};
return models.WorkerLog.create(logRecord, myOptions);
};
};

View File

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