Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 3893-supplier.fiscal-data
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
10cd9c5b87
|
@ -62,13 +62,13 @@ pipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// stage('Backend') {
|
stage('Backend') {
|
||||||
// steps {
|
steps {
|
||||||
// nodejs('node-v14') {
|
nodejs('node-v14') {
|
||||||
// sh 'gulp launchBackTest --ci'
|
sh 'npm run test:back:ci'
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Build') {
|
stage('Build') {
|
||||||
|
|
|
@ -54,17 +54,17 @@ $ gulp docker
|
||||||
|
|
||||||
For client-side unit tests run from project's root.
|
For client-side unit tests run from project's root.
|
||||||
```
|
```
|
||||||
$ jest
|
$ npm run test:front
|
||||||
```
|
```
|
||||||
|
|
||||||
For server-side unit tests run from project's root.
|
For server-side unit tests run from project's root.
|
||||||
```
|
```
|
||||||
$ gulp backTest
|
$ npm run test:back
|
||||||
```
|
```
|
||||||
|
|
||||||
For end-to-end tests run from project's root.
|
For end-to-end tests run from project's root.
|
||||||
```
|
```
|
||||||
$ gulp e2e
|
$ npm run test:e2e
|
||||||
```
|
```
|
||||||
|
|
||||||
## Visual Studio Code extensions
|
## Visual Studio Code extensions
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const {models} = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('account changePassword()', () => {
|
describe('account changePassword()', () => {
|
||||||
it('should throw an error when old password is wrong', async() => {
|
it('should throw an error when old password is wrong', async() => {
|
||||||
let req = app.models.Account.changePassword(null, 1, 'wrongOldPass', 'newPass');
|
let err;
|
||||||
|
await models.Account.changePassword(1, 'wrongPassword', 'nightmare.9999')
|
||||||
|
.catch(error => err = error.sqlMessage);
|
||||||
|
|
||||||
await expectAsync(req).toBeRejected();
|
expect(err).toBeDefined();
|
||||||
|
expect(err).toEqual('Invalid password');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,7 +46,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
const {data} = await Self.getUserStatus(recipient.name);
|
const {data} = await Self.getUserStatus(recipient.name);
|
||||||
if (data) {
|
if (data) {
|
||||||
if (data.status === 'offline') {
|
if (data.status === 'offline' || data.status === 'busy') {
|
||||||
// Send message to department room
|
// Send message to department room
|
||||||
const workerDepartment = await models.WorkerDepartment.findById(recipientId, {
|
const workerDepartment = await models.WorkerDepartment.findById(recipientId, {
|
||||||
include: {
|
include: {
|
||||||
|
@ -58,6 +58,8 @@ module.exports = Self => {
|
||||||
|
|
||||||
if (channelName)
|
if (channelName)
|
||||||
return Self.send(ctx, `#${channelName}`, `@${recipient.name} ➔ ${message}`);
|
return Self.send(ctx, `#${channelName}`, `@${recipient.name} ➔ ${message}`);
|
||||||
|
else
|
||||||
|
return Self.send(ctx, `@${recipient.name}`, message);
|
||||||
} else
|
} else
|
||||||
return Self.send(ctx, `@${recipient.name}`, message);
|
return Self.send(ctx, `@${recipient.name}`, message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethod('collectionFaults', {
|
|
||||||
description: 'Update sale of a collection',
|
|
||||||
accessType: 'WRITE',
|
|
||||||
accepts: [{
|
|
||||||
arg: 'shelvingFk',
|
|
||||||
type: 'String',
|
|
||||||
required: true,
|
|
||||||
description: 'The shalving id'
|
|
||||||
}, {
|
|
||||||
arg: 'quantity',
|
|
||||||
type: 'Number',
|
|
||||||
required: true,
|
|
||||||
description: 'The quantity to sale'
|
|
||||||
}, {
|
|
||||||
arg: 'itemFk',
|
|
||||||
type: 'Number',
|
|
||||||
required: true,
|
|
||||||
description: 'The ticket id'
|
|
||||||
}],
|
|
||||||
returns: {
|
|
||||||
type: 'Object',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
path: `/collectionFaults`,
|
|
||||||
verb: 'POST'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.collectionFaults = async(shelvingFk, quantity, itemFk) => {
|
|
||||||
query = `CALL vn.collection_faults(?,?,?)`;
|
|
||||||
return await Self.rawSql(query, [shelvingFk, quantity, itemFk]);
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -28,7 +28,7 @@ module.exports = Self => {
|
||||||
const args = ctx.args;
|
const args = ctx.args;
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
||||||
const sale = await models.Sale.findById(args.saleId,);
|
const sale = await models.Sale.findById(args.saleId);
|
||||||
return await sale.updateAttribute('quantity', args.quantity);
|
return await sale.updateAttribute('quantity', args.quantity);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
const app = require('vn-loopback/server/server');
|
|
||||||
describe('collectionFaults()', () => {
|
|
||||||
it('return shelving afected', async() => {
|
|
||||||
let response = await app.models.Collection.collectionFaults('UXN', 0, 1);
|
|
||||||
|
|
||||||
expect(response.length).toBeGreaterThan(0);
|
|
||||||
expect(response[0][0].shelvingFk).toEqual('UXN');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,8 +1,8 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
// #3400 analizar que hacer con rutas de back colletion
|
describe('newCollection()', () => {
|
||||||
xdescribe('newCollection()', () => {
|
it('should return a new collection', async() => {
|
||||||
it('return a new collection', async() => {
|
pending('#3400 analizar que hacer con rutas de back collection');
|
||||||
let ctx = {req: {accessToken: {userId: 1106}}};
|
let ctx = {req: {accessToken: {userId: 1106}}};
|
||||||
let response = await app.models.Collection.newCollection(ctx, 1, 1, 1);
|
let response = await app.models.Collection.newCollection(ctx, 1, 1, 1);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('deleteTrashFiles', {
|
||||||
|
description: 'Deletes files that have trash type',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/deleteTrashFiles`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deleteTrashFiles = async(options) => {
|
||||||
|
const tx = await Self.beginTransaction({});
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction)
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const DmsContainer = models.DmsContainer;
|
||||||
|
|
||||||
|
const trashDmsType = await models.DmsType.findOne({
|
||||||
|
where: {code: 'trash'}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const dmsToDelete = await models.Dms.find({
|
||||||
|
where: {
|
||||||
|
dmsTypeFk: trashDmsType.id
|
||||||
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
for (let dms of dmsToDelete) {
|
||||||
|
const pathHash = DmsContainer.getHash(dms.id);
|
||||||
|
const dmsContainer = await DmsContainer.container(pathHash);
|
||||||
|
const dstFile = path.join(dmsContainer.client.root, pathHash, dms.file);
|
||||||
|
await fs.unlink(dstFile);
|
||||||
|
await dms.destroy(myOptions);
|
||||||
|
}
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -12,9 +12,9 @@ describe('docuware download()', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fileCabinetName = 'deliveryClientTest';
|
const fileCabinetName = 'deliveryClient';
|
||||||
const dialogDisplayName = 'find';
|
const dialogDisplayName = 'find';
|
||||||
const dialogName = 'findTest';
|
const dialogName = 'findTicket';
|
||||||
|
|
||||||
const gotGetResponse = {
|
const gotGetResponse = {
|
||||||
body: JSON.stringify(
|
body: JSON.stringify(
|
||||||
|
|
|
@ -14,9 +14,9 @@ describe('docuware download()', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should return the downloaded file name', async() => {
|
it('should return the downloaded file name', async() => {
|
||||||
const fileCabinetName = 'deliveryClientTest';
|
const fileCabinetName = 'deliveryClient';
|
||||||
const dialogDisplayName = 'find';
|
const dialogDisplayName = 'find';
|
||||||
const dialogName = 'findTest';
|
const dialogName = 'findTicket';
|
||||||
const gotGetResponse = {
|
const gotGetResponse = {
|
||||||
body: JSON.stringify(
|
body: JSON.stringify(
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,5 +3,4 @@ module.exports = Self => {
|
||||||
require('../methods/collection/newCollection')(Self);
|
require('../methods/collection/newCollection')(Self);
|
||||||
require('../methods/collection/getSectors')(Self);
|
require('../methods/collection/getSectors')(Self);
|
||||||
require('../methods/collection/setSaleQuantity')(Self);
|
require('../methods/collection/setSaleQuantity')(Self);
|
||||||
require('../methods/collection/collectionFaults')(Self);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@ module.exports = Self => {
|
||||||
require('../methods/dms/uploadFile')(Self);
|
require('../methods/dms/uploadFile')(Self);
|
||||||
require('../methods/dms/removeFile')(Self);
|
require('../methods/dms/removeFile')(Self);
|
||||||
require('../methods/dms/updateFile')(Self);
|
require('../methods/dms/updateFile')(Self);
|
||||||
|
require('../methods/dms/deleteTrashFiles')(Self);
|
||||||
|
|
||||||
Self.checkRole = async function(ctx, id) {
|
Self.checkRole = async function(ctx, id) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"verbose": true,
|
||||||
|
"watch": [
|
||||||
|
"back/**/*.js",
|
||||||
|
"modules/**/*.js"
|
||||||
|
],
|
||||||
|
"ignore": [
|
||||||
|
"modules/account/front/**/*",
|
||||||
|
"modules/claim/front/**/*",
|
||||||
|
"modules/client/front/**/*",
|
||||||
|
"modules/entry/front/**/*",
|
||||||
|
"modules/invoiceIn/front/**/*",
|
||||||
|
"modules/invoiceOut/front/**/*",
|
||||||
|
"modules/item/front/**/*",
|
||||||
|
"modules/monitor/front/**/*",
|
||||||
|
"modules/order/front/**/*",
|
||||||
|
"modules/route/front/**/*",
|
||||||
|
"modules/supplier/front/**/*",
|
||||||
|
"modules/ticket/front/**/*",
|
||||||
|
"modules/travel/front/**/*",
|
||||||
|
"modules/worker/front/**/*",
|
||||||
|
"modules/zone/front/**/*"
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
require('require-yaml');
|
const Docker = require('../db/docker.js');
|
||||||
|
let dataSources = require('../loopback/server/datasources.json');
|
||||||
|
|
||||||
process.on('warning', warning => {
|
process.on('warning', warning => {
|
||||||
console.log(warning.name);
|
console.log(warning.name);
|
||||||
|
@ -6,34 +7,64 @@ process.on('warning', warning => {
|
||||||
console.log(warning.stack);
|
console.log(warning.stack);
|
||||||
});
|
});
|
||||||
|
|
||||||
let verbose = false;
|
async function test() {
|
||||||
|
let isCI = false;
|
||||||
|
|
||||||
if (process.argv[2] === '--v')
|
if (process.argv[2] === 'ci')
|
||||||
verbose = true;
|
isCI = true;
|
||||||
|
|
||||||
let Jasmine = require('jasmine');
|
const container = new Docker();
|
||||||
let jasmine = new Jasmine();
|
|
||||||
let SpecReporter = require('jasmine-spec-reporter').SpecReporter;
|
|
||||||
|
|
||||||
let serviceSpecs = [
|
await container.run(isCI);
|
||||||
`${__dirname}/**/*[sS]pec.js`,
|
dataSources = JSON.parse(JSON.stringify(dataSources));
|
||||||
`${__dirname}/../loopback/**/*[sS]pec.js`,
|
|
||||||
`${__dirname}/../modules/*/back/**/*.[sS]pec.js`
|
Object.assign(dataSources.vn, {
|
||||||
|
host: container.dbConf.host,
|
||||||
|
port: container.dbConf.port
|
||||||
|
});
|
||||||
|
|
||||||
|
const bootOptions = {dataSources};
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
app.boot(bootOptions);
|
||||||
|
|
||||||
|
const Jasmine = require('jasmine');
|
||||||
|
const jasmine = new Jasmine();
|
||||||
|
|
||||||
|
const SpecReporter = require('jasmine-spec-reporter').SpecReporter;
|
||||||
|
jasmine.addReporter(new SpecReporter({
|
||||||
|
spec: {
|
||||||
|
displaySuccessful: isCI,
|
||||||
|
displayPending: isCI
|
||||||
|
},
|
||||||
|
summary: {
|
||||||
|
displayPending: false,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (isCI) {
|
||||||
|
const JunitReporter = require('jasmine-reporters');
|
||||||
|
jasmine.addReporter(new JunitReporter.JUnitXmlReporter());
|
||||||
|
|
||||||
|
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backSpecs = [
|
||||||
|
'./back/**/*[sS]pec.js',
|
||||||
|
'./loopback/**/*[sS]pec.js',
|
||||||
|
'./modules/*/back/**/*.[sS]pec.js'
|
||||||
];
|
];
|
||||||
|
|
||||||
jasmine.loadConfig({
|
jasmine.loadConfig({
|
||||||
spec_dir: '.',
|
spec_dir: '.',
|
||||||
spec_files: serviceSpecs,
|
spec_files: backSpecs,
|
||||||
helpers: []
|
helpers: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
jasmine.addReporter(new SpecReporter({
|
jasmine.exitOnCompletion = false;
|
||||||
spec: {
|
await jasmine.execute();
|
||||||
// displayStacktrace: 'summary',
|
if (app) await app.disconnect();
|
||||||
displaySuccessful: verbose,
|
if (container) await container.rm();
|
||||||
displayFailedSpec: true,
|
console.log('app disconnected & container removed');
|
||||||
displaySpecDuration: true
|
|
||||||
}
|
}
|
||||||
}));
|
|
||||||
|
|
||||||
jasmine.execute();
|
test();
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES('ExpeditionState', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
|
|
@ -0,0 +1,5 @@
|
||||||
|
INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES('Expense', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
|
||||||
|
|
||||||
|
INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES('Expense', '*', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
|
|
@ -0,0 +1,14 @@
|
||||||
|
create table `vn`.`invoiceOut_queue`
|
||||||
|
(
|
||||||
|
invoiceFk int(10) unsigned not null,
|
||||||
|
queued datetime default now() not null,
|
||||||
|
printed datetime null,
|
||||||
|
`status` VARCHAR(50) default '' null,
|
||||||
|
constraint invoiceOut_queue_pk
|
||||||
|
primary key (invoiceFk),
|
||||||
|
constraint invoiceOut_queue_invoiceOut_id_fk
|
||||||
|
foreign key (invoiceFk) references invoiceOut (id)
|
||||||
|
on update cascade on delete cascade
|
||||||
|
)
|
||||||
|
comment 'Queue for PDF invoicing';
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
ALTER TABLE vn.propertyDms DROP FOREIGN KEY propertyDms_FK;
|
||||||
|
ALTER TABLE vn.propertyDms ADD CONSTRAINT propertyDms_FK FOREIGN KEY (dmsFk) REFERENCES vn.dms(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE vn.clientDms DROP FOREIGN KEY clientDms_ibfk_2;
|
||||||
|
ALTER TABLE vn.clientDms ADD CONSTRAINT clientDms_ibfk_2 FOREIGN KEY (dmsFk) REFERENCES vn.dms(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE vn.workerDocument DROP FOREIGN KEY workerDocument_ibfk_2;
|
||||||
|
ALTER TABLE vn.workerDocument ADD CONSTRAINT workerDocument_ibfk_2 FOREIGN KEY (document) REFERENCES vn.dms(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -0,0 +1,5 @@
|
||||||
|
ALTER TABLE vn.dmsType ADD monthToDelete INT UNSIGNED DEFAULT NULL NULL;
|
||||||
|
ALTER TABLE vn.dmsType MODIFY COLUMN monthToDelete int(10) unsigned DEFAULT NULL NULL COMMENT 'Meses en el pasado para ir borrando registros, dejar a null para no borrarlos nunca';
|
||||||
|
UPDATE vn.dmsType
|
||||||
|
SET monthToDelete=6
|
||||||
|
WHERE id=20;
|
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE TRIGGER dms_beforeDelete
|
||||||
|
BEFORE DELETE
|
||||||
|
ON dms FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
DECLARE vCanNotBeDeleted INT;
|
||||||
|
SELECT COUNT(*) INTO vCanNotBeDeleted
|
||||||
|
FROM dmsType dt
|
||||||
|
WHERE NOT (code <=> 'trash')
|
||||||
|
AND dt.id = OLD.dmsTypeFk;
|
||||||
|
|
||||||
|
IF vCanNotBeDeleted THEN
|
||||||
|
CALL util.throw('A dms can not be deleted');
|
||||||
|
END IF;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,175 @@
|
||||||
|
DROP PROCEDURE IF EXISTS vn.clean;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`clean`()
|
||||||
|
BEGIN
|
||||||
|
DECLARE vDateShort DATETIME;
|
||||||
|
DECLARE vOneYearAgo DATE;
|
||||||
|
DECLARE vFourYearsAgo DATE;
|
||||||
|
DECLARE v18Month DATE;
|
||||||
|
DECLARE v26Month DATE;
|
||||||
|
DECLARE v3Month DATE;
|
||||||
|
DECLARE vTrashId varchar(15);
|
||||||
|
|
||||||
|
SET vDateShort = TIMESTAMPADD(MONTH, -2, CURDATE());
|
||||||
|
SET vOneYearAgo = TIMESTAMPADD(YEAR,-1,CURDATE());
|
||||||
|
SET vFourYearsAgo = TIMESTAMPADD(YEAR,-4,CURDATE());
|
||||||
|
SET v18Month = TIMESTAMPADD(MONTH, -18,CURDATE());
|
||||||
|
SET v26Month = TIMESTAMPADD(MONTH, -26,CURDATE());
|
||||||
|
SET v3Month = TIMESTAMPADD(MONTH, -3, CURDATE());
|
||||||
|
|
||||||
|
DELETE FROM ticketParking WHERE created < vDateShort;
|
||||||
|
DELETE FROM routesMonitor WHERE dated < vDateShort;
|
||||||
|
DELETE FROM workerTimeControlLog WHERE created < vDateShort;
|
||||||
|
DELETE FROM `message` WHERE sendDate < vDateShort;
|
||||||
|
DELETE FROM messageInbox WHERE sendDate < vDateShort;
|
||||||
|
DELETE FROM messageInbox WHERE sendDate < vDateShort;
|
||||||
|
DELETE FROM workerTimeControl WHERE timed < vFourYearsAgo;
|
||||||
|
DELETE FROM itemShelving WHERE created < CURDATE() AND visible = 0;
|
||||||
|
DELETE FROM ticketDown WHERE created < TIMESTAMPADD(DAY,-1,CURDATE());
|
||||||
|
DELETE FROM entryLog WHERE creationDate < vDateShort;
|
||||||
|
DELETE IGNORE FROM expedition WHERE created < v26Month;
|
||||||
|
DELETE FROM sms WHERE created < v18Month;
|
||||||
|
DELETE FROM saleTracking WHERE created < vOneYearAgo;
|
||||||
|
DELETE tobs FROM ticketObservation tobs
|
||||||
|
JOIN ticket t ON tobs.ticketFk = t.id WHERE t.shipped < TIMESTAMPADD(YEAR,-2,CURDATE());
|
||||||
|
DELETE sc.* FROM saleCloned sc JOIN sale s ON s.id = sc.saleClonedFk JOIN ticket t ON t.id = s.ticketFk WHERE t.shipped < vOneYearAgo;
|
||||||
|
DELETE FROM sharingCart where ended < vDateShort;
|
||||||
|
DELETE FROM sharingClient where ended < vDateShort;
|
||||||
|
DELETE tw.* FROM ticketWeekly tw
|
||||||
|
LEFT JOIN sale s ON s.ticketFk = tw.ticketFk WHERE s.itemFk IS NULL;
|
||||||
|
DELETE FROM claim WHERE ticketCreated < vFourYearsAgo;
|
||||||
|
DELETE FROM message WHERE sendDate < vDateShort;
|
||||||
|
-- Robert ubicacion anterior de trevelLog comentario para debug
|
||||||
|
DELETE sc FROM saleChecked sc
|
||||||
|
JOIN sale s ON sc.saleFk = s.id WHERE s.created < vDateShort;
|
||||||
|
DELETE FROM zoneEvent WHERE `type` = 'day' AND dated < v3Month;
|
||||||
|
DELETE bm
|
||||||
|
FROM buyMark bm
|
||||||
|
JOIN buy b ON b.id = bm.id
|
||||||
|
JOIN entry e ON e.id = b.entryFk
|
||||||
|
JOIN travel t ON t.id = e.travelFk
|
||||||
|
WHERE t.landed <= vDateShort;
|
||||||
|
DELETE FROM stowaway WHERE created < v3Month;
|
||||||
|
DELETE FROM vn.buy WHERE created < vDateShort AND entryFk = 9200;
|
||||||
|
DELETE FROM vn.itemShelvingLog WHERE created < vDateShort;
|
||||||
|
DELETE FROM vn.stockBuyed WHERE creationDate < vDateShort;
|
||||||
|
|
||||||
|
|
||||||
|
-- Equipos duplicados
|
||||||
|
DELETE w.*
|
||||||
|
FROM workerTeam w
|
||||||
|
JOIN (SELECT id, team, workerFk, COUNT(*) - 1 as duplicated
|
||||||
|
FROM workerTeam
|
||||||
|
GROUP BY team,workerFk
|
||||||
|
HAVING duplicated
|
||||||
|
) d ON d.team = w.team AND d.workerFk = w.workerFk AND d.id != w.id;
|
||||||
|
|
||||||
|
DELETE sc
|
||||||
|
FROM saleComponent sc
|
||||||
|
JOIN sale s ON s.id= sc.saleFk
|
||||||
|
JOIN ticket t ON t.id= s.ticketFk
|
||||||
|
WHERE t.shipped < v18Month;
|
||||||
|
|
||||||
|
DELETE c
|
||||||
|
FROM vn.claim c
|
||||||
|
JOIN vn.claimState cs ON cs.id = c.claimStateFk
|
||||||
|
WHERE cs.description = "Anulado" AND
|
||||||
|
c.created < vDateShort;
|
||||||
|
DELETE
|
||||||
|
FROM vn.expeditionTruck
|
||||||
|
WHERE ETD < v3Month;
|
||||||
|
|
||||||
|
-- borrar travels sin entradas
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.thermographToDelete;
|
||||||
|
CREATE TEMPORARY TABLE tmp.thermographToDelete
|
||||||
|
SELECT th.id,th.dmsFk
|
||||||
|
FROM vn.travel t
|
||||||
|
LEFT JOIN vn.entry e ON e.travelFk = t.id
|
||||||
|
JOIN vn.travelThermograph th ON th.travelFk = t.id
|
||||||
|
WHERE t.shipped < TIMESTAMPADD(MONTH, -3, CURDATE()) AND e.travelFk IS NULL;
|
||||||
|
|
||||||
|
SELECT dt.id into vTrashId
|
||||||
|
FROM vn.dmsType dt
|
||||||
|
WHERE dt.code = 'trash';
|
||||||
|
|
||||||
|
UPDATE tmp.thermographToDelete th
|
||||||
|
JOIN vn.dms d ON d.id = th.dmsFk
|
||||||
|
SET d.dmsTypeFk = vTrashId;
|
||||||
|
|
||||||
|
DELETE th
|
||||||
|
FROM tmp.thermographToDelete tmp
|
||||||
|
JOIN vn.travelThermograph th ON th.id = tmp.id;
|
||||||
|
|
||||||
|
DELETE t
|
||||||
|
FROM vn.travel t
|
||||||
|
LEFT JOIN vn.entry e ON e.travelFk = t.id
|
||||||
|
WHERE t.shipped < TIMESTAMPADD(MONTH, -3, CURDATE()) AND e.travelFk IS NULL;
|
||||||
|
|
||||||
|
UPDATE dms d
|
||||||
|
JOIN dmsType dt ON dt.id = d.dmsTypeFk
|
||||||
|
SET d.dmsTypeFk = vTrashId
|
||||||
|
WHERE created < TIMESTAMPADD(MONTH, -dt.monthToDelete, CURDATE());
|
||||||
|
|
||||||
|
-- borrar entradas sin compras
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.entryToDelete;
|
||||||
|
CREATE TEMPORARY TABLE tmp.entryToDelete
|
||||||
|
SELECT e.*
|
||||||
|
FROM vn.entry e
|
||||||
|
LEFT JOIN vn.buy b ON b.entryFk = e.id
|
||||||
|
JOIN vn.entryConfig ec ON e.id != ec.defaultEntry
|
||||||
|
WHERE e.dated < TIMESTAMPADD(MONTH, -3, CURDATE()) AND b.entryFK IS NULL;
|
||||||
|
|
||||||
|
DELETE e
|
||||||
|
FROM vn.entry e
|
||||||
|
JOIN tmp.entryToDelete tmp ON tmp.id = e.id;
|
||||||
|
|
||||||
|
-- borrar de route registros menores a 4 años
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.routeToDelete;
|
||||||
|
CREATE TEMPORARY TABLE tmp.routeToDelete
|
||||||
|
SELECT *
|
||||||
|
FROM vn.route r
|
||||||
|
WHERE created < TIMESTAMPADD(YEAR,-4,CURDATE());
|
||||||
|
|
||||||
|
UPDATE tmp.routeToDelete tmp
|
||||||
|
JOIN vn.dms d ON d.id = tmp.gestdocFk
|
||||||
|
SET d.dmsTypeFk = vTrashId;
|
||||||
|
|
||||||
|
DELETE r
|
||||||
|
FROM tmp.routeToDelete tmp
|
||||||
|
JOIN vn.route r ON r.id = tmp.id;
|
||||||
|
|
||||||
|
-- borrar registros de dua y awb menores a 2 años
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.duaToDelete;
|
||||||
|
CREATE TEMPORARY TABLE tmp.duaToDelete
|
||||||
|
SELECT *
|
||||||
|
FROM vn.dua
|
||||||
|
WHERE operated < TIMESTAMPADD(YEAR,-2,CURDATE());
|
||||||
|
|
||||||
|
UPDATE tmp.duaToDelete tm
|
||||||
|
JOIN vn.dms d ON d.id = tm.gestdocFk
|
||||||
|
SET d.dmsTypeFk = vTrashId;
|
||||||
|
|
||||||
|
DELETE d
|
||||||
|
FROM tmp.duaToDelete tmp
|
||||||
|
JOIN vn.dua d ON d.id = tmp.id;
|
||||||
|
|
||||||
|
DELETE FROM vn.awb WHERE created < TIMESTAMPADD(YEAR,-2,CURDATE());
|
||||||
|
|
||||||
|
-- Borra los ficheros gestDoc
|
||||||
|
INSERT INTO vn.printServerQueue(priorityFk, labelReportFk)VALUES(1,11);
|
||||||
|
|
||||||
|
-- Borra los registros de collection y ticketcollection
|
||||||
|
DELETE FROM vn.collection WHERE created < vDateShort;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.thermographToDelete;
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.entryToDelete;
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.duaToDelete;
|
||||||
|
|
||||||
|
DELETE FROM travelLog WHERE creationDate < v3Month;
|
||||||
|
|
||||||
|
CALL shelving_clean;
|
||||||
|
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,2 @@
|
||||||
|
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
|
||||||
|
VALUES ('Dms','deleteTrashFiles','WRITE','ALLOW','ROLE','employee')
|
|
@ -0,0 +1 @@
|
||||||
|
Delete file
|
|
@ -24,7 +24,10 @@ module.exports = class Docker {
|
||||||
let d = new Date();
|
let d = new Date();
|
||||||
let pad = v => v < 10 ? '0' + v : v;
|
let pad = v => v < 10 ? '0' + v : v;
|
||||||
let stamp = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
let stamp = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
||||||
|
|
||||||
|
log('Building container image...');
|
||||||
await this.execP(`docker build --build-arg STAMP=${stamp} -t salix-db ./db`);
|
await this.execP(`docker build --build-arg STAMP=${stamp} -t salix-db ./db`);
|
||||||
|
log('Image built.');
|
||||||
|
|
||||||
let dockerArgs;
|
let dockerArgs;
|
||||||
|
|
||||||
|
@ -39,6 +42,7 @@ module.exports = class Docker {
|
||||||
|
|
||||||
let runChown = process.platform != 'linux';
|
let runChown = process.platform != 'linux';
|
||||||
|
|
||||||
|
log('Starting container...');
|
||||||
const container = await this.execP(`docker run --env RUN_CHOWN=${runChown} -d ${dockerArgs} salix-db`);
|
const container = await this.execP(`docker run --env RUN_CHOWN=${runChown} -d ${dockerArgs} salix-db`);
|
||||||
this.id = container.stdout.trim();
|
this.id = container.stdout.trim();
|
||||||
|
|
||||||
|
@ -158,6 +162,7 @@ module.exports = class Docker {
|
||||||
return reject(new Error('Docker exited, please see the docker logs for more info'));
|
return reject(new Error('Docker exited, please see the docker logs for more info'));
|
||||||
|
|
||||||
let conn = mysql.createConnection(myConf);
|
let conn = mysql.createConnection(myConf);
|
||||||
|
|
||||||
conn.on('error', () => {});
|
conn.on('error', () => {});
|
||||||
conn.connect(err => {
|
conn.connect(err => {
|
||||||
conn.destroy();
|
conn.destroy();
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -56,27 +56,6 @@ INSERT INTO `vn`.`worker`(`id`,`code`, `firstName`, `lastName`, `userFk`, `bossF
|
||||||
SELECT id,UPPER(LPAD(role, 3, '0')), name, name, id, 9
|
SELECT id,UPPER(LPAD(role, 3, '0')), name, name, id, 9
|
||||||
FROM `vn`.`user`;
|
FROM `vn`.`user`;
|
||||||
|
|
||||||
ALTER TABLE `vn`.`worker` ADD `originCountryFk` mediumint(8) unsigned NULL COMMENT 'País de origen';
|
|
||||||
ALTER TABLE `vn`.`worker` ADD `educationLevelFk` SMALLINT NULL;
|
|
||||||
ALTER TABLE `vn`.`worker` ADD `SSN` varchar(15) NULL;
|
|
||||||
ALTER TABLE `vn`.`worker` CHANGE `maritalStatus__` `maritalStatus` enum('S','M') CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL;
|
|
||||||
ALTER TABLE `vn`.`worker` MODIFY COLUMN `maritalStatus` enum('S','M') CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL;
|
|
||||||
ALTER TABLE `vn`.`worker` CHANGE `maritalStatus` maritalStatus enum('S','M') CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL AFTER sectorFk;
|
|
||||||
ALTER TABLE `vn`.`worker` ADD CONSTRAINT `worker_FK_2` FOREIGN KEY (`educationLevelFk`) REFERENCES `vn`.`educationLevel`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE `vn`.`worker` ADD CONSTRAINT `worker_FK_1` FOREIGN KEY (`originCountryFk`) REFERENCES `vn`.`country`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
UPDATE `vn`.`worker` `w`
|
|
||||||
SET `maritalStatus` = 'S';
|
|
||||||
|
|
||||||
UPDATE `vn`.`worker` `w`
|
|
||||||
SET `originCountryFk` = '1';
|
|
||||||
|
|
||||||
UPDATE `vn`.`worker` `w`
|
|
||||||
SET `educationLevelFk` = '2';
|
|
||||||
|
|
||||||
UPDATE `vn`.`worker` `w`
|
|
||||||
SET `SSN` = '123456789123';
|
|
||||||
|
|
||||||
UPDATE `vn`.`worker` SET bossFk = NULL WHERE id = 20;
|
UPDATE `vn`.`worker` SET bossFk = NULL WHERE id = 20;
|
||||||
UPDATE `vn`.`worker` SET bossFk = 20 WHERE id = 1 OR id = 9;
|
UPDATE `vn`.`worker` SET bossFk = 20 WHERE id = 1 OR id = 9;
|
||||||
UPDATE `vn`.`worker` SET bossFk = 19 WHERE id = 18;
|
UPDATE `vn`.`worker` SET bossFk = 19 WHERE id = 18;
|
||||||
|
@ -147,15 +126,6 @@ INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`,
|
||||||
(19,'Francia', 1, 'FR', 1, 27, 4, 0, 1),
|
(19,'Francia', 1, 'FR', 1, 27, 4, 0, 1),
|
||||||
(30,'Canarias', 1, 'IC', 1, 24, 4, 1, 2);
|
(30,'Canarias', 1, 'IC', 1, 24, 4, 1, 2);
|
||||||
|
|
||||||
INSERT INTO `hedera`.`language` (`code`, `name`, `orgName`, `isActive`)
|
|
||||||
VALUES
|
|
||||||
('ca', 'Català' , 'Catalan' , TRUE),
|
|
||||||
('en', 'English' , 'English' , TRUE),
|
|
||||||
('es', 'Español' , 'Spanish' , TRUE),
|
|
||||||
('fr', 'Français' , 'French' , TRUE),
|
|
||||||
('mn', 'Португалий', 'Mongolian' , TRUE),
|
|
||||||
('pt', 'Português' , 'Portuguese', TRUE);
|
|
||||||
|
|
||||||
INSERT INTO `vn`.`warehouseAlias`(`id`, `name`)
|
INSERT INTO `vn`.`warehouseAlias`(`id`, `name`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Main Warehouse'),
|
(1, 'Main Warehouse'),
|
||||||
|
@ -182,8 +152,9 @@ INSERT INTO `vn`.`parking` (`id`, `column`, `row`, `sectorFk`, `code`, `pickingO
|
||||||
|
|
||||||
INSERT INTO `vn`.`shelving` (`code`, `parkingFk`, `isPrinted`, `priority`, `parked`, `userFk`)
|
INSERT INTO `vn`.`shelving` (`code`, `parkingFk`, `isPrinted`, `priority`, `parked`, `userFk`)
|
||||||
VALUES
|
VALUES
|
||||||
('GVC', '1', '0', '1', '0', '1106'),
|
('GVC', 1, 0, 1, 0, 1106),
|
||||||
('HEJ', '2', '0', '1', '0', '1106');
|
('HEJ', 2, 0, 1, 0, 1106),
|
||||||
|
('UXN', 1, 0, 1, 0, 1106);
|
||||||
|
|
||||||
INSERT INTO `vn`.`accountingType`(`id`, `description`, `receiptDescription`,`code`, `maxAmount`)
|
INSERT INTO `vn`.`accountingType`(`id`, `description`, `receiptDescription`,`code`, `maxAmount`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -883,18 +854,35 @@ INSERT INTO `vn`.`packaging`(`id`, `volume`, `width`, `height`, `depth`, `isPack
|
||||||
('cc', 1640038.00, 56.00, 220.00, 128.00, 1, CURDATE(), 15, 90.00),
|
('cc', 1640038.00, 56.00, 220.00, 128.00, 1, CURDATE(), 15, 90.00),
|
||||||
('pallet 100', 2745600.00, 100.00, 220.00, 120.00, 1, CURDATE(), 16, 0.00);
|
('pallet 100', 2745600.00, 100.00, 220.00, 120.00, 1, CURDATE(), 16, 0.00);
|
||||||
|
|
||||||
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `isBox`, `created`, `itemFk`, `counter`, `checked`, `workerFk`, `externalId`, `packagingFk`)
|
INSERT INTO `vn`.`expeditionStateType`(`id`, `description`, `code`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 15, 1, 1, 18, 'UR9000006041', 94),
|
(1, 'En reparto', 'ON DELIVERY'),
|
||||||
(2, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 16, 2, 1, 18, 'UR9000006041', 94),
|
(2, 'Entregada', 'DELIVERED'),
|
||||||
(3, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 3, 1, 18, 'UR9000006041', 94),
|
(3, 'Perdida', 'LOST');
|
||||||
(4, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 4, 1, 18, 'UR9000006041', 94),
|
|
||||||
(5, 1, 2, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94),
|
|
||||||
(6, 7, 3, 71, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), NULL, 1, 1, 18, NULL, 94),
|
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `isBox`, `created`, `itemFk`, `counter`, `checked`, `workerFk`, `externalId`, `packagingFk`, `stateTypeFk`)
|
||||||
(7, 2, 4, 71, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), NULL, 1, 1, 18, NULL, 94),
|
VALUES
|
||||||
(8, 3, 5, 71, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), NULL, 1, 1, 18, NULL, 94),
|
(1, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 15, 1, 1, 18, 'UR9000006041', 94, 1),
|
||||||
(9, 3, 6, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94),
|
(2, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 16, 2, 1, 18, 'UR9000006041', 94, 1),
|
||||||
(10, 7, 7, 71, NOW(), NULL, 1, 1, 18, NULL, 94);
|
(3, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 3, 1, 18, 'UR9000006041', 94, 2),
|
||||||
|
(4, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 4, 1, 18, 'UR9000006041', 94, 2),
|
||||||
|
(5, 1, 2, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94, 3),
|
||||||
|
(6, 7, 3, 71, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), NULL, 1, 1, 18, NULL, 94, 3),
|
||||||
|
(7, 2, 4, 71, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), NULL, 1, 1, 18, NULL, 94, NULL),
|
||||||
|
(8, 3, 5, 71, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), NULL, 1, 1, 18, NULL, 94, 1),
|
||||||
|
(9, 3, 6, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94, 2),
|
||||||
|
(10, 7, 7, 71, NOW(), NULL, 1, 1, 18, NULL, 94, 3);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`expeditionState`(`id`, `created`, `expeditionFk`, `typeFk`, `userFk`)
|
||||||
|
VALUES
|
||||||
|
(1, CURDATE(), 1, 1, 1),
|
||||||
|
(2, CURDATE(), 2, 1, 1),
|
||||||
|
(3, CURDATE(), 3, 1, 1),
|
||||||
|
(4, CURDATE(), 3, 2, 1106),
|
||||||
|
(5, CURDATE(), 5, 1, 1106),
|
||||||
|
(6, CURDATE(), 5, 3, 1106);
|
||||||
|
|
||||||
INSERT INTO `vn`.`ticketPackaging`(`id`, `ticketFk`, `packagingFk`, `quantity`, `created`, `pvp`)
|
INSERT INTO `vn`.`ticketPackaging`(`id`, `ticketFk`, `packagingFk`, `quantity`, `created`, `pvp`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -1078,10 +1066,11 @@ INSERT INTO `vn`.`saleComponent`(`saleFk`, `componentFk`, `value`)
|
||||||
(32, 36, -92.324),
|
(32, 36, -92.324),
|
||||||
(32, 39, 0.994);
|
(32, 39, 0.994);
|
||||||
|
|
||||||
INSERT INTO `vn`.`itemShelving` (`id`, `itemFk`, `shelvingFk`, `shelve`, `deep`, `quantity`, `visible`, `available`, `grouping`, `packing`, `level`, `userFk`)
|
INSERT INTO `vn`.`itemShelving` (`itemFk`, `shelvingFk`, `shelve`, `visible`, `grouping`, `packing`, `userFk`)
|
||||||
VALUES
|
VALUES
|
||||||
('1', '2', 'GVC', 'A', '0', '1', '1', '1', '1', '1', '1', '1106'),
|
(2, 'GVC', 'A', 1, 1, 1, 1106),
|
||||||
('2', '4', 'HEJ', 'A', '0', '2', '1', '1', '1', '1', '1', '1106');
|
(4, 'HEJ', 'A', 1, 1, 1, 1106),
|
||||||
|
(1, 'UXN', 'A', 2, 12, 12, 1106);
|
||||||
|
|
||||||
INSERT INTO `vn`.`itemShelvingSale` (`itemShelvingFk`, `saleFk`, `quantity`, `created`, `userFk`)
|
INSERT INTO `vn`.`itemShelvingSale` (`itemShelvingFk`, `saleFk`, `quantity`, `created`, `userFk`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -1135,14 +1124,6 @@ INSERT INTO `vn`.`parking` (`column`, `row`, `sectorFk`, `code`, `pickingOrder`)
|
||||||
VALUES
|
VALUES
|
||||||
('100', '01', 1, '100-01', 1);
|
('100', '01', 1, '100-01', 1);
|
||||||
|
|
||||||
INSERT INTO `vn`.`shelving` (`code`, `parkingFk`, `priority`, `userFk`)
|
|
||||||
VALUES
|
|
||||||
('UXN', 1, 1, 1106);
|
|
||||||
|
|
||||||
INSERT INTO `vn`.`itemShelving` (`itemFk`, `shelvingFk`, `shelve`, `deep`, `quantity`, `visible`, `available`, `grouping`, `packing`, `level`, `userFk`)
|
|
||||||
VALUES
|
|
||||||
(1, 'UXN', 'A', 2, 12, 12, 12, 12, 12, 1, 1106);
|
|
||||||
|
|
||||||
INSERT INTO `vn`.`ticketCollection` (`ticketFk`, `collectionFk`, `level`)
|
INSERT INTO `vn`.`ticketCollection` (`ticketFk`, `collectionFk`, `level`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 1, 1);
|
(1, 1, 1);
|
||||||
|
@ -2271,8 +2252,14 @@ INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `wa
|
||||||
(3, 5, '3.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Client: 104', 'Client:104 readme', CURDATE()),
|
(3, 5, '3.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Client: 104', 'Client:104 readme', CURDATE()),
|
||||||
(4, 3, '4.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Worker: 106', 'Worker:106 readme', CURDATE()),
|
(4, 3, '4.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Worker: 106', 'Worker:106 readme', CURDATE()),
|
||||||
(5, 5, '5.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'travel: 1', 'dmsForThermograph', CURDATE()),
|
(5, 5, '5.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'travel: 1', 'dmsForThermograph', CURDATE()),
|
||||||
(6, 5, '6.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'NotExists', 'DoesNotExists', CURDATE());
|
(6, 5, '6.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'NotExists', 'DoesNotExists', CURDATE()),
|
||||||
|
(7, 20, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', CURDATE()),
|
||||||
|
(8, 20, '8.mp4', 'video/mp4', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', CURDATE());
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`claimDms`(`claimFk`, `dmsFk`)
|
||||||
|
VALUES
|
||||||
|
(1, 7),
|
||||||
|
(1, 8);
|
||||||
|
|
||||||
INSERT INTO `vn`.`ticketDms`(`ticketFk`, `dmsFk`)
|
INSERT INTO `vn`.`ticketDms`(`ticketFk`, `dmsFk`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2532,7 +2519,7 @@ INSERT INTO `bs`.`sale` (`saleFk`, `amount`, `dated`, `typeFk`, `clientFk`)
|
||||||
|
|
||||||
INSERT INTO `vn`.`docuware` (`code`, `fileCabinetName`, `dialogName` , `find`)
|
INSERT INTO `vn`.`docuware` (`code`, `fileCabinetName`, `dialogName` , `find`)
|
||||||
VALUES
|
VALUES
|
||||||
('deliveryClientTest', 'deliveryClientTest', 'findTest', 'word');
|
('deliveryClient', 'deliveryClient', 'findTicket', 'word');
|
||||||
|
|
||||||
INSERT INTO `vn`.`docuwareConfig` (`url`)
|
INSERT INTO `vn`.`docuwareConfig` (`url`)
|
||||||
VALUES
|
VALUES
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -14,6 +14,8 @@ echo "" > "$DUMPED_FILE"
|
||||||
TABLES=(
|
TABLES=(
|
||||||
util
|
util
|
||||||
config
|
config
|
||||||
|
version
|
||||||
|
versionLog
|
||||||
)
|
)
|
||||||
dump_tables ${TABLES[@]}
|
dump_tables ${TABLES[@]}
|
||||||
|
|
||||||
|
@ -74,10 +76,20 @@ dump_tables ${TABLES[@]}
|
||||||
|
|
||||||
TABLES=(
|
TABLES=(
|
||||||
hedera
|
hedera
|
||||||
|
browser
|
||||||
imageCollection
|
imageCollection
|
||||||
|
imageCollectionSize
|
||||||
|
language
|
||||||
|
link
|
||||||
|
location
|
||||||
|
menu
|
||||||
|
message
|
||||||
|
metatag
|
||||||
|
newsTag
|
||||||
|
restPriv
|
||||||
|
social
|
||||||
tpvError
|
tpvError
|
||||||
tpvResponse
|
tpvResponse
|
||||||
imageCollectionSize
|
|
||||||
|
|
||||||
)
|
)
|
||||||
dump_tables ${TABLES[@]}
|
dump_tables ${TABLES[@]}
|
||||||
|
|
|
@ -49,6 +49,7 @@ IGNORETABLES=(
|
||||||
--ignore-table=vn.grantGroup
|
--ignore-table=vn.grantGroup
|
||||||
--ignore-table=vn.invoiceCorrection__
|
--ignore-table=vn.invoiceCorrection__
|
||||||
--ignore-table=vn.itemTaxCountrySpain
|
--ignore-table=vn.itemTaxCountrySpain
|
||||||
|
--ignore-table=vn.itemShelvingPlacementSupplyStock__
|
||||||
--ignore-table=vn.itemFreeNumber__
|
--ignore-table=vn.itemFreeNumber__
|
||||||
--ignore-table=vn.mail__
|
--ignore-table=vn.mail__
|
||||||
--ignore-table=vn.manaSpellers
|
--ignore-table=vn.manaSpellers
|
||||||
|
|
|
@ -731,7 +731,7 @@ export default {
|
||||||
claimAction: {
|
claimAction: {
|
||||||
importClaimButton: 'vn-claim-action vn-button[label="Import claim"]',
|
importClaimButton: 'vn-claim-action vn-button[label="Import claim"]',
|
||||||
anyLine: 'vn-claim-action vn-tbody > vn-tr',
|
anyLine: 'vn-claim-action vn-tbody > vn-tr',
|
||||||
firstDeleteLine: 'vn-claim-action vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
|
firstDeleteLine: 'vn-claim-action tr:nth-child(1) vn-icon-button[icon="delete"]',
|
||||||
isPaidWithManaCheckbox: 'vn-claim-action vn-check[ng-model="$ctrl.claim.isChargedToMana"]'
|
isPaidWithManaCheckbox: 'vn-claim-action vn-check[ng-model="$ctrl.claim.isChargedToMana"]'
|
||||||
},
|
},
|
||||||
ordersIndex: {
|
ordersIndex: {
|
||||||
|
@ -982,8 +982,8 @@ export default {
|
||||||
save: 'vn-invoice-in-basic-data button[type=submit]'
|
save: 'vn-invoice-in-basic-data button[type=submit]'
|
||||||
},
|
},
|
||||||
invoiceInTax: {
|
invoiceInTax: {
|
||||||
addTaxButton: 'vn-invoice-in-tax vn-icon-button[icon="add_circle"]',
|
addTaxButton: 'vn-invoice-in-tax vn-icon-button[vn-tooltip="Add tax"]',
|
||||||
thirdExpence: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.expenseFk"]',
|
thirdExpense: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.expenseFk"]',
|
||||||
thirdTaxableBase: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-input-number[ng-model="invoiceInTax.taxableBase"]',
|
thirdTaxableBase: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-input-number[ng-model="invoiceInTax.taxableBase"]',
|
||||||
thirdTaxType: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.taxTypeSageFk"]',
|
thirdTaxType: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.taxTypeSageFk"]',
|
||||||
thirdTransactionType: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.transactionTypeSageFk"]',
|
thirdTransactionType: 'vn-invoice-in-tax vn-horizontal:nth-child(3) > vn-autocomplete[ng-model="invoiceInTax.transactionTypeSageFk"]',
|
||||||
|
@ -1133,7 +1133,7 @@ export default {
|
||||||
entryLatestBuys: {
|
entryLatestBuys: {
|
||||||
firstBuy: 'vn-entry-latest-buys tbody > tr:nth-child(1)',
|
firstBuy: 'vn-entry-latest-buys tbody > tr:nth-child(1)',
|
||||||
allBuysCheckBox: 'vn-entry-latest-buys thead vn-check',
|
allBuysCheckBox: 'vn-entry-latest-buys thead vn-check',
|
||||||
secondBuyCheckBox: 'vn-entry-latest-buys tbody tr:nth-child(2) vn-check[ng-model="buy.$checked"]',
|
secondBuyCheckBox: 'vn-entry-latest-buys tbody tr:nth-child(2) vn-check[ng-model="buy.checked"]',
|
||||||
editBuysButton: 'vn-entry-latest-buys vn-button[icon="edit"]',
|
editBuysButton: 'vn-entry-latest-buys vn-button[icon="edit"]',
|
||||||
fieldAutocomplete: 'vn-autocomplete[ng-model="$ctrl.editedColumn.field"]',
|
fieldAutocomplete: 'vn-autocomplete[ng-model="$ctrl.editedColumn.field"]',
|
||||||
newValueInput: 'vn-textfield[ng-model="$ctrl.editedColumn.newValue"]',
|
newValueInput: 'vn-textfield[ng-model="$ctrl.editedColumn.newValue"]',
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
require('@babel/register')({presets: ['@babel/env']});
|
||||||
|
require('core-js/stable');
|
||||||
|
require('regenerator-runtime/runtime');
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
|
const Docker = require('../../db/docker.js');
|
||||||
|
const e2eConfig = require('./config.js');
|
||||||
|
const log = require('fancy-log');
|
||||||
|
|
||||||
|
process.on('warning', warning => {
|
||||||
|
console.log(warning.name);
|
||||||
|
console.log(warning.message);
|
||||||
|
console.log(warning.stack);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function test() {
|
||||||
|
if (process.argv[2] === 'show')
|
||||||
|
process.env.E2E_SHOW = true;
|
||||||
|
|
||||||
|
const container = new Docker('salix-db');
|
||||||
|
|
||||||
|
await container.run();
|
||||||
|
|
||||||
|
const Jasmine = require('jasmine');
|
||||||
|
const jasmine = new Jasmine();
|
||||||
|
|
||||||
|
const specFiles = [
|
||||||
|
`./e2e/paths/01*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/02*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/03*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/04*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/05*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/06*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/07*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/08*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/09*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/10*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/11*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/12*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/13*/*[sS]pec.js`,
|
||||||
|
`./e2e/paths/**/*[sS]pec.js`
|
||||||
|
];
|
||||||
|
|
||||||
|
jasmine.loadConfig({
|
||||||
|
spec_dir: '.',
|
||||||
|
spec_files: specFiles,
|
||||||
|
helpers: [],
|
||||||
|
random: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await backendStatus();
|
||||||
|
|
||||||
|
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
|
||||||
|
await jasmine.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function backendStatus() {
|
||||||
|
log('Awaiting backend connection...');
|
||||||
|
|
||||||
|
const milliseconds = 1000;
|
||||||
|
const maxAttempts = 10;
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let timer;
|
||||||
|
let attempts = 1;
|
||||||
|
timer = setInterval(async() => {
|
||||||
|
try {
|
||||||
|
attempts++;
|
||||||
|
const url = `${e2eConfig.url}/api/Applications/status`;
|
||||||
|
const {data} = await axios.get(url);
|
||||||
|
|
||||||
|
if (data == true) {
|
||||||
|
clearInterval(timer);
|
||||||
|
log('Backend connection stablished!');
|
||||||
|
resolve(attempts);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error && attempts >= maxAttempts) {
|
||||||
|
log('Could not connect to backend');
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, milliseconds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test();
|
|
@ -111,7 +111,7 @@ describe('Client lock verified data path', () => {
|
||||||
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
||||||
const message = await page.waitForSnackbar();
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
expect(message.text).toContain(`You can't make changes on a client with verified data`);
|
expect(message.text).toContain(`Not enough privileges to edit a client with verified data`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -123,19 +123,19 @@ describe('Client lock verified data path', () => {
|
||||||
await page.accessToSection('client.card.fiscalData');
|
await page.accessToSection('client.card.fiscalData');
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
it('should confirm verified data button is enabled for salesAssistant', async() => {
|
it('should confirm verified data button is disabled for salesAssistant', async() => {
|
||||||
const isDisabled = await page.isDisabled(selectors.clientFiscalData.verifiedDataCheckbox);
|
const isDisabled = await page.isDisabled(selectors.clientFiscalData.verifiedDataCheckbox);
|
||||||
|
|
||||||
expect(isDisabled).toBeFalsy();
|
expect(isDisabled).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should now edit the social name', async() => {
|
it('should return error when edit the social name', async() => {
|
||||||
await page.clearInput(selectors.clientFiscalData.socialName);
|
await page.clearInput(selectors.clientFiscalData.socialName);
|
||||||
await page.write(selectors.clientFiscalData.socialName, 'new social name edition');
|
await page.write(selectors.clientFiscalData.socialName, 'new social name edition');
|
||||||
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
||||||
const message = await page.waitForSnackbar();
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
expect(message.text).toContain('Data saved!');
|
expect(message.text).toContain(`Not enough privileges to edit a client with verified data`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should now confirm the social name have been edited once and for all', async() => {
|
it('should now confirm the social name have been edited once and for all', async() => {
|
||||||
|
|
|
@ -19,7 +19,7 @@ describe('InvoiceIn tax path', () => {
|
||||||
|
|
||||||
it('should add a new tax', async() => {
|
it('should add a new tax', async() => {
|
||||||
await page.waitToClick(selectors.invoiceInTax.addTaxButton);
|
await page.waitToClick(selectors.invoiceInTax.addTaxButton);
|
||||||
await page.autocompleteSearch(selectors.invoiceInTax.thirdExpence, '6210000567');
|
await page.autocompleteSearch(selectors.invoiceInTax.thirdExpense, '6210000567');
|
||||||
await page.write(selectors.invoiceInTax.thirdTaxableBase, '100');
|
await page.write(selectors.invoiceInTax.thirdTaxableBase, '100');
|
||||||
await page.autocompleteSearch(selectors.invoiceInTax.thirdTaxType, '6');
|
await page.autocompleteSearch(selectors.invoiceInTax.thirdTaxType, '6');
|
||||||
await page.autocompleteSearch(selectors.invoiceInTax.thirdTransactionType, 'Operaciones exentas');
|
await page.autocompleteSearch(selectors.invoiceInTax.thirdTransactionType, 'Operaciones exentas');
|
||||||
|
@ -37,9 +37,9 @@ describe('InvoiceIn tax path', () => {
|
||||||
expect(result).toEqual('Taxable base €1,323.16');
|
expect(result).toEqual('Taxable base €1,323.16');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigate back to the tax section and check the reciently added line contains the expected expense', async() => {
|
it('should navigate back to tax section, check the reciently added line contains the expected expense', async() => {
|
||||||
await page.accessToSection('invoiceIn.card.tax');
|
await page.accessToSection('invoiceIn.card.tax');
|
||||||
const result = await page.waitToGetProperty(selectors.invoiceInTax.thirdExpence, 'value');
|
const result = await page.waitToGetProperty(selectors.invoiceInTax.thirdExpense, 'value');
|
||||||
|
|
||||||
expect(result).toEqual('6210000567: Alquiler VNH');
|
expect(result).toEqual('6210000567: Alquiler VNH');
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
SelectAllRows: Select the {{rows}} row(s)
|
|
@ -0,0 +1,3 @@
|
||||||
|
SelectAllRows: Seleccionar las {{rows}} fila(s)
|
||||||
|
All: Se han seleccionado
|
||||||
|
row(s) have been selected.: fila(s).
|
|
@ -3,3 +3,22 @@
|
||||||
indeterminate="$ctrl.isIndeterminate"
|
indeterminate="$ctrl.isIndeterminate"
|
||||||
translate-attr="{title: 'Check all'}">
|
translate-attr="{title: 'Check all'}">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
|
<vn-icon-button
|
||||||
|
class="vn-pl-none"
|
||||||
|
ng-if="$ctrl.checked && $ctrl.checkDummyEnabled"
|
||||||
|
icon="expand_more"
|
||||||
|
vn-popover="menu"
|
||||||
|
ng-click="$ctrl.countRows()">
|
||||||
|
</vn-icon-button>
|
||||||
|
<vn-menu vn-id="menu">
|
||||||
|
<vn-list>
|
||||||
|
<span translate>All</span>
|
||||||
|
<span class="bold">
|
||||||
|
{{$ctrl.rows}}
|
||||||
|
</span>
|
||||||
|
<span translate>row(s) have been selected.</span>
|
||||||
|
<span class="bold link" ng-click="$ctrl.checkDummy()">
|
||||||
|
{{$ctrl.allRowsText}}
|
||||||
|
</span>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
|
@ -106,6 +106,9 @@ export default class MultiCheck extends FormInput {
|
||||||
|
|
||||||
this.toggle();
|
this.toggle();
|
||||||
this.emit('change', value);
|
this.emit('change', value);
|
||||||
|
|
||||||
|
if (!value)
|
||||||
|
this.checkedDummyCount = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,12 +135,43 @@ export default class MultiCheck extends FormInput {
|
||||||
areAllUnchecked() {
|
areAllUnchecked() {
|
||||||
if (!this.model || !this.model.data) return;
|
if (!this.model || !this.model.data) return;
|
||||||
|
|
||||||
|
this.checkedDummyCount = null;
|
||||||
const data = this.model.data;
|
const data = this.model.data;
|
||||||
return data.every(item => {
|
return data.every(item => {
|
||||||
return item[this.checkField] === false;
|
return item[this.checkField] === false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
countRows() {
|
||||||
|
if (!this.model || !this.model.data) return;
|
||||||
|
|
||||||
|
const data = this.model.data;
|
||||||
|
const modelParams = this.model.userParams;
|
||||||
|
const params = {
|
||||||
|
filter: {
|
||||||
|
modelParams: modelParams,
|
||||||
|
limit: null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.rows = data.length;
|
||||||
|
|
||||||
|
this.$http.get(this.model.url, {params})
|
||||||
|
.then(res => {
|
||||||
|
this.allRowsCount = res.data.length;
|
||||||
|
this.allRowsText = this.$t('SelectAllRows', {
|
||||||
|
rows: this.allRowsCount
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDummy() {
|
||||||
|
if (this.checkedDummyCount)
|
||||||
|
return this.checkedDummyCount = null;
|
||||||
|
|
||||||
|
this.checkedDummyCount = this.allRowsCount;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles checked property on
|
* Toggles checked property on
|
||||||
* all instances
|
* all instances
|
||||||
|
@ -158,7 +192,9 @@ ngModule.vnComponent('vnMultiCheck', {
|
||||||
checkField: '@?',
|
checkField: '@?',
|
||||||
checkAll: '=?',
|
checkAll: '=?',
|
||||||
checked: '=?',
|
checked: '=?',
|
||||||
disabled: '<?'
|
disabled: '<?',
|
||||||
|
checkDummyEnabled: '<?',
|
||||||
|
checkedDummyCount: '=?'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,14 @@ import crudModel from 'core/mocks/crud-model';
|
||||||
describe('Component vnMultiCheck', () => {
|
describe('Component vnMultiCheck', () => {
|
||||||
let controller;
|
let controller;
|
||||||
let $element;
|
let $element;
|
||||||
|
let $httpBackend;
|
||||||
|
let $httpParamSerializer;
|
||||||
|
|
||||||
beforeEach(ngModule('vnCore'));
|
beforeEach(ngModule('vnCore'));
|
||||||
|
|
||||||
beforeEach(inject($componentController => {
|
beforeEach(inject(($componentController, _$httpBackend_, _$httpParamSerializer_) => {
|
||||||
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpParamSerializer = _$httpParamSerializer_;
|
||||||
$element = angular.element(`<div class="shown"></div>`);
|
$element = angular.element(`<div class="shown"></div>`);
|
||||||
controller = $componentController('vnMultiCheck', {$element: $element});
|
controller = $componentController('vnMultiCheck', {$element: $element});
|
||||||
controller._model = crudModel;
|
controller._model = crudModel;
|
||||||
|
@ -26,6 +30,14 @@ describe('Component vnMultiCheck', () => {
|
||||||
expect(controller._checked).toEqual(crudModel);
|
expect(controller._checked).toEqual(crudModel);
|
||||||
expect(controller.toggle).toHaveBeenCalledWith();
|
expect(controller.toggle).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should set checkedDummyCount to null`, () => {
|
||||||
|
jest.spyOn(controller, 'toggle');
|
||||||
|
controller.checkedDummyCount = 12;
|
||||||
|
controller.checked = null;
|
||||||
|
|
||||||
|
expect(controller.checkedDummyCount).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('toggle()', () => {
|
describe('toggle()', () => {
|
||||||
|
@ -132,4 +144,50 @@ describe('Component vnMultiCheck', () => {
|
||||||
expect(thirdRow.checked).toBeTruthy();
|
expect(thirdRow.checked).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('countRows()', () => {
|
||||||
|
it(`should count visible rows and all rows of model`, () => {
|
||||||
|
controller.model.url = 'modelUrl/filter';
|
||||||
|
const data = controller.model.data;
|
||||||
|
const filter = {
|
||||||
|
limit: null
|
||||||
|
};
|
||||||
|
const serializedParams = $httpParamSerializer({filter});
|
||||||
|
|
||||||
|
const response = [
|
||||||
|
{id: 1, name: 'My item 1'},
|
||||||
|
{id: 2, name: 'My item 2'},
|
||||||
|
{id: 3, name: 'My item 3'},
|
||||||
|
{id: 4, name: 'My item 4'},
|
||||||
|
{id: 5, name: 'My item 5'},
|
||||||
|
{id: 6, name: 'My item 6'}
|
||||||
|
];
|
||||||
|
|
||||||
|
controller.countRows();
|
||||||
|
$httpBackend.expectGET(`modelUrl/filter?${serializedParams}`).respond(response);
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.rows).toEqual(data.length);
|
||||||
|
expect(controller.allRowsCount).toEqual(response.length);
|
||||||
|
expect(controller.allRowsCount).toBeGreaterThan(controller.rows);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('checkDummy()', () => {
|
||||||
|
const allRows = 1234;
|
||||||
|
it(`should set the checked dummy count to all rows count if there was no count yet`, () => {
|
||||||
|
controller.checkedDummyCount = null;
|
||||||
|
controller.allRowsCount = allRows;
|
||||||
|
controller.checkDummy();
|
||||||
|
|
||||||
|
expect(controller.checkedDummyCount).toEqual(controller.allRowsCount);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should remove the dummy count if there was an existing one`, () => {
|
||||||
|
controller.checkedDummyCount = allRows;
|
||||||
|
controller.checkDummy();
|
||||||
|
|
||||||
|
expect(controller.checkedDummyCount).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
|
@import "variables";
|
||||||
vn-multi-check {
|
vn-multi-check {
|
||||||
.vn-check {
|
.vn-check {
|
||||||
margin-bottom: 12px
|
margin-bottom: 12px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vn-list{
|
||||||
|
padding: 50px;
|
||||||
|
}
|
||||||
|
vn-menu{
|
||||||
|
padding: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.bold{
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
|
@ -26,7 +26,7 @@
|
||||||
.icon-agency-term:before {
|
.icon-agency-term:before {
|
||||||
content: "\e950";
|
content: "\e950";
|
||||||
}
|
}
|
||||||
.icon-deaulter:before {
|
.icon-defaulter:before {
|
||||||
content: "\e94b";
|
content: "\e94b";
|
||||||
}
|
}
|
||||||
.icon-100:before {
|
.icon-100:before {
|
||||||
|
|
189
gulpfile.js
189
gulpfile.js
|
@ -3,8 +3,6 @@ const gulp = require('gulp');
|
||||||
const PluginError = require('plugin-error');
|
const PluginError = require('plugin-error');
|
||||||
const argv = require('minimist')(process.argv.slice(2));
|
const argv = require('minimist')(process.argv.slice(2));
|
||||||
const log = require('fancy-log');
|
const log = require('fancy-log');
|
||||||
const got = require('got');
|
|
||||||
const e2eConfig = require('./e2e/helpers/config.js');
|
|
||||||
const Docker = require('./db/docker.js');
|
const Docker = require('./db/docker.js');
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
|
@ -67,187 +65,6 @@ back.description = `Starts backend and database service`;
|
||||||
|
|
||||||
const defaultTask = gulp.parallel(front, back);
|
const defaultTask = gulp.parallel(front, back);
|
||||||
defaultTask.description = `Starts all application services`;
|
defaultTask.description = `Starts all application services`;
|
||||||
|
|
||||||
// Backend tests - Private method
|
|
||||||
|
|
||||||
async function launchBackTest(done) {
|
|
||||||
let err;
|
|
||||||
let dataSources = require('./loopback/server/datasources.json');
|
|
||||||
|
|
||||||
const container = new Docker();
|
|
||||||
await container.run(argv.ci);
|
|
||||||
|
|
||||||
dataSources = JSON.parse(JSON.stringify(dataSources));
|
|
||||||
|
|
||||||
Object.assign(dataSources.vn, {
|
|
||||||
host: container.dbConf.host,
|
|
||||||
port: container.dbConf.port
|
|
||||||
});
|
|
||||||
|
|
||||||
let bootOptions = {dataSources};
|
|
||||||
|
|
||||||
let app = require(`./loopback/server/server`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
app.boot(bootOptions);
|
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
const jasmine = require('gulp-jasmine');
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
verbose: false,
|
|
||||||
includeStackTrace: false,
|
|
||||||
errorOnFail: false,
|
|
||||||
timeout: 5000,
|
|
||||||
config: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (argv.ci) {
|
|
||||||
const reporters = require('jasmine-reporters');
|
|
||||||
options.reporter = new reporters.JUnitXmlReporter();
|
|
||||||
}
|
|
||||||
|
|
||||||
let backSpecFiles = [
|
|
||||||
'back/**/*.spec.js',
|
|
||||||
'loopback/**/*.spec.js',
|
|
||||||
'modules/*/back/**/*.spec.js'
|
|
||||||
];
|
|
||||||
|
|
||||||
gulp.src(backSpecFiles)
|
|
||||||
.pipe(jasmine(options))
|
|
||||||
.on('end', resolve)
|
|
||||||
.on('error', reject)
|
|
||||||
.resume();
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
await app.disconnect();
|
|
||||||
await container.rm();
|
|
||||||
done();
|
|
||||||
if (err)
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
launchBackTest.description = `
|
|
||||||
Runs the backend tests once using a random container, can receive --ci arg to save reports on a xml file`;
|
|
||||||
|
|
||||||
// Backend tests
|
|
||||||
|
|
||||||
function backTest(done) {
|
|
||||||
const nodemon = require('gulp-nodemon');
|
|
||||||
|
|
||||||
nodemon({
|
|
||||||
exec: ['node --tls-min-v1.0 ./node_modules/gulp/bin/gulp.js'],
|
|
||||||
args: ['launchBackTest'],
|
|
||||||
watch: backSources,
|
|
||||||
done: done
|
|
||||||
});
|
|
||||||
}
|
|
||||||
backTest.description = `Watches for changes in modules to execute backTest task`;
|
|
||||||
|
|
||||||
// End to end tests
|
|
||||||
function e2eSingleRun() {
|
|
||||||
require('@babel/register')({presets: ['@babel/env']});
|
|
||||||
require('core-js/stable');
|
|
||||||
require('regenerator-runtime/runtime');
|
|
||||||
|
|
||||||
const jasmine = require('gulp-jasmine');
|
|
||||||
const SpecReporter = require('jasmine-spec-reporter').SpecReporter;
|
|
||||||
|
|
||||||
if (argv.show || argv.s)
|
|
||||||
process.env.E2E_SHOW = true;
|
|
||||||
|
|
||||||
const specFiles = [
|
|
||||||
`${__dirname}/e2e/paths/01*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/02*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/03*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/04*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/05*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/06*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/07*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/08*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/09*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/10*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/11*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/12*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/13*/*[sS]pec.js`,
|
|
||||||
`${__dirname}/e2e/paths/**/*[sS]pec.js`
|
|
||||||
];
|
|
||||||
|
|
||||||
return gulp.src(specFiles).pipe(jasmine({
|
|
||||||
errorOnFail: false,
|
|
||||||
timeout: 30000,
|
|
||||||
config: {
|
|
||||||
random: false,
|
|
||||||
// TODO: Waiting for this option to be implemented
|
|
||||||
// https://github.com/jasmine/jasmine/issues/1533
|
|
||||||
stopSpecOnExpectationFailure: false
|
|
||||||
},
|
|
||||||
reporter: [
|
|
||||||
new SpecReporter({
|
|
||||||
spec: {
|
|
||||||
displayStacktrace: 'none',
|
|
||||||
displaySuccessful: true,
|
|
||||||
displayFailedSpec: true,
|
|
||||||
displaySpecDuration: true,
|
|
||||||
},
|
|
||||||
summary: {
|
|
||||||
displayStacktrace: 'raw',
|
|
||||||
displayPending: false
|
|
||||||
},
|
|
||||||
colors: {
|
|
||||||
enabled: true,
|
|
||||||
successful: 'brightGreen',
|
|
||||||
failed: 'brightRed'
|
|
||||||
},
|
|
||||||
// stacktrace: {
|
|
||||||
// filter: stacktrace => {
|
|
||||||
// const lines = stacktrace.split('\n');
|
|
||||||
// const filtered = [];
|
|
||||||
// for (let i = 1; i < lines.length; i++) {
|
|
||||||
// if (/e2e\/paths/.test(lines[i]))
|
|
||||||
// filtered.push(lines[i]);
|
|
||||||
// }
|
|
||||||
// return filtered.join('\n');
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
e2e = gulp.series(docker, async function isBackendReady() {
|
|
||||||
const attempts = await backendStatus();
|
|
||||||
log(`Backend ready after ${attempts} attempt(s)`);
|
|
||||||
|
|
||||||
return attempts;
|
|
||||||
}, e2eSingleRun);
|
|
||||||
e2e.description = `Restarts database and runs the e2e tests`;
|
|
||||||
|
|
||||||
async function backendStatus() {
|
|
||||||
const milliseconds = 250;
|
|
||||||
return new Promise(resolve => {
|
|
||||||
let timer;
|
|
||||||
let attempts = 1;
|
|
||||||
timer = setInterval(async() => {
|
|
||||||
try {
|
|
||||||
const url = `${e2eConfig.url}/api/Applications/status`;
|
|
||||||
const {body} = await got.get(url);
|
|
||||||
|
|
||||||
if (body == 'true') {
|
|
||||||
clearInterval(timer);
|
|
||||||
resolve(attempts);
|
|
||||||
} else
|
|
||||||
attempts++;
|
|
||||||
} catch (error) {
|
|
||||||
if (error || attempts > 100) // 250ms * 100 => 25s timeout
|
|
||||||
throw new Error('Could not connect to backend');
|
|
||||||
}
|
|
||||||
}, milliseconds);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
backendStatus.description = `Performs a simple requests to check the backend status`;
|
|
||||||
|
|
||||||
function install() {
|
function install() {
|
||||||
const install = require('gulp-install');
|
const install = require('gulp-install');
|
||||||
const print = require('gulp-print');
|
const print = require('gulp-print');
|
||||||
|
@ -431,9 +248,6 @@ module.exports = {
|
||||||
back,
|
back,
|
||||||
backOnly,
|
backOnly,
|
||||||
backWatch,
|
backWatch,
|
||||||
backTest,
|
|
||||||
launchBackTest,
|
|
||||||
e2e,
|
|
||||||
i,
|
i,
|
||||||
install,
|
install,
|
||||||
build,
|
build,
|
||||||
|
@ -444,6 +258,5 @@ module.exports = {
|
||||||
locales,
|
locales,
|
||||||
localesRoutes,
|
localesRoutes,
|
||||||
watch,
|
watch,
|
||||||
docker,
|
docker
|
||||||
backendStatus,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -47,5 +47,6 @@ module.exports = {
|
||||||
'^.+\\.js?$': 'babel-jest',
|
'^.+\\.js?$': 'babel-jest',
|
||||||
'^.+\\.html$': 'html-loader-jest'
|
'^.+\\.html$': 'html-loader-jest'
|
||||||
},
|
},
|
||||||
|
reporters: ['default', 'jest-junit']
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
"That payment method requires an IBAN": "That payment method requires an IBAN",
|
"That payment method requires an IBAN": "That payment method requires an IBAN",
|
||||||
"That payment method requires a BIC": "That payment method requires a BIC",
|
"That payment method requires a BIC": "That payment method requires a BIC",
|
||||||
"The default consignee can not be unchecked": "The default consignee can not be unchecked",
|
"The default consignee can not be unchecked": "The default consignee can not be unchecked",
|
||||||
"You can't make changes on a client with verified data": "You can't make changes on a client with verified data",
|
|
||||||
"Enter an integer different to zero": "Enter an integer different to zero",
|
"Enter an integer different to zero": "Enter an integer different to zero",
|
||||||
"Package cannot be blank": "Package cannot be blank",
|
"Package cannot be blank": "Package cannot be blank",
|
||||||
"The new quantity should be smaller than the old one": "The new quantity should be smaller than the old one",
|
"The new quantity should be smaller than the old one": "The new quantity should be smaller than the old one",
|
||||||
|
@ -123,5 +122,6 @@
|
||||||
"The type of business must be filled in basic data": "The type of business must be filled in basic data",
|
"The type of business must be filled in basic data": "The type of business must be filled in basic data",
|
||||||
"The worker has hours recorded that day": "The worker has hours recorded that day",
|
"The worker has hours recorded that day": "The worker has hours recorded that day",
|
||||||
"isWithoutNegatives": "isWithoutNegatives",
|
"isWithoutNegatives": "isWithoutNegatives",
|
||||||
"routeFk": "routeFk"
|
"routeFk": "routeFk",
|
||||||
|
"Not enough privileges to edit a client with verified data": "Not enough privileges to edit a client with verified data"
|
||||||
}
|
}
|
|
@ -50,7 +50,7 @@
|
||||||
"You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo",
|
"You don't have enough privileges to change that field": "No tienes permisos para cambiar ese campo",
|
||||||
"Warehouse cannot be blank": "El almacén no puede quedar en blanco",
|
"Warehouse cannot be blank": "El almacén no puede quedar en blanco",
|
||||||
"Agency cannot be blank": "La agencia no puede quedar en blanco",
|
"Agency cannot be blank": "La agencia no puede quedar en blanco",
|
||||||
"You can't make changes on a client with verified data": "No puedes hacer cambios en un cliente con datos comprobados",
|
"Not enough privileges to edit a client with verified data": "No tienes permisos para hacer cambios en un cliente con datos comprobados",
|
||||||
"This address doesn't exist": "Este consignatario no existe",
|
"This address doesn't exist": "Este consignatario no existe",
|
||||||
"You must delete the claim id %d first": "Antes debes borrar la reclamación %d",
|
"You must delete the claim id %d first": "Antes debes borrar la reclamación %d",
|
||||||
"You don't have enough privileges": "No tienes suficientes permisos",
|
"You don't have enough privileges": "No tienes suficientes permisos",
|
||||||
|
|
|
@ -39,7 +39,8 @@
|
||||||
"multipart/x-zip",
|
"multipart/x-zip",
|
||||||
"image/png",
|
"image/png",
|
||||||
"image/jpeg",
|
"image/jpeg",
|
||||||
"image/jpg"
|
"image/jpg",
|
||||||
|
"video/mp4"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dmsStorage": {
|
"dmsStorage": {
|
||||||
|
@ -84,5 +85,18 @@
|
||||||
"application/octet-stream",
|
"application/octet-stream",
|
||||||
"application/pdf"
|
"application/pdf"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"claimStorage": {
|
||||||
|
"name": "claimStorage",
|
||||||
|
"connector": "loopback-component-storage",
|
||||||
|
"provider": "filesystem",
|
||||||
|
"root": "./storage/dms",
|
||||||
|
"maxFileSize": "31457280",
|
||||||
|
"allowedContentTypes": [
|
||||||
|
"image/png",
|
||||||
|
"image/jpeg",
|
||||||
|
"image/jpg",
|
||||||
|
"video/mp4"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('deleteClamedSales', {
|
||||||
|
description: 'Deletes the claimed sales',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'sales',
|
||||||
|
type: ['object'],
|
||||||
|
required: true,
|
||||||
|
description: 'The sales to remove'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/deleteClamedSales`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deleteClamedSales = async(ctx, sales, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const myOptions = {};
|
||||||
|
const tx = await Self.beginTransaction({});
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction)
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const promises = [];
|
||||||
|
for (let sale of sales) {
|
||||||
|
const deletedSale = models.ClaimEnd.destroyById(sale.id, myOptions);
|
||||||
|
promises.push(deletedSale);
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletedSales = await Promise.all(promises);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return deletedSales;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,69 @@
|
||||||
|
|
||||||
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('filter', {
|
||||||
|
description: 'Find all instances of the model matched by filter from the data source.',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||||
|
http: {source: 'query'}
|
||||||
|
}, {
|
||||||
|
arg: 'search',
|
||||||
|
type: 'string',
|
||||||
|
description: `If it's and integer searchs by id, otherwise it searchs by client id`,
|
||||||
|
http: {source: 'query'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/filter`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.filter = async(ctx, filter, options) => {
|
||||||
|
const conn = Self.dataSource.connector;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const stmts = [];
|
||||||
|
|
||||||
|
const stmt = new ParameterizedSQL(
|
||||||
|
`SELECT *
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
ce.id,
|
||||||
|
ce.claimFk,
|
||||||
|
s.itemFk,
|
||||||
|
s.ticketFk,
|
||||||
|
ce.claimDestinationFk,
|
||||||
|
t.landed,
|
||||||
|
s.quantity,
|
||||||
|
s.concept,
|
||||||
|
s.price,
|
||||||
|
s.discount,
|
||||||
|
s.quantity * s.price * ((100 - s.discount) / 100) total
|
||||||
|
FROM vn.claimEnd ce
|
||||||
|
LEFT JOIN vn.sale s ON s.id = ce.saleFk
|
||||||
|
LEFT JOIN vn.ticket t ON t.id = s.ticketFk
|
||||||
|
) ce`
|
||||||
|
);
|
||||||
|
|
||||||
|
stmt.merge(conn.makeSuffix(filter));
|
||||||
|
const itemsIndex = stmts.push(stmt) - 1;
|
||||||
|
|
||||||
|
const sql = ParameterizedSQL.join(stmts, ';');
|
||||||
|
const result = await conn.executeStmt(sql, myOptions);
|
||||||
|
|
||||||
|
return itemsIndex === 0 ? result : result[itemsIndex];
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,59 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('downloadFile', {
|
||||||
|
description: 'Get the claim file',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'Number',
|
||||||
|
description: 'The document id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
arg: 'body',
|
||||||
|
type: 'file',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'Content-Type',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'Content-Disposition',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/:id/downloadFile`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.downloadFile = async function(ctx, id) {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const ClaimContainer = models.ClaimContainer;
|
||||||
|
const dms = await models.Dms.findById(id);
|
||||||
|
const pathHash = ClaimContainer.getHash(dms.id);
|
||||||
|
try {
|
||||||
|
await ClaimContainer.getFile(pathHash, dms.file);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code != 'ENOENT')
|
||||||
|
throw e;
|
||||||
|
|
||||||
|
const error = new UserError(`File doesn't exists`);
|
||||||
|
error.statusCode = 404;
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stream = ClaimContainer.downloadStream(pathHash, dms.file);
|
||||||
|
|
||||||
|
return [stream, dms.contentType, `filename="${dms.file}"`];
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,13 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
|
describe('claim downloadFile()', () => {
|
||||||
|
const dmsId = 7;
|
||||||
|
|
||||||
|
it('should return a response for an employee with image content-type', async() => {
|
||||||
|
const workerId = 1107;
|
||||||
|
const ctx = {req: {accessToken: {userId: workerId}}};
|
||||||
|
const result = await app.models.Claim.downloadFile(ctx, dmsId);
|
||||||
|
|
||||||
|
expect(result[1]).toEqual('image/jpeg');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,18 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
|
describe('claim uploadFile()', () => {
|
||||||
|
it(`should return an error for a user without enough privileges`, async() => {
|
||||||
|
const clientId = 1101;
|
||||||
|
const ticketDmsTypeId = 14;
|
||||||
|
const ctx = {req: {accessToken: {userId: clientId}}, args: {dmsTypeId: ticketDmsTypeId}};
|
||||||
|
|
||||||
|
let error;
|
||||||
|
await app.models.Claim.uploadFile(ctx).catch(e => {
|
||||||
|
error = e;
|
||||||
|
}).finally(() => {
|
||||||
|
expect(error.message).toEqual(`You don't have enough privileges`);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,55 @@
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('updateClaimDestination', {
|
||||||
|
description: 'Update a claim with privileges',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'rows',
|
||||||
|
type: ['object'],
|
||||||
|
required: true,
|
||||||
|
description: `the sales which will be modified the claimDestinationFk`
|
||||||
|
}, {
|
||||||
|
arg: 'claimDestinationFk',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/updateClaimDestination`,
|
||||||
|
verb: 'post'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.updateClaimDestination = async(rows, claimDestinationFk, options) => {
|
||||||
|
const tx = await Self.beginTransaction({});
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction)
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const promises = [];
|
||||||
|
for (let row of rows) {
|
||||||
|
const claimEnd = await models.ClaimEnd.findById(row.id, null, myOptions);
|
||||||
|
const updatedClaimEnd = claimEnd.updateAttribute('claimDestinationFk', claimDestinationFk, myOptions);
|
||||||
|
promises.push(updatedClaimEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedSales = await Promise.all(promises);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return updatedSales;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,6 +1,10 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('uploadFile', {
|
Self.remoteMethodCtx('uploadFile', {
|
||||||
description: 'Upload and attach a document',
|
description: 'Upload and attach a file',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [{
|
accepts: [{
|
||||||
arg: 'id',
|
arg: 'id',
|
||||||
|
@ -53,22 +57,54 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.uploadFile = async(ctx, id, options) => {
|
Self.uploadFile = async(ctx, id, options) => {
|
||||||
let tx;
|
const tx = await Self.beginTransaction({});
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
|
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
if (!myOptions.transaction) {
|
if (!myOptions.transaction)
|
||||||
tx = await Self.beginTransaction({});
|
|
||||||
myOptions.transaction = tx;
|
myOptions.transaction = tx;
|
||||||
}
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
const TempContainer = models.TempContainer;
|
||||||
|
const ClaimContainer = models.ClaimContainer;
|
||||||
|
const fileOptions = {};
|
||||||
|
const args = ctx.args;
|
||||||
|
|
||||||
|
let srcFile;
|
||||||
try {
|
try {
|
||||||
const uploadedFiles = await models.Dms.uploadFile(ctx, myOptions);
|
const hasWriteRole = await models.DmsType.hasWriteRole(ctx, args.dmsTypeId, myOptions);
|
||||||
uploadedFiles.forEach(dms => {
|
if (!hasWriteRole)
|
||||||
|
throw new UserError(`You don't have enough privileges`);
|
||||||
|
|
||||||
|
// Upload file to temporary path
|
||||||
|
const tempContainer = await TempContainer.container('dms');
|
||||||
|
const uploaded = await TempContainer.upload(tempContainer.name, ctx.req, ctx.result, fileOptions);
|
||||||
|
const files = Object.values(uploaded.files).map(file => {
|
||||||
|
return file[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
const addedDms = [];
|
||||||
|
for (const uploadedFile of files) {
|
||||||
|
const newDms = await createDms(ctx, uploadedFile, myOptions);
|
||||||
|
const pathHash = ClaimContainer.getHash(newDms.id);
|
||||||
|
|
||||||
|
const file = await TempContainer.getFile(tempContainer.name, uploadedFile.name);
|
||||||
|
srcFile = path.join(file.client.root, file.container, file.name);
|
||||||
|
|
||||||
|
const claimContainer = await ClaimContainer.container(pathHash);
|
||||||
|
const dstFile = path.join(claimContainer.client.root, pathHash, newDms.file);
|
||||||
|
|
||||||
|
await fs.move(srcFile, dstFile, {
|
||||||
|
overwrite: true
|
||||||
|
});
|
||||||
|
|
||||||
|
addedDms.push(newDms);
|
||||||
|
}
|
||||||
|
|
||||||
|
addedDms.forEach(dms => {
|
||||||
const newClaimDms = models.ClaimDms.create({
|
const newClaimDms = models.ClaimDms.create({
|
||||||
claimFk: id,
|
claimFk: id,
|
||||||
dmsFk: dms.id
|
dmsFk: dms.id
|
||||||
|
@ -83,7 +119,34 @@ module.exports = Self => {
|
||||||
return resolvedPromises;
|
return resolvedPromises;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (tx) await tx.rollback();
|
if (tx) await tx.rollback();
|
||||||
|
|
||||||
|
if (fs.existsSync(srcFile))
|
||||||
|
await fs.unlink(srcFile);
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function createDms(ctx, file, myOptions) {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const myUserId = ctx.req.accessToken.userId;
|
||||||
|
const args = ctx.args;
|
||||||
|
|
||||||
|
const newDms = await models.Dms.create({
|
||||||
|
workerFk: myUserId,
|
||||||
|
dmsTypeFk: args.dmsTypeId,
|
||||||
|
companyFk: args.companyId,
|
||||||
|
warehouseFk: args.warehouseId,
|
||||||
|
reference: args.reference,
|
||||||
|
description: args.description,
|
||||||
|
contentType: file.type,
|
||||||
|
hasFile: args.hasFile
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
let fileName = file.name;
|
||||||
|
const extension = models.DmsContainer.getFileExtension(fileName);
|
||||||
|
fileName = `${newDms.id}.${extension}`;
|
||||||
|
|
||||||
|
return newDms.updateAttribute('file', fileName, myOptions);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,5 +37,8 @@
|
||||||
},
|
},
|
||||||
"ClaimLog": {
|
"ClaimLog": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"ClaimContainer": {
|
||||||
|
"dataSource": "claimStorage"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"name": "ClaimContainer",
|
||||||
|
"base": "Container",
|
||||||
|
"acls": [{
|
||||||
|
"accessType": "READ",
|
||||||
|
"principalType": "ROLE",
|
||||||
|
"principalId": "$everyone",
|
||||||
|
"permission": "ALLOW"
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
require('../methods/claim-end/filter')(Self);
|
||||||
|
require('../methods/claim-end/deleteClamedSales')(Self);
|
||||||
|
};
|
|
@ -7,4 +7,6 @@ module.exports = Self => {
|
||||||
require('../methods/claim/uploadFile')(Self);
|
require('../methods/claim/uploadFile')(Self);
|
||||||
require('../methods/claim/updateClaimAction')(Self);
|
require('../methods/claim/updateClaimAction')(Self);
|
||||||
require('../methods/claim/isEditable')(Self);
|
require('../methods/claim/isEditable')(Self);
|
||||||
|
require('../methods/claim/updateClaimDestination')(Self);
|
||||||
|
require('../methods/claim/downloadFile')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
<vn-crud-model vn-id="model"
|
<vn-crud-model vn-id="model"
|
||||||
url="ClaimEnds"
|
url="ClaimEnds/filter"
|
||||||
filter="$ctrl.filter"
|
|
||||||
link="{claimFk: $ctrl.$params.id}"
|
link="{claimFk: $ctrl.$params.id}"
|
||||||
data="$ctrl.salesClaimed"
|
data="$ctrl.salesClaimed"
|
||||||
auto-load="true"
|
auto-load="true"
|
||||||
auto-save="true"
|
|
||||||
on-save="$ctrl.onSave()">
|
on-save="$ctrl.onSave()">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
|
@ -19,6 +17,11 @@
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-card class="vn-pa-lg vn-w-lg">
|
<vn-card class="vn-pa-lg vn-w-lg">
|
||||||
|
<smart-table
|
||||||
|
model="model"
|
||||||
|
options="$ctrl.smartTableOptions"
|
||||||
|
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||||
|
<slot-actions>
|
||||||
<section class="header">
|
<section class="header">
|
||||||
<vn-tool-bar class="vn-mb-md">
|
<vn-tool-bar class="vn-mb-md">
|
||||||
<vn-button
|
<vn-button
|
||||||
|
@ -26,6 +29,11 @@
|
||||||
disabled="$ctrl.claim.claimStateFk == $ctrl.resolvedStateId"
|
disabled="$ctrl.claim.claimStateFk == $ctrl.resolvedStateId"
|
||||||
vn-http-click="$ctrl.importToNewRefundTicket()"
|
vn-http-click="$ctrl.importToNewRefundTicket()"
|
||||||
translate-attr="{title: 'Imports claim details'}">
|
translate-attr="{title: 'Imports claim details'}">
|
||||||
|
</vn-button>
|
||||||
|
<vn-button
|
||||||
|
label="Change destination"
|
||||||
|
disabled="$ctrl.checked.length == 0"
|
||||||
|
ng-click="changeDestination.show()">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
<vn-range
|
<vn-range
|
||||||
label="Responsability"
|
label="Responsability"
|
||||||
|
@ -38,82 +46,95 @@
|
||||||
on-change="$ctrl.save({responsibility: value})">
|
on-change="$ctrl.save({responsibility: value})">
|
||||||
</vn-range>
|
</vn-range>
|
||||||
</vn-tool-bar>
|
</vn-tool-bar>
|
||||||
<vn-check vn-one
|
<vn-check class="right"
|
||||||
|
vn-one
|
||||||
label="Is paid with mana"
|
label="Is paid with mana"
|
||||||
ng-model="$ctrl.claim.isChargedToMana"
|
ng-model="$ctrl.claim.isChargedToMana"
|
||||||
on-change="$ctrl.save({isChargedToMana: value})">
|
on-change="$ctrl.save({isChargedToMana: value})">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</section>
|
</section>
|
||||||
<vn-data-viewer model="model">
|
</slot-actions>
|
||||||
<vn-table model="model">
|
<slot-table>
|
||||||
<vn-thead>
|
<table model="model">
|
||||||
<vn-tr>
|
<thead>
|
||||||
<vn-th number>Id</vn-th>
|
<tr>
|
||||||
<vn-th number>Ticket</vn-th>
|
<th shrink>
|
||||||
<vn-th>Destination</vn-th>
|
<vn-multi-check
|
||||||
<vn-th expand>Landed</vn-th>
|
model="model"
|
||||||
<vn-th number>Quantity</vn-th>
|
check-field="$checked">
|
||||||
<vn-th>Description</vn-th>
|
</vn-multi-check>
|
||||||
<vn-th number>Price</vn-th>
|
</th>
|
||||||
<vn-th number>Disc.</vn-th>
|
<th number field="itemFk">Id</th>
|
||||||
<vn-th number>Total</vn-th>
|
<th number field="ticketFk">Ticket</th>
|
||||||
</vn-tr>
|
<th field="claimDestinationFk">Destination</th>
|
||||||
</vn-thead>
|
<th expand field="landed">Landed</th>
|
||||||
<vn-tbody>
|
<th number field="quantity">Quantity</th>
|
||||||
<vn-tr
|
<th field="concept">Description</th>
|
||||||
|
<th number field="price">Price</th>
|
||||||
|
<th number field="discount">Disc.</th>
|
||||||
|
<th number field="total">Total</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
ng-repeat="saleClaimed in $ctrl.salesClaimed"
|
ng-repeat="saleClaimed in $ctrl.salesClaimed"
|
||||||
vn-repeat-last on-last="$ctrl.focusLastInput()">
|
vn-repeat-last on-last="$ctrl.focusLastInput()">
|
||||||
<vn-td number>
|
<td>
|
||||||
<span
|
<vn-check
|
||||||
ng-click="itemDescriptor.show($event, saleClaimed.sale.itemFk)"
|
ng-model="saleClaimed.$checked"
|
||||||
|
vn-click-stop>
|
||||||
|
</vn-check>
|
||||||
|
</td>
|
||||||
|
<td number>
|
||||||
|
<vn-span
|
||||||
|
ng-click="itemDescriptor.show($event, saleClaimed.itemFk)"
|
||||||
class="link">
|
class="link">
|
||||||
{{::saleClaimed.sale.itemFk | zeroFill:6}}
|
{{::saleClaimed.itemFk | zeroFill:6}}
|
||||||
</span>
|
</vn-span>
|
||||||
</vn-td>
|
</td>
|
||||||
<vn-td number>
|
<td number>
|
||||||
<span
|
<vn-span
|
||||||
class="link"
|
class="link"
|
||||||
ng-click="ticketDescriptor.show($event, saleClaimed.sale.ticketFk)">
|
ng-click="ticketDescriptor.show($event, saleClaimed.ticketFk)">
|
||||||
{{::saleClaimed.sale.ticketFk}}
|
{{::saleClaimed.ticketFk}}
|
||||||
</span>
|
</vn-span>
|
||||||
</vn-td>
|
</td>
|
||||||
<vn-td expand>
|
<td expand>
|
||||||
<vn-autocomplete vn-one id="claimDestinationFk"
|
<vn-autocomplete vn-one id="claimDestinationFk"
|
||||||
ng-model="saleClaimed.claimDestinationFk"
|
ng-model="saleClaimed.claimDestinationFk"
|
||||||
data="claimDestinations"
|
data="claimDestinations"
|
||||||
|
on-change="$ctrl.updateDestination(saleClaimed, value)"
|
||||||
fields="['id','description']"
|
fields="['id','description']"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
show-field="description">
|
show-field="description">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-td>
|
</td>
|
||||||
<vn-td expand>{{::saleClaimed.sale.ticket.landed | date: 'dd/MM/yyyy'}}</vn-td>
|
<td expand>{{::saleClaimed.landed | date: 'dd/MM/yyyy'}}</td>
|
||||||
<vn-td number>{{::saleClaimed.sale.quantity}}</vn-td>
|
<td number>{{::saleClaimed.quantity}}</td>
|
||||||
<vn-td expand>{{::saleClaimed.sale.concept}}</vn-td>
|
<td expand>{{::saleClaimed.concept}}</td>
|
||||||
<vn-td number>{{::saleClaimed.sale.price | currency: 'EUR':2}}</vn-td>
|
<td number>{{::saleClaimed.price | currency: 'EUR':2}}</td>
|
||||||
<vn-td number>{{::saleClaimed.sale.discount}} %</vn-td>
|
<td number>{{::saleClaimed.discount}} %</td>
|
||||||
<vn-td number>
|
<td number>{{saleClaimed.total | currency: 'EUR':2}}</td>
|
||||||
{{saleClaimed.sale.quantity * saleClaimed.sale.price *
|
<td shrink>
|
||||||
((100 - saleClaimed.sale.discount) / 100) | currency: 'EUR':2}}
|
|
||||||
</vn-td>
|
|
||||||
<vn-td shrink>
|
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
vn-tooltip="Remove line"
|
vn-tooltip="Remove line"
|
||||||
icon="delete"
|
icon="delete"
|
||||||
ng-click="model.remove($index)"
|
ng-click="$ctrl.removeSales(saleClaimed)"
|
||||||
tabindex="-1">
|
tabindex="-1">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</vn-td>
|
</td>
|
||||||
</vn-tr>
|
</tr>
|
||||||
</vn-tbody>
|
</tbody>
|
||||||
</vn-table>
|
</table>
|
||||||
</vn-data-viewer>
|
</slot-table>
|
||||||
<vn-button-bar>
|
</smart-table>
|
||||||
|
<button-bar class="vn-pa-md">
|
||||||
<vn-button
|
<vn-button
|
||||||
label="Regularize"
|
label="Regularize"
|
||||||
disabled="$ctrl.claim.claimStateFk == $ctrl.resolvedStateId"
|
disabled="$ctrl.claim.claimStateFk == $ctrl.resolvedStateId"
|
||||||
vn-http-click="$ctrl.regularize()">
|
vn-http-click="$ctrl.regularize()">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</vn-button-bar>
|
</button-bar>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-item-descriptor-popover
|
<vn-item-descriptor-popover
|
||||||
vn-id="item-descriptor"
|
vn-id="item-descriptor"
|
||||||
|
@ -128,3 +149,28 @@
|
||||||
message="Do you want to insert greuges?"
|
message="Do you want to insert greuges?"
|
||||||
on-accept="$ctrl.onUpdateGreugeAccept()">
|
on-accept="$ctrl.onUpdateGreugeAccept()">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
|
|
||||||
|
<!-- Dialog of change destionation -->
|
||||||
|
<vn-dialog
|
||||||
|
vn-id="changeDestination"
|
||||||
|
on-accept="$ctrl.onResponse()">
|
||||||
|
<tpl-body>
|
||||||
|
<section class="SMSDialog">
|
||||||
|
<h5 class="vn-py-sm">{{$ctrl.$t('Change destination to all selected rows', {total: $ctrl.checked.length})}}</h5>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-autocomplete vn-one id="claimDestinationFk"
|
||||||
|
ng-model="$ctrl.newDestination"
|
||||||
|
data="claimDestinations"
|
||||||
|
fields="['id','description']"
|
||||||
|
value-field="id"
|
||||||
|
show-field="description"
|
||||||
|
vn-focus>
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-horizontal>
|
||||||
|
</section>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
|
<button response="accept" translate>Save</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
|
@ -5,6 +5,7 @@ import './style.scss';
|
||||||
export default class Controller extends Section {
|
export default class Controller extends Section {
|
||||||
constructor($element, $) {
|
constructor($element, $) {
|
||||||
super($element, $);
|
super($element, $);
|
||||||
|
this.newDestination;
|
||||||
this.filter = {
|
this.filter = {
|
||||||
include: [
|
include: [
|
||||||
{relation: 'sale',
|
{relation: 'sale',
|
||||||
|
@ -21,6 +22,81 @@ export default class Controller extends Section {
|
||||||
};
|
};
|
||||||
this.getResolvedState();
|
this.getResolvedState();
|
||||||
this.maxResponsibility = 5;
|
this.maxResponsibility = 5;
|
||||||
|
this.smartTableOptions = {
|
||||||
|
activeButtons: {
|
||||||
|
search: true
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
field: 'claimDestinationFk',
|
||||||
|
autocomplete: {
|
||||||
|
url: 'ClaimDestinations',
|
||||||
|
showField: 'description',
|
||||||
|
valueField: 'id'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'landed',
|
||||||
|
searchable: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
exprBuilder(param, value) {
|
||||||
|
switch (param) {
|
||||||
|
case 'itemFk':
|
||||||
|
case 'ticketFk':
|
||||||
|
case 'claimDestinationFk':
|
||||||
|
case 'quantity':
|
||||||
|
case 'price':
|
||||||
|
case 'discount':
|
||||||
|
case 'total':
|
||||||
|
return {[param]: value};
|
||||||
|
case 'concept':
|
||||||
|
return {[param]: {like: `%${value}%`}};
|
||||||
|
case 'landed':
|
||||||
|
return {[param]: {between: this.dateRange(value)}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dateRange(value) {
|
||||||
|
const minHour = new Date(value);
|
||||||
|
minHour.setHours(0, 0, 0, 0);
|
||||||
|
const maxHour = new Date(value);
|
||||||
|
maxHour.setHours(23, 59, 59, 59);
|
||||||
|
|
||||||
|
return [minHour, maxHour];
|
||||||
|
}
|
||||||
|
|
||||||
|
get checked() {
|
||||||
|
const salesClaimed = this.$.model.data || [];
|
||||||
|
|
||||||
|
const checkedSalesClaimed = [];
|
||||||
|
for (let saleClaimed of salesClaimed) {
|
||||||
|
if (saleClaimed.$checked)
|
||||||
|
checkedSalesClaimed.push(saleClaimed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkedSalesClaimed;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDestination(saleClaimed, claimDestinationFk) {
|
||||||
|
const data = {rows: [saleClaimed], claimDestinationFk: claimDestinationFk};
|
||||||
|
this.$http.post(`Claims/updateClaimDestination`, data).then(() => {
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
}).catch(e => {
|
||||||
|
this.$.model.refresh();
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSales(saleClaimed) {
|
||||||
|
const params = {sales: [saleClaimed]};
|
||||||
|
this.$http.post(`ClaimEnds/deleteClamedSales`, params).then(() => {
|
||||||
|
this.$.model.refresh();
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getResolvedState() {
|
getResolvedState() {
|
||||||
|
@ -54,8 +130,8 @@ export default class Controller extends Section {
|
||||||
calculateTotals() {
|
calculateTotals() {
|
||||||
this.claimedTotal = 0;
|
this.claimedTotal = 0;
|
||||||
this.salesClaimed.forEach(sale => {
|
this.salesClaimed.forEach(sale => {
|
||||||
const price = sale.sale.quantity * sale.sale.price;
|
const price = sale.quantity * sale.price;
|
||||||
const discount = (sale.sale.discount * (sale.sale.quantity * sale.sale.price)) / 100;
|
const discount = (sale.discount * (sale.quantity * sale.price)) / 100;
|
||||||
this.claimedTotal += price - discount;
|
this.claimedTotal += price - discount;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -125,6 +201,24 @@ export default class Controller extends Section {
|
||||||
onSave() {
|
onSave() {
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onResponse() {
|
||||||
|
const rowsToEdit = [];
|
||||||
|
for (let row of this.checked)
|
||||||
|
rowsToEdit.push({id: row.id});
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
rows: rowsToEdit,
|
||||||
|
claimDestinationFk: this.newDestination
|
||||||
|
};
|
||||||
|
|
||||||
|
const query = `Claims/updateClaimDestination`;
|
||||||
|
this.$http.post(query, data)
|
||||||
|
.then(() => {
|
||||||
|
this.$.model.refresh();
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnClaimAction', {
|
ngModule.vnComponent('vnClaimAction', {
|
||||||
|
|
|
@ -43,9 +43,9 @@ describe('claim', () => {
|
||||||
describe('calculateTotals()', () => {
|
describe('calculateTotals()', () => {
|
||||||
it('should calculate the total price of the items claimed', () => {
|
it('should calculate the total price of the items claimed', () => {
|
||||||
controller.salesClaimed = [
|
controller.salesClaimed = [
|
||||||
{sale: {quantity: 5, price: 2, discount: 0}},
|
{quantity: 5, price: 2, discount: 0},
|
||||||
{sale: {quantity: 10, price: 2, discount: 0}},
|
{quantity: 10, price: 2, discount: 0},
|
||||||
{sale: {quantity: 10, price: 2, discount: 0}}
|
{quantity: 10, price: 2, discount: 0}
|
||||||
];
|
];
|
||||||
controller.calculateTotals();
|
controller.calculateTotals();
|
||||||
|
|
||||||
|
@ -151,5 +151,17 @@ describe('claim', () => {
|
||||||
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('Greuge added');
|
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('Greuge added');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('onResponse()', () => {
|
||||||
|
it('should perform a post query and show a success message', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
|
||||||
|
$httpBackend.expect('POST', `Claims/updateClaimDestination`).respond({});
|
||||||
|
controller.onResponse();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,3 +8,6 @@ Do you want to insert greuges?: Desea insertar greuges?
|
||||||
Insert greuges on client card: Insertar greuges en la ficha del cliente
|
Insert greuges on client card: Insertar greuges en la ficha del cliente
|
||||||
Greuge added: Greuge añadido
|
Greuge added: Greuge añadido
|
||||||
ClaimGreugeDescription: Reclamación id {{claimId}}
|
ClaimGreugeDescription: Reclamación id {{claimId}}
|
||||||
|
Change destination: Cambiar destino
|
||||||
|
Change destination to all selected rows: Cambiar destino a {{total}} fila(s) seleccionada(s)
|
||||||
|
Add observation to all selected clients: Añadir observación a {{total}} cliente(s) seleccionado(s)
|
||||||
|
|
|
@ -6,7 +6,7 @@ vn-claim-action {
|
||||||
align-content: center;
|
align-content: center;
|
||||||
|
|
||||||
vn-tool-bar {
|
vn-tool-bar {
|
||||||
flex: 1
|
flex: none
|
||||||
}
|
}
|
||||||
|
|
||||||
.vn-check {
|
.vn-check {
|
||||||
|
@ -39,4 +39,8 @@ vn-claim-action {
|
||||||
max-height: 350px;
|
max-height: 350px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
margin-left: 370px;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
auto-load="true"
|
auto-load="true"
|
||||||
|
filter="::$ctrl.filter"
|
||||||
url="ClaimDms"
|
url="ClaimDms"
|
||||||
link="{claimFk: $ctrl.$params.id}"
|
link="{claimFk: $ctrl.$params.id}"
|
||||||
limit="20"
|
limit="20"
|
||||||
|
@ -14,8 +15,13 @@
|
||||||
<section class="photo" ng-repeat="photo in $ctrl.photos">
|
<section class="photo" ng-repeat="photo in $ctrl.photos">
|
||||||
<section class="image vn-shadow" on-error-src
|
<section class="image vn-shadow" on-error-src
|
||||||
ng-style="{'background': 'url(' + $ctrl.getImagePath(photo.dmsFk) + ')'}"
|
ng-style="{'background': 'url(' + $ctrl.getImagePath(photo.dmsFk) + ')'}"
|
||||||
zoom-image="{{$ctrl.getImagePath(photo.dmsFk)}}">
|
zoom-image="{{$ctrl.getImagePath(photo.dmsFk)}}"
|
||||||
|
ng-if="photo.dms.contentType != 'video/mp4'">
|
||||||
</section>
|
</section>
|
||||||
|
<video id="videobcg" muted="muted" controls ng-if="photo.dms.contentType == 'video/mp4'"
|
||||||
|
class="video">
|
||||||
|
<source src="{{$ctrl.getImagePath(photo.dmsFk)}}" type="video/mp4">
|
||||||
|
</video>
|
||||||
<section class="actions">
|
<section class="actions">
|
||||||
<vn-button
|
<vn-button
|
||||||
class="round"
|
class="round"
|
||||||
|
@ -35,7 +41,7 @@
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
<vn-float-button
|
<vn-float-button
|
||||||
icon="add"
|
icon="add"
|
||||||
vn-tooltip="Select photo"
|
vn-tooltip="Select file"
|
||||||
vn-bind="+"
|
vn-bind="+"
|
||||||
ng-click="$ctrl.openUploadDialog()"
|
ng-click="$ctrl.openUploadDialog()"
|
||||||
fixed-bottom-right>
|
fixed-bottom-right>
|
||||||
|
|
|
@ -6,6 +6,13 @@ class Controller extends Section {
|
||||||
constructor($element, $, vnFile) {
|
constructor($element, $, vnFile) {
|
||||||
super($element, $);
|
super($element, $);
|
||||||
this.vnFile = vnFile;
|
this.vnFile = vnFile;
|
||||||
|
this.filter = {
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'dms'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteDms(index) {
|
deleteDms(index) {
|
||||||
|
@ -13,7 +20,7 @@ class Controller extends Section {
|
||||||
return this.$http.post(`ClaimDms/${dmsFk}/removeFile`)
|
return this.$http.post(`ClaimDms/${dmsFk}/removeFile`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$.model.remove(index);
|
this.$.model.remove(index);
|
||||||
this.vnApp.showSuccess(this.$t('Photo deleted'));
|
this.vnApp.showSuccess(this.$t('File deleted'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,13 +88,13 @@ class Controller extends Section {
|
||||||
data: this.dms.files
|
data: this.dms.files
|
||||||
};
|
};
|
||||||
this.$http(options).then(() => {
|
this.$http(options).then(() => {
|
||||||
this.vnApp.showSuccess(this.$t('Photo uploaded!'));
|
this.vnApp.showSuccess(this.$t('File uploaded!'));
|
||||||
this.$.model.refresh();
|
this.$.model.refresh();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getImagePath(dmsId) {
|
getImagePath(dmsId) {
|
||||||
return this.vnFile.getPath(`/api/dms/${dmsId}/downloadFile`);
|
return this.vnFile.getPath(`/api/Claims/${dmsId}/downloadFile`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Are you sure you want to continue?: ¿Seguro que quieres continuar?
|
Are you sure you want to continue?: ¿Seguro que quieres continuar?
|
||||||
Drag & Drop photos here...: Arrastra y suelta fotos aquí...
|
Drag & Drop photos here...: Arrastra y suelta fotos aquí...
|
||||||
Photo deleted: Foto eliminada
|
File deleted: Archivo eliminado
|
||||||
Photo uploaded!: Foto subida!
|
File uploaded!: Archivo subido!
|
||||||
Select photo: Seleccionar foto
|
Select file: Seleccionar fichero
|
|
@ -29,4 +29,19 @@ vn-claim-photos {
|
||||||
height: 288px;
|
height: 288px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),
|
||||||
|
0 3px 1px -2px rgba(0,0,0,.2),
|
||||||
|
0 1px 5px 0 rgba(0,0,0,.12);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
|
||||||
|
}
|
||||||
|
.video:hover {
|
||||||
|
border: 2px solid $color-primary
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,22 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('Client addressesPropagateRe', () => {
|
describe('Client addressesPropagateRe', () => {
|
||||||
|
beforeAll(async() => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should propagate the isEqualizated on both addresses of Mr Wayne and set hasToInvoiceByAddress to false', async() => {
|
it('should propagate the isEqualizated on both addresses of Mr Wayne and set hasToInvoiceByAddress to false', async() => {
|
||||||
const tx = await models.Client.beginTransaction({});
|
const tx = await models.Client.beginTransaction({});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('Address createAddress', () => {
|
describe('Address createAddress', () => {
|
||||||
const clientFk = 1101;
|
const clientFk = 1101;
|
||||||
|
@ -6,6 +7,21 @@ describe('Address createAddress', () => {
|
||||||
const incotermsFk = 'FAS';
|
const incotermsFk = 'FAS';
|
||||||
const customAgentOneId = 1;
|
const customAgentOneId = 1;
|
||||||
|
|
||||||
|
beforeAll(async() => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should throw a non uee member error if no incoterms is defined', async() => {
|
it('should throw a non uee member error if no incoterms is defined', async() => {
|
||||||
const tx = await models.Client.beginTransaction({});
|
const tx = await models.Client.beginTransaction({});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('Client Create', () => {
|
describe('Client Create', () => {
|
||||||
const newAccount = {
|
const newAccount = {
|
||||||
|
@ -12,6 +13,21 @@ describe('Client Create', () => {
|
||||||
businessTypeFk: 'florist'
|
businessTypeFk: 'florist'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
beforeAll(async() => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it(`should not find Deadpool as he's not created yet`, async() => {
|
it(`should not find Deadpool as he's not created yet`, async() => {
|
||||||
const tx = await models.Client.beginTransaction({});
|
const tx = await models.Client.beginTransaction({});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('Address updateAddress', () => {
|
describe('Address updateAddress', () => {
|
||||||
const clientId = 1101;
|
const clientId = 1101;
|
||||||
|
@ -13,6 +14,21 @@ describe('Address updateAddress', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
beforeAll(async() => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should throw the non uee member error if no incoterms is defined', async() => {
|
it('should throw the non uee member error if no incoterms is defined', async() => {
|
||||||
const tx = await models.Client.beginTransaction({});
|
const tx = await models.Client.beginTransaction({});
|
||||||
|
|
||||||
|
@ -33,7 +49,6 @@ describe('Address updateAddress', () => {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
expect(error.message).toEqual('Incoterms is required for a non UEE member');
|
expect(error.message).toEqual('Incoterms is required for a non UEE member');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -57,7 +72,6 @@ describe('Address updateAddress', () => {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
expect(error.message).toEqual('Customs agent is required for a non UEE member');
|
expect(error.message).toEqual('Customs agent is required for a non UEE member');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -91,8 +105,11 @@ describe('Address updateAddress', () => {
|
||||||
it('should return an error for a user without enough privileges', async() => {
|
it('should return an error for a user without enough privileges', async() => {
|
||||||
const tx = await models.Client.beginTransaction({});
|
const tx = await models.Client.beginTransaction({});
|
||||||
|
|
||||||
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
ctx.req.accessToken.userId = employeeId;
|
||||||
ctx.args = {
|
ctx.args = {
|
||||||
isLogifloraAllowed: true
|
isLogifloraAllowed: true
|
||||||
};
|
};
|
||||||
|
@ -124,8 +141,10 @@ describe('Address updateAddress', () => {
|
||||||
expect(address.isLogifloraAllowed).toEqual(true);
|
expect(address.isLogifloraAllowed).toEqual(true);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
ctx.req.accessToken.userId = employeeId;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
ctx.req.accessToken.userId = employeeId;
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,10 +1,25 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('Client updateFiscalData', () => {
|
describe('Client updateFiscalData', () => {
|
||||||
const clientId = 1101;
|
const clientId = 1101;
|
||||||
const employeeId = 1;
|
const employeeId = 1;
|
||||||
const salesAssistantId = 21;
|
const salesAssistantId = 21;
|
||||||
const administrativeId = 5;
|
const administrativeId = 5;
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: employeeId},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should return an error if the user is not salesAssistant and the isTaxDataChecked value is true', async() => {
|
it('should return an error if the user is not salesAssistant and the isTaxDataChecked value is true', async() => {
|
||||||
const tx = await models.Client.beginTransaction({});
|
const tx = await models.Client.beginTransaction({});
|
||||||
|
@ -24,7 +39,7 @@ describe('Client updateFiscalData', () => {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(error.message).toEqual(`You can't make changes on a client with verified data`);
|
expect(error.message).toEqual(`Not enough privileges to edit a client with verified data`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an error if the salesAssistant did not fill the sage data before checking verified data', async() => {
|
it('should return an error if the salesAssistant did not fill the sage data before checking verified data', async() => {
|
||||||
|
|
|
@ -12,11 +12,13 @@ describe('Client updatePortfolio', () => {
|
||||||
|
|
||||||
const expectedResult = 841.63;
|
const expectedResult = 841.63;
|
||||||
|
|
||||||
await models.Client.rawSql(`UPDATE vn.client SET salesPersonFk = ${salesPersonId} WHERE id = ${clientId}; `);
|
const clientQuery = `UPDATE vn.client SET salesPersonFk = ${salesPersonId} WHERE id = ${clientId}; `;
|
||||||
|
await models.Client.rawSql(clientQuery);
|
||||||
|
|
||||||
await models.Client.updatePortfolio();
|
await models.Client.updatePortfolio();
|
||||||
|
|
||||||
let [salesPerson] = await models.Client.rawSql(`SELECT portfolioWeight FROM bs.salesPerson WHERE workerFk = ${salesPersonId}; `, null, options);
|
const portfolioQuery = `SELECT portfolioWeight FROM bs.salesPerson WHERE workerFk = ${salesPersonId}; `;
|
||||||
|
const [salesPerson] = await models.Client.rawSql(portfolioQuery, null, options);
|
||||||
|
|
||||||
expect(salesPerson.portfolioWeight).toEqual(expectedResult);
|
expect(salesPerson.portfolioWeight).toEqual(expectedResult);
|
||||||
|
|
||||||
|
@ -26,8 +28,9 @@ describe('Client updatePortfolio', () => {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// task 3817
|
|
||||||
xit('should keep the same portfolioWeight when a salesperson is unassigned of a client', async() => {
|
it('should keep the same portfolioWeight when a salesperson is unassigned of a client', async() => {
|
||||||
|
pending('task 3817');
|
||||||
const salesPersonId = 19;
|
const salesPersonId = 19;
|
||||||
const tx = await models.Client.beginTransaction({});
|
const tx = await models.Client.beginTransaction({});
|
||||||
|
|
||||||
|
@ -40,7 +43,8 @@ describe('Client updatePortfolio', () => {
|
||||||
|
|
||||||
await models.Client.updatePortfolio();
|
await models.Client.updatePortfolio();
|
||||||
|
|
||||||
let [salesPerson] = await models.Client.rawSql(`SELECT portfolioWeight FROM bs.salesPerson WHERE workerFk = ${salesPersonId}; `, null, options);
|
const portfolioQuery = `SELECT portfolioWeight FROM bs.salesPerson WHERE workerFk = ${salesPersonId}; `;
|
||||||
|
const [salesPerson] = await models.Client.rawSql(portfolioQuery, null, options);
|
||||||
|
|
||||||
expect(salesPerson.portfolioWeight).toEqual(expectedResult);
|
expect(salesPerson.portfolioWeight).toEqual(expectedResult);
|
||||||
|
|
||||||
|
|
|
@ -89,11 +89,12 @@ module.exports = function(Self) {
|
||||||
const args = ctx.args;
|
const args = ctx.args;
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions);
|
|
||||||
|
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions);
|
||||||
|
|
||||||
if (args.isLogifloraAllowed && !isSalesAssistant)
|
if (args.isLogifloraAllowed && !isSalesAssistant)
|
||||||
throw new UserError(`You don't have enough privileges`);
|
throw new UserError(`You don't have enough privileges`);
|
||||||
|
|
||||||
|
|
|
@ -125,11 +125,11 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions);
|
const isAdministrative = await models.Account.hasRole(userId, 'administrative', myOptions);
|
||||||
const client = await models.Client.findById(clientId, null, myOptions);
|
const client = await models.Client.findById(clientId, null, myOptions);
|
||||||
|
|
||||||
if (!isSalesAssistant && client.isTaxDataChecked)
|
if (!isAdministrative && client.isTaxDataChecked)
|
||||||
throw new UserError(`You can't make changes on a client with verified data`);
|
throw new UserError(`Not enough privileges to edit a client with verified data`);
|
||||||
|
|
||||||
// Sage data validation
|
// Sage data validation
|
||||||
const taxDataChecked = args.isTaxDataChecked;
|
const taxDataChecked = args.isTaxDataChecked;
|
||||||
|
|
|
@ -46,10 +46,6 @@ module.exports = Self => {
|
||||||
message: 'TIN must be unique'
|
message: 'TIN must be unique'
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.validatesUniquenessOf('socialName', {
|
|
||||||
message: 'The company name must be unique'
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.validatesFormatOf('email', {
|
Self.validatesFormatOf('email', {
|
||||||
message: 'Invalid email',
|
message: 'Invalid email',
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
@ -63,17 +59,37 @@ module.exports = Self => {
|
||||||
min: 3, max: 10
|
min: 3, max: 10
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Self.validateAsync('socialName', socialNameIsUnique, {
|
||||||
|
message: 'The company name must be unique'
|
||||||
|
});
|
||||||
|
|
||||||
|
async function socialNameIsUnique(err, done) {
|
||||||
|
const filter = {
|
||||||
|
where: {
|
||||||
|
and: [
|
||||||
|
{socialName: this.socialName},
|
||||||
|
{isActive: true},
|
||||||
|
{id: {neq: this.id}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const client = await Self.app.models.Client.findOne(filter);
|
||||||
|
if (client)
|
||||||
|
err();
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
Self.validateAsync('iban', ibanNeedsValidation, {
|
Self.validateAsync('iban', ibanNeedsValidation, {
|
||||||
message: 'The IBAN does not have the correct format'
|
message: 'The IBAN does not have the correct format'
|
||||||
});
|
});
|
||||||
|
|
||||||
async function ibanNeedsValidation(err, done) {
|
async function ibanNeedsValidation(err, done) {
|
||||||
let filter = {
|
const filter = {
|
||||||
fields: ['code'],
|
fields: ['code'],
|
||||||
where: {id: this.countryFk}
|
where: {id: this.countryFk}
|
||||||
};
|
};
|
||||||
let country = await Self.app.models.Country.findOne(filter);
|
const country = await Self.app.models.Country.findOne(filter);
|
||||||
let code = country ? country.code.toLowerCase() : null;
|
const code = country ? country.code.toLowerCase() : null;
|
||||||
if (code != 'es')
|
if (code != 'es')
|
||||||
return done();
|
return done();
|
||||||
|
|
||||||
|
@ -90,12 +106,12 @@ module.exports = Self => {
|
||||||
if (!this.isTaxDataChecked)
|
if (!this.isTaxDataChecked)
|
||||||
return done();
|
return done();
|
||||||
|
|
||||||
let filter = {
|
const filter = {
|
||||||
fields: ['code'],
|
fields: ['code'],
|
||||||
where: {id: this.countryFk}
|
where: {id: this.countryFk}
|
||||||
};
|
};
|
||||||
let country = await Self.app.models.Country.findOne(filter);
|
const country = await Self.app.models.Country.findOne(filter);
|
||||||
let code = country ? country.code.toLowerCase() : null;
|
const code = country ? country.code.toLowerCase() : null;
|
||||||
|
|
||||||
if (!this.fi || !validateTin(this.fi, code))
|
if (!this.fi || !validateTin(this.fi, code))
|
||||||
err();
|
err();
|
||||||
|
@ -118,8 +134,8 @@ module.exports = Self => {
|
||||||
function cannotHaveET(err) {
|
function cannotHaveET(err) {
|
||||||
if (!this.fi) return;
|
if (!this.fi) return;
|
||||||
|
|
||||||
let tin = this.fi.toUpperCase();
|
const tin = this.fi.toUpperCase();
|
||||||
let cannotHaveET = /^[A-B]/.test(tin);
|
const cannotHaveET = /^[A-B]/.test(tin);
|
||||||
|
|
||||||
if (cannotHaveET && this.isEqualizated)
|
if (cannotHaveET && this.isEqualizated)
|
||||||
err();
|
err();
|
||||||
|
@ -208,6 +224,34 @@ module.exports = Self => {
|
||||||
throw new UserError(`The type of business must be filled in basic data`);
|
throw new UserError(`The type of business must be filled in basic data`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Self.observe('before save', async ctx => {
|
||||||
|
const changes = ctx.data || ctx.instance;
|
||||||
|
const orgData = ctx.currentInstance;
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||||
|
const userId = loopBackContext.active.accessToken.userId;
|
||||||
|
|
||||||
|
const isAdministrative = await models.Account.hasRole(userId, 'administrative', ctx.options);
|
||||||
|
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', ctx.options);
|
||||||
|
const hasChanges = orgData && changes;
|
||||||
|
|
||||||
|
const isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked);
|
||||||
|
const isTaxDataCheckedChanged = hasChanges && orgData.isTaxDataChecked != isTaxDataChecked;
|
||||||
|
|
||||||
|
const sageTaxType = hasChanges && (changes.sageTaxTypeFk || orgData.sageTaxTypeFk);
|
||||||
|
const sageTaxTypeChanged = hasChanges && orgData.sageTaxTypeFk != sageTaxType;
|
||||||
|
|
||||||
|
const sageTransactionType = hasChanges && (changes.sageTransactionTypeFk || orgData.sageTransactionTypeFk);
|
||||||
|
const sageTransactionTypeChanged = hasChanges && orgData.sageTransactionTypeFk != sageTransactionType;
|
||||||
|
|
||||||
|
const cantEditVerifiedData = isTaxDataCheckedChanged && !isAdministrative;
|
||||||
|
const cantChangeSageData = (sageTaxTypeChanged || sageTransactionTypeChanged) && !isSalesAssistant;
|
||||||
|
|
||||||
|
if (cantEditVerifiedData || cantChangeSageData)
|
||||||
|
throw new UserError(`You don't have enough privileges`);
|
||||||
|
});
|
||||||
|
|
||||||
Self.observe('before save', async function(ctx) {
|
Self.observe('before save', async function(ctx) {
|
||||||
const changes = ctx.data || ctx.instance;
|
const changes = ctx.data || ctx.instance;
|
||||||
const orgData = ctx.currentInstance;
|
const orgData = ctx.currentInstance;
|
||||||
|
@ -221,10 +265,10 @@ module.exports = Self => {
|
||||||
|
|
||||||
const socialNameChanged = hasChanges
|
const socialNameChanged = hasChanges
|
||||||
&& orgData.socialName != socialName;
|
&& orgData.socialName != socialName;
|
||||||
const dataCheckedChanged = hasChanges
|
const isTaxDataCheckedChanged = hasChanges
|
||||||
&& orgData.isTaxDataChecked != isTaxDataChecked;
|
&& orgData.isTaxDataChecked != isTaxDataChecked;
|
||||||
|
|
||||||
if ((socialNameChanged || dataCheckedChanged) && !isAlpha(socialName))
|
if ((socialNameChanged || isTaxDataCheckedChanged) && !isAlpha(socialName))
|
||||||
throw new UserError(`The social name has an invalid format`);
|
throw new UserError(`The social name has an invalid format`);
|
||||||
|
|
||||||
if (changes.salesPerson === null) {
|
if (changes.salesPerson === null) {
|
||||||
|
|
|
@ -1,20 +1,36 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('loopback model address', () => {
|
describe('loopback model address', () => {
|
||||||
let createdAddressId;
|
let createdAddressId;
|
||||||
const clientId = 1101;
|
const clientId = 1101;
|
||||||
|
|
||||||
afterAll(async() => {
|
const activeCtx = {
|
||||||
let client = await app.models.Client.findById(clientId);
|
accessToken: {userId: 9},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
await app.models.Address.destroyById(createdAddressId);
|
beforeAll(() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async() => {
|
||||||
|
const client = await models.Client.findById(clientId);
|
||||||
|
|
||||||
|
await models.Address.destroyById(createdAddressId);
|
||||||
await client.updateAttribute('isEqualizated', false);
|
await client.updateAttribute('isEqualizated', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('observe()', () => {
|
describe('observe()', () => {
|
||||||
it('should throw an error when deactivating a consignee if its the default address', async() => {
|
it('should throw an error when deactivating a consignee if its the default address', async() => {
|
||||||
let error;
|
let error;
|
||||||
let address = await app.models.Address.findById(1);
|
const address = await models.Address.findById(1);
|
||||||
|
|
||||||
await address.updateAttribute('isActive', false)
|
await address.updateAttribute('isActive', false)
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
|
@ -27,13 +43,13 @@ describe('loopback model address', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set isEqualizated to true of a given Client to trigger any new address to have it', async() => {
|
it('should set isEqualizated to true of a given Client to trigger any new address to have it', async() => {
|
||||||
let client = await app.models.Client.findById(clientId);
|
const client = await models.Client.findById(clientId);
|
||||||
|
|
||||||
expect(client.isEqualizated).toBeFalsy();
|
expect(client.isEqualizated).toBeFalsy();
|
||||||
|
|
||||||
await client.updateAttribute('isEqualizated', true);
|
await client.updateAttribute('isEqualizated', true);
|
||||||
|
|
||||||
let newAddress = await app.models.Address.create({
|
const newAddress = await models.Address.create({
|
||||||
clientFk: clientId,
|
clientFk: clientId,
|
||||||
agencyModeFk: 5,
|
agencyModeFk: 5,
|
||||||
city: 'here',
|
city: 'here',
|
||||||
|
|
|
@ -105,7 +105,7 @@
|
||||||
<div ng-transclude="btnFive">
|
<div ng-transclude="btnFive">
|
||||||
<vn-quick-link
|
<vn-quick-link
|
||||||
ng-if="$ctrl.client.supplier.nif"
|
ng-if="$ctrl.client.supplier.nif"
|
||||||
tooltip="Go to client"
|
tooltip="Go to supplier"
|
||||||
state="['supplier.card.summary', {id: $ctrl.client.supplier.id}]"
|
state="['supplier.card.summary', {id: $ctrl.client.supplier.id}]"
|
||||||
icon="icon-supplier">
|
icon="icon-supplier">
|
||||||
</vn-quick-link>
|
</vn-quick-link>
|
||||||
|
|
|
@ -3,5 +3,6 @@ View consumer report: Ver informe de consumo
|
||||||
From date: Fecha desde
|
From date: Fecha desde
|
||||||
To date: Fecha hasta
|
To date: Fecha hasta
|
||||||
Go to user: Ir al usuario
|
Go to user: Ir al usuario
|
||||||
|
Go to supplier: Ir al proveedor
|
||||||
Client invoices list: Listado de facturas del cliente
|
Client invoices list: Listado de facturas del cliente
|
||||||
Pay method: Forma de pago
|
Pay method: Forma de pago
|
|
@ -33,7 +33,7 @@
|
||||||
label="Social name"
|
label="Social name"
|
||||||
ng-model="$ctrl.client.socialName"
|
ng-model="$ctrl.client.socialName"
|
||||||
rule
|
rule
|
||||||
info="You can use letters and spaces"
|
info="Only letters, numbers and spaces can be used"
|
||||||
required="true">
|
required="true">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
|
@ -182,7 +182,7 @@
|
||||||
vn-one
|
vn-one
|
||||||
label="Verified data"
|
label="Verified data"
|
||||||
ng-model="$ctrl.client.isTaxDataChecked"
|
ng-model="$ctrl.client.isTaxDataChecked"
|
||||||
vn-acl="salesAssistant">
|
vn-acl="administrative">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
|
@ -3,7 +3,7 @@ You changed the equalization tax: Has cambiado el recargo de equivalencia
|
||||||
Do you want to spread the change?: ¿Deseas propagar el cambio a sus consignatarios?
|
Do you want to spread the change?: ¿Deseas propagar el cambio a sus consignatarios?
|
||||||
Frozen: Congelado
|
Frozen: Congelado
|
||||||
In order to invoice, this field is not consulted, but the consignee's ET. When modifying this field if the invoice by address option is not checked, the change will be automatically propagated to all addresses, otherwise the user will be asked if he wants to propagate it or not.: Para facturar no se consulta este campo, sino el RE de consignatario. Al modificar este campo si no esta marcada la casilla Facturar por consignatario, se propagará automáticamente el cambio a todos los consignatarios, en caso contrario preguntará al usuario si quiere o no propagar.
|
In order to invoice, this field is not consulted, but the consignee's ET. When modifying this field if the invoice by address option is not checked, the change will be automatically propagated to all addresses, otherwise the user will be asked if he wants to propagate it or not.: Para facturar no se consulta este campo, sino el RE de consignatario. Al modificar este campo si no esta marcada la casilla Facturar por consignatario, se propagará automáticamente el cambio a todos los consignatarios, en caso contrario preguntará al usuario si quiere o no propagar.
|
||||||
You can use letters and spaces: Se pueden utilizar letras y espacios
|
Only letters, numbers and spaces can be used: Sólo se pueden usar letras, numeros y espacios
|
||||||
Found a client with this data: Se ha encontrado un cliente con estos datos
|
Found a client with this data: Se ha encontrado un cliente con estos datos
|
||||||
Found a client with this phone or email: El cliente con id <a href="#!/client/{{clientId}}/summary" target="_blank">{{clientId}}</a> ya tiene este teléfono o email. <br/> ¿Quieres continuar?
|
Found a client with this phone or email: El cliente con id <a href="#!/client/{{clientId}}/summary" target="_blank">{{clientId}}</a> ya tiene este teléfono o email. <br/> ¿Quieres continuar?
|
||||||
Sage tax type: Tipo de impuesto Sage
|
Sage tax type: Tipo de impuesto Sage
|
||||||
|
|
|
@ -81,7 +81,7 @@ export default class Controller extends Section {
|
||||||
clientIds: clientIds
|
clientIds: clientIds
|
||||||
}, this.campaign);
|
}, this.campaign);
|
||||||
|
|
||||||
this.$http.post('notify/consumption', params)
|
this.$http.post('schedule/consumption', params)
|
||||||
.then(() => this.$.filters.hide())
|
.then(() => this.$.filters.hide())
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('Notifications sent!')));
|
.then(() => this.vnApp.showSuccess(this.$t('Notifications sent!')));
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ describe('Client notification', () => {
|
||||||
clientIds: [1101, 1102]
|
clientIds: [1101, 1102]
|
||||||
}, controller.campaign);
|
}, controller.campaign);
|
||||||
|
|
||||||
$httpBackend.expect('POST', `notify/consumption`, params).respond(200, params);
|
$httpBackend.expect('POST', `schedule/consumption`, params).respond(200, params);
|
||||||
controller.onSendClientConsumption();
|
controller.onSendClientConsumption();
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,32 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethod('editLatestBuys', {
|
Self.remoteMethodCtx('editLatestBuys', {
|
||||||
description: 'Updates a column for one or more buys',
|
description: 'Updates a column for one or more buys',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [{
|
accepts: [{
|
||||||
arg: 'field',
|
arg: 'field',
|
||||||
type: 'String',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
description: `the column to edit`
|
description: `the column to edit`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
arg: 'newValue',
|
arg: 'newValue',
|
||||||
type: 'Any',
|
type: 'any',
|
||||||
required: true,
|
required: true,
|
||||||
description: `The new value to save`
|
description: `The new value to save`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
arg: 'lines',
|
arg: 'lines',
|
||||||
type: ['Object'],
|
type: ['object'],
|
||||||
required: true,
|
required: true,
|
||||||
description: `the buys which will be modified`
|
description: `the buys which will be modified`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string'
|
||||||
}],
|
}],
|
||||||
returns: {
|
returns: {
|
||||||
type: 'Object',
|
type: 'object',
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
|
@ -30,7 +35,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.editLatestBuys = async(field, newValue, lines, options) => {
|
Self.editLatestBuys = async(ctx, field, newValue, lines, filter, options) => {
|
||||||
let tx;
|
let tx;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
|
|
||||||
|
@ -64,17 +69,19 @@ module.exports = Self => {
|
||||||
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const model = models[modelName];
|
const model = models[modelName];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
const value = {};
|
||||||
|
value[field] = newValue;
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
ctx.args.filter = {where: filter, limit: null};
|
||||||
|
lines = await models.Buy.latestBuysFilter(ctx, null, myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
const targets = lines.map(line => {
|
const targets = lines.map(line => {
|
||||||
return line[identifier];
|
return line[identifier];
|
||||||
});
|
});
|
||||||
|
|
||||||
const value = {};
|
|
||||||
value[field] = newValue;
|
|
||||||
|
|
||||||
for (let target of targets)
|
for (let target of targets)
|
||||||
promises.push(model.upsertWithWhere({id: target}, value, myOptions));
|
promises.push(model.upsertWithWhere({id: target}, value, myOptions));
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
type: ['Object'],
|
type: ['object'],
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
|
@ -98,6 +98,9 @@ module.exports = Self => {
|
||||||
Self.latestBuysFilter = async(ctx, filter, options) => {
|
Self.latestBuysFilter = async(ctx, filter, options) => {
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (filter && filter.modelParams)
|
||||||
|
ctx.args = filter.modelParams;
|
||||||
|
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ describe('Buy editLatestsBuys()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let ctx = {
|
const ctx = {
|
||||||
args: {
|
args: {
|
||||||
search: 'Ranged weapon longbow 2m'
|
search: 'Ranged weapon longbow 2m'
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,35 @@ describe('Buy editLatestsBuys()', () => {
|
||||||
const newValue = 99;
|
const newValue = 99;
|
||||||
const lines = [{itemFk: original.itemFk, id: original.id}];
|
const lines = [{itemFk: original.itemFk, id: original.id}];
|
||||||
|
|
||||||
await models.Buy.editLatestBuys(field, newValue, lines, options);
|
await models.Buy.editLatestBuys(ctx, field, newValue, lines, null, options);
|
||||||
|
|
||||||
|
const [result] = await models.Buy.latestBuysFilter(ctx, null, options);
|
||||||
|
|
||||||
|
expect(result[field]).toEqual(newValue);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change the value of a given column for filter', async() => {
|
||||||
|
const tx = await models.Buy.beginTransaction({});
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const filter = {typeFk: 1};
|
||||||
|
const ctx = {
|
||||||
|
args: {
|
||||||
|
filter: filter
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const field = 'size';
|
||||||
|
const newValue = 88;
|
||||||
|
|
||||||
|
await models.Buy.editLatestBuys(ctx, field, newValue, null, filter, options);
|
||||||
|
|
||||||
const [result] = await models.Buy.latestBuysFilter(ctx, null, options);
|
const [result] = await models.Buy.latestBuysFilter(ctx, null, options);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
const LoopBackContext = require('loopback-context');
|
const LoopBackContext = require('loopback-context');
|
||||||
|
const activeCtx = {accessToken: {userId: 9}};
|
||||||
|
|
||||||
describe('entry importBuysPreview()', () => {
|
describe('entry importBuysPreview()', () => {
|
||||||
const entryId = 1;
|
const entryId = 1;
|
||||||
|
|
|
@ -29,9 +29,11 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th shrink>
|
<th shrink>
|
||||||
<vn-multi-check
|
<vn-multi-check
|
||||||
checked="$ctrl.checkAll"
|
|
||||||
model="model"
|
model="model"
|
||||||
check-field="$checked">
|
checked="$ctrl.checkAll"
|
||||||
|
check-field="checked"
|
||||||
|
check-dummy-enabled="true"
|
||||||
|
checked-dummy-count="$ctrl.checkedDummyCount">
|
||||||
</vn-multi-check>
|
</vn-multi-check>
|
||||||
</th>
|
</th>
|
||||||
<th translate>Picture</th>
|
<th translate>Picture</th>
|
||||||
|
@ -126,7 +128,7 @@
|
||||||
}">
|
}">
|
||||||
<td>
|
<td>
|
||||||
<vn-check
|
<vn-check
|
||||||
ng-model="buy.$checked"
|
ng-model="buy.checked"
|
||||||
vn-click-stop>
|
vn-click-stop>
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -6,7 +6,7 @@ export default class Controller extends Section {
|
||||||
constructor($element, $) {
|
constructor($element, $) {
|
||||||
super($element, $);
|
super($element, $);
|
||||||
this.editedColumn;
|
this.editedColumn;
|
||||||
this.$checkAll = false;
|
this.checkAll = false;
|
||||||
|
|
||||||
this.smartTableOptions = {
|
this.smartTableOptions = {
|
||||||
activeButtons: {
|
activeButtons: {
|
||||||
|
@ -91,7 +91,7 @@ export default class Controller extends Section {
|
||||||
const buys = this.$.model.data || [];
|
const buys = this.$.model.data || [];
|
||||||
const checkedBuys = [];
|
const checkedBuys = [];
|
||||||
for (let buy of buys) {
|
for (let buy of buys) {
|
||||||
if (buy.$checked)
|
if (buy.checked)
|
||||||
checkedBuys.push(buy);
|
checkedBuys.push(buy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +142,9 @@ export default class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
get totalChecked() {
|
get totalChecked() {
|
||||||
|
if (this.checkedDummyCount)
|
||||||
|
return this.checkedDummyCount;
|
||||||
|
|
||||||
return this.checked.length;
|
return this.checked.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +159,9 @@ export default class Controller extends Section {
|
||||||
lines: rowsToEdit
|
lines: rowsToEdit
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.checkedDummyCount && this.checkedDummyCount > 0)
|
||||||
|
data.filter = this.$.model.userParams;
|
||||||
|
|
||||||
return this.$http.post('Buys/editLatestBuys', data)
|
return this.$http.post('Buys/editLatestBuys', data)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.uncheck();
|
this.uncheck();
|
||||||
|
|
|
@ -31,10 +31,10 @@ describe('Entry', () => {
|
||||||
describe('get checked', () => {
|
describe('get checked', () => {
|
||||||
it(`should return a set of checked lines`, () => {
|
it(`should return a set of checked lines`, () => {
|
||||||
controller.$.model.data = [
|
controller.$.model.data = [
|
||||||
{$checked: true, id: 1},
|
{checked: true, id: 1},
|
||||||
{$checked: true, id: 2},
|
{checked: true, id: 2},
|
||||||
{$checked: true, id: 3},
|
{checked: true, id: 3},
|
||||||
{$checked: false, id: 4},
|
{checked: false, id: 4},
|
||||||
];
|
];
|
||||||
|
|
||||||
let result = controller.checked;
|
let result = controller.checked;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="buysModel"
|
vn-id="buysModel"
|
||||||
url="Entries/{{$ctrl.$params.id}}/getBuys"
|
url="Entries/{{$ctrl.entry.id}}/getBuys"
|
||||||
limit="5"
|
limit="5"
|
||||||
data="buys"
|
data="buys"
|
||||||
auto-load="true">
|
auto-load="true">
|
||||||
|
|
|
@ -4,6 +4,9 @@ import Summary from 'salix/components/summary';
|
||||||
|
|
||||||
class Controller extends Summary {
|
class Controller extends Summary {
|
||||||
get entry() {
|
get entry() {
|
||||||
|
if (!this._entry)
|
||||||
|
return this.$params;
|
||||||
|
|
||||||
return this._entry;
|
return this._entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,6 +144,7 @@ module.exports = Self => {
|
||||||
ii.isBooked,
|
ii.isBooked,
|
||||||
ii.supplierRef,
|
ii.supplierRef,
|
||||||
ii.docFk AS dmsFk,
|
ii.docFk AS dmsFk,
|
||||||
|
dm.file,
|
||||||
ii.supplierFk,
|
ii.supplierFk,
|
||||||
ii.expenceFkDeductible deductibleExpenseFk,
|
ii.expenceFkDeductible deductibleExpenseFk,
|
||||||
s.name AS supplierName,
|
s.name AS supplierName,
|
||||||
|
@ -156,7 +157,8 @@ module.exports = Self => {
|
||||||
LEFT JOIN duaInvoiceIn dii ON dii.invoiceInFk = ii.id
|
LEFT JOIN duaInvoiceIn dii ON dii.invoiceInFk = ii.id
|
||||||
LEFT JOIN dua d ON d.id = dii.duaFk
|
LEFT JOIN dua d ON d.id = dii.duaFk
|
||||||
LEFT JOIN awb ON awb.id = d.awbFk
|
LEFT JOIN awb ON awb.id = d.awbFk
|
||||||
LEFT JOIN company co ON co.id = ii.companyFk`
|
LEFT JOIN company co ON co.id = ii.companyFk
|
||||||
|
LEFT JOIN dms dm ON dm.id = ii.docFk`
|
||||||
);
|
);
|
||||||
|
|
||||||
const sqlWhere = conn.makeWhere(filter.where);
|
const sqlWhere = conn.makeWhere(filter.where);
|
||||||
|
|
|
@ -30,7 +30,7 @@ module.exports = Self => {
|
||||||
SUM(iidd.amount) totalDueDay
|
SUM(iidd.amount) totalDueDay
|
||||||
FROM vn.invoiceIn ii
|
FROM vn.invoiceIn ii
|
||||||
LEFT JOIN (SELECT SUM(iit.taxableBase) totalTaxableBase,
|
LEFT JOIN (SELECT SUM(iit.taxableBase) totalTaxableBase,
|
||||||
SUM(iit.taxableBase * (1 + (ti.PorcentajeIva / 100))) totalVat
|
CAST(SUM(iit.taxableBase * (1 + (ti.PorcentajeIva / 100))) AS DECIMAL(10,2)) totalVat
|
||||||
FROM vn.invoiceInTax iit
|
FROM vn.invoiceInTax iit
|
||||||
LEFT JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk
|
LEFT JOIN sage.TiposIva ti ON ti.CodigoIva = iit.taxTypeSageFk
|
||||||
WHERE iit.invoiceInFk = ?) iit ON TRUE
|
WHERE iit.invoiceInFk = ?) iit ON TRUE
|
||||||
|
|
|
@ -13,13 +13,14 @@
|
||||||
vn-id="supplier"
|
vn-id="supplier"
|
||||||
url="Suppliers"
|
url="Suppliers"
|
||||||
label="Supplier"
|
label="Supplier"
|
||||||
search-function="{or: [{id: $search}, {name: {like: '%'+ $search +'%'}}]}"
|
search-function="{or: [{id: $search}, {name: {like: '%'+ $search +'%'}}, {nif: {like: '%'+ $search +'%'}}]}"
|
||||||
|
fields="['nif']"
|
||||||
show-field="name"
|
show-field="name"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
ng-model="$ctrl.invoiceIn.supplierFk"
|
ng-model="$ctrl.invoiceIn.supplierFk"
|
||||||
order="id"
|
order="id"
|
||||||
vn-focus>
|
vn-focus>
|
||||||
<tpl-item>{{id}}: {{name}}</tpl-item>
|
<tpl-item>{{id}}: {{nif}}: {{name}}</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
|
@ -30,14 +31,6 @@
|
||||||
label="Issued"
|
label="Issued"
|
||||||
ng-model="$ctrl.invoiceIn.issued">
|
ng-model="$ctrl.invoiceIn.issued">
|
||||||
</vn-date-picker>
|
</vn-date-picker>
|
||||||
<vn-autocomplete
|
|
||||||
vn-one
|
|
||||||
label="Currency"
|
|
||||||
ng-model="$ctrl.invoiceIn.currencyFk"
|
|
||||||
url="Currencies"
|
|
||||||
show-field="code"
|
|
||||||
value-field="id">
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
label="Company"
|
label="Company"
|
||||||
|
|
|
@ -25,6 +25,9 @@
|
||||||
ng-model="invoiceInDueDay.bankFk"
|
ng-model="invoiceInDueDay.bankFk"
|
||||||
url="Banks"
|
url="Banks"
|
||||||
show-field="bank"
|
show-field="bank"
|
||||||
|
select-fields="['id','bank']"
|
||||||
|
order="id"
|
||||||
|
search-function="$ctrl.bankSearchFunc($search)"
|
||||||
rule>
|
rule>
|
||||||
<tpl-item>{{id}}: {{bank}}</tpl-item>
|
<tpl-item>{{id}}: {{bank}}</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
|
|
|
@ -17,6 +17,12 @@ class Controller extends Section {
|
||||||
this.card.reload();
|
this.card.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bankSearchFunc($search) {
|
||||||
|
return /^\d+$/.test($search)
|
||||||
|
? {id: $search}
|
||||||
|
: {bank: {like: '%' + $search + '%'}};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInDueDay', {
|
ngModule.vnComponent('vnInvoiceInDueDay', {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<vn-th field="supplierRef">Supplier ref.</vn-th>
|
<vn-th field="supplierRef">Supplier ref.</vn-th>
|
||||||
<vn-th field="serialNumber">Serial number</vn-th>
|
<vn-th field="serialNumber">Serial number</vn-th>
|
||||||
<vn-th field="serial">Serial</vn-th>
|
<vn-th field="serial">Serial</vn-th>
|
||||||
<vn-th field="account">Account</vn-th>
|
<vn-th field="dmsFk">File</vn-th>
|
||||||
<vn-th field="issued" expand>Issued</vn-th>
|
<vn-th field="issued" expand>Issued</vn-th>
|
||||||
<vn-th field="isBooked" center>Is booked</vn-th>
|
<vn-th field="isBooked" center>Is booked</vn-th>
|
||||||
<vn-th field="awbCode" vn-tooltip="Air Waybill">AWB</vn-th>
|
<vn-th field="awbCode" vn-tooltip="Air Waybill">AWB</vn-th>
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
class="clickable vn-tr search-result"
|
class="clickable vn-tr search-result"
|
||||||
ui-sref="invoiceIn.card.summary({id: {{::invoiceIn.id}}})">
|
ui-sref="invoiceIn.card.summary({id: {{::invoiceIn.id}}})">
|
||||||
<vn-td>{{::invoiceIn.id}}</vn-td>
|
<vn-td>{{::invoiceIn.id}}</vn-td>
|
||||||
<vn-td>
|
<vn-td expand>
|
||||||
<span
|
<span
|
||||||
class="link"
|
class="link"
|
||||||
vn-click-stop="supplierDescriptor.show($event, invoiceIn.supplierFk)">
|
vn-click-stop="supplierDescriptor.show($event, invoiceIn.supplierFk)">
|
||||||
|
@ -37,7 +37,12 @@
|
||||||
<vn-td>{{::invoiceIn.supplierRef | dashIfEmpty}}</vn-td>
|
<vn-td>{{::invoiceIn.supplierRef | dashIfEmpty}}</vn-td>
|
||||||
<vn-td>{{::invoiceIn.serialNumber}}</vn-td>
|
<vn-td>{{::invoiceIn.serialNumber}}</vn-td>
|
||||||
<vn-td>{{::invoiceIn.serial}}</vn-td>
|
<vn-td>{{::invoiceIn.serial}}</vn-td>
|
||||||
<vn-td>{{::invoiceIn.account}}</vn-td>
|
<vn-td>
|
||||||
|
<span title="{{'Download file' | translate}}" class="link"
|
||||||
|
ng-click="$ctrl.downloadFile(invoiceIn.dmsFk)">
|
||||||
|
{{::invoiceIn.file}}
|
||||||
|
</span>
|
||||||
|
</vn-td>
|
||||||
<vn-td expand>{{::invoiceIn.issued | date:'dd/MM/yyyy' | dashIfEmpty}}</vn-td>
|
<vn-td expand>{{::invoiceIn.issued | date:'dd/MM/yyyy' | dashIfEmpty}}</vn-td>
|
||||||
<vn-td center>
|
<vn-td center>
|
||||||
<vn-check disabled="true"
|
<vn-check disabled="true"
|
||||||
|
|
|
@ -2,6 +2,11 @@ import ngModule from '../module';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
|
|
||||||
export default class Controller extends Section {
|
export default class Controller extends Section {
|
||||||
|
constructor($element, $, vnFile) {
|
||||||
|
super($element, $, vnFile);
|
||||||
|
this.vnFile = vnFile;
|
||||||
|
}
|
||||||
|
|
||||||
exprBuilder(param, value) {
|
exprBuilder(param, value) {
|
||||||
switch (param) {
|
switch (param) {
|
||||||
case 'issued':
|
case 'issued':
|
||||||
|
@ -39,7 +44,13 @@ export default class Controller extends Section {
|
||||||
this.selectedInvoiceIn = invoiceIn;
|
this.selectedInvoiceIn = invoiceIn;
|
||||||
this.$.summary.show();
|
this.$.summary.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadFile(dmsId) {
|
||||||
|
this.vnFile.download(`api/dms/${dmsId}/downloadFile`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.$inject = ['$element', '$scope', 'vnFile'];
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInIndex', {
|
ngModule.vnComponent('vnInvoiceInIndex', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -33,6 +33,13 @@
|
||||||
show-field="id"
|
show-field="id"
|
||||||
rule>
|
rule>
|
||||||
<tpl-item>{{id}}: {{name}}</tpl-item>
|
<tpl-item>{{id}}: {{name}}</tpl-item>
|
||||||
|
<append>
|
||||||
|
<vn-icon-button
|
||||||
|
vn-tooltip="Create expense"
|
||||||
|
icon="add_circle"
|
||||||
|
vn-click-stop="createExpense.show()">
|
||||||
|
</vn-icon-button>
|
||||||
|
</append>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-input-number vn-one
|
<vn-input-number vn-one
|
||||||
disabled="$ctrl.invoiceIn.currency.code != 'EUR'"
|
disabled="$ctrl.invoiceIn.currency.code != 'EUR'"
|
||||||
|
@ -97,3 +104,40 @@
|
||||||
</vn-submit>
|
</vn-submit>
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Dialog of create expense-->
|
||||||
|
<vn-dialog
|
||||||
|
vn-id="createExpense"
|
||||||
|
on-accept="$ctrl.onResponse()">
|
||||||
|
<tpl-body>
|
||||||
|
<section>
|
||||||
|
<h5 class="vn-py-sm">{{$ctrl.$t('New expense')}}</h5>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield vn-one
|
||||||
|
vn-id="code"
|
||||||
|
label="Code"
|
||||||
|
ng-model="$ctrl.expense.code"
|
||||||
|
required="true"
|
||||||
|
vn-focus>
|
||||||
|
</vn-textfield>
|
||||||
|
<vn-check
|
||||||
|
vn-one
|
||||||
|
label="It's a withholding"
|
||||||
|
ng-model="$ctrl.expense.isWithheld">
|
||||||
|
</vn-check>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-textfield vn-one
|
||||||
|
vn-id="description"
|
||||||
|
label="Description"
|
||||||
|
ng-model="$ctrl.expense.description"
|
||||||
|
required="true">
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
|
</section>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
|
<button response="accept" translate>Save</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
|
@ -1,7 +1,12 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
|
import UserError from 'core/lib/user-error';
|
||||||
|
|
||||||
class Controller extends Section {
|
class Controller extends Section {
|
||||||
|
constructor($element, $, vnWeekDays) {
|
||||||
|
super($element, $);
|
||||||
|
this.expense = {};
|
||||||
|
}
|
||||||
taxRate(invoiceInTax, taxRateSelection) {
|
taxRate(invoiceInTax, taxRateSelection) {
|
||||||
const taxTypeSage = taxRateSelection && taxRateSelection.rate;
|
const taxTypeSage = taxRateSelection && taxRateSelection.rate;
|
||||||
const taxableBase = invoiceInTax && invoiceInTax.taxableBase;
|
const taxableBase = invoiceInTax && invoiceInTax.taxableBase;
|
||||||
|
@ -26,6 +31,27 @@ class Controller extends Section {
|
||||||
this.card.reload();
|
this.card.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onResponse() {
|
||||||
|
try {
|
||||||
|
if (!this.expense.code)
|
||||||
|
throw new Error(`The code can't be empty`);
|
||||||
|
if (!this.expense.description)
|
||||||
|
throw new UserError(`The description can't be empty`);
|
||||||
|
|
||||||
|
const data = [{
|
||||||
|
id: this.expense.code,
|
||||||
|
isWithheld: this.expense.isWithheld,
|
||||||
|
name: this.expense.description
|
||||||
|
}];
|
||||||
|
|
||||||
|
this.$http.post(`Expenses`, data) .then(() => {
|
||||||
|
this.vnApp.showSuccess(this.$t('Expense saved!'));
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
this.vnApp.showError(this.$t(e.message));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceInTax', {
|
ngModule.vnComponent('vnInvoiceInTax', {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue