Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 4045-incoterms_authorization
gitea/salix/pipeline/head This commit is unstable
Details
gitea/salix/pipeline/head This commit is unstable
Details
This commit is contained in:
commit
9ab8b84290
|
@ -5,17 +5,17 @@ module.exports = Self => {
|
|||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: 'The user id',
|
||||
http: {source: 'path'}
|
||||
}, {
|
||||
arg: 'oldPassword',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
description: 'The old password',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'newPassword',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
description: 'The new password',
|
||||
required: true
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const axios = require('axios');
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('send', {
|
||||
description: 'Send a RocketChat message',
|
||||
description: 'Creates a direct message in the chat model for a user or a channel',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'to',
|
||||
|
@ -31,39 +30,19 @@ module.exports = Self => {
|
|||
const recipient = to.replace('@', '');
|
||||
|
||||
if (sender.name != recipient) {
|
||||
await sendMessage(sender, to, message);
|
||||
await models.Chat.create({
|
||||
senderFk: sender.id,
|
||||
recipient: to,
|
||||
dated: new Date(),
|
||||
checkUserStatus: 0,
|
||||
message: message,
|
||||
status: 0,
|
||||
attempts: 0
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
async function sendMessage(sender, channel, message) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
return new Promise(resolve => {
|
||||
return resolve({
|
||||
statusCode: 200,
|
||||
message: 'Fake notification sent'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const login = await Self.getServiceAuth();
|
||||
const avatar = `${login.host}/avatar/${sender.name}`;
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
'X-Auth-Token': login.auth.token,
|
||||
'X-User-Id': login.auth.userId
|
||||
},
|
||||
};
|
||||
|
||||
return axios.post(`${login.api}/chat.postMessage`, {
|
||||
'channel': channel,
|
||||
'avatar': avatar,
|
||||
'alias': sender.nickname,
|
||||
'text': message
|
||||
}, options);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
const axios = require('axios');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('sendCheckingPresence', {
|
||||
description: 'Sends a RocketChat message to a connected user or department channel',
|
||||
description: 'Creates a message in the chat model checking the user status',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'workerId',
|
||||
|
@ -36,6 +34,7 @@ module.exports = Self => {
|
|||
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const sender = await models.Account.findById(userId);
|
||||
const recipient = await models.Account.findById(recipientId, null, myOptions);
|
||||
|
||||
// Prevent sending messages to yourself
|
||||
|
@ -44,54 +43,16 @@ module.exports = Self => {
|
|||
if (!recipient)
|
||||
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
|
||||
|
||||
const {data} = await Self.getUserStatus(recipient.name);
|
||||
if (data) {
|
||||
if (data.status === 'offline' || data.status === 'busy') {
|
||||
// Send message to department room
|
||||
const workerDepartment = await models.WorkerDepartment.findById(recipientId, {
|
||||
include: {
|
||||
relation: 'department'
|
||||
}
|
||||
}, myOptions);
|
||||
const department = workerDepartment && workerDepartment.department();
|
||||
const channelName = department && department.chatName;
|
||||
await models.Chat.create({
|
||||
senderFk: sender.id,
|
||||
recipient: `@${recipient.name}`,
|
||||
dated: new Date(),
|
||||
checkUserStatus: 1,
|
||||
message: message,
|
||||
status: 0,
|
||||
attempts: 0
|
||||
});
|
||||
|
||||
if (channelName)
|
||||
return Self.send(ctx, `#${channelName}`, `@${recipient.name} ➔ ${message}`);
|
||||
else
|
||||
return Self.send(ctx, `@${recipient.name}`, message);
|
||||
} else
|
||||
return Self.send(ctx, `@${recipient.name}`, message);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current user status on Rocketchat
|
||||
*
|
||||
* @param {string} username - The recipient user name
|
||||
* @return {Promise} - The request promise
|
||||
*/
|
||||
Self.getUserStatus = async function getUserStatus(username) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
return new Promise(resolve => {
|
||||
return resolve({
|
||||
data: {
|
||||
status: 'online'
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const login = await Self.getServiceAuth();
|
||||
|
||||
const options = {
|
||||
params: {username},
|
||||
headers: {
|
||||
'X-Auth-Token': login.auth.token,
|
||||
'X-User-Id': login.auth.userId
|
||||
},
|
||||
};
|
||||
|
||||
return axios.get(`${login.api}/users.getStatus`, options);
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
const axios = require('axios');
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('sendQueued', {
|
||||
description: 'Send a RocketChat message',
|
||||
accessType: 'WRITE',
|
||||
accepts: [],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/sendQueued`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.sendQueued = async() => {
|
||||
const models = Self.app.models;
|
||||
const maxAttempts = 3;
|
||||
const sentStatus = 1;
|
||||
const errorStatus = 2;
|
||||
|
||||
const chats = await models.Chat.find({
|
||||
where: {
|
||||
status: {neq: sentStatus},
|
||||
attempts: {lt: maxAttempts}
|
||||
}
|
||||
});
|
||||
|
||||
for (let chat of chats) {
|
||||
if (chat.checkUserStatus) {
|
||||
try {
|
||||
await Self.sendCheckingUserStatus(chat);
|
||||
await updateChat(chat, sentStatus);
|
||||
} catch (error) {
|
||||
await updateChat(chat, errorStatus);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await Self.sendMessage(chat.senderFk, chat.recipient, chat.message);
|
||||
await updateChat(chat, sentStatus);
|
||||
} catch (error) {
|
||||
await updateChat(chat, errorStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check user status in Rocket
|
||||
*
|
||||
* @param {object} chat - The sender id
|
||||
* @return {Promise} - The request promise
|
||||
*/
|
||||
Self.sendCheckingUserStatus = async function sendCheckingUserStatus(chat) {
|
||||
const models = Self.app.models;
|
||||
|
||||
const recipientName = chat.recipient.slice(1);
|
||||
const recipient = await models.Account.findOne({
|
||||
where: {
|
||||
name: recipientName
|
||||
}
|
||||
});
|
||||
|
||||
const {data} = await Self.getUserStatus(recipient.name);
|
||||
if (data) {
|
||||
if (data.status === 'offline' || data.status === 'busy') {
|
||||
// Send message to department room
|
||||
const workerDepartment = await models.WorkerDepartment.findById(recipient.id, {
|
||||
include: {
|
||||
relation: 'department'
|
||||
}
|
||||
});
|
||||
const department = workerDepartment && workerDepartment.department();
|
||||
const channelName = department && department.chatName;
|
||||
|
||||
if (channelName)
|
||||
return Self.sendMessage(chat.senderFk, `#${channelName}`, `@${recipient.name} ➔ ${message}`);
|
||||
else
|
||||
return Self.sendMessage(chat.senderFk, `@${recipient.name}`, chat.message);
|
||||
} else
|
||||
return Self.sendMessage(chat.senderFk, `@${recipient.name}`, chat.message);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a rocket message
|
||||
*
|
||||
* @param {object} senderFk - The sender id
|
||||
* @param {string} recipient - The user (@) or channel (#) to send the message
|
||||
* @param {string} message - The message to send
|
||||
* @return {Promise} - The request promise
|
||||
*/
|
||||
Self.sendMessage = async function sendMessage(senderFk, recipient, message) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
return new Promise(resolve => {
|
||||
return resolve({
|
||||
statusCode: 200,
|
||||
message: 'Fake notification sent'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const models = Self.app.models;
|
||||
const sender = await models.Account.findById(senderFk);
|
||||
|
||||
const login = await Self.getServiceAuth();
|
||||
const avatar = `${login.host}/avatar/${sender.name}`;
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
'X-Auth-Token': login.auth.token,
|
||||
'X-User-Id': login.auth.userId
|
||||
},
|
||||
};
|
||||
|
||||
return axios.post(`${login.api}/chat.postMessage`, {
|
||||
'channel': recipient,
|
||||
'avatar': avatar,
|
||||
'alias': sender.nickname,
|
||||
'text': message
|
||||
}, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update status and attempts of a chat
|
||||
*
|
||||
* @param {object} chat - The chat
|
||||
* @param {string} status - The new status
|
||||
* @return {Promise} - The request promise
|
||||
*/
|
||||
async function updateChat(chat, status) {
|
||||
return chat.updateAttributes({
|
||||
status: status,
|
||||
attempts: ++chat.attempts
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current user status on Rocketchat
|
||||
*
|
||||
* @param {string} username - The recipient user name
|
||||
* @return {Promise} - The request promise
|
||||
*/
|
||||
Self.getUserStatus = async function getUserStatus(username) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
return new Promise(resolve => {
|
||||
return resolve({
|
||||
data: {
|
||||
status: 'online'
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const login = await Self.getServiceAuth();
|
||||
|
||||
const options = {
|
||||
params: {username},
|
||||
headers: {
|
||||
'X-Auth-Token': login.auth.token,
|
||||
'X-User-Id': login.auth.userId
|
||||
},
|
||||
};
|
||||
|
||||
return axios.get(`${login.api}/users.getStatus`, options);
|
||||
};
|
||||
};
|
|
@ -1,14 +1,14 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('Chat send()', () => {
|
||||
it('should return a "Fake notification sent" as response', async() => {
|
||||
it('should return true as response', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 1}}};
|
||||
let response = await app.models.Chat.send(ctx, '@salesPerson', 'I changed something');
|
||||
|
||||
expect(response).toEqual(true);
|
||||
});
|
||||
|
||||
it('should retrun false as response', async() => {
|
||||
it('should return false as response', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 18}}};
|
||||
let response = await app.models.Chat.send(ctx, '@salesPerson', 'I changed something');
|
||||
|
||||
|
|
|
@ -1,58 +1,21 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Chat sendCheckingPresence()', () => {
|
||||
const today = new Date();
|
||||
today.setHours(6, 0);
|
||||
const ctx = {req: {accessToken: {userId: 1}}};
|
||||
const chatModel = models.Chat;
|
||||
const departmentId = 23;
|
||||
const workerId = 1107;
|
||||
it('should return true as response', async() => {
|
||||
const workerId = 1107;
|
||||
|
||||
it(`should call to send() method with "@HankPym" as recipient argument`, async() => {
|
||||
spyOn(chatModel, 'send').and.callThrough();
|
||||
spyOn(chatModel, 'getUserStatus').and.returnValue(
|
||||
new Promise(resolve => {
|
||||
return resolve({
|
||||
data: {
|
||||
status: 'online'
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
let ctx = {req: {accessToken: {userId: 1}}};
|
||||
let response = await models.Chat.sendCheckingPresence(ctx, workerId, 'I changed something');
|
||||
|
||||
await chatModel.sendCheckingPresence(ctx, workerId, 'I changed something');
|
||||
|
||||
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', 'I changed something');
|
||||
expect(response).toEqual(true);
|
||||
});
|
||||
|
||||
it(`should call to send() method with "#cooler" as recipient argument`, async() => {
|
||||
spyOn(chatModel, 'send').and.callThrough();
|
||||
spyOn(chatModel, 'getUserStatus').and.returnValue(
|
||||
new Promise(resolve => {
|
||||
return resolve({
|
||||
data: {
|
||||
status: 'offline'
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
it('should return false as response', async() => {
|
||||
const salesPersonId = 18;
|
||||
|
||||
const tx = await models.Claim.beginTransaction({});
|
||||
let ctx = {req: {accessToken: {userId: 18}}};
|
||||
let response = await models.Chat.sendCheckingPresence(ctx, salesPersonId, 'I changed something');
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const department = await models.Department.findById(departmentId, null, options);
|
||||
await department.updateAttribute('chatName', 'cooler');
|
||||
|
||||
await chatModel.sendCheckingPresence(ctx, workerId, 'I changed something');
|
||||
|
||||
expect(chatModel.send).toHaveBeenCalledWith(ctx, '#cooler', '@HankPym ➔ I changed something');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
expect(response).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Chat sendCheckingPresence()', () => {
|
||||
const today = new Date();
|
||||
today.setHours(6, 0);
|
||||
const chatModel = models.Chat;
|
||||
|
||||
it(`should call to sendCheckingUserStatus()`, async() => {
|
||||
spyOn(chatModel, 'sendCheckingUserStatus').and.callThrough();
|
||||
|
||||
const chat = {
|
||||
checkUserStatus: 1,
|
||||
status: 0,
|
||||
attempts: 0
|
||||
};
|
||||
|
||||
await chatModel.destroyAll();
|
||||
await chatModel.create(chat);
|
||||
|
||||
await chatModel.sendQueued();
|
||||
|
||||
expect(chatModel.sendCheckingUserStatus).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it(`should call to sendMessage() method`, async() => {
|
||||
spyOn(chatModel, 'sendMessage').and.callThrough();
|
||||
|
||||
const chat = {
|
||||
checkUserStatus: 0,
|
||||
status: 0,
|
||||
attempts: 0
|
||||
};
|
||||
|
||||
await chatModel.destroyAll();
|
||||
await chatModel.create(chat);
|
||||
|
||||
await chatModel.sendQueued();
|
||||
|
||||
expect(chatModel.sendMessage).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('setSaleQuantity', {
|
||||
Self.remoteMethod('setSaleQuantity', {
|
||||
description: 'Update sale quantity',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
|
@ -24,11 +24,13 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.setSaleQuantity = async ctx => {
|
||||
const args = ctx.args;
|
||||
Self.setSaleQuantity = async(saleId, quantity) => {
|
||||
const models = Self.app.models;
|
||||
|
||||
const sale = await models.Sale.findById(args.saleId);
|
||||
return await sale.updateAttribute('quantity', args.quantity);
|
||||
const sale = await models.Sale.findById(saleId);
|
||||
return await sale.updateAttributes({
|
||||
originalQuantity: sale.quantity,
|
||||
quantity: quantity
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,19 +5,12 @@ describe('setSaleQuantity()', () => {
|
|||
const saleId = 30;
|
||||
const newQuantity = 10;
|
||||
|
||||
const ctx = {
|
||||
args: {
|
||||
saleId: saleId,
|
||||
quantity: newQuantity
|
||||
}
|
||||
};
|
||||
|
||||
const originalSale = await models.Sale.findById(saleId);
|
||||
|
||||
await models.Collection.setSaleQuantity(ctx);
|
||||
await models.Collection.setSaleQuantity(saleId, newQuantity);
|
||||
const updateSale = await models.Sale.findById(saleId);
|
||||
|
||||
expect(updateSale.quantity).toBeLessThan(originalSale.quantity);
|
||||
expect(updateSale.originalQuantity).toEqual(originalSale.quantity);
|
||||
expect(updateSale.quantity).toEqual(newQuantity);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE bucket
|
||||
INTO TABLE `edi`.`bucket`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9, @col10, @col11, @col12)
|
||||
SET
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE bucket_type
|
||||
INTO TABLE `edi`.`bucket_type`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6)
|
||||
SET
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `feature`
|
||||
INTO TABLE `edi`.`feature`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7)
|
||||
SET
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE genus
|
||||
INTO TABLE `edi`.`genus`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6)
|
||||
SET
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE item
|
||||
INTO TABLE `edi`.`item`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9, @col10, @col11, @col12)
|
||||
SET
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `item_feature`
|
||||
INTO TABLE `edi`.`item_feature`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8)
|
||||
SET
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE item_group
|
||||
INTO TABLE `edi`.`item_group`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6)
|
||||
SET
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE plant
|
||||
INTO TABLE `edi`.`plant`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9)
|
||||
SET
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE specie
|
||||
INTO TABLE `edi`.`specie`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7)
|
||||
SET
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE edi.supplier
|
||||
INTO TABLE `edi`.`supplier`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9, @col10, @col11, @col12, @col13, @col14, @col15, @col16, @col17, @col18, @col19, @col20)
|
||||
SET
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `type`
|
||||
INTO TABLE `edi`.`type`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7)
|
||||
SET
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `value`
|
||||
INTO TABLE `edi`.`value`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7)
|
||||
SET
|
||||
|
|
|
@ -19,137 +19,218 @@ module.exports = Self => {
|
|||
Self.updateData = async() => {
|
||||
const models = Self.app.models;
|
||||
|
||||
// Get files checksum
|
||||
const files = await Self.rawSql('SELECT name, checksum, keyValue FROM edi.fileConfig');
|
||||
|
||||
const updatableFiles = [];
|
||||
for (const file of files) {
|
||||
const fileChecksum = await getChecksum(file);
|
||||
|
||||
if (file.checksum != fileChecksum) {
|
||||
updatableFiles.push({
|
||||
name: file.name,
|
||||
checksum: fileChecksum
|
||||
});
|
||||
} else
|
||||
console.debug(`File already updated, skipping...`);
|
||||
}
|
||||
|
||||
if (updatableFiles.length === 0)
|
||||
return false;
|
||||
|
||||
// Download files
|
||||
const container = await models.TempContainer.container('edi');
|
||||
const tempPath = path.join(container.client.root, container.name);
|
||||
|
||||
const [ftpConfig] = await Self.rawSql('SELECT host, user, password FROM edi.ftpConfig');
|
||||
console.debug(`Openning FTP connection to ${ftpConfig.host}...\n`);
|
||||
|
||||
const FtpClient = require('ftps');
|
||||
const ftpClient = new FtpClient({
|
||||
host: ftpConfig.host,
|
||||
username: ftpConfig.user,
|
||||
password: ftpConfig.password,
|
||||
procotol: 'ftp'
|
||||
});
|
||||
|
||||
const files = await Self.rawSql('SELECT fileName, toTable, file, updated FROM edi.fileConfig');
|
||||
|
||||
let remoteFile;
|
||||
let tempDir;
|
||||
let tempFile;
|
||||
for (const file of files) {
|
||||
|
||||
const fileNames = updatableFiles.map(file => file.name);
|
||||
|
||||
const tables = await Self.rawSql(`
|
||||
SELECT fileName, toTable, file
|
||||
FROM edi.tableConfig
|
||||
WHERE file IN (?)`, [fileNames]);
|
||||
|
||||
for (const table of tables) {
|
||||
const fileName = table.file;
|
||||
|
||||
console.debug(`Downloading file ${fileName}...`);
|
||||
|
||||
remoteFile = `codes/${fileName}.ZIP`;
|
||||
tempDir = `${tempPath}/${fileName}`;
|
||||
tempFile = `${tempPath}/${fileName}.zip`;
|
||||
|
||||
try {
|
||||
const fileName = file.file;
|
||||
|
||||
console.debug(`Downloading file ${fileName}...`);
|
||||
|
||||
remoteFile = `codes/${fileName}.ZIP`;
|
||||
tempDir = `${tempPath}/${fileName}`;
|
||||
tempFile = `${tempPath}/${fileName}.zip`;
|
||||
|
||||
await extractFile({
|
||||
ftpClient: ftpClient,
|
||||
file: file,
|
||||
paths: {
|
||||
remoteFile: remoteFile,
|
||||
tempDir: tempDir,
|
||||
tempFile: tempFile
|
||||
}
|
||||
});
|
||||
await fs.readFile(tempFile);
|
||||
} catch (error) {
|
||||
if (fs.existsSync(tempFile))
|
||||
await fs.unlink(tempFile);
|
||||
|
||||
await fs.rmdir(tempDir, {recursive: true});
|
||||
console.error(error);
|
||||
if (error.code === 'ENOENT') {
|
||||
const downloadOutput = await downloadFile(remoteFile, tempFile);
|
||||
if (downloadOutput.error)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
console.debug(`Extracting file ${fileName}...`);
|
||||
await extractFile(tempFile, tempDir);
|
||||
|
||||
console.debug(`Updating table ${table.toTable}...`);
|
||||
await dumpData(tempDir, table);
|
||||
}
|
||||
|
||||
// Update files checksum
|
||||
for (const file of updatableFiles) {
|
||||
await Self.rawSql(`
|
||||
UPDATE edi.fileConfig
|
||||
SET checksum = ?
|
||||
WHERE name = ?`,
|
||||
[file.checksum, file.name]);
|
||||
}
|
||||
|
||||
// Clean files
|
||||
try {
|
||||
await fs.remove(tempPath);
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT')
|
||||
throw e;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
async function extractFile({ftpClient, file, paths}) {
|
||||
// Download the zip file
|
||||
ftpClient.get(paths.remoteFile, paths.tempFile);
|
||||
let ftpClient;
|
||||
async function getFtpClient() {
|
||||
if (!ftpClient) {
|
||||
const [ftpConfig] = await Self.rawSql('SELECT host, user, password FROM edi.ftpConfig');
|
||||
console.debug(`Openning FTP connection to ${ftpConfig.host}...\n`);
|
||||
|
||||
// Execute download command
|
||||
ftpClient.exec(async(err, response) => {
|
||||
if (response.error) {
|
||||
console.debug(`Error downloading file... ${response.error}`);
|
||||
return;
|
||||
}
|
||||
const FtpClient = require('ftps');
|
||||
|
||||
const AdmZip = require('adm-zip');
|
||||
const zip = new AdmZip(paths.tempFile);
|
||||
const entries = zip.getEntries();
|
||||
ftpClient = new FtpClient({
|
||||
host: ftpConfig.host,
|
||||
username: ftpConfig.user,
|
||||
password: ftpConfig.password,
|
||||
procotol: 'ftp'
|
||||
});
|
||||
}
|
||||
|
||||
zip.extractAllTo(paths.tempDir, false);
|
||||
return ftpClient;
|
||||
}
|
||||
|
||||
if (fs.existsSync(paths.tempFile))
|
||||
await fs.unlink(paths.tempFile);
|
||||
async function getChecksum(file) {
|
||||
const ftpClient = await getFtpClient();
|
||||
console.debug(`Checking checksum for file ${file.name}...`);
|
||||
|
||||
await dumpData({file, entries, paths});
|
||||
ftpClient.cat(`codes/${file.name}.txt`);
|
||||
|
||||
await fs.rmdir(paths.tempDir, {recursive: true});
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
ftpClient.exec((err, response) => {
|
||||
if (response.error) {
|
||||
console.debug(`Error downloading checksum file... ${response.error}`);
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
|
||||
if (response && response.data) {
|
||||
const fileContents = response.data;
|
||||
const rows = fileContents.split('\n');
|
||||
const row = rows[4];
|
||||
const columns = row.split(/\s+/);
|
||||
|
||||
let fileChecksum;
|
||||
if (file.keyValue)
|
||||
fileChecksum = columns[1];
|
||||
|
||||
if (!file.keyValue)
|
||||
fileChecksum = columns[0];
|
||||
|
||||
return fileChecksum;
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadFile(remoteFile, tempFile) {
|
||||
const ftpClient = await getFtpClient();
|
||||
|
||||
ftpClient.get(remoteFile, tempFile);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
ftpClient.exec((err, response) => {
|
||||
if (response.error) {
|
||||
console.debug(`Error downloading file... ${response.error}`);
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function dumpData({file, entries, paths}) {
|
||||
const toTable = file.toTable;
|
||||
const baseName = file.fileName;
|
||||
async function extractFile(tempFile, tempDir) {
|
||||
const JSZip = require('jszip');
|
||||
|
||||
for (const zipEntry of entries) {
|
||||
const entryName = zipEntry.entryName;
|
||||
console.log(`Reading file ${entryName}...`);
|
||||
try {
|
||||
await fs.mkdir(tempDir);
|
||||
} catch (error) {
|
||||
if (error.code !== 'EEXIST')
|
||||
throw e;
|
||||
}
|
||||
|
||||
const startIndex = (entryName.length - 10);
|
||||
const endIndex = (entryName.length - 4);
|
||||
const dateString = entryName.substring(startIndex, endIndex);
|
||||
const lastUpdated = new Date();
|
||||
const fileStream = await fs.readFile(tempFile);
|
||||
if (fileStream) {
|
||||
const zip = new JSZip();
|
||||
const zipContents = await zip.loadAsync(fileStream);
|
||||
|
||||
// Format string date to a date object
|
||||
let updated = null;
|
||||
if (file.updated) {
|
||||
updated = new Date(file.updated);
|
||||
updated.setHours(0, 0, 0, 0);
|
||||
if (!zipContents) return;
|
||||
|
||||
const fileNames = Object.keys(zipContents.files);
|
||||
|
||||
for (const fileName of fileNames) {
|
||||
const fileContent = await zip.file(fileName).async('nodebuffer');
|
||||
const dest = path.join(tempDir, fileName);
|
||||
await fs.writeFile(dest, fileContent);
|
||||
}
|
||||
|
||||
lastUpdated.setFullYear(`20${dateString.substring(4, 6)}`);
|
||||
lastUpdated.setMonth(parseInt(dateString.substring(2, 4)) - 1);
|
||||
lastUpdated.setDate(dateString.substring(0, 2));
|
||||
lastUpdated.setHours(0, 0, 0, 0);
|
||||
|
||||
if (updated && lastUpdated <= updated) {
|
||||
console.debug(`Table ${toTable} already updated, skipping...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log('Dumping data...');
|
||||
const templatePath = path.join(__dirname, `./sql/${toTable}.sql`);
|
||||
const sqlTemplate = fs.readFileSync(templatePath, 'utf8');
|
||||
|
||||
const rawPath = path.join(paths.tempDir, entryName);
|
||||
|
||||
try {
|
||||
const tx = await Self.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
|
||||
await Self.rawSql(`DELETE FROM edi.${toTable}`, null, options);
|
||||
await Self.rawSql(sqlTemplate, [rawPath], options);
|
||||
await Self.rawSql(`
|
||||
UPDATE edi.fileConfig
|
||||
SET updated = ?
|
||||
WHERE fileName = ?
|
||||
`, [lastUpdated, baseName], options);
|
||||
|
||||
tx.commit();
|
||||
} catch (error) {
|
||||
tx.rollback();
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.log(`Updated table ${toTable}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
async function dumpData(tempDir, table) {
|
||||
const toTable = table.toTable;
|
||||
const baseName = table.fileName;
|
||||
|
||||
const tx = await Self.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const tableName = `edi.${toTable}`;
|
||||
await Self.rawSql(`DELETE FROM ??`, [tableName], options);
|
||||
|
||||
const dirFiles = await fs.readdir(tempDir);
|
||||
const files = dirFiles.filter(file => file.startsWith(baseName));
|
||||
|
||||
for (const file of files) {
|
||||
console.log(`Dumping data from file ${file}...`);
|
||||
|
||||
const templatePath = path.join(__dirname, `./sql/${toTable}.sql`);
|
||||
const sqlTemplate = await fs.readFile(templatePath, 'utf8');
|
||||
const filePath = path.join(tempDir, file);
|
||||
|
||||
await Self.rawSql(sqlTemplate, [filePath], options);
|
||||
await Self.rawSql(`
|
||||
UPDATE edi.tableConfig
|
||||
SET updated = ?
|
||||
WHERE fileName = ?
|
||||
`, [new Date(), baseName], options);
|
||||
}
|
||||
|
||||
tx.commit();
|
||||
} catch (error) {
|
||||
tx.rollback();
|
||||
throw error;
|
||||
}
|
||||
console.log(`Updated table ${toTable}\n`);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -68,6 +68,12 @@
|
|||
"Language": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"MachineWorker": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"MobileAppVersionControl": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Module": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
},
|
||||
"maxAmount": {
|
||||
"type": "number"
|
||||
},
|
||||
"daysInFuture": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"acls": [{
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "MobileAppVersionControl",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "vn.mobileAppVersionControl"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"id": true
|
||||
},
|
||||
"appName": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"isVersionCritical": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,4 +3,5 @@ module.exports = Self => {
|
|||
require('../methods/chat/send')(Self);
|
||||
require('../methods/chat/sendCheckingPresence')(Self);
|
||||
require('../methods/chat/notifyIssues')(Self);
|
||||
require('../methods/chat/sendQueued')(Self);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,39 @@
|
|||
{
|
||||
"name": "Chat",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "chat"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "number",
|
||||
"description": "Identifier"
|
||||
},
|
||||
"senderFk": {
|
||||
"type": "number"
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string"
|
||||
},
|
||||
"dated": {
|
||||
"type": "date"
|
||||
},
|
||||
"checkUserStatus": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"attempts": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"acls": [{
|
||||
"property": "validations",
|
||||
"accessType": "EXECUTE",
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "MachineWorker",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "vn.machineWorker"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"id": true
|
||||
},
|
||||
"workerFk": {
|
||||
"type": "number"
|
||||
},
|
||||
"machineFk": {
|
||||
"type": "number"
|
||||
},
|
||||
"inTime": {
|
||||
"type": "date",
|
||||
"mysql": {
|
||||
"columnName": "inTimed"
|
||||
}
|
||||
},
|
||||
"outTime": {
|
||||
"type": "date",
|
||||
"mysql": {
|
||||
"columnName": "outTimed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
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';
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
DROP PROCEDURE IF EXISTS vn.ticket_doRefund;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_doRefund`(IN vOriginTicket INT, OUT vNewTicket INT)
|
||||
BEGIN
|
||||
|
||||
DECLARE vDone BIT DEFAULT 0;
|
||||
DECLARE vCustomer MEDIUMINT;
|
||||
DECLARE vWarehouse TINYINT;
|
||||
DECLARE vCompany MEDIUMINT;
|
||||
DECLARE vAddress MEDIUMINT;
|
||||
DECLARE vRefundAgencyMode INT;
|
||||
DECLARE vItemFk INT;
|
||||
DECLARE vQuantity DECIMAL (10,2);
|
||||
DECLARE vConcept VARCHAR(50);
|
||||
DECLARE vPrice DECIMAL (10,2);
|
||||
DECLARE vDiscount TINYINT;
|
||||
DECLARE vSaleNew INT;
|
||||
DECLARE vSaleMain INT;
|
||||
DECLARE vZoneFk INT;
|
||||
DECLARE vDescription VARCHAR(50);
|
||||
DECLARE vTaxClassFk INT;
|
||||
DECLARE vTicketServiceTypeFk INT;
|
||||
|
||||
DECLARE cSales CURSOR FOR
|
||||
SELECT *
|
||||
FROM tmp.sale;
|
||||
|
||||
DECLARE cTicketServices CURSOR FOR
|
||||
SELECT *
|
||||
FROM tmp.ticketService;
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = 1;
|
||||
|
||||
SELECT id INTO vRefundAgencyMode
|
||||
FROM agencyMode WHERE `name` = 'ABONO';
|
||||
|
||||
SELECT clientFk, warehouseFk, companyFk, addressFk
|
||||
INTO vCustomer, vWarehouse, vCompany, vAddress
|
||||
FROM ticket
|
||||
WHERE id = vOriginTicket;
|
||||
|
||||
SELECT id INTO vZoneFk
|
||||
FROM zone WHERE agencyModeFk = vRefundAgencyMode
|
||||
LIMIT 1;
|
||||
|
||||
INSERT INTO vn.ticket (
|
||||
clientFk,
|
||||
shipped,
|
||||
addressFk,
|
||||
agencyModeFk,
|
||||
nickname,
|
||||
warehouseFk,
|
||||
companyFk,
|
||||
landed,
|
||||
zoneFk
|
||||
)
|
||||
SELECT
|
||||
vCustomer,
|
||||
CURDATE(),
|
||||
vAddress,
|
||||
vRefundAgencyMode,
|
||||
a.nickname,
|
||||
vWarehouse,
|
||||
vCompany,
|
||||
CURDATE(),
|
||||
vZoneFk
|
||||
FROM address a
|
||||
WHERE a.id = vAddress;
|
||||
|
||||
SET vNewTicket = LAST_INSERT_ID();
|
||||
|
||||
SET vDone := 0;
|
||||
OPEN cSales;
|
||||
FETCH cSales INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount;
|
||||
|
||||
WHILE NOT vDone DO
|
||||
|
||||
INSERT INTO vn.sale(ticketFk, itemFk, quantity, concept, price, discount)
|
||||
VALUES( vNewTicket, vItemFk, vQuantity, vConcept, vPrice, vDiscount );
|
||||
|
||||
SET vSaleNew = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO vn.saleComponent(saleFk,componentFk,`value`)
|
||||
SELECT vSaleNew,componentFk,`value`
|
||||
FROM vn.saleComponent
|
||||
WHERE saleFk = vSaleMain;
|
||||
|
||||
FETCH cSales INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount;
|
||||
|
||||
END WHILE;
|
||||
CLOSE cSales;
|
||||
|
||||
SET vDone := 0;
|
||||
OPEN cTicketServices;
|
||||
FETCH cTicketServices INTO vDescription, vQuantity, vPrice, vTaxClassFk, vTicketServiceTypeFk;
|
||||
|
||||
WHILE NOT vDone DO
|
||||
|
||||
INSERT INTO vn.ticketService(description, quantity, price, taxClassFk, ticketFk, ticketServiceTypeFk)
|
||||
VALUES(vDescription, vQuantity, vPrice, vTaxClassFk, vNewTicket, vTicketServiceTypeFk);
|
||||
|
||||
FETCH cTicketServices INTO vDescription, vQuantity, vPrice, vTaxClassFk, vTicketServiceTypeFk;
|
||||
|
||||
END WHILE;
|
||||
CLOSE cTicketServices;
|
||||
|
||||
INSERT INTO vn.ticketRefund(refundTicketFk, originalTicketFk)
|
||||
VALUES(vNewTicket, vOriginTicket);
|
||||
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -3,8 +3,8 @@ CREATE TABLE `vn`.`clientUnpaid` (
|
|||
`dated` date NOT NULL,
|
||||
`amount` double DEFAULT 0,
|
||||
PRIMARY KEY (`clientFk`),
|
||||
CONSTRAINT `clientUnpaid_clientFk` FOREIGN KEY (`clientFk`) REFERENCES `client` (`id`) ON UPDATE CASCADE
|
||||
CONSTRAINT `clientUnpaid_clientFk` FOREIGN KEY (`clientFk`) REFERENCES `vn`.`client` (`id`) ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES('ClientUnpaid', '*', '*', 'ALLOW', 'ROLE', 'administrative');
|
||||
VALUES('ClientUnpaid', '*', '*', 'ALLOW', 'ROLE', 'administrative');
|
|
@ -0,0 +1,8 @@
|
|||
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 `vn`.`invoiceOut` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
|
||||
) comment 'Queue for PDF invoicing';
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE `vn`.`accountingType` ADD daysInFuture INT NULL;
|
||||
ALTER TABLE `vn`.`accountingType` MODIFY COLUMN daysInFuture int(11) DEFAULT 0 NULL;
|
|
@ -0,0 +1,4 @@
|
|||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES
|
||||
('ItemType', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('ItemType', '*', 'WRITE', 'ALLOW', 'ROLE', 'buyer');
|
|
@ -0,0 +1,14 @@
|
|||
CREATE TABLE `vn`.`mdbBranch` (
|
||||
`name` VARCHAR(255),
|
||||
PRIMARY KEY(`name`)
|
||||
);
|
||||
|
||||
CREATE TABLE `vn`.`mdbVersion` (
|
||||
`app` VARCHAR(255) NOT NULL,
|
||||
`branchFk` VARCHAR(255) NOT NULL,
|
||||
`version` INT,
|
||||
CONSTRAINT `mdbVersion_branchFk` FOREIGN KEY (`branchFk`) REFERENCES `vn`.`mdbBranch` (`name`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES('MdbVersion', '*', '*', 'ALLOW', 'ROLE', 'developer');
|
|
@ -0,0 +1,13 @@
|
|||
CREATE TABLE `vn`.`chat` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`senderFk` int(10) unsigned DEFAULT NULL,
|
||||
`recipient` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||
`dated` date DEFAULT NULL,
|
||||
`checkUserStatus` tinyint(1) DEFAULT NULL,
|
||||
`message` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||
`status` tinyint(1) DEFAULT NULL,
|
||||
`attempts` int(1) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `chat_FK` (`senderFk`),
|
||||
CONSTRAINT `chat_FK` FOREIGN KEY (`senderFk`) REFERENCES `account`.`user` (`id`) ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
|
@ -0,0 +1,3 @@
|
|||
INSERT INTO `salix`.`defaultViewConfig` (tableCode, columns)
|
||||
VALUES ('clientsDetail', '{"id":true,"phone":true,"city":true,"socialName":true,"salesPersonFk":true,"email":true,"name":false,"fi":false,"credit":false,"creditInsurance":false,"mobile":false,"street":false,"countryFk":false,"provinceFk":false,"postcode":false,"created":false,"businessTypeFk":false,"payMethodFk":false,"sageTaxTypeFk":false,"sageTransactionTypeFk":false,"isActive":false,"isVies":false,"isTaxDataChecked":false,"isEqualizated":false,"isFreezed":false,"hasToInvoice":false,"hasToInvoiceByAddress":false,"isToBeMailed":false,"hasLcr":false,"hasCoreVnl":false,"hasSepaVnl":false}');
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
DROP PROCEDURE IF EXISTS `vn`.`ticket_doRefund`;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_doRefund`(OUT vNewTicket INT)
|
||||
BEGIN
|
||||
/**
|
||||
* Crea un ticket de abono a partir de tmp.sale y/o tmp.ticketService
|
||||
*
|
||||
* @return vNewTicket
|
||||
*/
|
||||
DECLARE vDone BIT DEFAULT 0;
|
||||
DECLARE vClientFk MEDIUMINT;
|
||||
DECLARE vWarehouse TINYINT;
|
||||
DECLARE vCompany MEDIUMINT;
|
||||
DECLARE vAddress MEDIUMINT;
|
||||
DECLARE vRefundAgencyMode INT;
|
||||
DECLARE vItemFk INT;
|
||||
DECLARE vQuantity DECIMAL (10,2);
|
||||
DECLARE vConcept VARCHAR(50);
|
||||
DECLARE vPrice DECIMAL (10,2);
|
||||
DECLARE vDiscount TINYINT;
|
||||
DECLARE vSaleNew INT;
|
||||
DECLARE vSaleMain INT;
|
||||
DECLARE vZoneFk INT;
|
||||
DECLARE vDescription VARCHAR(50);
|
||||
DECLARE vTaxClassFk INT;
|
||||
DECLARE vTicketServiceTypeFk INT;
|
||||
DECLARE vOriginTicket INT;
|
||||
|
||||
DECLARE cSales CURSOR FOR
|
||||
SELECT s.id, s.itemFk, - s.quantity, s.concept, s.price, s.discount
|
||||
FROM tmp.sale s;
|
||||
|
||||
DECLARE cTicketServices CURSOR FOR
|
||||
SELECT ts.description, - ts.quantity, ts.price, ts.taxClassFk, ts.ticketServiceTypeFk
|
||||
FROM tmp.ticketService ts;
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
|
||||
|
||||
SELECT sub.ticketFk INTO vOriginTicket
|
||||
FROM (
|
||||
SELECT s.ticketFk
|
||||
FROM tmp.sale s
|
||||
UNION ALL
|
||||
SELECT ts.ticketFk
|
||||
FROM tmp.ticketService ts
|
||||
) sub
|
||||
LIMIT 1;
|
||||
|
||||
SELECT id INTO vRefundAgencyMode
|
||||
FROM agencyMode WHERE `name` = 'ABONO';
|
||||
|
||||
SELECT clientFk, warehouseFk, companyFk, addressFk
|
||||
INTO vClientFk, vWarehouse, vCompany, vAddress
|
||||
FROM ticket
|
||||
WHERE id = vOriginTicket;
|
||||
|
||||
SELECT id INTO vZoneFk
|
||||
FROM zone WHERE agencyModeFk = vRefundAgencyMode
|
||||
LIMIT 1;
|
||||
|
||||
INSERT INTO vn.ticket (
|
||||
clientFk,
|
||||
shipped,
|
||||
addressFk,
|
||||
agencyModeFk,
|
||||
nickname,
|
||||
warehouseFk,
|
||||
companyFk,
|
||||
landed,
|
||||
zoneFk
|
||||
)
|
||||
SELECT
|
||||
vClientFk,
|
||||
CURDATE(),
|
||||
vAddress,
|
||||
vRefundAgencyMode,
|
||||
a.nickname,
|
||||
vWarehouse,
|
||||
vCompany,
|
||||
CURDATE(),
|
||||
vZoneFk
|
||||
FROM address a
|
||||
WHERE a.id = vAddress;
|
||||
|
||||
SET vNewTicket = LAST_INSERT_ID();
|
||||
|
||||
SET vDone := FALSE;
|
||||
OPEN cSales;
|
||||
FETCH cSales INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount;
|
||||
|
||||
WHILE NOT vDone DO
|
||||
|
||||
INSERT INTO vn.sale(ticketFk, itemFk, quantity, concept, price, discount)
|
||||
VALUES( vNewTicket, vItemFk, vQuantity, vConcept, vPrice, vDiscount );
|
||||
|
||||
SET vSaleNew = LAST_INSERT_ID();
|
||||
|
||||
INSERT INTO vn.saleComponent(saleFk,componentFk,`value`)
|
||||
SELECT vSaleNew,componentFk,`value`
|
||||
FROM vn.saleComponent
|
||||
WHERE saleFk = vSaleMain;
|
||||
|
||||
FETCH cSales INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount;
|
||||
|
||||
END WHILE;
|
||||
CLOSE cSales;
|
||||
|
||||
SET vDone := FALSE;
|
||||
OPEN cTicketServices;
|
||||
FETCH cTicketServices INTO vDescription, vQuantity, vPrice, vTaxClassFk, vTicketServiceTypeFk;
|
||||
|
||||
WHILE NOT vDone DO
|
||||
|
||||
INSERT INTO vn.ticketService(description, quantity, price, taxClassFk, ticketFk, ticketServiceTypeFk)
|
||||
VALUES(vDescription, vQuantity, vPrice, vTaxClassFk, vNewTicket, vTicketServiceTypeFk);
|
||||
|
||||
FETCH cTicketServices INTO vDescription, vQuantity, vPrice, vTaxClassFk, vTicketServiceTypeFk;
|
||||
|
||||
END WHILE;
|
||||
CLOSE cTicketServices;
|
||||
|
||||
INSERT INTO vn.ticketRefund(refundTicketFk, originalTicketFk)
|
||||
VALUES(vNewTicket, vOriginTicket);
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1 @@
|
|||
RENAME TABLE `edi`.`fileConfig` to `edi`.`tableConfig`;
|
|
@ -0,0 +1,22 @@
|
|||
CREATE TABLE `edi`.`fileConfig`
|
||||
(
|
||||
name varchar(25) NOT NULL,
|
||||
checksum text NULL,
|
||||
keyValue tinyint(1) default true NOT NULL,
|
||||
constraint fileConfig_pk
|
||||
primary key (name)
|
||||
);
|
||||
|
||||
create unique index fileConfig_name_uindex
|
||||
on `edi`.`fileConfig` (name);
|
||||
|
||||
|
||||
INSERT INTO `edi`.`fileConfig` (name, checksum, keyValue)
|
||||
VALUES ('FEC010104', null, 0);
|
||||
|
||||
INSERT INTO `edi`.`fileConfig` (name, checksum, keyValue)
|
||||
VALUES ('VBN020101', null, 1);
|
||||
|
||||
INSERT INTO `edi`.`fileConfig` (name, checksum, keyValue)
|
||||
VALUES ('florecompc2', null, 1);
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
ALTER TABLE `vn`.`creditInsurance` ADD creditClassificationFk int(11) NULL;
|
||||
|
||||
UPDATE `vn`.`creditInsurance` AS `destiny`
|
||||
SET `destiny`.`creditClassificationFk`= (SELECT creditClassification FROM `vn`.`creditInsurance` AS `origin` WHERE `origin`.id = `destiny`.id);
|
||||
|
||||
ALTER TABLE `vn`.`creditInsurance`
|
||||
ADD CONSTRAINT `creditInsurance_creditClassificationFk` FOREIGN KEY (`creditClassificationFk`)
|
||||
REFERENCES `vn`.`creditClassification` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -0,0 +1,11 @@
|
|||
DELIMITER $$
|
||||
$$
|
||||
CREATE DEFINER=`root`@`localhost` TRIGGER `vn`.`creditInsurance_beforeInsert`
|
||||
BEFORE INSERT ON `creditInsurance`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NEW.creditClassificationFk THEN
|
||||
SET NEW.creditClassification = NEW.creditClassificationFk;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -99,13 +99,19 @@ INSERT INTO `account`.`mailForward`(`account`, `forwardTo`)
|
|||
VALUES
|
||||
(1, 'employee@domain.local');
|
||||
|
||||
INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossFk`, `phone`)
|
||||
INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`)
|
||||
VALUES
|
||||
(1106, 'LGN', 'David Charles', 'Haller', 1106, 19, 432978106),
|
||||
(1107, 'ANT', 'Hank' , 'Pym' , 1107, 19, 432978107),
|
||||
(1108, 'DCX', 'Charles' , 'Xavier', 1108, 19, 432978108),
|
||||
(1109, 'HLK', 'Bruce' , 'Banner', 1109, 19, 432978109),
|
||||
(1110, 'JJJ', 'Jessica' , 'Jones' , 1110, 19, 432978110);
|
||||
(1, 'printer1', 'path1', 0),
|
||||
(2, 'printer2', 'path2', 1);
|
||||
|
||||
|
||||
INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossFk`, `phone`, `sectorFk`, `labelerFk`)
|
||||
VALUES
|
||||
(1106, 'LGN', 'David Charles', 'Haller', 1106, 19, 432978106, NULL, NULL),
|
||||
(1107, 'ANT', 'Hank' , 'Pym' , 1107, 19, 432978107, NULL, 1),
|
||||
(1108, 'DCX', 'Charles' , 'Xavier', 1108, 19, 432978108, 1, NULL),
|
||||
(1109, 'HLK', 'Bruce' , 'Banner', 1109, 19, 432978109, 1, 2),
|
||||
(1110, 'JJJ', 'Jessica' , 'Jones' , 1110, 19, 432978110, 2, 1);
|
||||
|
||||
INSERT INTO `vn`.`currency`(`id`, `code`, `name`, `ratio`)
|
||||
VALUES
|
||||
|
@ -113,7 +119,7 @@ INSERT INTO `vn`.`currency`(`id`, `code`, `name`, `ratio`)
|
|||
(2, 'USD', 'Dollar USA', 1.4),
|
||||
(3, 'GBP', 'Libra', 1),
|
||||
(4, 'JPY', 'Yen Japones', 1);
|
||||
|
||||
|
||||
INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`, `continentFk`, `hasDailyInvoice`, `CEE`)
|
||||
VALUES
|
||||
(1, 'España', 1, 'ES', 1, 24, 4, 0, 1),
|
||||
|
@ -156,22 +162,23 @@ INSERT INTO `vn`.`shelving` (`code`, `parkingFk`, `isPrinted`, `priority`, `park
|
|||
('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`, `daysInFuture`)
|
||||
VALUES
|
||||
(1, 'CC y Polizas de crédito', NULL, NULL, NULL),
|
||||
(2, 'Cash', 'Cash', 'cash', 1000),
|
||||
(3, 'Credit card', 'Credit Card', 'creditCard', NULL),
|
||||
(4, 'Finalcial lines', NULL, NULL, NULL),
|
||||
(5, 'Other products', NULL, NULL, NULL),
|
||||
(6, 'Loans', NULL, NULL, NULL),
|
||||
(7, 'Leasing', NULL, NULL, NULL),
|
||||
(8, 'Compensations', 'Compensations', 'compensation', NULL);
|
||||
(1, 'CC and credit policies', 'Transfers', 'wireTransfer', NULL, 1),
|
||||
(2, 'Cash', 'Cash', 'cash', 1000, 0),
|
||||
(3, 'Credit card', 'Credit Card', 'creditCard', NULL, 0),
|
||||
(4, 'Finalcial lines', NULL, NULL, NULL, 0),
|
||||
(5, 'Other products', NULL, NULL, NULL, 0),
|
||||
(6, 'Loans', NULL, NULL, NULL, 0),
|
||||
(7, 'Leasing', NULL, NULL, NULL, 0),
|
||||
(8, 'Compensations', 'Compensations', 'compensation', NULL, 0);
|
||||
|
||||
INSERT INTO `vn`.`bank`(`id`, `bank`, `account`, `cash`, `entityFk`, `isActive`, `currencyFk`)
|
||||
VALUES
|
||||
(1, 'Pay on receipt', '5720000001', 3, 0, 1, 1),
|
||||
(2, 'Cash', '5700000001', 2, 0, 1, 1),
|
||||
(3, 'Compensation', '4000000000', 8, 0, 1, 1),
|
||||
(4, 'Transfers', '4000000001', 1, 0, 1, 1),
|
||||
(3117, 'Caixa Rural d''Algemesi', '5720000000', 8, 3117, 1, 1);
|
||||
|
||||
|
||||
|
@ -451,7 +458,7 @@ INSERT INTO `vn`.`creditClassification`(`id`, `client`, `dateStart`, `dateEnd`)
|
|||
(4, 1104, CURDATE(), CURDATE()),
|
||||
(5, 1105, CURDATE(), CURDATE());
|
||||
|
||||
INSERT INTO `vn`.`creditInsurance`(`id`, `creditClassification`, `credit`, `creationDate`, `grade`)
|
||||
INSERT INTO `vn`.`creditInsurance`(`id`, `creditClassificationFk`, `credit`, `creationDate`, `grade`)
|
||||
VALUES
|
||||
(1, 1, 3000, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL),
|
||||
(2, 2, 6000, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), NULL),
|
||||
|
@ -744,14 +751,19 @@ INSERT INTO `vn`.`itemCategory`(`id`, `name`, `display`, `color`, `icon`, `code`
|
|||
(7, 'Accessories', 1, NULL, 'icon-accessory', 'accessory'),
|
||||
(8, 'Fruit', 1, NULL, 'icon-fruit', 'fruit');
|
||||
|
||||
INSERT INTO `vn`.`itemType`(`id`, `code`, `name`, `categoryFk`, `warehouseFk`, `life`,`workerFk`, `isPackaging`)
|
||||
INSERT INTO `vn`.`temperature`(`code`, `name`, `description`)
|
||||
VALUES
|
||||
(1, 'CRI', 'Crisantemo', 2, 1, 31, 35, 0),
|
||||
(2, 'ITG', 'Anthurium', 1, 1, 31, 35, 0),
|
||||
(3, 'WPN', 'Paniculata', 2, 1, 31, 35, 0),
|
||||
(4, 'PRT', 'Delivery ports', 3, 1, NULL, 35, 1),
|
||||
(5, 'CON', 'Container', 3, 1, NULL, 35, 1),
|
||||
(6, 'ALS', 'Alstroemeria', 1, 1, 31, 16, 0);
|
||||
('warm', 'Warm', 'Warm'),
|
||||
('cool', 'Cool', 'Cool');
|
||||
|
||||
INSERT INTO `vn`.`itemType`(`id`, `code`, `name`, `categoryFk`, `warehouseFk`, `life`,`workerFk`, `isPackaging`, `temperatureFk`)
|
||||
VALUES
|
||||
(1, 'CRI', 'Crisantemo', 2, 1, 31, 35, 0, 'cool'),
|
||||
(2, 'ITG', 'Anthurium', 1, 1, 31, 35, 0, 'cool'),
|
||||
(3, 'WPN', 'Paniculata', 2, 1, 31, 35, 0, 'cool'),
|
||||
(4, 'PRT', 'Delivery ports', 3, 1, NULL, 35, 1, 'warm'),
|
||||
(5, 'CON', 'Container', 3, 1, NULL, 35, 1, 'warm'),
|
||||
(6, 'ALS', 'Alstroemeria', 1, 1, 31, 16, 0, 'warm');
|
||||
|
||||
INSERT INTO `vn`.`ink`(`id`, `name`, `picture`, `showOrder`, `hex`)
|
||||
VALUES
|
||||
|
@ -809,25 +821,25 @@ INSERT INTO `vn`.`itemFamily`(`code`, `description`)
|
|||
('VT', 'Sales');
|
||||
|
||||
INSERT INTO `vn`.`item`(`id`, `typeFk`, `size`, `inkFk`, `stems`, `originFk`, `description`, `producerFk`, `intrastatFk`, `expenceFk`,
|
||||
`comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`)
|
||||
`comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`, `packingShelve`)
|
||||
VALUES
|
||||
(1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'VT', 0, NULL, 'V', 0),
|
||||
(2, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H', 0),
|
||||
(3, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL, 0),
|
||||
(4, 1, 60, 'YEL', 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL, 0),
|
||||
(5, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '5', NULL, 0, 3, 'VT', 0, NULL, NULL, 0),
|
||||
(6, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '6', NULL, 0, 4, 'VT', 0, NULL, NULL, 0),
|
||||
(7, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '7', NULL, 0, 4, 'VT', 0, NULL, NULL, 0),
|
||||
(8, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '8', NULL, 0, 5, 'VT', 0, NULL, NULL, 0),
|
||||
(9, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '9', NULL, 0, 4, 'VT', 1, NULL, NULL, 0),
|
||||
(10, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '10', NULL, 0, 4, 'VT', 0, NULL, NULL, 0),
|
||||
(11, 1, 60, 'YEL', 1, 1, NULL, 1, 05080000, 4751000000, NULL, 0, '11', NULL, 0, 4, 'VT', 0, NULL, NULL, 0),
|
||||
(12, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '12', NULL, 0, 3, 'VT', 0, NULL, NULL, 0),
|
||||
(13, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '13', NULL, 1, 2, 'VT', 1, NULL, NULL, 1),
|
||||
(14, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 4, 'VT', 1, NULL, NULL, 0),
|
||||
(15, 4, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0),
|
||||
(16, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0),
|
||||
(71, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'VT', 0, NULL, NULL, 0);
|
||||
(1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'VT', 0, NULL, 'V', 0, 15),
|
||||
(2, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H', 0, 10),
|
||||
(3, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, 5),
|
||||
(4, 1, 60, 'YEL', 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL),
|
||||
(5, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '5', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL),
|
||||
(6, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '6', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL),
|
||||
(7, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '7', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL),
|
||||
(8, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '8', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, NULL),
|
||||
(9, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '9', NULL, 0, 4, 'VT', 1, NULL, NULL, 0, NULL),
|
||||
(10, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '10', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL),
|
||||
(11, 1, 60, 'YEL', 1, 1, NULL, 1, 05080000, 4751000000, NULL, 0, '11', NULL, 0, 4, 'VT', 0, NULL, NULL, 0, NULL),
|
||||
(12, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '12', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL),
|
||||
(13, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '13', NULL, 1, 2, 'VT', 1, NULL, NULL, 1, NULL),
|
||||
(14, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 4, 'VT', 1, NULL, NULL, 0, NULL),
|
||||
(15, 4, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0, NULL),
|
||||
(16, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0, NULL),
|
||||
(71, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'VT', 0, NULL, NULL, 0, NULL);
|
||||
|
||||
-- Update the taxClass after insert of the items
|
||||
UPDATE `vn`.`itemTaxCountry` SET `taxClassFk` = 2
|
||||
|
@ -1621,51 +1633,59 @@ INSERT INTO `hedera`.`orderRowComponent`(`rowFk`, `componentFk`, `price`)
|
|||
|
||||
INSERT INTO `hedera`.`visit`(`id`, `firstAgentFk`)
|
||||
VALUES
|
||||
(1, NULL),
|
||||
(2, NULL),
|
||||
(3, NULL),
|
||||
(4, NULL),
|
||||
(5, NULL),
|
||||
(6, NULL),
|
||||
(7, NULL),
|
||||
(8, NULL),
|
||||
(9, NULL);
|
||||
(1, NULL),
|
||||
(2, NULL),
|
||||
(3, NULL),
|
||||
(4, NULL),
|
||||
(5, NULL),
|
||||
(6, NULL),
|
||||
(7, NULL),
|
||||
(8, NULL),
|
||||
(9, NULL),
|
||||
(10, NULL),
|
||||
(11, NULL);
|
||||
|
||||
INSERT INTO `hedera`.`visitAgent`(`id`, `visitFk`)
|
||||
VALUES
|
||||
(1, 1),
|
||||
(2, 2),
|
||||
(3, 3),
|
||||
(4, 4),
|
||||
(5, 5),
|
||||
(6, 6),
|
||||
(7, 7),
|
||||
(8, 8),
|
||||
(9, 9);
|
||||
(1, 1),
|
||||
(2, 2),
|
||||
(3, 3),
|
||||
(4, 4),
|
||||
(5, 5),
|
||||
(6, 6),
|
||||
(7, 7),
|
||||
(8, 8),
|
||||
(9, 9),
|
||||
(10, 10),
|
||||
(11, 11);
|
||||
|
||||
INSERT INTO `hedera`.`visitAccess`(`id`, `agentFk`, `stamp`)
|
||||
VALUES
|
||||
(1, 1, CURDATE()),
|
||||
(2, 2, CURDATE()),
|
||||
(3, 3, CURDATE()),
|
||||
(4, 4, CURDATE()),
|
||||
(5, 5, CURDATE()),
|
||||
(6, 6, CURDATE()),
|
||||
(7, 7, CURDATE()),
|
||||
(8, 8, CURDATE()),
|
||||
(9, 9, CURDATE());
|
||||
(1, 1, CURDATE()),
|
||||
(2, 2, CURDATE()),
|
||||
(3, 3, CURDATE()),
|
||||
(4, 4, CURDATE()),
|
||||
(5, 5, CURDATE()),
|
||||
(6, 6, CURDATE()),
|
||||
(7, 7, CURDATE()),
|
||||
(8, 8, CURDATE()),
|
||||
(9, 9, CURDATE()),
|
||||
(10, 10, CURDATE()),
|
||||
(11, 11, CURDATE());
|
||||
|
||||
INSERT INTO `hedera`.`visitUser`(`id`, `accessFk`, `userFk`, `stamp`)
|
||||
VALUES
|
||||
(1, 1, 1101, CURDATE()),
|
||||
(2, 2, 1101, CURDATE()),
|
||||
(3, 3, 1101, CURDATE()),
|
||||
(4, 4, 1102, CURDATE()),
|
||||
(5, 5, 1102, CURDATE()),
|
||||
(6, 6, 1102, CURDATE()),
|
||||
(7, 7, 1103, CURDATE()),
|
||||
(8, 8, 1103, CURDATE()),
|
||||
(9, 9, 1103, CURDATE());
|
||||
(1, 1, 1101, CURDATE()),
|
||||
(2, 2, 1101, CURDATE()),
|
||||
(3, 3, 1101, CURDATE()),
|
||||
(4, 4, 1102, CURDATE()),
|
||||
(5, 5, 1102, CURDATE()),
|
||||
(6, 6, 1102, CURDATE()),
|
||||
(7, 7, 1103, CURDATE()),
|
||||
(8, 8, 1103, CURDATE()),
|
||||
(9, 9, 1103, CURDATE()),
|
||||
(10, 10, 1102, DATE_SUB(CURDATE(), INTERVAL 1 DAY)),
|
||||
(11, 11, 1103, DATE_SUB(CURDATE(), INTERVAL 1 DAY));
|
||||
|
||||
INSERT INTO `hedera`.`userSession`(`created`, `lastUpdate`, `ssid`, `data`, `userVisitFk`)
|
||||
VALUES
|
||||
|
@ -2290,11 +2310,6 @@ INSERT INTO `vn`.`workerTimeControlParams` (`id`, `dayBreak`, `weekBreak`, `week
|
|||
(1, 43200, 129600, 734400, 43200, 50400, 259200, 1296000, 36000);
|
||||
|
||||
INSERT IGNORE INTO `vn`.`greugeConfig` (`id`, `freightPickUpPrice`) VALUES ('1', '11');
|
||||
|
||||
INSERT INTO `vn`.`temperature`(`code`, `name`, `description`)
|
||||
VALUES
|
||||
('warm', 'Warm', 'Warm'),
|
||||
('cool', 'Cool', 'Cool');
|
||||
|
||||
INSERT INTO `vn`.`thermograph`(`id`, `model`)
|
||||
VALUES
|
||||
|
@ -2543,4 +2558,37 @@ INSERT INTO `vn`.`supplierAgencyTerm` (`agencyFk`, `supplierFk`, `minimumPackage
|
|||
(2, 1, 60, 0.00, 0.00, NULL, 0, 5.00, 33),
|
||||
(3, 2, 0, 15.00, 0.00, NULL, 0, 0.00, 0),
|
||||
(4, 2, 0, 20.00, 0.00, NULL, 0, 0.00, 0),
|
||||
(5, 442, 0, 0.00, 3.05, NULL, 0, 0.00, 0);
|
||||
(5, 442, 0, 0.00, 3.05, NULL, 0, 0.00, 0);
|
||||
|
||||
INSERT INTO `vn`.`chat` (`senderFk`, `recipient`, `dated`, `checkUserStatus`, `message`, `status`, `attempts`)
|
||||
VALUES
|
||||
(1101, '@PetterParker', CURDATE(), 1, 'First test message', 0, 0),
|
||||
(1101, '@PetterParker', CURDATE(), 0, 'Second test message', 0, 0);
|
||||
|
||||
|
||||
INSERT INTO `vn`.`mobileAppVersionControl` (`appName`, `version`, `isVersionCritical`)
|
||||
VALUES
|
||||
('delivery', '9.2', 0),
|
||||
('warehouse', '8.1', 0);
|
||||
|
||||
INSERT INTO `vn`.`machine` (`plate`, `maker`, `model`, `warehouseFk`, `departmentFk`, `type`, `use`, `productionYear`, `workerFk`, `companyFk`)
|
||||
VALUES
|
||||
('RE-001', 'STILL', 'LTX-20', 60, 23, 'ELECTRIC TOW', 'Drag cars', 2020, 103, 442),
|
||||
('RE-002', 'STILL', 'LTX-20', 60, 23, 'ELECTRIC TOW', 'Drag cars', 2020, 103, 442);
|
||||
|
||||
INSERT INTO `vn`.`machineWorker` (`workerFk`, `machineFk`, `inTimed`, `outTimed`)
|
||||
VALUES
|
||||
(1106, 1, CURDATE(), CURDATE()),
|
||||
(1106, 1, DATE_ADD(CURDATE(), INTERVAL + 1 DAY), DATE_ADD(CURDATE(), INTERVAL +1 DAY)),
|
||||
(1106, 2, CURDATE(), NULL),
|
||||
(1106, 2, DATE_ADD(CURDATE(), INTERVAL + 1 DAY), DATE_ADD(CURDATE(), INTERVAL +1 DAY));
|
||||
|
||||
INSERT INTO `vn`.`mdbBranch` (`name`)
|
||||
VALUES
|
||||
('test'),
|
||||
('master');
|
||||
|
||||
INSERT INTO `vn`.`mdbVersion` (`app`, `branchFk`, `version`)
|
||||
VALUES
|
||||
('tpv', 'test', '1'),
|
||||
('lab', 'master', '1');
|
|
@ -37460,6 +37460,31 @@ SET character_set_client = utf8;
|
|||
) ENGINE=MyISAM */;
|
||||
SET character_set_client = @saved_cs_client;
|
||||
|
||||
--
|
||||
-- Temporary table structure for view `printer`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `printer`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `printer` (
|
||||
`id` tinyint(3) unsigned NOT NULL,
|
||||
`name` varchar(50) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
||||
`path` varchar(50) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
||||
`modelFk` varchar(50) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
||||
`macWifi` varchar(20) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
||||
`ipAddress` varchar(15) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
||||
`reference` varchar(50) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
||||
`isLabeler` tinyint(1) DEFAULT 0 COMMENT 'Indica si es impresora de etiquetas',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `printer_UN` (`reference`),
|
||||
UNIQUE KEY `printer_UN1` (`macWifi`),
|
||||
UNIQUE KEY `printer_UN2` (`name`),
|
||||
KEY `printer_FK` (`modelFk`),
|
||||
CONSTRAINT `printer_FK` FOREIGN KEY (`modelFk`) REFERENCES `printerModel` (`code`) ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `printingQueueCheck`
|
||||
--
|
||||
|
|
|
@ -60,7 +60,6 @@ IGNORETABLES=(
|
|||
--ignore-table=vn.plantpassportAuthority__
|
||||
--ignore-table=vn.preparationException
|
||||
--ignore-table=vn.priceFixed__
|
||||
--ignore-table=vn.printer
|
||||
--ignore-table=vn.printingQueue
|
||||
--ignore-table=vn.printServerQueue__
|
||||
--ignore-table=vn.promissoryNote
|
||||
|
|
|
@ -32,6 +32,7 @@ services:
|
|||
- /mnt/appdata/pdfs:/var/lib/salix/pdfs
|
||||
- /mnt/appdata/dms:/var/lib/salix/dms
|
||||
- /mnt/appdata/image:/var/lib/salix/image
|
||||
- /mnt/appdata/vn-access:/var/lib/salix/vn-access
|
||||
deploy:
|
||||
replicas: ${BACK_REPLICAS:?}
|
||||
placement:
|
||||
|
|
|
@ -101,6 +101,47 @@ export default {
|
|||
email: 'vn-user-mail-forwarding vn-textfield[ng-model="data.forwardTo"]',
|
||||
save: 'vn-user-mail-forwarding vn-submit'
|
||||
},
|
||||
accountAcl: {
|
||||
addAcl: 'vn-acl-index button vn-icon[icon="add"]',
|
||||
thirdAcl: 'vn-acl-index vn-list> a:nth-child(3)',
|
||||
deleteThirdAcl: 'vn-acl-index vn-list > a:nth-child(3) > vn-item-section > vn-icon-button[icon="delete"]',
|
||||
role: 'vn-acl-create vn-autocomplete[ng-model="$ctrl.acl.principalId"]',
|
||||
model: 'vn-acl-create vn-autocomplete[ng-model="$ctrl.acl.model"]',
|
||||
property: 'vn-acl-create vn-autocomplete[ng-model="$ctrl.acl.property"]',
|
||||
accessType: 'vn-acl-create vn-autocomplete[ng-model="$ctrl.acl.accessType"]',
|
||||
permission: 'vn-acl-create vn-autocomplete[ng-model="$ctrl.acl.permission"]',
|
||||
save: 'vn-acl-create vn-submit'
|
||||
},
|
||||
accountConnections: {
|
||||
firstConnection: 'vn-connections vn-list > a:nth-child(1)',
|
||||
deleteFirstConnection: 'vn-connections vn-list > a:nth-child(1) > vn-item-section > vn-icon-button[icon="exit_to_app"]'
|
||||
},
|
||||
accountAccounts: {
|
||||
syncRoles: 'vn-account-accounts vn-button[label="Synchronize roles"]',
|
||||
syncUser: 'vn-account-accounts vn-button[label="Synchronize user"]',
|
||||
syncAll: 'vn-account-accounts vn-button[label="Synchronize all"]',
|
||||
syncUserName: 'vn-textfield[ng-model="$ctrl.syncUser"]',
|
||||
syncUserPassword: 'vn-textfield[ng-model="$ctrl.syncPassword"]',
|
||||
buttonAccept: 'button[response="accept"]'
|
||||
},
|
||||
accountLdap: {
|
||||
checkEnable: 'vn-account-ldap vn-check[ng-model="watcher.hasData"]',
|
||||
server: 'vn-account-ldap vn-textfield[ng-model="$ctrl.config.server"]',
|
||||
rdn: 'vn-account-ldap vn-textfield[ng-model="$ctrl.config.rdn"]',
|
||||
password: 'vn-account-ldap vn-textfield[ng-model="$ctrl.config.password"]',
|
||||
userDn: 'vn-account-ldap vn-textfield[ng-model="$ctrl.config.userDn"]',
|
||||
groupDn: 'vn-account-ldap vn-textfield[ng-model="$ctrl.config.groupDn"]',
|
||||
save: 'vn-account-ldap vn-submit'
|
||||
},
|
||||
accountSamba: {
|
||||
checkEnable: 'vn-account-samba vn-check[ng-model="watcher.hasData"]',
|
||||
adDomain: 'vn-account-samba vn-textfield[ng-model="$ctrl.config.adDomain"]',
|
||||
adController: 'vn-account-samba vn-textfield[ng-model="$ctrl.config.adController"]',
|
||||
adUser: 'vn-account-samba vn-textfield[ng-model="$ctrl.config.adUser"]',
|
||||
adPassword: 'vn-account-samba vn-textfield[ng-model="$ctrl.config.adPassword"]',
|
||||
verifyCert: 'vn-account-samba vn-check[ng-model="$ctrl.config.verifyCert"]',
|
||||
save: 'vn-account-samba vn-submit'
|
||||
},
|
||||
clientsIndex: {
|
||||
createClientButton: `vn-float-button`
|
||||
},
|
||||
|
|
|
@ -123,19 +123,19 @@ describe('Client lock verified data path', () => {
|
|||
await page.accessToSection('client.card.fiscalData');
|
||||
}, 20000);
|
||||
|
||||
it('should confirm verified data button is disabled for salesAssistant', async() => {
|
||||
it('should confirm verified data button is enabled for salesAssistant', async() => {
|
||||
const isDisabled = await page.isDisabled(selectors.clientFiscalData.verifiedDataCheckbox);
|
||||
|
||||
expect(isDisabled).toBeTrue();
|
||||
expect(isDisabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return error when edit the social name', async() => {
|
||||
it('should now edit the social name', async() => {
|
||||
await page.clearInput(selectors.clientFiscalData.socialName);
|
||||
await page.write(selectors.clientFiscalData.socialName, 'new social name edition');
|
||||
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain(`Not enough privileges to edit a client with verified data`);
|
||||
expect(message.text).toContain(`Data saved!`);
|
||||
});
|
||||
|
||||
it('should now confirm the social name have been edited once and for all', async() => {
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Account ACL path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('developer', 'account');
|
||||
await page.accessToSection('account.acl');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should go to create new acl', async() => {
|
||||
await page.waitToClick(selectors.accountAcl.addAcl);
|
||||
await page.waitForState('account.acl.create');
|
||||
});
|
||||
|
||||
it('should create new acl', async() => {
|
||||
await page.autocompleteSearch(selectors.accountAcl.role, 'sysadmin');
|
||||
await page.autocompleteSearch(selectors.accountAcl.model, 'UserAccount');
|
||||
await page.autocompleteSearch(selectors.accountAcl.accessType, '*');
|
||||
await page.autocompleteSearch(selectors.accountAcl.permission, 'ALLOW');
|
||||
await page.waitToClick(selectors.accountAcl.save);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should navigate to edit', async() => {
|
||||
await page.doSearch();
|
||||
await page.waitToClick(selectors.accountAcl.thirdAcl);
|
||||
await page.waitForState('account.acl.edit');
|
||||
});
|
||||
|
||||
it('should edit the third acl', async() => {
|
||||
await page.autocompleteSearch(selectors.accountAcl.model, 'Supplier');
|
||||
await page.autocompleteSearch(selectors.accountAcl.accessType, 'READ');
|
||||
await page.waitToClick(selectors.accountAcl.save);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should delete the third result', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.accountAcl.thirdAcl, 'innerText');
|
||||
await page.waitToClick(selectors.accountAcl.deleteThirdAcl);
|
||||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
const newResult = await page.waitToGetProperty(selectors.accountAcl.thirdAcl, 'innerText');
|
||||
|
||||
expect(message.text).toContain('ACL removed');
|
||||
expect(result).not.toEqual(newResult);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,33 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Account Connections path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
const account = 'sysadmin';
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule(account, 'account');
|
||||
await page.accessToSection('account.connections');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should check this is the last connection', async() => {
|
||||
const firstResult = await page.waitToGetProperty(selectors.accountConnections.firstConnection, 'innerText');
|
||||
|
||||
expect(firstResult).toContain(account);
|
||||
});
|
||||
|
||||
it('should kill this connection and then get redirected to the login page', async() => {
|
||||
await page.waitToClick(selectors.accountConnections.deleteFirstConnection);
|
||||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Your session has expired, please login again');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Account Accounts path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('sysadmin', 'account');
|
||||
await page.accessToSection('account.accounts');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should sync roles', async() => {
|
||||
await page.waitToClick(selectors.accountAccounts.syncRoles);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Roles synchronized!');
|
||||
});
|
||||
|
||||
it('should sync user', async() => {
|
||||
await page.waitToClick(selectors.accountAccounts.syncUser);
|
||||
await page.write(selectors.accountAccounts.syncUserName, 'sysadmin');
|
||||
await page.write(selectors.accountAccounts.syncUserPassword, 'nightmare');
|
||||
|
||||
await page.waitToClick(selectors.accountAccounts.buttonAccept);
|
||||
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('User synchronized!');
|
||||
});
|
||||
|
||||
it('should relogin', async() => {
|
||||
await page.loginAndModule('sysadmin', 'account');
|
||||
await page.accessToSection('account.accounts');
|
||||
});
|
||||
|
||||
it('should sync all', async() => {
|
||||
await page.waitToClick(selectors.accountAccounts.syncAll);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Synchronizing in the background');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Account LDAP path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('sysadmin', 'account');
|
||||
await page.accessToSection('account.ldap');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should set data and save', async() => {
|
||||
await page.waitToClick(selectors.accountLdap.checkEnable);
|
||||
await page.write(selectors.accountLdap.server, '1234');
|
||||
await page.write(selectors.accountLdap.rdn, '1234');
|
||||
await page.write(selectors.accountLdap.password, 'nightmare');
|
||||
await page.write(selectors.accountLdap.userDn, 'sysadmin');
|
||||
await page.write(selectors.accountLdap.groupDn, '1234');
|
||||
await page.waitToClick(selectors.accountLdap.save);
|
||||
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Account Samba path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('sysadmin', 'account');
|
||||
await page.accessToSection('account.samba');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should set data and save', async() => {
|
||||
await page.waitToClick(selectors.accountSamba.checkEnable);
|
||||
await page.write(selectors.accountSamba.adDomain, '1234');
|
||||
await page.write(selectors.accountSamba.adController, '1234');
|
||||
await page.write(selectors.accountSamba.adUser, 'nightmare');
|
||||
await page.write(selectors.accountSamba.adPassword, 'sysadmin');
|
||||
await page.waitToClick(selectors.accountSamba.verifyCert);
|
||||
await page.waitToClick(selectors.accountSamba.save);
|
||||
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
});
|
|
@ -146,16 +146,17 @@ export default class MultiCheck extends FormInput {
|
|||
if (!this.model || !this.model.data) return;
|
||||
|
||||
const data = this.model.data;
|
||||
const modelParams = this.model.userParams;
|
||||
const params = {
|
||||
filter: {
|
||||
modelParams: modelParams,
|
||||
limit: null
|
||||
}
|
||||
};
|
||||
if (this.model.userFilter)
|
||||
Object.assign(params.filter, this.model.userFilter);
|
||||
if (this.model.userParams)
|
||||
Object.assign(params, this.model.userParams);
|
||||
|
||||
this.rows = data.length;
|
||||
|
||||
this.$http.get(this.model.url, {params})
|
||||
.then(res => {
|
||||
this.allRowsCount = res.data.length;
|
||||
|
|
|
@ -46,11 +46,13 @@
|
|||
</div>
|
||||
</vn-horizontal>
|
||||
<div id="table"></div>
|
||||
<vn-pagination
|
||||
ng-if="$ctrl.model"
|
||||
model="$ctrl.model"
|
||||
class="vn-pt-md">
|
||||
</vn-pagination>
|
||||
<div ng-transclude="pagination">
|
||||
<vn-pagination
|
||||
ng-if="$ctrl.model"
|
||||
model="$ctrl.model"
|
||||
class="vn-pt-md">
|
||||
</vn-pagination>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<vn-confirm
|
||||
|
|
|
@ -318,6 +318,8 @@ export default class SmartTable extends Component {
|
|||
for (let column of columns) {
|
||||
const field = column.getAttribute('field');
|
||||
const cell = document.createElement('td');
|
||||
cell.setAttribute('centered', '');
|
||||
|
||||
if (field) {
|
||||
let input;
|
||||
let options;
|
||||
|
@ -331,6 +333,15 @@ export default class SmartTable extends Component {
|
|||
continue;
|
||||
}
|
||||
|
||||
input = this.$compile(`
|
||||
<vn-textfield
|
||||
class="dense"
|
||||
name="${field}"
|
||||
ng-model="searchProps['${field}']"
|
||||
ng-keydown="$ctrl.searchWithEvent($event, '${field}')"
|
||||
clear-disabled="true"
|
||||
/>`)(this.$inputsScope);
|
||||
|
||||
if (options && options.autocomplete) {
|
||||
let props = ``;
|
||||
|
||||
|
@ -346,16 +357,29 @@ export default class SmartTable extends Component {
|
|||
on-change="$ctrl.searchByColumn('${field}')"
|
||||
clear-disabled="true"
|
||||
/>`)(this.$inputsScope);
|
||||
} else {
|
||||
}
|
||||
|
||||
if (options && options.checkbox) {
|
||||
input = this.$compile(`
|
||||
<vn-textfield
|
||||
<vn-check
|
||||
class="dense"
|
||||
name="${field}"
|
||||
ng-model="searchProps['${field}']"
|
||||
ng-keydown="$ctrl.searchWithEvent($event, '${field}')"
|
||||
clear-disabled="true"
|
||||
on-change="$ctrl.searchByColumn('${field}')"
|
||||
triple-state="true"
|
||||
/>`)(this.$inputsScope);
|
||||
}
|
||||
|
||||
if (options && options.datepicker) {
|
||||
input = this.$compile(`
|
||||
<vn-date-picker
|
||||
class="dense"
|
||||
name="${field}"
|
||||
ng-model="searchProps['${field}']"
|
||||
on-change="$ctrl.searchByColumn('${field}')"
|
||||
/>`)(this.$inputsScope);
|
||||
}
|
||||
|
||||
cell.appendChild(input[0]);
|
||||
}
|
||||
searchRow.appendChild(cell);
|
||||
|
@ -372,13 +396,12 @@ export default class SmartTable extends Component {
|
|||
|
||||
searchByColumn(field) {
|
||||
const searchCriteria = this.$inputsScope.searchProps[field];
|
||||
const emptySearch = searchCriteria == '' || null;
|
||||
const emptySearch = searchCriteria === '' || searchCriteria == null;
|
||||
|
||||
const filters = this.filterSanitizer(field);
|
||||
|
||||
if (filters && filters.userFilter)
|
||||
this.model.userFilter = filters.userFilter;
|
||||
|
||||
if (!emptySearch)
|
||||
this.addFilter(field, this.$inputsScope.searchProps[field]);
|
||||
else this.model.refresh();
|
||||
|
@ -497,7 +520,8 @@ ngModule.vnComponent('smartTable', {
|
|||
controller: SmartTable,
|
||||
transclude: {
|
||||
table: '?slotTable',
|
||||
actions: '?slotActions'
|
||||
actions: '?slotActions',
|
||||
pagination: '?slotPagination'
|
||||
},
|
||||
bindings: {
|
||||
model: '<?',
|
||||
|
|
|
@ -85,7 +85,6 @@
|
|||
}
|
||||
.icon-bucket:before {
|
||||
content: "\e97a";
|
||||
color: #000;
|
||||
}
|
||||
.icon-buscaman:before {
|
||||
content: "\e93b";
|
||||
|
@ -95,32 +94,26 @@
|
|||
}
|
||||
.icon-calc_volum .path1:before {
|
||||
content: "\e915";
|
||||
color: rgb(0, 0, 0);
|
||||
}
|
||||
.icon-calc_volum .path2:before {
|
||||
content: "\e916";
|
||||
margin-left: -1em;
|
||||
color: rgb(0, 0, 0);
|
||||
}
|
||||
.icon-calc_volum .path3:before {
|
||||
content: "\e917";
|
||||
margin-left: -1em;
|
||||
color: rgb(0, 0, 0);
|
||||
}
|
||||
.icon-calc_volum .path4:before {
|
||||
content: "\e918";
|
||||
margin-left: -1em;
|
||||
color: rgb(0, 0, 0);
|
||||
}
|
||||
.icon-calc_volum .path5:before {
|
||||
content: "\e919";
|
||||
margin-left: -1em;
|
||||
color: rgb(0, 0, 0);
|
||||
}
|
||||
.icon-calc_volum .path6:before {
|
||||
content: "\e91a";
|
||||
margin-left: -1em;
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
.icon-calendar:before {
|
||||
content: "\e93d";
|
||||
|
|
|
@ -98,5 +98,15 @@
|
|||
"image/jpg",
|
||||
"video/mp4"
|
||||
]
|
||||
},
|
||||
"accessStorage": {
|
||||
"name": "accessStorage",
|
||||
"connector": "loopback-component-storage",
|
||||
"provider": "filesystem",
|
||||
"root": "./storage/access",
|
||||
"maxFileSize": "524288000",
|
||||
"allowedContentTypes": [
|
||||
"application/x-7z-compressed"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ class Controller extends Component {
|
|||
this._role = value;
|
||||
this.$.summary = null;
|
||||
if (!value) return;
|
||||
|
||||
this.$http.get(`Roles/${value.id}`)
|
||||
.then(res => this.$.summary = res.data);
|
||||
}
|
||||
|
|
|
@ -86,7 +86,6 @@ module.exports = Self => {
|
|||
};
|
||||
ticketFk = await createTicket(ctx, myOptions);
|
||||
}
|
||||
|
||||
await models.Sale.create({
|
||||
ticketFk: ticketFk,
|
||||
itemFk: sale.itemFk,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<vn-crud-model vn-id="model"
|
||||
url="ClaimDms"
|
||||
filter="::$ctrl.filter"
|
||||
data="photos">
|
||||
</vn-crud-model>
|
||||
<vn-card class="summary">
|
||||
|
@ -106,8 +107,13 @@
|
|||
<section class="photo" ng-repeat="photo in photos">
|
||||
<section class="image" on-error-src
|
||||
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>
|
||||
<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>
|
||||
</vn-horizontal>
|
||||
</vn-auto>
|
||||
|
|
|
@ -6,6 +6,13 @@ class Controller extends Summary {
|
|||
constructor($element, $, vnFile) {
|
||||
super($element, $);
|
||||
this.vnFile = vnFile;
|
||||
this.filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'dms'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
$onChanges() {
|
||||
|
|
|
@ -10,4 +10,19 @@ vn-claim-summary {
|
|||
vn-textarea *{
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
|
||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
const buildFilter = require('vn-loopback/util/filter').buildFilter;
|
||||
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('extendedListFilter', {
|
||||
description: 'Find all clients matched by the filter',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'object',
|
||||
},
|
||||
{
|
||||
arg: 'search',
|
||||
type: 'string',
|
||||
description: `If it's and integer searchs by id, otherwise it searchs by name`,
|
||||
},
|
||||
{
|
||||
arg: 'name',
|
||||
type: 'string',
|
||||
description: 'The client name',
|
||||
},
|
||||
{
|
||||
arg: 'salesPersonFk',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
arg: 'fi',
|
||||
type: 'string',
|
||||
description: 'The client fiscal id',
|
||||
},
|
||||
{
|
||||
arg: 'socialName',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
arg: 'city',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
arg: 'postcode',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
arg: 'provinceFk',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
arg: 'email',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
arg: 'phone',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/extendedListFilter`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.extendedListFilter = async(ctx, filter, options) => {
|
||||
const conn = Self.dataSource.connector;
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const where = buildFilter(ctx.args, (param, value) => {
|
||||
switch (param) {
|
||||
case 'search':
|
||||
return /^\d+$/.test(value)
|
||||
? {'c.id': {inq: value}}
|
||||
: {'c.name': {like: `%${value}%`}};
|
||||
case 'name':
|
||||
case 'salesPersonFk':
|
||||
case 'fi':
|
||||
case 'socialName':
|
||||
case 'city':
|
||||
case 'postcode':
|
||||
case 'provinceFk':
|
||||
case 'email':
|
||||
case 'phone':
|
||||
param = `c.${param}`;
|
||||
return {[param]: value};
|
||||
}
|
||||
});
|
||||
|
||||
filter = mergeFilters(filter, {where});
|
||||
|
||||
const stmts = [];
|
||||
const stmt = new ParameterizedSQL(
|
||||
`SELECT
|
||||
c.id,
|
||||
c.name,
|
||||
c.socialName,
|
||||
c.fi,
|
||||
c.credit,
|
||||
c.creditInsurance,
|
||||
c.phone,
|
||||
c.mobile,
|
||||
c.street,
|
||||
c.city,
|
||||
c.postcode,
|
||||
c.email,
|
||||
c.created,
|
||||
c.isActive,
|
||||
c.isVies,
|
||||
c.isTaxDataChecked,
|
||||
c.isEqualizated,
|
||||
c.isFreezed,
|
||||
c.hasToInvoice,
|
||||
c.hasToInvoiceByAddress,
|
||||
c.isToBeMailed,
|
||||
c.hasSepaVnl,
|
||||
c.hasLcr,
|
||||
c.hasCoreVnl,
|
||||
ct.id AS countryFk,
|
||||
ct.country,
|
||||
p.id AS provinceFk,
|
||||
p.name AS province,
|
||||
u.id AS salesPersonFk,
|
||||
u.name AS salesPerson,
|
||||
bt.code AS businessTypeFk,
|
||||
bt.description AS businessType,
|
||||
pm.id AS payMethodFk,
|
||||
pm.name AS payMethod,
|
||||
sti.CodigoIva AS sageTaxTypeFk,
|
||||
sti.Iva AS sageTaxType,
|
||||
stt.CodigoTransaccion AS sageTransactionTypeFk,
|
||||
stt.Transaccion AS sageTransactionType
|
||||
FROM client c
|
||||
LEFT JOIN account.user u ON u.id = c.salesPersonFk
|
||||
LEFT JOIN country ct ON ct.id = c.countryFk
|
||||
LEFT JOIN province p ON p.id = c.provinceFk
|
||||
LEFT JOIN businessType bt ON bt.code = c.businessTypeFk
|
||||
LEFT JOIN payMethod pm ON pm.id = c.payMethodFk
|
||||
LEFT JOIN sage.TiposIva sti ON sti.CodigoIva = c.taxTypeSageFk
|
||||
LEFT JOIN sage.TiposTransacciones stt ON stt.CodigoTransaccion = c.transactionTypeSageFk
|
||||
`
|
||||
);
|
||||
|
||||
stmt.merge(conn.makeWhere(filter.where));
|
||||
stmt.merge(conn.makePagination(filter));
|
||||
|
||||
const clientsIndex = stmts.push(stmt) - 1;
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await conn.executeStmt(sql, myOptions);
|
||||
|
||||
return clientsIndex === 0 ? result : result[clientsIndex];
|
||||
};
|
||||
};
|
|
@ -39,7 +39,7 @@ module.exports = Self => {
|
|||
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
|
||||
const sms = await models.Sms.send(ctx, id, destination, message);
|
||||
const sms = await models.Sms.send(ctx, destination, message);
|
||||
const logRecord = {
|
||||
originFk: id,
|
||||
userFk: userId,
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
const { models } = require('vn-loopback/server/server');
|
||||
|
||||
describe('client extendedListFilter()', () => {
|
||||
it('should return the clients matching the filter with a limit of 20 rows', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {req: {accessToken: {userId: 1}}, args: {}};
|
||||
const filter = {limit: '20'};
|
||||
const result = await models.Client.extendedListFilter(ctx, filter, options);
|
||||
|
||||
expect(result.length).toEqual(20);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return the client "Bruce Wayne" matching the search argument with his name', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {req: {accessToken: {userId: 1}}, args: {search: 'Bruce Wayne'}};
|
||||
const filter = {};
|
||||
const result = await models.Client.extendedListFilter(ctx, filter, options);
|
||||
|
||||
const firstResult = result[0];
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(firstResult.name).toEqual('Bruce Wayne');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return the client "Bruce Wayne" matching the search argument with his id', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {req: {accessToken: {userId: 1}}, args: {search: '1101'}};
|
||||
const filter = {};
|
||||
const result = await models.Client.extendedListFilter(ctx, filter, options);
|
||||
|
||||
const firstResult = result[0];
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(firstResult.name).toEqual('Bruce Wayne');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return the client "Bruce Wayne" matching the name argument', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {req: {accessToken: {userId: 1}}, args: {name: 'Bruce Wayne'}};
|
||||
const filter = {};
|
||||
const result = await models.Client.extendedListFilter(ctx, filter, options);
|
||||
|
||||
const firstResult = result[0];
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(firstResult.name).toEqual('Bruce Wayne');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return the clients matching the "salesPersonFk" argument', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
const salesPersonId = 18;
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {req: {accessToken: {userId: 1}}, args: {salesPersonFk: salesPersonId}};
|
||||
const filter = {};
|
||||
const result = await models.Client.extendedListFilter(ctx, filter, options);
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * result.length);
|
||||
const randomResultClient = result[randomIndex];
|
||||
|
||||
expect(result.length).toBeGreaterThanOrEqual(5);
|
||||
expect(randomResultClient.salesPersonFk).toEqual(salesPersonId);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return the clients matching the "fi" argument', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {req: {accessToken: {userId: 1}}, args: {fi: '251628698'}};
|
||||
const filter = {};
|
||||
const result = await models.Client.extendedListFilter(ctx, filter, options);
|
||||
|
||||
const firstClient = result[0];
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(firstClient.name).toEqual('Max Eisenhardt');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return the clients matching the "city" argument', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {req: {accessToken: {userId: 1}}, args: {city: 'Silla'}};
|
||||
const filter = {};
|
||||
const result = await models.Client.extendedListFilter(ctx, filter, options);
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * result.length);
|
||||
const randomResultClient = result[randomIndex];
|
||||
|
||||
expect(result.length).toBeGreaterThanOrEqual(20);
|
||||
expect(randomResultClient.city.toLowerCase()).toEqual('silla');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return the clients matching the "postcode" argument', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {req: {accessToken: {userId: 1}}, args: {postcode: '46460'}};
|
||||
const filter = {};
|
||||
const result = await models.Client.extendedListFilter(ctx, filter, options);
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * result.length);
|
||||
const randomResultClient = result[randomIndex];
|
||||
|
||||
expect(result.length).toBeGreaterThanOrEqual(20);
|
||||
expect(randomResultClient.postcode).toEqual('46460');
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,21 +1,39 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('Client updatePortfolio', () => {
|
||||
const clientId = 1108;
|
||||
const activeCtx = {
|
||||
accessToken: {userId: 9},
|
||||
http: {
|
||||
req: {
|
||||
headers: {origin: 'http://localhost'},
|
||||
[`__`]: value => {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||
active: activeCtx
|
||||
});
|
||||
});
|
||||
|
||||
it('should update the portfolioWeight when the salesPerson of a client changes', async() => {
|
||||
const clientId = 1108;
|
||||
const salesPersonId = 18;
|
||||
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const expectedResult = 841.63;
|
||||
|
||||
const clientQuery = `UPDATE vn.client SET salesPersonFk = ${salesPersonId} WHERE id = ${clientId}; `;
|
||||
await models.Client.rawSql(clientQuery);
|
||||
const client = await models.Client.findById(clientId, null, options);
|
||||
await client.updateAttribute('salesPersonFk', salesPersonId, options);
|
||||
|
||||
await models.Client.updatePortfolio();
|
||||
await models.Client.updatePortfolio(options);
|
||||
|
||||
const portfolioQuery = `SELECT portfolioWeight FROM bs.salesPerson WHERE workerFk = ${salesPersonId}; `;
|
||||
const [salesPerson] = await models.Client.rawSql(portfolioQuery, null, options);
|
||||
|
@ -30,21 +48,21 @@ describe('Client updatePortfolio', () => {
|
|||
});
|
||||
|
||||
it('should keep the same portfolioWeight when a salesperson is unassigned of a client', async() => {
|
||||
pending('task 3817');
|
||||
const clientId = 1107;
|
||||
const salesPersonId = 19;
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const expectedResult = 34.40;
|
||||
|
||||
await models.Client.rawSql(`UPDATE vn.client SET salesPersonFk = NULL WHERE id = ${clientId}; `);
|
||||
const client = await models.Client.findById(clientId, null, options);
|
||||
await client.updateAttribute('salesPersonFk', null, options);
|
||||
|
||||
await models.Client.updatePortfolio();
|
||||
|
||||
const portfolioQuery = `SELECT portfolioWeight FROM bs.salesPerson WHERE workerFk = ${salesPersonId}; `;
|
||||
const [salesPerson] = await models.Client.rawSql(portfolioQuery, null, options);
|
||||
const [salesPerson] = await models.Client.rawSql(portfolioQuery);
|
||||
|
||||
expect(salesPerson.portfolioWeight).toEqual(expectedResult);
|
||||
|
||||
|
|
|
@ -129,10 +129,10 @@ module.exports = Self => {
|
|||
}
|
||||
|
||||
try {
|
||||
const isAdministrative = await models.Account.hasRole(userId, 'administrative', myOptions);
|
||||
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant', myOptions);
|
||||
const client = await models.Client.findById(clientId, null, myOptions);
|
||||
|
||||
if (!isAdministrative && client.isTaxDataChecked)
|
||||
if (!isSalesAssistant && client.isTaxDataChecked)
|
||||
throw new UserError(`Not enough privileges to edit a client with verified data`);
|
||||
|
||||
// Sage data validation
|
||||
|
|
|
@ -13,8 +13,13 @@ module.exports = function(Self) {
|
|||
}
|
||||
});
|
||||
|
||||
Self.updatePortfolio = async() => {
|
||||
Self.updatePortfolio = async options => {
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
query = `CALL bs.salesPerson_updatePortfolio()`;
|
||||
return await Self.rawSql(query);
|
||||
return Self.rawSql(query, null, myOptions);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -38,7 +38,7 @@ module.exports = Self => {
|
|||
}, myOptions);
|
||||
|
||||
await models.CreditInsurance.create({
|
||||
creditClassification: newClassification.id,
|
||||
creditClassificationFk: newClassification.id,
|
||||
credit: data.credit,
|
||||
grade: data.grade
|
||||
}, myOptions);
|
||||
|
|
|
@ -6,10 +6,6 @@ module.exports = Self => {
|
|||
description: 'Sends SMS to a destination phone',
|
||||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'destinationFk',
|
||||
type: 'integer'
|
||||
},
|
||||
{
|
||||
arg: 'destination',
|
||||
type: 'string',
|
||||
|
@ -31,7 +27,7 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.send = async(ctx, destinationFk, destination, message) => {
|
||||
Self.send = async(ctx, destination, message) => {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const smsConfig = await Self.app.models.SmsConfig.findOne();
|
||||
|
||||
|
@ -68,7 +64,6 @@ module.exports = Self => {
|
|||
|
||||
const newSms = {
|
||||
senderFk: userId,
|
||||
destinationFk: destinationFk || null,
|
||||
destination: destination,
|
||||
message: message,
|
||||
status: error
|
||||
|
|
|
@ -3,7 +3,7 @@ const app = require('vn-loopback/server/server');
|
|||
describe('sms send()', () => {
|
||||
it('should not return status error', async() => {
|
||||
const ctx = {req: {accessToken: {userId: 1}}};
|
||||
const result = await app.models.Sms.send(ctx, 1105, '123456789', 'My SMS Body');
|
||||
const result = await app.models.Sms.send(ctx, '123456789', 'My SMS Body');
|
||||
|
||||
expect(result.status).toBeUndefined();
|
||||
});
|
||||
|
|
|
@ -31,6 +31,7 @@ module.exports = Self => {
|
|||
require('../methods/client/createReceipt')(Self);
|
||||
require('../methods/client/updatePortfolio')(Self);
|
||||
require('../methods/client/checkDuplicated')(Self);
|
||||
require('../methods/client/extendedListFilter')(Self);
|
||||
|
||||
// Validations
|
||||
|
||||
|
@ -232,7 +233,6 @@ module.exports = Self => {
|
|||
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;
|
||||
|
||||
|
@ -245,7 +245,7 @@ module.exports = Self => {
|
|||
const sageTransactionType = hasChanges && (changes.sageTransactionTypeFk || orgData.sageTransactionTypeFk);
|
||||
const sageTransactionTypeChanged = hasChanges && orgData.sageTransactionTypeFk != sageTransactionType;
|
||||
|
||||
const cantEditVerifiedData = isTaxDataCheckedChanged && !isAdministrative;
|
||||
const cantEditVerifiedData = isTaxDataCheckedChanged && !isSalesAssistant;
|
||||
const cantChangeSageData = (sageTaxTypeChanged || sageTransactionTypeChanged) && !isSalesAssistant;
|
||||
|
||||
if (cantEditVerifiedData || cantChangeSageData)
|
||||
|
|
|
@ -139,6 +139,9 @@
|
|||
"mysql": {
|
||||
"columnName": "businessTypeFk"
|
||||
}
|
||||
},
|
||||
"salesPersonFk": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
"insurances": {
|
||||
"type": "hasMany",
|
||||
"model": "CreditInsurance",
|
||||
"foreignKey": "creditClassification"
|
||||
"foreignKey": "creditClassificationFk"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ module.exports = function(Self) {
|
|||
let filter = {
|
||||
fields: ['grade'],
|
||||
where: {
|
||||
creditClassification: this.creditClassification
|
||||
creditClassificationFk: this.creditClassificationFk
|
||||
},
|
||||
order: 'created DESC'
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
"classification": {
|
||||
"type": "belongsTo",
|
||||
"model": "CreditClassification",
|
||||
"foreignKey": "creditClassification"
|
||||
"foreignKey": "creditClassificationFk"
|
||||
}
|
||||
},
|
||||
"scope": {
|
||||
|
|
|
@ -6,12 +6,7 @@ class Controller extends Dialog {
|
|||
super($element, $, $transclude);
|
||||
|
||||
this.vnReport = vnReport;
|
||||
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
this.receipt = {
|
||||
payed: tomorrow
|
||||
};
|
||||
this.receipt = {};
|
||||
}
|
||||
|
||||
set payed(value) {
|
||||
|
@ -72,6 +67,10 @@ class Controller extends Dialog {
|
|||
`${accountingType && accountingType.receiptDescription}`;
|
||||
}
|
||||
this.maxAmount = accountingType && accountingType.maxAmount;
|
||||
|
||||
this.receipt.payed = new Date();
|
||||
if (accountingType.daysInFuture)
|
||||
this.receipt.payed.setDate(this.receipt.payed.getDate() + accountingType.daysInFuture);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,14 +54,7 @@
|
|||
show-field="bic"
|
||||
vn-acl="salesAssistant"
|
||||
disabled="$ctrl.ibanCountry == 'ES'">
|
||||
<tpl-item>
|
||||
<vn-horizontal>
|
||||
<vn-one>{{bic}}</vn-one>
|
||||
<vn-one>
|
||||
<div class="ellipsize" style="max-width: 10em">{{name}}</div>
|
||||
</vn-one>
|
||||
</vn-horizontal>
|
||||
</tpl-item>
|
||||
<tpl-item>{{bic}} {{name}}</tpl-item>
|
||||
<append>
|
||||
<vn-icon-button
|
||||
vn-auto
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="CreditInsurances"
|
||||
link="{creditClassification: $ctrl.$params.classificationId}"
|
||||
link="{creditClassificationFk: $ctrl.$params.classificationId}"
|
||||
limit="20"
|
||||
data="insurances"
|
||||
auto-load="true">
|
||||
|
|
|
@ -0,0 +1,319 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Clients/extendedListFilter"
|
||||
limit="20">
|
||||
</vn-crud-model>
|
||||
<vn-portal slot="topbar">
|
||||
<vn-searchbar
|
||||
vn-focus
|
||||
panel="vn-client-search-panel"
|
||||
placeholder="Search client"
|
||||
info="Search client by id or name"
|
||||
auto-state="false"
|
||||
model="model">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-card>
|
||||
<smart-table
|
||||
model="model"
|
||||
view-config-id="clientsDetail"
|
||||
options="$ctrl.smartTableOptions"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
<slot-table>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th field="id">
|
||||
<span translate>Identifier</span>
|
||||
</th>
|
||||
<th field="name">
|
||||
<span translate>Name</span>
|
||||
</th>
|
||||
<th field="socialName">
|
||||
<span translate>Social name</span>
|
||||
</th>
|
||||
<th field="fi">
|
||||
<span translate>Tax number</span>
|
||||
</th>
|
||||
<th field="salesPersonFk">
|
||||
<span translate>Salesperson</span>
|
||||
</th>
|
||||
<th field="credit">
|
||||
<span translate>Credit</span>
|
||||
</th>
|
||||
<th field="creditInsurance">
|
||||
<span translate>Credit insurance</span>
|
||||
</th>
|
||||
<th field="phone">
|
||||
<span translate>Phone</span>
|
||||
</th>
|
||||
<th field="mobile">
|
||||
<span translate>Mobile</span>
|
||||
</th>
|
||||
<th field="street">
|
||||
<span translate>Street</span>
|
||||
</th>
|
||||
<th field="countryFk">
|
||||
<span translate>Country</span>
|
||||
</th>
|
||||
<th field="provinceFk">
|
||||
<span translate>Province</span>
|
||||
</th>
|
||||
<th field="city">
|
||||
<span translate>City</span>
|
||||
</th>
|
||||
<th field="postcode">
|
||||
<span translate>Postcode</span>
|
||||
</th>
|
||||
<th field="email">
|
||||
<span translate>Email</span>
|
||||
</th>
|
||||
<th field="created">
|
||||
<span translate>Created</span>
|
||||
</th>
|
||||
<th field="businessTypeFk">
|
||||
<span translate>Business type</span>
|
||||
</th>
|
||||
<th field="payMethodFk">
|
||||
<span translate>Billing data</span>
|
||||
</th>
|
||||
<th field="sageTaxTypeFk">
|
||||
<span translate>Sage tax type</span>
|
||||
</th>
|
||||
<th field="sageTransactionTypeFk">
|
||||
<span translate>Sage tr. type</span>
|
||||
</th>
|
||||
<th field="isActive" centered>
|
||||
<span translate>Active</span>
|
||||
</th>
|
||||
<th field="isVies" centered>
|
||||
<span translate>Vies</span>
|
||||
</th>
|
||||
<th field="isTaxDataChecked" centered>
|
||||
<span translate>Verified data</span>
|
||||
</th>
|
||||
<th field="isEqualizated" centered>
|
||||
<span translate>Is equalizated</span>
|
||||
</th>
|
||||
<th field="isFreezed" centered>
|
||||
<span translate>Freezed</span>
|
||||
</th>
|
||||
<th field="hasToInvoice" centered>
|
||||
<span translate>Invoice</span>
|
||||
</th>
|
||||
<th field="hasToInvoiceByAddress" centered>
|
||||
<span translate>Invoice by address</span>
|
||||
</th>
|
||||
<th field="isToBeMailed" centered>
|
||||
<span translate>Mailing</span>
|
||||
</th>
|
||||
<th field="hasLcr" centered>
|
||||
<span translate>Received LCR</span>
|
||||
</th>
|
||||
<th field="hasCoreVnl" centered>
|
||||
<span translate>Received core VNL</span>
|
||||
</th>
|
||||
<th field="hasSepaVnl" centered>
|
||||
<span translate>Received B2B VNL</span>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="client in model.data"
|
||||
vn-anchor="::{
|
||||
state: 'client.card.summary',
|
||||
params: {id: client.id}
|
||||
}">
|
||||
<td>
|
||||
<vn-icon-button ng-show="::client.isActive == false"
|
||||
vn-tooltip="Client inactive"
|
||||
icon="icon-disabled">
|
||||
</vn-icon-button>
|
||||
<vn-icon-button ng-show="::client.isActive && client.isFreezed == true"
|
||||
vn-tooltip="Client frozen"
|
||||
icon="icon-frozen">
|
||||
</vn-icon-button>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
vn-click-stop="clientDescriptor.show($event, client.id)"
|
||||
class="link">
|
||||
{{::client.id}}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{::client.name}}</td>
|
||||
<td>{{::client.socialName}}</td>
|
||||
<td>{{::client.fi}}</td>
|
||||
<td>
|
||||
<span
|
||||
vn-click-stop="workerDescriptor.show($event, client.salesPersonFk)"
|
||||
ng-class="{'link': client.salesPersonFk}">
|
||||
{{::client.salesPerson | dashIfEmpty}}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{::client.credit}}</td>
|
||||
<td>{{::client.creditInsurance | dashIfEmpty}}</td>
|
||||
<td>{{::client.phone | dashIfEmpty}}</td>
|
||||
<td>{{::client.mobile | dashIfEmpty}}</td>
|
||||
<td>{{::client.street | dashIfEmpty}}</td>
|
||||
<td>{{::client.country | dashIfEmpty}}</td>
|
||||
<td>{{::client.province | dashIfEmpty}}</td>
|
||||
<td>{{::client.city | dashIfEmpty}}</td>
|
||||
<td>{{::client.postcode | dashIfEmpty}}</td>
|
||||
<td>{{::client.email | dashIfEmpty}}</td>
|
||||
<td>{{::client.created | date:'dd/MM/yyyy'}}</td>
|
||||
<td>{{::client.businessType | dashIfEmpty}}</td>
|
||||
<td>{{::client.payMethod | dashIfEmpty}}</td>
|
||||
<td>{{::client.sageTaxType | dashIfEmpty}}</td>
|
||||
<td>{{::client.sageTransactionType | dashIfEmpty}}</td>
|
||||
<td centered>
|
||||
<vn-chip ng-class="::{
|
||||
'success': client.isActive,
|
||||
'alert': !client.isActive,
|
||||
}">
|
||||
{{ ::client.isActive ? 'Yes' : 'No' | translate}}
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td centered>
|
||||
<vn-chip ng-class="::{
|
||||
'success': client.isVies,
|
||||
'alert': !client.isVies,
|
||||
}">
|
||||
{{ ::client.isVies ? 'Yes' : 'No' | translate}}
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td centered>
|
||||
<vn-chip ng-class="::{
|
||||
'success': client.isTaxDataChecked,
|
||||
'alert': !client.isTaxDataChecked,
|
||||
}">
|
||||
{{ ::client.isTaxDataChecked ? 'Yes' : 'No' | translate}}
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td centered>
|
||||
<vn-chip ng-class="::{
|
||||
'success': client.isEqualizated,
|
||||
'alert': !client.isEqualizated,
|
||||
}">
|
||||
{{ ::client.isEqualizated ? 'Yes' : 'No' | translate}}
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td centered>
|
||||
<vn-chip ng-class="::{
|
||||
'success': client.isFreezed,
|
||||
'alert': !client.isFreezed,
|
||||
}">
|
||||
{{ ::client.isFreezed ? 'Yes' : 'No' | translate}}
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td centered>
|
||||
<vn-chip ng-class="::{
|
||||
'success': client.hasToInvoice,
|
||||
'alert': !client.hasToInvoice,
|
||||
}">
|
||||
{{ ::client.hasToInvoice ? 'Yes' : 'No' | translate}}
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td centered>
|
||||
<vn-chip ng-class="::{
|
||||
'success': client.hasToInvoiceByAddress,
|
||||
'alert': !client.hasToInvoiceByAddress,
|
||||
}">
|
||||
{{ ::client.hasToInvoiceByAddress ? 'Yes' : 'No' | translate}}
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td centered>
|
||||
<vn-chip ng-class="::{
|
||||
'success': client.isToBeMailed,
|
||||
'alert': !client.isToBeMailed,
|
||||
}">
|
||||
{{ ::client.isToBeMailed ? 'Yes' : 'No' | translate}}
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td centered>
|
||||
<vn-chip ng-class="::{
|
||||
'success': client.hasLcr,
|
||||
'alert': !client.hasLcr,
|
||||
}">
|
||||
{{ ::client.hasLcr ? 'Yes' : 'No' | translate}}
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td centered>
|
||||
<vn-chip ng-class="::{
|
||||
'success': client.hasCoreVnl,
|
||||
'alert': !client.hasCoreVnl,
|
||||
}">
|
||||
{{ ::client.hasCoreVnl ? 'Yes' : 'No' | translate}}
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td centered>
|
||||
<vn-chip ng-class="::{
|
||||
'success': client.hasSepaVnl,
|
||||
'alert': !client.hasSepaVnl,
|
||||
}">
|
||||
{{ ::client.hasSepaVnl ? 'Yes' : 'No' | translate}}
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td shrink>
|
||||
<vn-horizontal class="buttons">
|
||||
<vn-icon-button vn-anchor="{state: 'ticket.index', params: {q: {clientFk: client.id} } }"
|
||||
vn-tooltip="Client tickets"
|
||||
icon="icon-ticket">
|
||||
</vn-icon-button>
|
||||
<vn-icon-button
|
||||
vn-click-stop="$ctrl.preview(client)"
|
||||
vn-tooltip="Preview"
|
||||
icon="preview">
|
||||
</vn-icon-button>
|
||||
</vn-horizontal>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</slot-table>
|
||||
</smart-table>
|
||||
</vn-card>
|
||||
<a ui-sref="client.create" vn-tooltip="New client" vn-bind="+" fixed-bottom-right>
|
||||
<vn-float-button icon="add"></vn-float-button>
|
||||
</a>
|
||||
<vn-client-descriptor-popover
|
||||
vn-id="client-descriptor">
|
||||
</vn-client-descriptor-popover>
|
||||
<vn-worker-descriptor-popover
|
||||
vn-id="worker-descriptor">
|
||||
</vn-worker-descriptor-popover>
|
||||
|
||||
<vn-popup vn-id="preview">
|
||||
<vn-client-summary
|
||||
client="$ctrl.clientSelected">
|
||||
</vn-client-summary>
|
||||
</vn-popup>
|
||||
<vn-contextmenu
|
||||
vn-id="contextmenu"
|
||||
targets="['smart-table']"
|
||||
model="model"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
<slot-menu>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.filterBySelection()">
|
||||
Filter by selection
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.excludeSelection()">
|
||||
Exclude selection
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.removeFilter()">
|
||||
Remove filter
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-click="contextmenu.removeAllFilters()">
|
||||
Remove all filters
|
||||
</vn-item>
|
||||
</slot-menu>
|
||||
</vn-contextmenu>
|
|
@ -0,0 +1,184 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
import './style.scss';
|
||||
|
||||
class Controller extends Section {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
|
||||
this.smartTableOptions = {
|
||||
activeButtons: {
|
||||
search: true,
|
||||
shownColumns: true,
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'socialName',
|
||||
autocomplete: {
|
||||
url: 'Clients',
|
||||
showField: 'socialName',
|
||||
valueField: 'socialName',
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'created',
|
||||
datepicker: true
|
||||
},
|
||||
{
|
||||
field: 'countryFk',
|
||||
autocomplete: {
|
||||
url: 'Countries',
|
||||
showField: 'country',
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'provinceFk',
|
||||
autocomplete: {
|
||||
url: 'Provinces'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'salesPersonFk',
|
||||
autocomplete: {
|
||||
url: 'Workers/activeWithInheritedRole',
|
||||
where: `{role: 'salesPerson'}`,
|
||||
searchFunction: '{firstName: $search}',
|
||||
showField: 'nickname',
|
||||
valueField: 'id',
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'businessTypeFk',
|
||||
autocomplete: {
|
||||
url: 'BusinessTypes',
|
||||
valueField: 'code',
|
||||
showField: 'description',
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'payMethodFk',
|
||||
autocomplete: {
|
||||
url: 'PayMethods',
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'sageTaxTypeFk',
|
||||
autocomplete: {
|
||||
url: 'SageTaxTypes',
|
||||
showField: 'vat',
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'sageTransactionTypeFk',
|
||||
autocomplete: {
|
||||
url: 'SageTransactionTypes',
|
||||
showField: 'transaction',
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'isActive',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'isVies',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'isTaxDataChecked',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'isEqualizated',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'isFreezed',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'hasToInvoice',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'hasToInvoiceByAddress',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'isToBeMailed',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'hasSepaVnl',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'hasLcr',
|
||||
checkbox: true
|
||||
},
|
||||
{
|
||||
field: 'hasCoreVnl',
|
||||
checkbox: true
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
case 'created':
|
||||
return {'c.created': {
|
||||
between: this.dateRange(value)}
|
||||
};
|
||||
case 'id':
|
||||
case 'name':
|
||||
case 'socialName':
|
||||
case 'fi':
|
||||
case 'credit':
|
||||
case 'creditInsurance':
|
||||
case 'phone':
|
||||
case 'mobile':
|
||||
case 'street':
|
||||
case 'city':
|
||||
case 'postcode':
|
||||
case 'email':
|
||||
case 'isActive':
|
||||
case 'isVies':
|
||||
case 'isTaxDataChecked':
|
||||
case 'isEqualizated':
|
||||
case 'isFreezed':
|
||||
case 'hasToInvoice':
|
||||
case 'hasToInvoiceByAddress':
|
||||
case 'isToBeMailed':
|
||||
case 'hasSepaVnl':
|
||||
case 'hasLcr':
|
||||
case 'hasCoreVnl':
|
||||
case 'countryFk':
|
||||
case 'provinceFk':
|
||||
case 'salesPersonFk':
|
||||
case 'businessTypeFk':
|
||||
case 'payMethodFk':
|
||||
case 'sageTaxTypeFk':
|
||||
case 'sageTransactionTypeFk':
|
||||
return {[`c.${param}`]: 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];
|
||||
}
|
||||
|
||||
preview(client) {
|
||||
this.clientSelected = client;
|
||||
this.$.preview.show();
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnClientExtendedList', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
Mailing: Env. emails
|
||||
Sage tr. type: Tipo tr. sage
|
||||
Yes: Sí
|
|
@ -0,0 +1,6 @@
|
|||
@import "variables";
|
||||
|
||||
vn-chip.success,
|
||||
vn-chip.alert {
|
||||
color: $color-font-bg
|
||||
}
|
|
@ -182,7 +182,7 @@
|
|||
vn-one
|
||||
label="Verified data"
|
||||
ng-model="$ctrl.client.isTaxDataChecked"
|
||||
vn-acl="administrative">
|
||||
vn-acl="salesAssistant">
|
||||
</vn-check>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
|
|
|
@ -47,3 +47,4 @@ import './consumption-search-panel';
|
|||
import './defaulter';
|
||||
import './notification';
|
||||
import './unpaid';
|
||||
import './extended-list';
|
||||
|
|
|
@ -33,6 +33,7 @@ Search client by id or name: Buscar clientes por identificador o nombre
|
|||
# Sections
|
||||
|
||||
Clients: Clientes
|
||||
Extended list: Listado extendido
|
||||
Defaulter: Morosos
|
||||
New client: Nuevo cliente
|
||||
Fiscal data: Datos fiscales
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"menus": {
|
||||
"main": [
|
||||
{"state": "client.index", "icon": "person"},
|
||||
{"state": "client.extendedList", "icon": "person"},
|
||||
{"state": "client.notification", "icon": "campaign"},
|
||||
{"state": "client.defaulter", "icon": "icon-defaulter"}
|
||||
],
|
||||
|
@ -381,6 +382,12 @@
|
|||
"component": "vn-client-unpaid",
|
||||
"acl": ["administrative"],
|
||||
"description": "Unpaid"
|
||||
},
|
||||
{
|
||||
"url": "/extended-list",
|
||||
"state": "client.extendedList",
|
||||
"component": "vn-client-extended-list",
|
||||
"description": "Extended list"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Client id: Id cliente
|
||||
Tax number: NIF/CIF
|
||||
Name: Nombre
|
||||
Social name: Razon social
|
||||
Social name: Razón social
|
||||
Town/City: Ciudad
|
||||
Postcode: Código postal
|
||||
Email: E-mail
|
||||
|
|
|
@ -98,9 +98,6 @@ module.exports = Self => {
|
|||
Self.latestBuysFilter = async(ctx, filter, options) => {
|
||||
const myOptions = {};
|
||||
|
||||
if (filter && filter.modelParams)
|
||||
ctx.args = filter.modelParams;
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
|
|
|
@ -148,12 +148,12 @@
|
|||
</td>
|
||||
<td number>
|
||||
<vn-chip class="transparent" translate-attr="buy.groupingMode == 2 ? {title: 'Minimun amount'} : {title: 'Packing'}" ng-class="{'message': buy.groupingMode == 2}">
|
||||
<span translate>{{::buy.packing | dashIfEmpty}}</span>
|
||||
<span>{{::buy.packing | dashIfEmpty}}</span>
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td number>
|
||||
<vn-chip class="transparent" translate-attr="buy.groupingMode == 1 ? {title: 'Minimun amount'} : {title: 'Grouping'}" ng-class="{'message': buy.groupingMode == 1}">
|
||||
<span translate>{{::buy.grouping | dashIfEmpty}}</span>
|
||||
<span>{{::buy.grouping | dashIfEmpty}}</span>
|
||||
</vn-chip>
|
||||
</td>
|
||||
<td number>{{::buy.quantity}}</td>
|
||||
|
|
|
@ -159,8 +159,22 @@ export default class Controller extends Section {
|
|||
lines: rowsToEdit
|
||||
};
|
||||
|
||||
if (this.checkedDummyCount && this.checkedDummyCount > 0)
|
||||
data.filter = this.$.model.userParams;
|
||||
if (this.checkedDummyCount && this.checkedDummyCount > 0) {
|
||||
const params = {};
|
||||
if (this.$.model.userParams) {
|
||||
const userParams = this.$.model.userParams;
|
||||
for (let param in userParams) {
|
||||
let newParam = this.exprBuilder(param, userParams[param]);
|
||||
if (!newParam)
|
||||
newParam = {[param]: userParams[param]};
|
||||
Object.assign(params, newParam);
|
||||
}
|
||||
}
|
||||
if (this.$.model.userFilter)
|
||||
Object.assign(params, this.$.model.userFilter.where);
|
||||
|
||||
data.filter = params;
|
||||
}
|
||||
|
||||
return this.$http.post('Buys/editLatestBuys', data)
|
||||
.then(() => {
|
||||
|
|
|
@ -76,6 +76,13 @@
|
|||
translate>
|
||||
Show CITES letter
|
||||
</vn-item>
|
||||
<vn-item
|
||||
ng-click="refundConfirmation.show()"
|
||||
name="refundInvoice"
|
||||
vn-tooltip="Create a single ticket with all the content of the current invoice"
|
||||
translate>
|
||||
Refund
|
||||
</vn-item>
|
||||
</vn-list>
|
||||
</vn-menu>
|
||||
<vn-confirm
|
||||
|
@ -88,6 +95,11 @@
|
|||
on-accept="$ctrl.bookInvoiceOut()"
|
||||
question="Are you sure you want to book this invoice?">
|
||||
</vn-confirm>
|
||||
<vn-confirm
|
||||
vn-id="refundConfirmation"
|
||||
on-accept="$ctrl.refundInvoiceOut()"
|
||||
question="Are you sure you want to refund this invoice?">
|
||||
</vn-confirm>
|
||||
<vn-client-descriptor-popover
|
||||
vn-id="clientDescriptor">
|
||||
</vn-client-descriptor-popover>
|
||||
|
|
|
@ -116,6 +116,35 @@ class Controller extends Section {
|
|||
invoiceId: this.id
|
||||
});
|
||||
}
|
||||
|
||||
async refundInvoiceOut() {
|
||||
let filter = {
|
||||
where: {refFk: this.invoiceOut.ref}
|
||||
};
|
||||
const tickets = await this.$http.get('Tickets', {filter});
|
||||
this.tickets = tickets.data;
|
||||
this.ticketsIds = [];
|
||||
for (let ticket of this.tickets)
|
||||
this.ticketsIds.push(ticket.id);
|
||||
|
||||
filter = {
|
||||
where: {ticketFk: {inq: this.ticketsIds}}
|
||||
};
|
||||
const sales = await this.$http.get('Sales', {filter});
|
||||
this.sales = sales.data;
|
||||
|
||||
const ticketServices = await this.$http.get('TicketServices', {filter});
|
||||
this.services = ticketServices.data;
|
||||
|
||||
const params = {
|
||||
sales: this.sales,
|
||||
services: this.services
|
||||
};
|
||||
const query = `Sales/refund`;
|
||||
return this.$http.post(query, params).then(res => {
|
||||
this.$state.go('ticket.card.sale', {id: res.data});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail'];
|
||||
|
|
|
@ -122,4 +122,34 @@ describe('vnInvoiceOutDescriptorMenu', () => {
|
|||
expect(controller.vnApp.showMessage).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
// #4084 review with Juan
|
||||
xdescribe('refundInvoiceOut()', () => {
|
||||
it('should make a query and go to ticket.card.sale', () => {
|
||||
controller.$state.go = jest.fn();
|
||||
|
||||
const invoiceOut = {
|
||||
id: 1,
|
||||
ref: 'T1111111'
|
||||
};
|
||||
controller.invoiceOut = invoiceOut;
|
||||
const tickets = [{id: 1}];
|
||||
const sales = [{id: 1}];
|
||||
const services = [{id: 2}];
|
||||
|
||||
$httpBackend.expectGET(`Tickets`).respond(tickets);
|
||||
$httpBackend.expectGET(`Sales`).respond(sales);
|
||||
$httpBackend.expectGET(`TicketServices`).respond(services);
|
||||
|
||||
const expectedParams = {
|
||||
sales: sales,
|
||||
services: services
|
||||
};
|
||||
$httpBackend.expectPOST(`Sales/refund`, expectedParams).respond();
|
||||
controller.refundInvoiceOut();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('ticket.card.sale', {id: undefined});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,6 +12,8 @@ Are you sure you want to delete this invoice?: Estas seguro de eliminar esta fac
|
|||
Are you sure you want to clone this invoice?: Estas seguro de clonar esta factura?
|
||||
InvoiceOut booked: Factura asentada
|
||||
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
|
||||
Are you sure you want to refund this invoice?: Estas seguro de querer abonar esta factura?
|
||||
Create a single ticket with all the content of the current invoice: Crear un ticket unico con todo el contenido de la factura actual
|
||||
Regenerate PDF invoice: Regenerar PDF factura
|
||||
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
|
||||
The email can't be empty: El correo no puede estar vacío
|
||||
|
|
|
@ -21,8 +21,11 @@
|
|||
"life": {
|
||||
"type": "number"
|
||||
},
|
||||
"isPackaging": {
|
||||
"type": "boolean"
|
||||
"promo": {
|
||||
"type": "number"
|
||||
},
|
||||
"isUnconventionalSize": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
@ -40,6 +43,16 @@
|
|||
"type": "belongsTo",
|
||||
"model": "ItemCategory",
|
||||
"foreignKey": "categoryFk"
|
||||
},
|
||||
"itemPackingType": {
|
||||
"type": "belongsTo",
|
||||
"model": "ItemPackingType",
|
||||
"foreignKey": "itemPackingTypeFk"
|
||||
},
|
||||
"temperature": {
|
||||
"type": "belongsTo",
|
||||
"model": "Temperature",
|
||||
"foreignKey": "temperatureFk"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
|
|
|
@ -140,6 +140,9 @@
|
|||
},
|
||||
"isFloramondo": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"packingShelve": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -23,4 +23,4 @@ import './waste/index/';
|
|||
import './waste/detail';
|
||||
import './fixed-price';
|
||||
import './fixed-price-search-panel';
|
||||
|
||||
import './item-type';
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue