Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 4045-incoterms_authorization
This commit is contained in:
commit
2a758fd12a
|
@ -0,0 +1,5 @@
|
|||
const baseTime = null; // new Date(2022, 0, 19, 8, 0, 0, 0);
|
||||
if (baseTime) {
|
||||
jasmine.clock().install();
|
||||
jasmine.clock().mockDate(baseTime);
|
||||
}
|
|
@ -1,16 +1,15 @@
|
|||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('setPassword', {
|
||||
description: 'Sets the user password',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: 'The user id',
|
||||
http: {source: 'path'}
|
||||
}, {
|
||||
arg: 'newPassword',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
description: 'The new password',
|
||||
required: true
|
||||
}
|
||||
|
|
|
@ -200,6 +200,31 @@ module.exports = Self => {
|
|||
const toTable = table.toTable;
|
||||
const baseName = table.fileName;
|
||||
|
||||
const firstEntry = entries[0];
|
||||
const entryName = firstEntry.entryName;
|
||||
const startIndex = (entryName.length - 10);
|
||||
const endIndex = (entryName.length - 4);
|
||||
const dateString = entryName.substring(startIndex, endIndex);
|
||||
|
||||
const lastUpdated = new Date();
|
||||
|
||||
let updated = null;
|
||||
if (file.updated) {
|
||||
updated = new Date(file.updated);
|
||||
updated.setHours(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Format string date to a date object
|
||||
lastUpdated.setFullYear(`20${dateString.substring(4, 6)}`);
|
||||
lastUpdated.setMonth(parseInt(dateString.substring(2, 4)) - 1);
|
||||
lastUpdated.setDate(dateString.substring(0, 2));
|
||||
lastUpdated.setHours(0, 0, 0, 0);
|
||||
|
||||
if (updated && lastUpdated <= updated) {
|
||||
console.debug(`Table ${toTable} already updated, skipping...`);
|
||||
return;
|
||||
}
|
||||
|
||||
const tx = await Self.beginTransaction({});
|
||||
|
||||
try {
|
||||
|
|
|
@ -31,11 +31,13 @@ COPY \
|
|||
import-changes.sh \
|
||||
config.ini \
|
||||
dump/mysqlPlugins.sql \
|
||||
dump/mockDate.sql \
|
||||
dump/structure.sql \
|
||||
dump/dumpedFixtures.sql \
|
||||
./
|
||||
RUN gosu mysql docker-init.sh \
|
||||
&& docker-dump.sh mysqlPlugins \
|
||||
&& docker-dump.sh mockDate \
|
||||
&& docker-dump.sh structure \
|
||||
&& docker-dump.sh dumpedFixtures \
|
||||
&& gosu mysql docker-temp-stop.sh
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
CREATE TABLE `vn`.`mdbBranch` (
|
||||
CREATE TABLE IF NOT EXISTS `vn`.`mdbBranch` (
|
||||
`name` VARCHAR(255),
|
||||
PRIMARY KEY(`name`)
|
||||
);
|
||||
|
||||
CREATE TABLE `vn`.`mdbVersion` (
|
||||
CREATE TABLE IF NOT EXISTS `vn`.`mdbVersion` (
|
||||
`app` VARCHAR(255) NOT NULL,
|
||||
`branchFk` VARCHAR(255) NOT NULL,
|
||||
`version` INT,
|
||||
CONSTRAINT `mdbVersion_branchFk` FOREIGN KEY (`branchFk`) REFERENCES `vn`.`mdbBranch` (`name`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES('MdbVersion', '*', '*', 'ALLOW', 'ROLE', 'developer');
|
||||
INSERT IGNORE INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES(318, 'MdbVersion', '*', '*', 'ALLOW', 'ROLE', 'developer');
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
|
||||
VALUES
|
||||
('Client','setPassword','WRITE','ALLOW','ROLE','salesPerson'),
|
||||
('Client','updateUser','WRITE','ALLOW','ROLE','salesPerson');
|
1518
db/dump/fixtures.sql
1518
db/dump/fixtures.sql
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,43 @@
|
|||
CREATE SCHEMA IF NOT EXISTS `util`;
|
||||
USE `util`;
|
||||
|
||||
DELIMITER ;;
|
||||
DROP FUNCTION IF EXISTS `util`.`mockedDate`;
|
||||
CREATE FUNCTION `util`.`mockedDate`()
|
||||
RETURNS DATETIME
|
||||
DETERMINISTIC
|
||||
BEGIN
|
||||
RETURN NOW();
|
||||
-- '2022-01-19 08:00:00'
|
||||
END ;;
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER ;;
|
||||
DROP FUNCTION IF EXISTS `util`.`VN_CURDATE`;
|
||||
CREATE FUNCTION `util`.`VN_CURDATE`()
|
||||
RETURNS DATE
|
||||
DETERMINISTIC
|
||||
BEGIN
|
||||
RETURN DATE(mockedDate());
|
||||
END ;;
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER ;;
|
||||
DROP FUNCTION IF EXISTS `util`.`VN_CURTIME`;
|
||||
CREATE FUNCTION `util`.`VN_CURTIME`()
|
||||
RETURNS TIME
|
||||
DETERMINISTIC
|
||||
BEGIN
|
||||
RETURN TIME(mockedDate());
|
||||
END ;;
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER ;;
|
||||
DROP FUNCTION IF EXISTS `util`.`VN_NOW`;
|
||||
CREATE FUNCTION `util`.`VN_NOW`()
|
||||
RETURNS DATETIME
|
||||
DETERMINISTIC
|
||||
BEGIN
|
||||
RETURN mockedDate();
|
||||
END ;;
|
||||
DELIMITER ;
|
File diff suppressed because it is too large
Load Diff
|
@ -96,5 +96,12 @@ mysqldump \
|
|||
--databases \
|
||||
${SCHEMAS[@]} \
|
||||
${IGNORETABLES[@]} \
|
||||
| sed 's/\bCURDATE\b/vn.VN_CURDATE/ig'\
|
||||
| sed 's/\bCURTIME\b/vn.VN_CURTIME/ig' \
|
||||
| sed 's/\bNOW\b/vn.VN_NOW/ig' \
|
||||
| sed 's/\bCURRENT_DATE\b/vn.VN_CURDATE/ig' \
|
||||
| sed 's/\bCURRENT_TIME\b/vn.VN_CURTIME/ig' \
|
||||
| sed 's/\bLOCALTIME\b/vn.VN_NOW/ig' \
|
||||
| sed 's/\bLOCALTIMESTAMP\b/vn.VN_NOW/ig' \
|
||||
| sed 's/ AUTO_INCREMENT=[0-9]* //g' \
|
||||
> dump/structure.sql
|
||||
> dump/structure.sql
|
|
@ -5,8 +5,9 @@ describe('zone zone_getLanded()', () => {
|
|||
it(`should return data for a shipped in the past`, async() => {
|
||||
let stmts = [];
|
||||
let stmt;
|
||||
|
||||
stmts.push('START TRANSACTION');
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
|
||||
let params = {
|
||||
addressFk: 121,
|
||||
|
@ -14,7 +15,8 @@ describe('zone zone_getLanded()', () => {
|
|||
warehouseFk: 1,
|
||||
showExpiredZones: true};
|
||||
|
||||
stmt = new ParameterizedSQL('CALL zone_getLanded(DATE_ADD(CURDATE(), INTERVAL -1 DAY), ?, ?, ?, ?)', [
|
||||
stmt = new ParameterizedSQL('CALL zone_getLanded(DATE_ADD(?, INTERVAL -1 DAY), ?, ?, ?, ?)', [
|
||||
date,
|
||||
params.addressFk,
|
||||
params.agencyModeFk,
|
||||
params.warehouseFk,
|
||||
|
@ -38,6 +40,8 @@ describe('zone zone_getLanded()', () => {
|
|||
it(`should return data for a shipped tomorrow`, async() => {
|
||||
let stmts = [];
|
||||
let stmt;
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
|
||||
stmts.push('START TRANSACTION');
|
||||
|
||||
|
@ -47,7 +51,8 @@ describe('zone zone_getLanded()', () => {
|
|||
warehouseFk: 1,
|
||||
showExpiredZones: false};
|
||||
|
||||
stmt = new ParameterizedSQL('CALL zone_getLanded(DATE_ADD(CURDATE(), INTERVAL +2 DAY), ?, ?, ?, ?)', [
|
||||
stmt = new ParameterizedSQL('CALL zone_getLanded(DATE_ADD(?, INTERVAL +2 DAY), ?, ?, ?, ?)', [
|
||||
date,
|
||||
params.addressFk,
|
||||
params.agencyModeFk,
|
||||
params.warehouseFk,
|
||||
|
|
|
@ -55,6 +55,7 @@ export default {
|
|||
setPassword: '.vn-menu [name="setPassword"]',
|
||||
activateAccount: '.vn-menu [name="enableAccount"]',
|
||||
activateUser: '.vn-menu [name="activateUser"]',
|
||||
deactivateUser: '.vn-menu [name="deactivateUser"]',
|
||||
newPassword: 'vn-textfield[ng-model="$ctrl.newPassword"]',
|
||||
repeatPassword: 'vn-textfield[ng-model="$ctrl.repeatPassword"]',
|
||||
newRole: 'vn-autocomplete[ng-model="$ctrl.newRole"]',
|
||||
|
@ -275,6 +276,7 @@ export default {
|
|||
clientWebAccess: {
|
||||
enableWebAccessCheckbox: 'vn-check[label="Enable web access"]',
|
||||
userName: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.name"]',
|
||||
email: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.email"]',
|
||||
saveButton: 'button[type=submit]'
|
||||
},
|
||||
clientNotes: {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint max-len: ["error", { "code": 150 }]*/
|
||||
import selectors from '../../helpers/selectors';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
|
@ -8,7 +9,7 @@ describe('Client Edit web access path', () => {
|
|||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('employee', 'client');
|
||||
await page.accessToSearchResult('Bruce Banner');
|
||||
await page.accessToSearchResult('max');
|
||||
await page.accessToSection('client.card.webAccess');
|
||||
});
|
||||
|
||||
|
@ -26,7 +27,16 @@ describe('Client Edit web access path', () => {
|
|||
|
||||
it(`should update the name`, async() => {
|
||||
await page.clearInput(selectors.clientWebAccess.userName);
|
||||
await page.write(selectors.clientWebAccess.userName, 'Hulk');
|
||||
await page.write(selectors.clientWebAccess.userName, 'Legion');
|
||||
await page.waitToClick(selectors.clientWebAccess.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it(`should update the email`, async() => {
|
||||
await page.clearInput(selectors.clientWebAccess.email);
|
||||
await page.write(selectors.clientWebAccess.email, 'legion@marvel.com');
|
||||
await page.waitToClick(selectors.clientWebAccess.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
|
@ -43,30 +53,36 @@ describe('Client Edit web access path', () => {
|
|||
it('should confirm web access name have been updated', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.clientWebAccess.userName, 'value');
|
||||
|
||||
expect(result).toEqual('Hulk');
|
||||
expect(result).toEqual('Legion');
|
||||
});
|
||||
|
||||
it('should confirm web access email have been updated', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.clientWebAccess.email, 'value');
|
||||
|
||||
expect(result).toEqual('legion@marvel.com');
|
||||
});
|
||||
|
||||
it(`should navigate to the log section`, async() => {
|
||||
await page.accessToSection('client.card.log');
|
||||
});
|
||||
|
||||
it(`should confirm the last log is showing the updated client name and no modifications on the active checkbox`, async() => {
|
||||
it(`should confirm the last log shows the updated client name and no modifications on active checkbox`, async() => {
|
||||
let lastModificationPreviousValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.lastModificationPreviousValue, 'innerText');
|
||||
let lastModificationCurrentValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText');
|
||||
|
||||
expect(lastModificationPreviousValue).toEqual('name BruceBanner active false');
|
||||
expect(lastModificationCurrentValue).toEqual('name Hulk active false');
|
||||
expect(lastModificationPreviousValue).toEqual('name MaxEisenhardt active false');
|
||||
expect(lastModificationCurrentValue).toEqual('name Legion active false');
|
||||
});
|
||||
|
||||
it(`should confirm the penultimate log is showing the updated avtive field and no modifications on the client name`, async() => {
|
||||
it(`should confirm the penultimate log shows the updated active and no modifications on client name`, async() => {
|
||||
let penultimateModificationPreviousValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.penultimateModificationPreviousValue, 'innerText');
|
||||
let penultimateModificationCurrentValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.penultimateModificationCurrentValue, 'innerText');
|
||||
|
||||
expect(penultimateModificationPreviousValue).toEqual('name BruceBanner active true');
|
||||
expect(penultimateModificationCurrentValue).toEqual('name BruceBanner active false');
|
||||
expect(penultimateModificationPreviousValue).toEqual('name MaxEisenhardt active true');
|
||||
expect(penultimateModificationCurrentValue).toEqual('name MaxEisenhardt active false');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -28,12 +28,12 @@ describe('Client defaulter path', () => {
|
|||
const salesPersonName =
|
||||
await page.waitToGetProperty(selectors.clientDefaulter.firstSalesPersonName, 'innerText');
|
||||
|
||||
expect(clientName).toEqual('Ororo Munroe');
|
||||
expect(salesPersonName).toEqual('salesPersonNick');
|
||||
expect(clientName).toEqual('Bruce Banner');
|
||||
expect(salesPersonName).toEqual('developer');
|
||||
});
|
||||
|
||||
it('should first observation not changed', async() => {
|
||||
const expectedObservation = 'Madness, as you know, is like gravity, all it takes is a little push';
|
||||
const expectedObservation = 'Meeting with Black Widow 21st 9am';
|
||||
const result = await page.waitToGetProperty(selectors.clientDefaulter.firstObservation, 'value');
|
||||
|
||||
expect(result).toContain(expectedObservation);
|
||||
|
|
|
@ -36,8 +36,7 @@ describe('Account create and basic data path', () => {
|
|||
await page.waitForState('account.card.basicData');
|
||||
});
|
||||
|
||||
it('should reload the section and check the name is as expected', async() => {
|
||||
await page.reloadSection('account.card.basicData');
|
||||
it('should check the name is as expected', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.accountBasicData.name, 'value');
|
||||
|
||||
expect(result).toEqual('Remy');
|
||||
|
@ -103,25 +102,39 @@ describe('Account create and basic data path', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// creating the account without the active property set to true seems to be creating an active user anyways
|
||||
// describe('activate user', () => {
|
||||
// it(`should check the inactive user icon is present in the descriptor`, async() => {
|
||||
// await page.waitForSelector(selectors.accountDescriptor.activeUserIcon, {visible: true});
|
||||
// });
|
||||
describe('deactivate user', () => {
|
||||
it(`should check the inactive user icon isn't present in the descriptor just yet`, async() => {
|
||||
await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 0);
|
||||
});
|
||||
|
||||
// it('should activate the user using the descriptor menu', async() => {
|
||||
// await page.waitToClick(selectors.accountDescriptor.menuButton);
|
||||
// await page.waitToClick(selectors.accountDescriptor.activateUser);
|
||||
// await page.waitToClick(selectors.accountDescriptor.acceptButton);
|
||||
// const message = await page.waitForSnackbar();
|
||||
it('should deactivate the user using the descriptor menu', async() => {
|
||||
await page.waitToClick(selectors.accountDescriptor.menuButton);
|
||||
await page.waitToClick(selectors.accountDescriptor.deactivateUser);
|
||||
await page.waitToClick(selectors.accountDescriptor.acceptButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
// expect(message.text).toContain('user enabled?');
|
||||
// });
|
||||
expect(message.text).toContain('User deactivated!');
|
||||
});
|
||||
|
||||
// it('should check the inactive user icon is not present anymore', async() => {
|
||||
// await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 0);
|
||||
// });
|
||||
// });
|
||||
it('should check the inactive user icon is now present', async() => {
|
||||
await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('activate user', () => {
|
||||
it('should activate the user using the descriptor menu', async() => {
|
||||
await page.waitToClick(selectors.accountDescriptor.menuButton);
|
||||
await page.waitToClick(selectors.accountDescriptor.activateUser);
|
||||
await page.waitToClick(selectors.accountDescriptor.acceptButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('User activated!');
|
||||
});
|
||||
|
||||
it('should check the inactive user icon is not present anymore', async() => {
|
||||
await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mail forwarding', () => {
|
||||
it('should activate the mail forwarding and set the recipent email', async() => {
|
||||
|
|
|
@ -56,7 +56,7 @@ describe('Account Alias create and basic data path', () => {
|
|||
expect(result).toContain('psykers');
|
||||
});
|
||||
|
||||
it('should search for the IT alias group then access to the users section then check the role listed is the expected one', async() => {
|
||||
it('should search IT alias then access the user section to check the role listed is the expected one', async() => {
|
||||
await page.accessToSearchResult('IT');
|
||||
await page.accessToSection('account.alias.card.users');
|
||||
const rolesCount = await page.countElement(selectors.accountAliasUsers.anyResult);
|
||||
|
|
|
@ -15,8 +15,9 @@ export default function currency($translate) {
|
|||
maximumFractionDigits: fractionSize
|
||||
};
|
||||
|
||||
const lang = $translate.use() == 'es' ? 'de' : $translate.use();
|
||||
if (typeof input == 'number') {
|
||||
return new Intl.NumberFormat($translate.use(), options)
|
||||
return new Intl.NumberFormat(lang, options)
|
||||
.format(input);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ export default class App {
|
|||
constructor() {
|
||||
this.loaderStatus = 0;
|
||||
this.loading = false;
|
||||
this.versionInterval = setInterval(this.getVersion.bind(this), 300000);
|
||||
}
|
||||
|
||||
showMessage(message) {
|
||||
|
@ -38,6 +39,21 @@ export default class App {
|
|||
if (this.loaderStatus === 0)
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
getVersion() {
|
||||
this.logger.$http.get('Applications/status');
|
||||
}
|
||||
|
||||
setVersion(newVersion) {
|
||||
if (newVersion) {
|
||||
const currentVersion = localStorage.getItem('salix-version');
|
||||
if (newVersion != currentVersion) {
|
||||
this.hasNewVersion = true;
|
||||
clearInterval(this.versionInterval);
|
||||
}
|
||||
localStorage.setItem('salix-version', newVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.service('vnApp', App);
|
||||
|
|
|
@ -30,14 +30,17 @@ function interceptor($q, vnApp, vnToken, $translate) {
|
|||
},
|
||||
response(response) {
|
||||
vnApp.popLoader();
|
||||
const newVersion = response.headers('salix-version');
|
||||
vnApp.setVersion(newVersion);
|
||||
|
||||
return response;
|
||||
},
|
||||
responseError(rejection) {
|
||||
vnApp.popLoader();
|
||||
let err = new HttpError(rejection.statusText);
|
||||
const err = new HttpError(rejection.statusText);
|
||||
Object.assign(err, rejection);
|
||||
return $q.reject(err);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
ngModule.factory('vnInterceptor', interceptor);
|
||||
|
|
|
@ -19,6 +19,14 @@
|
|||
</div>
|
||||
<vn-slot name="topbar"></vn-slot>
|
||||
<div class="side end">
|
||||
<vn-icon-button
|
||||
id="refresh"
|
||||
icon="refresh"
|
||||
ng-if="$ctrl.vnApp.hasNewVersion"
|
||||
ng-click="$ctrl.refresh()"
|
||||
class="refresh"
|
||||
translate-attr="{title: 'There is a new version, click here to reload'}">
|
||||
</vn-icon-button>
|
||||
<vn-icon-button
|
||||
id="apps"
|
||||
icon="apps"
|
||||
|
@ -39,7 +47,6 @@
|
|||
translate-attr="{title: 'Account'}"
|
||||
on-error-src/>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<vn-menu vn-id="apps-menu">
|
||||
<vn-list class="modules-menu">
|
||||
|
|
|
@ -26,6 +26,10 @@ export class Layout extends Component {
|
|||
const token = this.vnToken.token;
|
||||
return `/api/Images/user/160x160/${userId}/download?access_token=${token}`;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
Layout.$inject = ['$element', '$scope', 'vnModules'];
|
||||
|
||||
|
|
|
@ -60,6 +60,9 @@ vn-layout {
|
|||
font-size: 1.05rem;
|
||||
padding: 0;
|
||||
}
|
||||
.vn-icon-button.refresh {
|
||||
color: $color-alert;
|
||||
}
|
||||
}
|
||||
& > vn-side-menu > .menu {
|
||||
display: flex;
|
||||
|
|
|
@ -17,6 +17,7 @@ Go to module summary: Ir a la vista previa del módulo
|
|||
Show summary: Mostrar vista previa
|
||||
What is new: Novedades de la versión
|
||||
Settings: Ajustes
|
||||
There is a new version, click here to reload: Hay una nueva versión, pulse aquí para recargar
|
||||
|
||||
# Actions
|
||||
|
||||
|
|
|
@ -123,5 +123,6 @@
|
|||
"The worker has hours recorded that day": "The worker has hours recorded that day",
|
||||
"isWithoutNegatives": "isWithoutNegatives",
|
||||
"routeFk": "routeFk",
|
||||
"Not enough privileges to edit a client with verified data": "Not enough privileges to edit a client with verified data"
|
||||
"Not enough privileges to edit a client with verified data": "Not enough privileges to edit a client with verified data",
|
||||
"Can't change the password of another worker": "Can't change the password of another worker"
|
||||
}
|
|
@ -226,5 +226,6 @@
|
|||
"reference duplicated": "Referencia duplicada",
|
||||
"This ticket is already a refund": "Este ticket ya es un abono",
|
||||
"isWithoutNegatives": "isWithoutNegatives",
|
||||
"routeFk": "routeFk"
|
||||
"routeFk": "routeFk",
|
||||
"Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador"
|
||||
}
|
|
@ -31,7 +31,8 @@
|
|||
"loopback#token": {}
|
||||
},
|
||||
"auth:after": {
|
||||
"./middleware/current-user": {}
|
||||
"./middleware/current-user": {},
|
||||
"./middleware/salix-version": {}
|
||||
},
|
||||
"parse": {
|
||||
"body-parser#json":{}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
const packageJson = require('../../../package.json');
|
||||
|
||||
module.exports = function(options) {
|
||||
return function(req, res, next) {
|
||||
res.set('Salix-Version', packageJson.version);
|
||||
next();
|
||||
};
|
||||
};
|
|
@ -51,6 +51,9 @@ module.exports = function(Self) {
|
|||
Self.createReceipt = async(ctx, options) => {
|
||||
const models = Self.app.models;
|
||||
const args = ctx.args;
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
|
||||
let tx;
|
||||
const myOptions = {};
|
||||
|
||||
|
@ -92,8 +95,9 @@ module.exports = function(Self) {
|
|||
throw new UserError('Invalid account');
|
||||
|
||||
await Self.rawSql(
|
||||
`CALL vn.ledger_doCompensation(CURDATE(), ?, ?, ?, ?, ?, ?)`,
|
||||
`CALL vn.ledger_doCompensation(?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
date,
|
||||
args.compensationAccount,
|
||||
args.bankFk,
|
||||
accountingType.receiptDescription + originalClient.accountingAccount,
|
||||
|
@ -106,9 +110,10 @@ module.exports = function(Self) {
|
|||
} else if (accountingType.isAutoConciliated == true) {
|
||||
const description = `${originalClient.id} : ${originalClient.socialName} - ${accountingType.receiptDescription}`;
|
||||
const [xdiarioNew] = await Self.rawSql(
|
||||
`SELECT xdiario_new(?, CURDATE(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ledger;`,
|
||||
`SELECT xdiario_new(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ledger;`,
|
||||
[
|
||||
null,
|
||||
date,
|
||||
bank.account,
|
||||
originalClient.accountingAccount,
|
||||
description,
|
||||
|
@ -126,9 +131,10 @@ module.exports = function(Self) {
|
|||
);
|
||||
|
||||
await Self.rawSql(
|
||||
`SELECT xdiario_new(?, CURDATE(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`,
|
||||
`SELECT xdiario_new(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`,
|
||||
[
|
||||
xdiarioNew.ledger,
|
||||
date,
|
||||
originalClient.accountingAccount,
|
||||
bank.account,
|
||||
description,
|
||||
|
|
|
@ -62,7 +62,7 @@ module.exports = function(Self) {
|
|||
{
|
||||
relation: 'account',
|
||||
scope: {
|
||||
fields: ['id', 'name', 'active']
|
||||
fields: ['id', 'name', 'email', 'active']
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -74,8 +74,10 @@ module.exports = function(Self) {
|
|||
]
|
||||
}, myOptions);
|
||||
|
||||
const query = `SELECT vn.clientGetDebt(?, CURDATE()) AS debt`;
|
||||
const data = await Self.rawSql(query, [id], myOptions);
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const query = `SELECT vn.clientGetDebt(?, ?) AS debt`;
|
||||
const data = await Self.rawSql(query, [id, date], myOptions);
|
||||
|
||||
client.debt = data[0].debt;
|
||||
|
||||
|
|
|
@ -25,8 +25,10 @@ module.exports = Self => {
|
|||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const query = `SELECT vn.clientGetDebt(?, CURDATE()) AS debt`;
|
||||
const [debt] = await Self.rawSql(query, [clientFk], myOptions);
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const query = `SELECT vn.clientGetDebt(?, ?) AS debt`;
|
||||
const [debt] = await Self.rawSql(query, [clientFk, date], myOptions);
|
||||
|
||||
return debt;
|
||||
};
|
||||
|
|
|
@ -32,6 +32,8 @@ module.exports = Self => {
|
|||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const ticket = await Self.app.models.Ticket.findById(ticketId, null, myOptions);
|
||||
const query = `
|
||||
SELECT
|
||||
|
@ -50,12 +52,12 @@ module.exports = Self => {
|
|||
JOIN vn.warehouse w ON t.warehouseFk = w.id
|
||||
JOIN vn.address ad ON t.addressFk = ad.id
|
||||
JOIN vn.province pr ON ad.provinceFk = pr.id
|
||||
WHERE t.shipped >= CURDATE() AND t.clientFk = ? AND ts.alertLevel = 0
|
||||
WHERE t.shipped >= ? AND t.clientFk = ? AND ts.alertLevel = 0
|
||||
AND t.id <> ? AND t.warehouseFk = ?
|
||||
ORDER BY t.shipped
|
||||
LIMIT 10`;
|
||||
|
||||
return Self.rawSql(query, [id, ticketId, ticket.warehouseFk], myOptions);
|
||||
return Self.rawSql(query, [date, id, ticketId, ticket.warehouseFk], myOptions);
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('setPassword', {
|
||||
description: 'Sets the password of a non-worker client',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
description: 'The user id',
|
||||
http: {source: 'path'}
|
||||
}, {
|
||||
arg: 'newPassword',
|
||||
type: 'string',
|
||||
description: 'The new password',
|
||||
required: true
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: `/:id/setPassword`,
|
||||
verb: 'PATCH'
|
||||
}
|
||||
});
|
||||
|
||||
Self.setPassword = async function(id, newPassword) {
|
||||
const models = Self.app.models;
|
||||
|
||||
const isWorker = await models.Worker.findById(id);
|
||||
if (isWorker)
|
||||
throw new Error(`Can't change the password of another worker`);
|
||||
|
||||
await models.Account.setPassword(id, newPassword);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Client setPassword', () => {
|
||||
it('should throw an error the setPassword target is not just a client but a worker', async() => {
|
||||
let error;
|
||||
|
||||
try {
|
||||
await models.Client.setPassword(1106, 'newPass?');
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(`Can't change the password of another worker`);
|
||||
});
|
||||
|
||||
it('should change the password of the client', async() => {
|
||||
let error;
|
||||
|
||||
try {
|
||||
await models.Client.setPassword(1101, 't0pl3v3l.p455w0rd!');
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
});
|
|
@ -120,7 +120,6 @@ describe('client summary()', () => {
|
|||
const result = await models.Client.summary(clientId, options);
|
||||
|
||||
expect(result.recovery.id).toEqual(3);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const LoopBackContext = require('loopback-context');
|
||||
describe('Client updateUser', () => {
|
||||
const employeeId = 1;
|
||||
const activeCtx = {
|
||||
accessToken: {userId: employeeId},
|
||||
http: {
|
||||
req: {
|
||||
headers: {origin: 'http://localhost'}
|
||||
}
|
||||
}
|
||||
};
|
||||
const ctx = {
|
||||
req: {accessToken: {userId: employeeId}},
|
||||
args: {name: 'test', active: true}
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||
active: activeCtx
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error the target user is not just a client but a worker', async() => {
|
||||
let error;
|
||||
try {
|
||||
const clientID = 1106;
|
||||
await models.Client.updateUser(ctx, clientID);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(`Can't update the user details of another worker`);
|
||||
});
|
||||
|
||||
it('should update the user data', async() => {
|
||||
let error;
|
||||
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const clientID = 1105;
|
||||
await models.Client.updateUser(ctx, clientID, options);
|
||||
const client = await models.Account.findById(clientID, null, options);
|
||||
|
||||
expect(client.name).toEqual('test');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
});
|
|
@ -122,7 +122,6 @@ module.exports = Self => {
|
|||
|
||||
return clientModel.findOne(filter, options);
|
||||
}
|
||||
|
||||
async function getRecoveries(recoveryModel, clientId, options) {
|
||||
const filter = {
|
||||
where: {
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('updateUser', {
|
||||
description: 'Updates the user information',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
description: 'The user id',
|
||||
http: {source: 'path'}
|
||||
},
|
||||
{
|
||||
arg: 'name',
|
||||
type: 'string',
|
||||
description: 'the user name'
|
||||
},
|
||||
{
|
||||
arg: 'email',
|
||||
type: 'string',
|
||||
description: 'the user email'
|
||||
},
|
||||
{
|
||||
arg: 'active',
|
||||
type: 'boolean',
|
||||
description: 'whether the user is active or not'
|
||||
},
|
||||
],
|
||||
http: {
|
||||
path: '/:id/updateUser',
|
||||
verb: 'PATCH'
|
||||
}
|
||||
});
|
||||
|
||||
Self.updateUser = async function(ctx, id, options) {
|
||||
const models = Self.app.models;
|
||||
let tx;
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
if (!myOptions.transaction) {
|
||||
tx = await models.Account.beginTransaction({});
|
||||
myOptions.transaction = tx;
|
||||
}
|
||||
|
||||
try {
|
||||
const isWorker = await models.Worker.findById(id, null, myOptions);
|
||||
if (isWorker)
|
||||
throw new Error(`Can't update the user details of another worker`);
|
||||
|
||||
const user = await models.Account.findById(id, null, myOptions);
|
||||
|
||||
await user.updateAttributes(ctx.args, myOptions);
|
||||
|
||||
if (tx) await tx.commit();
|
||||
} catch (e) {
|
||||
if (tx) await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -51,6 +51,8 @@ module.exports = Self => {
|
|||
|
||||
const stmts = [];
|
||||
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const stmt = new ParameterizedSQL(
|
||||
`SELECT *
|
||||
FROM (
|
||||
|
@ -58,12 +60,12 @@ module.exports = Self => {
|
|||
DISTINCT c.id clientFk,
|
||||
c.name clientName,
|
||||
c.salesPersonFk,
|
||||
u.nickname salesPersonName,
|
||||
u.name salesPersonName,
|
||||
d.amount,
|
||||
co.created,
|
||||
co.text observation,
|
||||
uw.id workerFk,
|
||||
uw.nickname workerName,
|
||||
uw.name workerName,
|
||||
c.creditInsurance,
|
||||
d.defaulterSinced
|
||||
FROM vn.defaulter d
|
||||
|
@ -72,10 +74,10 @@ module.exports = Self => {
|
|||
LEFT JOIN account.user u ON u.id = c.salesPersonFk
|
||||
LEFT JOIN account.user uw ON uw.id = co.workerFk
|
||||
WHERE
|
||||
d.created = CURDATE()
|
||||
d.created = ?
|
||||
AND d.amount > 0
|
||||
ORDER BY co.created DESC) d`
|
||||
);
|
||||
, [date]);
|
||||
|
||||
stmt.merge(conn.makeWhere(filter.where));
|
||||
stmt.merge(`GROUP BY d.clientFk`);
|
||||
|
|
|
@ -27,13 +27,15 @@ module.exports = Self => {
|
|||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const query = `
|
||||
SELECT count(*) AS hasActiveRecovery
|
||||
FROM vn.recovery
|
||||
WHERE clientFk = ?
|
||||
AND IFNULL(finished,CURDATE()) >= CURDATE();
|
||||
AND IFNULL(finished, ?) >= ?;
|
||||
`;
|
||||
const [result] = await Self.rawSql(query, [id], myOptions);
|
||||
const [result] = await Self.rawSql(query, [id, date, date], myOptions);
|
||||
|
||||
return result.hasActiveRecovery != 0;
|
||||
};
|
||||
|
|
|
@ -8,30 +8,32 @@ const LoopBackContext = require('loopback-context');
|
|||
|
||||
module.exports = Self => {
|
||||
// Methods
|
||||
require('../methods/client/getCard')(Self);
|
||||
require('../methods/client/createWithUser')(Self);
|
||||
require('../methods/client/hasCustomerRole')(Self);
|
||||
require('../methods/client/canCreateTicket')(Self);
|
||||
require('../methods/client/isValidClient')(Self);
|
||||
require('../methods/client/addressesPropagateRe')(Self);
|
||||
require('../methods/client/canBeInvoiced')(Self);
|
||||
require('../methods/client/canCreateTicket')(Self);
|
||||
require('../methods/client/checkDuplicated')(Self);
|
||||
require('../methods/client/confirmTransaction')(Self);
|
||||
require('../methods/client/consumption')(Self);
|
||||
require('../methods/client/createAddress')(Self);
|
||||
require('../methods/client/createReceipt')(Self);
|
||||
require('../methods/client/createWithUser')(Self);
|
||||
require('../methods/client/extendedListFilter')(Self);
|
||||
require('../methods/client/getAverageInvoiced')(Self);
|
||||
require('../methods/client/getCard')(Self);
|
||||
require('../methods/client/getDebt')(Self);
|
||||
require('../methods/client/getMana')(Self);
|
||||
require('../methods/client/getAverageInvoiced')(Self);
|
||||
require('../methods/client/summary')(Self);
|
||||
require('../methods/client/updateFiscalData')(Self);
|
||||
require('../methods/client/getTransactions')(Self);
|
||||
require('../methods/client/confirmTransaction')(Self);
|
||||
require('../methods/client/canBeInvoiced')(Self);
|
||||
require('../methods/client/uploadFile')(Self);
|
||||
require('../methods/client/hasCustomerRole')(Self);
|
||||
require('../methods/client/isValidClient')(Self);
|
||||
require('../methods/client/lastActiveTickets')(Self);
|
||||
require('../methods/client/sendSms')(Self);
|
||||
require('../methods/client/createAddress')(Self);
|
||||
require('../methods/client/setPassword')(Self);
|
||||
require('../methods/client/summary')(Self);
|
||||
require('../methods/client/updateAddress')(Self);
|
||||
require('../methods/client/consumption')(Self);
|
||||
require('../methods/client/createReceipt')(Self);
|
||||
require('../methods/client/updateFiscalData')(Self);
|
||||
require('../methods/client/updatePortfolio')(Self);
|
||||
require('../methods/client/checkDuplicated')(Self);
|
||||
require('../methods/client/extendedListFilter')(Self);
|
||||
require('../methods/client/updateUser')(Self);
|
||||
require('../methods/client/uploadFile')(Self);
|
||||
|
||||
// Validations
|
||||
|
||||
|
@ -446,7 +448,7 @@ module.exports = Self => {
|
|||
|
||||
const app = require('vn-loopback/server/server');
|
||||
app.on('started', function() {
|
||||
let account = app.models.Account;
|
||||
const account = app.models.Account;
|
||||
|
||||
account.observe('before save', async ctx => {
|
||||
if (ctx.isNewInstance) return;
|
||||
|
@ -454,22 +456,26 @@ module.exports = Self => {
|
|||
});
|
||||
|
||||
account.observe('after save', async ctx => {
|
||||
let changes = ctx.data || ctx.instance;
|
||||
const changes = ctx.data || ctx.instance;
|
||||
if (!ctx.isNewInstance && changes) {
|
||||
let oldData = ctx.hookState.oldInstance;
|
||||
let hasChanges = oldData.name != changes.name || oldData.active != changes.active;
|
||||
const oldData = ctx.hookState.oldInstance;
|
||||
const hasChanges = oldData.name != changes.name || oldData.active != changes.active;
|
||||
if (!hasChanges) return;
|
||||
|
||||
let userId = ctx.options.accessToken.userId;
|
||||
let logRecord = {
|
||||
originFk: oldData.id,
|
||||
userFk: userId,
|
||||
action: 'update',
|
||||
changedModel: 'Account',
|
||||
oldInstance: {name: oldData.name, active: oldData.active},
|
||||
newInstance: {name: changes.name, active: changes.active}
|
||||
};
|
||||
await Self.app.models.ClientLog.create(logRecord);
|
||||
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: 'Account',
|
||||
oldInstance: {name: oldData.name, active: oldData.active},
|
||||
newInstance: {name: changes.name, active: changes.active}
|
||||
};
|
||||
await Self.app.models.ClientLog.create(logRecord);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
url="Defaulters/filter"
|
||||
filter="::$ctrl.filter"
|
||||
limit="20"
|
||||
order="amount DESC"
|
||||
data="defaulters"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
|
@ -70,13 +71,13 @@
|
|||
</th>
|
||||
<th
|
||||
vn-tooltip="Last observation date"
|
||||
field="created"
|
||||
shrink-datetime>
|
||||
<span translate>Last observation D.</span>
|
||||
field="created">
|
||||
<span translate>L. O. Date</span>
|
||||
</th>
|
||||
<th
|
||||
vn-tooltip="Credit insurance"
|
||||
field="creditInsurance" >
|
||||
field="creditInsurance"
|
||||
shrink>
|
||||
<span translate>Credit I.</span>
|
||||
</th>
|
||||
<th field="defaulterSinced">
|
||||
|
@ -124,13 +125,13 @@
|
|||
ng-model="defaulter.observation">
|
||||
</vn-textarea>
|
||||
</td>
|
||||
<td shrink-datetime>
|
||||
<td shrink-date>
|
||||
<span class="chip {{::$ctrl.chipColor(defaulter.created)}}">
|
||||
{{::defaulter.created | date: 'dd/MM/yyyy'}}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{::defaulter.creditInsurance | currency: 'EUR': 2}}</td>
|
||||
<td>{{::defaulter.defaulterSinced | date: 'dd/MM/yyyy'}}</td>
|
||||
<td shrink>{{::defaulter.creditInsurance | currency: 'EUR': 2}}</td>
|
||||
<td shrink-date>{{::defaulter.defaulterSinced | date: 'dd/MM/yyyy'}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -26,7 +26,7 @@ export default class Controller extends Section {
|
|||
url: 'Workers/activeWithInheritedRole',
|
||||
where: `{role: 'salesPerson'}`,
|
||||
searchFunction: '{firstName: $search}',
|
||||
showField: 'nickname',
|
||||
showField: 'name',
|
||||
valueField: 'id',
|
||||
}
|
||||
},
|
||||
|
@ -35,7 +35,7 @@ export default class Controller extends Section {
|
|||
autocomplete: {
|
||||
url: 'Workers/activeWithInheritedRole',
|
||||
searchFunction: '{firstName: $search}',
|
||||
showField: 'nickname',
|
||||
showField: 'name',
|
||||
valueField: 'id',
|
||||
}
|
||||
},
|
||||
|
@ -53,16 +53,8 @@ export default class Controller extends Section {
|
|||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
get balanceDueTotal() {
|
||||
let balanceDueTotal = 0;
|
||||
const defaulters = this.$.model.data || [];
|
||||
|
||||
for (let defaulter of defaulters)
|
||||
balanceDueTotal += defaulter.amount;
|
||||
|
||||
return balanceDueTotal;
|
||||
this.getBalanceDueTotal();
|
||||
}
|
||||
|
||||
get checked() {
|
||||
|
@ -76,6 +68,18 @@ export default class Controller extends Section {
|
|||
return checkedLines;
|
||||
}
|
||||
|
||||
getBalanceDueTotal() {
|
||||
this.$http.get('Defaulters/filter')
|
||||
.then(res => {
|
||||
if (!res.data) return 0;
|
||||
|
||||
this.balanceDueTotal = res.data.reduce(
|
||||
(accumulator, currentValue) => {
|
||||
return accumulator + (currentValue['amount'] || 0);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
chipColor(date) {
|
||||
const day = 24 * 60 * 60 * 1000;
|
||||
const today = new Date();
|
||||
|
|
|
@ -36,17 +36,6 @@ describe('client defaulter', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('balanceDueTotal() getter', () => {
|
||||
it('should return balance due total', () => {
|
||||
const data = controller.$.model.data;
|
||||
const expectedAmount = data[0].amount + data[1].amount + data[2].amount;
|
||||
|
||||
const result = controller.balanceDueTotal;
|
||||
|
||||
expect(result).toEqual(expectedAmount);
|
||||
});
|
||||
});
|
||||
|
||||
describe('chipColor()', () => {
|
||||
it('should return undefined when the date is the present', () => {
|
||||
let today = new Date();
|
||||
|
@ -93,6 +82,7 @@ describe('client defaulter', () => {
|
|||
const params = [{text: controller.defaulter.observation, clientFk: data[1].clientFk}];
|
||||
|
||||
jest.spyOn(controller.vnApp, 'showMessage');
|
||||
$httpBackend.expect('GET', `Defaulters/filter`).respond(200);
|
||||
$httpBackend.expect('POST', `ClientObservations`, params).respond(200, params);
|
||||
|
||||
controller.onResponse();
|
||||
|
@ -115,5 +105,17 @@ describe('client defaulter', () => {
|
|||
expect(expr).toEqual({'d.clientFk': '5'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBalanceDueTotal()', () => {
|
||||
it('should return balance due total', () => {
|
||||
const defaulters = controller.$.model.data;
|
||||
$httpBackend.when('GET', `Defaulters/filter`).respond(defaulters);
|
||||
|
||||
controller.getBalanceDueTotal();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.balanceDueTotal).toEqual(875);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ Add observation to all selected clients: Añadir observación a {{total}} client
|
|||
Balance D.: Saldo V.
|
||||
Credit I.: Crédito A.
|
||||
Last observation: Última observación
|
||||
Last observation D.: Fecha última O.
|
||||
L. O. Date: Fecha Ú. O.
|
||||
Last observation date: Fecha última observación
|
||||
Search client: Buscar clientes
|
||||
Worker who made the last observation: Trabajador que ha realizado la última observación
|
|
@ -1,11 +1,16 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="Accounts"
|
||||
url="Accounts"
|
||||
id-field="id"
|
||||
data="$ctrl.account"
|
||||
form="form">
|
||||
</vn-watcher>
|
||||
<form name="form" ng-submit="watcher.submit()" class="vn-w-md">
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="UserPasswords"
|
||||
data="$ctrl.passRequirements">
|
||||
</vn-crud-model>
|
||||
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-check
|
||||
|
@ -28,6 +33,17 @@
|
|||
rule>
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
vn-id="email"
|
||||
disabled="watcher.orgData.active != $ctrl.account.active"
|
||||
vn-one
|
||||
label="Recovery email"
|
||||
ng-model="$ctrl.account.email"
|
||||
info="This email is used for user to regain access their account."
|
||||
rule>
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit
|
||||
|
@ -55,6 +71,7 @@
|
|||
<vn-textfield
|
||||
type="password"
|
||||
label="New password"
|
||||
info="{{'Password requirements' | translate:$ctrl.passRequirements[0]}}"
|
||||
ng-model="$ctrl.newPassword">
|
||||
</vn-textfield>
|
||||
<vn-textfield
|
||||
|
|
|
@ -44,11 +44,11 @@ export default class Controller extends Section {
|
|||
throw new Error(`You must enter a new password`);
|
||||
if (this.newPassword != this.repeatPassword)
|
||||
throw new Error(`Passwords don't match`);
|
||||
let account = {
|
||||
password: this.newPassword
|
||||
const data = {
|
||||
newPassword: this.newPassword
|
||||
};
|
||||
|
||||
this.$http.patch(`Accounts/${this.client.id}`, account).then(res => {
|
||||
this.$http.patch(`Clients/${this.client.id}/setPassword`, data).then(() => {
|
||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||
});
|
||||
} catch (e) {
|
||||
|
@ -59,6 +59,18 @@ export default class Controller extends Section {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
const data = {
|
||||
name: this.account.name,
|
||||
email: this.account.email,
|
||||
active: this.account.active
|
||||
};
|
||||
this.$http.patch(`Clients/${this.client.id}/updateUser`, data).then(() => {
|
||||
this.$.watcher.notifySaved();
|
||||
this.$.watcher.updateOriginalData();
|
||||
});
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$element', '$scope'];
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ describe('Component VnClientWebAccess', () => {
|
|||
});
|
||||
|
||||
describe('checkConditions()', () => {
|
||||
it(`should perform a query to check if the client is valid and then store a boolean into the controller`, () => {
|
||||
it('should perform a query to check if the client is valid', () => {
|
||||
controller.client = {id: '1234'};
|
||||
|
||||
expect(controller.canEnableCheckBox).toBeTruthy();
|
||||
|
@ -82,7 +82,9 @@ describe('Component VnClientWebAccess', () => {
|
|||
controller.newPassword = 'm24x8';
|
||||
controller.repeatPassword = 'm24x8';
|
||||
controller.canChangePassword = true;
|
||||
$httpBackend.expectPATCH('Accounts/1234', {password: 'm24x8'}).respond('done');
|
||||
|
||||
const query = `Clients/${controller.client.id}/setPassword`;
|
||||
$httpBackend.expectPATCH(query, {newPassword: controller.newPassword}).respond('done');
|
||||
controller.onPassChange();
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
|
|
@ -4,4 +4,6 @@ New password: Nueva contraseña
|
|||
Repeat password: Repetir contraseña
|
||||
Change password: Cambiar contraseña
|
||||
Passwords don't match: Las contraseñas no coinciden
|
||||
You must enter a new password: Debes introducir una nueva contraseña
|
||||
You must enter a new password: Debes introducir una nueva contraseña
|
||||
Recovery email: Correo de recuperación
|
||||
This email is used for user to regain access their account.: Este correo electrónico se usa para que el usuario recupere el acceso a su cuenta.
|
|
@ -96,6 +96,7 @@ module.exports = Self => {
|
|||
});
|
||||
|
||||
Self.latestBuysFilter = async(ctx, filter, options) => {
|
||||
const models = Self.app.models;
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
|
@ -143,8 +144,12 @@ module.exports = Self => {
|
|||
const stmts = [];
|
||||
let stmt;
|
||||
|
||||
stmts.push('CALL cache.visible_refresh(@calc_id, FALSE, 1)');
|
||||
const warehouse = await models.Warehouse.findOne({where: {code: 'ALG'}}, myOptions);
|
||||
stmt = new ParameterizedSQL(`CALL cache.visible_refresh(@calc_id, FALSE, ?)`, [warehouse.id]);
|
||||
stmts.push(stmt);
|
||||
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
stmt = new ParameterizedSQL(`
|
||||
SELECT
|
||||
i.image,
|
||||
|
@ -202,9 +207,9 @@ module.exports = Self => {
|
|||
LEFT JOIN itemType t ON t.id = i.typeFk
|
||||
LEFT JOIN intrastat intr ON intr.id = i.intrastatFk
|
||||
LEFT JOIN origin ori ON ori.id = i.originFk
|
||||
LEFT JOIN entry e ON e.id = b.entryFk AND e.created >= DATE_SUB(CURDATE(),INTERVAL 1 YEAR)
|
||||
LEFT JOIN entry e ON e.id = b.entryFk AND e.created >= DATE_SUB(? ,INTERVAL 1 YEAR)
|
||||
LEFT JOIN supplier s ON s.id = e.supplierFk`
|
||||
);
|
||||
, [date]);
|
||||
|
||||
if (ctx.args.tags) {
|
||||
let i = 1;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
vn-id="model"
|
||||
url="InvoiceOuts/filter"
|
||||
limit="20"
|
||||
order="issued DESC">
|
||||
order="issued DESC, id DESC">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
|
|
|
@ -32,6 +32,8 @@ module.exports = Self => {
|
|||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const wastes = await Self.rawSql(`
|
||||
SELECT *, 100 * dwindle / total AS percentage
|
||||
FROM (
|
||||
|
@ -42,11 +44,11 @@ module.exports = Self => {
|
|||
sum(ws.saleWaste) AS dwindle
|
||||
FROM bs.waste ws
|
||||
WHERE buyer = ? AND family = ?
|
||||
AND year = YEAR(TIMESTAMPADD(WEEK,-1,CURDATE()))
|
||||
AND week = WEEK(TIMESTAMPADD(WEEK,-1,CURDATE()), 1)
|
||||
AND year = YEAR(TIMESTAMPADD(WEEK,-1, ?))
|
||||
AND week = WEEK(TIMESTAMPADD(WEEK,-1, ?), 1)
|
||||
GROUP BY buyer, itemFk
|
||||
) sub
|
||||
ORDER BY family, percentage DESC`, [buyer, family], myOptions);
|
||||
ORDER BY family, percentage DESC`, [buyer, family, date, date], myOptions);
|
||||
|
||||
const details = [];
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ module.exports = Self => {
|
|||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const wastes = await Self.rawSql(`
|
||||
SELECT *, 100 * dwindle / total AS percentage
|
||||
FROM (
|
||||
|
@ -27,11 +29,11 @@ module.exports = Self => {
|
|||
sum(ws.saleTotal) AS total,
|
||||
sum(ws.saleWaste) AS dwindle
|
||||
FROM bs.waste ws
|
||||
WHERE year = YEAR(TIMESTAMPADD(WEEK,-1,CURDATE()))
|
||||
AND week = WEEK(TIMESTAMPADD(WEEK,-1,CURDATE()), 1)
|
||||
WHERE year = YEAR(TIMESTAMPADD(WEEK,-1, ?))
|
||||
AND week = WEEK(TIMESTAMPADD(WEEK,-1, ?), 1)
|
||||
GROUP BY buyer, family
|
||||
) sub
|
||||
ORDER BY percentage DESC`, null, myOptions);
|
||||
ORDER BY percentage DESC`, [date, date], myOptions);
|
||||
|
||||
const details = [];
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ module.exports = Self => {
|
|||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const stmt = new ParameterizedSQL(`
|
||||
SELECT
|
||||
u.name AS salesPerson,
|
||||
|
@ -47,9 +49,10 @@ module.exports = Self => {
|
|||
JOIN client c ON c.id = v.userFk
|
||||
JOIN account.user u ON c.salesPersonFk = u.id
|
||||
LEFT JOIN sharingCart sc ON sc.workerFk = c.salesPersonFk
|
||||
AND CURDATE() BETWEEN sc.started AND sc.ended
|
||||
AND ? BETWEEN sc.started AND sc.ended
|
||||
LEFT JOIN workerTeamCollegues wtc
|
||||
ON wtc.collegueFk = IFNULL(sc.workerSubstitute, c.salesPersonFk)`);
|
||||
ON wtc.collegueFk = IFNULL(sc.workerSubstitute, c.salesPersonFk)`,
|
||||
[date]);
|
||||
|
||||
if (!filter.where) filter.where = {};
|
||||
|
||||
|
|
|
@ -104,6 +104,8 @@ module.exports = Self => {
|
|||
const userId = ctx.req.accessToken.userId;
|
||||
const conn = Self.dataSource.connector;
|
||||
const models = Self.app.models;
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const args = ctx.args;
|
||||
const myOptions = {};
|
||||
|
||||
|
@ -264,15 +266,18 @@ module.exports = Self => {
|
|||
`);
|
||||
|
||||
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.sale_getProblems');
|
||||
stmts.push(`
|
||||
|
||||
stmt = new ParameterizedSQL(`
|
||||
CREATE TEMPORARY TABLE tmp.sale_getProblems
|
||||
(INDEX (ticketFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT f.id ticketFk, f.clientFk, f.warehouseFk, f.shipped
|
||||
FROM tmp.filter f
|
||||
LEFT JOIN alertLevel al ON al.id = f.alertLevel
|
||||
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)
|
||||
AND f.shipped >= CURDATE()`);
|
||||
(INDEX (ticketFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT f.id ticketFk, f.clientFk, f.warehouseFk, f.shipped
|
||||
FROM tmp.filter f
|
||||
LEFT JOIN alertLevel al ON al.id = f.alertLevel
|
||||
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)
|
||||
AND f.shipped >= ?`, [date]);
|
||||
stmts.push(stmt);
|
||||
|
||||
stmts.push('CALL ticket_getProblems(FALSE)');
|
||||
|
||||
stmts.push(`
|
||||
|
|
|
@ -74,6 +74,8 @@ module.exports = Self => {
|
|||
|
||||
filter = mergeFilters(filter, {where});
|
||||
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const stmts = [];
|
||||
const stmt = new ParameterizedSQL(
|
||||
`SELECT *
|
||||
|
@ -101,10 +103,10 @@ module.exports = Self => {
|
|||
LEFT JOIN vn.ticket t ON t.routeFk = r.id
|
||||
LEFT JOIN vn.supplierAgencyTerm sat ON sat.agencyFk = a.id
|
||||
LEFT JOIN vn.supplier s ON s.id = sat.supplierFk
|
||||
WHERE r.created > DATE_ADD(CURDATE(), INTERVAL -2 MONTH) AND sat.supplierFk IS NOT NULL
|
||||
WHERE r.created > DATE_ADD(?, INTERVAL -2 MONTH) AND sat.supplierFk IS NOT NULL
|
||||
GROUP BY r.id
|
||||
) a`
|
||||
);
|
||||
, [date]);
|
||||
|
||||
stmt.merge(conn.makeWhere(filter.where));
|
||||
stmt.merge(conn.makePagination(filter));
|
||||
|
|
|
@ -2,17 +2,17 @@ const app = require('vn-loopback/server/server');
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Route filter()', () => {
|
||||
let today = new Date();
|
||||
const today = new Date();
|
||||
today.setHours(2, 0, 0, 0);
|
||||
|
||||
it('should return the routes matching "search"', async() => {
|
||||
let ctx = {
|
||||
const ctx = {
|
||||
args: {
|
||||
search: 1,
|
||||
}
|
||||
};
|
||||
|
||||
let result = await app.models.Route.filter(ctx);
|
||||
const result = await app.models.Route.filter(ctx);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(result[0].id).toEqual(1);
|
||||
|
@ -28,7 +28,6 @@ describe('Route filter()', () => {
|
|||
|
||||
const to = new Date();
|
||||
to.setHours(23, 59, 59, 999);
|
||||
|
||||
const ctx = {
|
||||
args: {
|
||||
from: from,
|
||||
|
@ -48,43 +47,43 @@ describe('Route filter()', () => {
|
|||
});
|
||||
|
||||
it('should return the routes matching "m3"', async() => {
|
||||
let ctx = {
|
||||
const ctx = {
|
||||
args: {
|
||||
m3: 0.1,
|
||||
}
|
||||
};
|
||||
|
||||
let result = await app.models.Route.filter(ctx);
|
||||
const result = await app.models.Route.filter(ctx);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should return the routes matching "description"', async() => {
|
||||
let ctx = {
|
||||
const ctx = {
|
||||
args: {
|
||||
description: 'third route',
|
||||
}
|
||||
};
|
||||
|
||||
let result = await app.models.Route.filter(ctx);
|
||||
const result = await app.models.Route.filter(ctx);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should return the routes matching "workerFk"', async() => {
|
||||
let ctx = {
|
||||
const ctx = {
|
||||
args: {
|
||||
workerFk: 56,
|
||||
}
|
||||
};
|
||||
|
||||
let result = await app.models.Route.filter(ctx);
|
||||
const result = await app.models.Route.filter(ctx);
|
||||
|
||||
expect(result.length).toEqual(5);
|
||||
});
|
||||
|
||||
it('should return the routes matching "warehouseFk"', async() => {
|
||||
let ctx = {
|
||||
const ctx = {
|
||||
args: {
|
||||
warehouseFk: 1,
|
||||
}
|
||||
|
@ -102,25 +101,25 @@ describe('Route filter()', () => {
|
|||
});
|
||||
|
||||
it('should return the routes matching "vehicleFk"', async() => {
|
||||
let ctx = {
|
||||
const ctx = {
|
||||
args: {
|
||||
vehicleFk: 2,
|
||||
}
|
||||
};
|
||||
|
||||
let result = await app.models.Route.filter(ctx);
|
||||
const result = await app.models.Route.filter(ctx);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should return the routes matching "agencyModeFk"', async() => {
|
||||
let ctx = {
|
||||
const ctx = {
|
||||
args: {
|
||||
agencyModeFk: 7,
|
||||
}
|
||||
};
|
||||
|
||||
let result = await app.models.Route.filter(ctx);
|
||||
const result = await app.models.Route.filter(ctx);
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
});
|
||||
|
|
|
@ -1,30 +1,32 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const models = require('vn-loopback/server/server').models;
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('route getSuggestedTickets()', () => {
|
||||
const routeID = 1;
|
||||
const ticketId = 12;
|
||||
|
||||
it('should return an array of suggested tickets', async() => {
|
||||
const activeCtx = {
|
||||
accessToken: {userId: 19},
|
||||
headers: {origin: 'http://localhost'}
|
||||
};
|
||||
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||
active: activeCtx
|
||||
});
|
||||
|
||||
const tx = await app.models.Ticket.beginTransaction({});
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const ticketInRoute = await app.models.Ticket.findById(ticketId, null, options);
|
||||
const ticketInRoute = await models.Ticket.findById(ticketId, null, options);
|
||||
|
||||
await ticketInRoute.updateAttributes({
|
||||
routeFk: null,
|
||||
landed: new Date()
|
||||
}, options);
|
||||
|
||||
const result = await app.models.Route.getSuggestedTickets(routeID, options);
|
||||
const result = await models.Route.getSuggestedTickets(routeID, options);
|
||||
|
||||
const length = result.length;
|
||||
const anyResult = result[Math.floor(Math.random() * Math.floor(length))];
|
||||
|
|
|
@ -24,6 +24,8 @@ module.exports = Self => {
|
|||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const query = `
|
||||
SELECT
|
||||
s.id AS saleFk,
|
||||
|
@ -39,11 +41,11 @@ module.exports = Self => {
|
|||
INNER JOIN vn.sale s ON s.ticketFk = t.id
|
||||
LEFT JOIN vn.claimBeginning cb ON cb.saleFk = s.id
|
||||
|
||||
WHERE (t.landed) >= TIMESTAMPADD(DAY, -7, CURDATE())
|
||||
WHERE (t.landed) >= TIMESTAMPADD(DAY, -7, ?)
|
||||
AND t.id = ? AND cb.id IS NULL
|
||||
ORDER BY t.landed DESC, t.id DESC`;
|
||||
|
||||
const claimableSales = await Self.rawSql(query, [ticketFk], myOptions);
|
||||
const claimableSales = await Self.rawSql(query, [date, ticketFk], myOptions);
|
||||
|
||||
return claimableSales;
|
||||
};
|
||||
|
|
|
@ -56,7 +56,7 @@ module.exports = Self => {
|
|||
salesIds.push(null);
|
||||
|
||||
const servicesIds = [];
|
||||
if (services) {
|
||||
if (services && services.length) {
|
||||
for (let service of services)
|
||||
servicesIds.push(service.id);
|
||||
} else
|
||||
|
|
|
@ -132,6 +132,8 @@ module.exports = Self => {
|
|||
Self.filter = async(ctx, filter, options) => {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const conn = Self.dataSource.connector;
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const models = Self.app.models;
|
||||
const args = ctx.args;
|
||||
|
||||
|
@ -297,7 +299,8 @@ module.exports = Self => {
|
|||
stmts.push(stmt);
|
||||
|
||||
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.sale_getProblems');
|
||||
stmts.push(`
|
||||
|
||||
stmt = new ParameterizedSQL(`
|
||||
CREATE TEMPORARY TABLE tmp.sale_getProblems
|
||||
(INDEX (ticketFk))
|
||||
ENGINE = MEMORY
|
||||
|
@ -305,7 +308,9 @@ module.exports = Self => {
|
|||
FROM tmp.filter f
|
||||
LEFT JOIN alertLevel al ON al.id = f.alertLevel
|
||||
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)
|
||||
AND f.shipped >= CURDATE()`);
|
||||
AND f.shipped >= ?`, [date]);
|
||||
stmts.push(stmt);
|
||||
|
||||
stmts.push('CALL ticket_getProblems(FALSE)');
|
||||
|
||||
stmt = new ParameterizedSQL(`
|
||||
|
|
|
@ -26,6 +26,8 @@ module.exports = function(Self) {
|
|||
Self.makeInvoice = async(ctx, ticketsIds, options) => {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const models = Self.app.models;
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
|
||||
const myOptions = {};
|
||||
let tx;
|
||||
|
@ -81,7 +83,7 @@ module.exports = function(Self) {
|
|||
WHERE id IN(?) AND refFk IS NULL
|
||||
`, [ticketsIds], myOptions);
|
||||
|
||||
await Self.rawSql('CALL invoiceOut_new(?, CURDATE(), null, @invoiceId)', [serial], myOptions);
|
||||
await Self.rawSql('CALL invoiceOut_new(?, ?, null, @invoiceId)', [serial, date], myOptions);
|
||||
|
||||
const [resultInvoice] = await Self.rawSql('SELECT @invoiceId id', [], myOptions);
|
||||
|
||||
|
|
|
@ -142,6 +142,25 @@ module.exports = Self => {
|
|||
|
||||
const updatedTicket = await ticket.updateAttribute('isDeleted', true, myOptions);
|
||||
|
||||
const [ticketCollection] = await models.TicketCollection.find({
|
||||
fields: ['id'],
|
||||
where: {
|
||||
ticketFk: ticket.id
|
||||
}
|
||||
});
|
||||
|
||||
if (ticketCollection)
|
||||
await models.TicketCollection.destroyById(ticketCollection.id, myOptions);
|
||||
|
||||
await Self.rawSql(`
|
||||
DELETE sc
|
||||
FROM vn.saleGroup sg
|
||||
JOIN vn.sectorCollectionSaleGroup scsg ON scsg.saleGroupFk = sg.id
|
||||
JOIN vn.sectorCollection sc ON sc.id = scsg.sectorCollectionFk
|
||||
JOIN vn.saleGroupDetail sgd ON sgd.saleGroupFk = sg.id
|
||||
JOIN vn.sale s ON s.id = sgd.saleFk
|
||||
WHERE s.ticketFk = ?;`, [ticket.id], myOptions);
|
||||
|
||||
if (tx) await tx.commit();
|
||||
|
||||
return updatedTicket;
|
||||
|
|
|
@ -56,7 +56,6 @@ describe('ticket canBeInvoiced()', () => {
|
|||
|
||||
it('should return falsy for a ticket shipping in future', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ describe('ticket filter()', () => {
|
|||
const filter = {};
|
||||
const result = await models.Ticket.filter(ctx, filter, options);
|
||||
|
||||
expect(result.length).toEqual(2);
|
||||
expect(result.length).toEqual(3);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
|
|
|
@ -8,6 +8,12 @@ describe('ticket setDeleted()', () => {
|
|||
accessToken: {userId: userId},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||
active: activeCtx
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if the given ticket has a claim', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
|
@ -29,6 +35,70 @@ describe('ticket setDeleted()', () => {
|
|||
expect(error.message).toEqual('You must delete the claim id %d first');
|
||||
});
|
||||
|
||||
it('should delete a sectorCollection row', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {
|
||||
req: {
|
||||
accessToken: {userId: 9},
|
||||
headers: {origin: 'http://localhost:5000'},
|
||||
}
|
||||
};
|
||||
ctx.req.__ = value => {
|
||||
return value;
|
||||
};
|
||||
const ticketId = 23;
|
||||
|
||||
await models.Ticket.setDeleted(ctx, ticketId, options);
|
||||
|
||||
const [sectorCollection] = await models.Ticket.rawSql(
|
||||
`SELECT COUNT(*) numberRows
|
||||
FROM vn.sectorCollection`, [], options);
|
||||
|
||||
expect(sectorCollection.numberRows).toEqual(0);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should delete a ticketCollection row', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {
|
||||
req: {
|
||||
accessToken: {userId: 9},
|
||||
headers: {origin: 'http://localhost:5000'},
|
||||
}
|
||||
};
|
||||
ctx.req.__ = value => {
|
||||
return value;
|
||||
};
|
||||
const ticketId = 23;
|
||||
|
||||
await models.Ticket.setDeleted(ctx, ticketId, options);
|
||||
|
||||
const [ticketCollection] = await models.Ticket.rawSql(
|
||||
`SELECT COUNT(*) numberRows
|
||||
FROM vn.ticketCollection`, [], options);
|
||||
|
||||
expect(ticketCollection.numberRows).toEqual(3);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should delete ticket, remove stowaway and itemshelving then change stowaway state to "FIXING" ', async() => {
|
||||
pending('test excluded by task #3693');
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
|
|
@ -44,6 +44,9 @@
|
|||
"Ticket": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"TicketCollection": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"TicketDms": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "TicketCollection",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "ticketCollection"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
"ticket": {
|
||||
"type": "belongsTo",
|
||||
"model": "Ticket",
|
||||
"foreignKey": "ticketFk"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
disabled="!$ctrl.clientId"
|
||||
url="{{ $ctrl.clientId ? 'Clients/'+ $ctrl.clientId +'/addresses' : null }}"
|
||||
fields="['nickname', 'street', 'city']"
|
||||
where="{isActive: true}"
|
||||
ng-model="$ctrl.addressId"
|
||||
show-field="nickname"
|
||||
value-field="id"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
filter="::$ctrl.filter"
|
||||
limit="20"
|
||||
data="weeklies"
|
||||
order="ticketFk"
|
||||
order="weekDay, ticketFk"
|
||||
primary-key="ticketFk"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
|
|
|
@ -40,6 +40,7 @@ module.exports = Self => {
|
|||
const models = Self.app.models;
|
||||
let tx;
|
||||
const myOptions = {};
|
||||
const date = new Date();
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
@ -57,8 +58,8 @@ module.exports = Self => {
|
|||
|
||||
await Self.rawSql(`
|
||||
INSERT INTO travelThermograph(thermographFk, warehouseFk, temperatureFk, created)
|
||||
VALUES (?, ?, ?, NOW())
|
||||
`, [thermograph.id, warehouseId, temperatureFk], myOptions);
|
||||
VALUES (?, ?, ?, ?)
|
||||
`, [thermograph.id, warehouseId, temperatureFk, date], myOptions);
|
||||
|
||||
if (tx) await tx.commit();
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('workerTimeControl filter()', () => {
|
||||
it('should return 1 result filtering by id', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 1106}}, args: {workerFk: 1106}};
|
||||
const ctx = {req: {accessToken: {userId: 1106}}, args: {workerFk: 1106}};
|
||||
const firstHour = new Date();
|
||||
firstHour.setHours(7, 0, 0, 0);
|
||||
const lastHour = new Date();
|
||||
|
@ -14,13 +14,13 @@ describe('workerTimeControl filter()', () => {
|
|||
timed: {between: [firstHour, lastHour]}
|
||||
}
|
||||
};
|
||||
let result = await app.models.WorkerTimeControl.filter(ctx, filter);
|
||||
const result = await models.WorkerTimeControl.filter(ctx, filter);
|
||||
|
||||
expect(result.length).toEqual(4);
|
||||
});
|
||||
|
||||
it('should return a privilege error for a non subordinate worker', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 1107}}, args: {workerFk: 1106}};
|
||||
const ctx = {req: {accessToken: {userId: 1107}}, args: {workerFk: 1106}};
|
||||
const firstHour = new Date();
|
||||
firstHour.setHours(7, 0, 0, 0);
|
||||
const lastHour = new Date();
|
||||
|
@ -34,7 +34,7 @@ describe('workerTimeControl filter()', () => {
|
|||
};
|
||||
|
||||
let error;
|
||||
await app.models.WorkerTimeControl.filter(ctx, filter).catch(e => {
|
||||
await models.WorkerTimeControl.filter(ctx, filter).catch(e => {
|
||||
error = e;
|
||||
}).finally(() => {
|
||||
expect(error.message).toEqual(`You don't have enough privileges`);
|
||||
|
|
|
@ -8,10 +8,10 @@ describe('workerTimeControl add/delete timeEntry()', () => {
|
|||
const employeeId = 1;
|
||||
const salesPersonId = 1106;
|
||||
const salesBossId = 19;
|
||||
let activeCtx = {
|
||||
const activeCtx = {
|
||||
accessToken: {userId: 50},
|
||||
};
|
||||
let ctx = {req: activeCtx};
|
||||
const ctx = {req: activeCtx};
|
||||
|
||||
beforeAll(() => {
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||
|
@ -82,7 +82,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
|
|||
const workerId = salesPersonId;
|
||||
let error;
|
||||
|
||||
let calendar = await app.models.Calendar.findById(3);
|
||||
const calendar = await app.models.Calendar.findById(3);
|
||||
|
||||
try {
|
||||
ctx.args = {timed: new Date(calendar.dated), direction: 'in'};
|
||||
|
|
|
@ -80,6 +80,9 @@ module.exports = Self => {
|
|||
if (hasHoursRecorded && isNotHalfAbsence)
|
||||
throw new UserError(`The worker has hours recorded that day`);
|
||||
|
||||
const date = new Date();
|
||||
const now = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
const [result] = await Self.rawSql(
|
||||
`SELECT COUNT(*) halfHolidayCounter
|
||||
FROM vn.calendar c
|
||||
|
@ -88,8 +91,8 @@ module.exports = Self => {
|
|||
JOIN vn.person pe ON pe.id = p.person_id
|
||||
WHERE c.dayOffTypeFk = 6
|
||||
AND pe.workerFk = ?
|
||||
AND c.dated BETWEEN util.firstDayOfYear(CURDATE())
|
||||
AND LAST_DAY(DATE_ADD(NOW(), INTERVAL 12-MONTH(NOW()) MONTH))`, [id]);
|
||||
AND c.dated BETWEEN util.firstDayOfYear(?)
|
||||
AND LAST_DAY(DATE_ADD(?, INTERVAL 12 - MONTH(?) MONTH))`, [id, date, now, now]);
|
||||
|
||||
const hasHalfHoliday = result.halfHolidayCounter > 0;
|
||||
const isHalfHoliday = absenceType.code === 'halfHoliday';
|
||||
|
|
|
@ -57,25 +57,9 @@ module.exports = Self => {
|
|||
ended.setDate(0);
|
||||
ended.setHours(23, 59, 59, 59);
|
||||
|
||||
const filter = {
|
||||
where: {
|
||||
and: [
|
||||
{workerFk: id},
|
||||
{
|
||||
or: [
|
||||
{started: {between: [started, ended]}},
|
||||
{ended: {between: [started, ended]}},
|
||||
{and: [{started: {lt: started}}, {ended: {gt: ended}}]},
|
||||
{and: [{started: {lt: started}}, {ended: null}]}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
}
|
||||
};
|
||||
const contracts = await models.WorkerLabour.find(filter, myOptions);
|
||||
let [firstContract] = contracts;
|
||||
const payedHolidays = firstContract.payedHolidays;
|
||||
const filter = {where: {businessFk: args.businessFk}};
|
||||
const contract = await models.WorkerLabour.findOne(filter, myOptions);
|
||||
const payedHolidays = contract.payedHolidays;
|
||||
|
||||
let queryIndex;
|
||||
const year = started.getFullYear();
|
||||
|
|
|
@ -27,4 +27,15 @@ describe('Worker holidays()', () => {
|
|||
expect(result.totalHolidays).toEqual(27.5);
|
||||
expect(result.holidaysEnjoyed).toEqual(5);
|
||||
});
|
||||
|
||||
it('should now get the payed holidays calendar for a worker', async() => {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
|
||||
ctx.args = {businessFk: businessId, year: year};
|
||||
|
||||
const result = await app.models.Worker.holidays(ctx, workerId);
|
||||
|
||||
expect(result.payedHolidays).toEqual(8);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,6 @@ describe('zone getZoneClosing()', () => {
|
|||
const options = {transaction: tx};
|
||||
const date = new Date();
|
||||
const today = date.toISOString().split('T')[0];
|
||||
|
||||
const result = await models.Zone.getZoneClosing([1, 2, 3], today, options);
|
||||
|
||||
expect(result.length).toEqual(3);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "salix-back",
|
||||
"version": "1.0.0",
|
||||
"version": "6.8.0",
|
||||
"author": "Verdnatura Levante SL",
|
||||
"description": "Salix backend",
|
||||
"license": "GPL-3.0",
|
||||
|
|
|
@ -167,7 +167,8 @@ module.exports = {
|
|||
|
||||
const body = `No se ha podido enviar el albarán <strong>${ticket.id}</strong>
|
||||
al cliente <strong>${ticket.clientFk} - ${ticket.clientName}</strong>
|
||||
porque la dirección de email <strong>"${ticket.recipient}"</strong> no es correcta o no está disponible.<br/><br/>
|
||||
porque la dirección de email <strong>"${ticket.recipient}"</strong> no es correcta
|
||||
o no está disponible.<br/><br/>
|
||||
Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente.
|
||||
Actualiza la dirección de email con una correcta.`;
|
||||
|
||||
|
|
|
@ -93,11 +93,12 @@ module.exports = async function(request, response, next) {
|
|||
await email.send(mailOptions);
|
||||
}
|
||||
// Update queue status
|
||||
const date = new Date();
|
||||
sql = `UPDATE invoiceOut_queue
|
||||
SET status = "printed",
|
||||
printed = NOW()
|
||||
printed = ?
|
||||
WHERE invoiceFk = ?`;
|
||||
connection.query(sql, [invoiceOut.id]);
|
||||
connection.query(sql, [date, invoiceOut.id]);
|
||||
connection.query('COMMIT');
|
||||
} catch (error) {
|
||||
connection.query('ROLLBACK');
|
||||
|
|
Loading…
Reference in New Issue