refs #5867 fix: tickets sql #1800
|
@ -5,7 +5,13 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2342.01] - 2023-10-19
|
||||
## [2346.01] - 2023-11-16
|
||||
|
||||
### Added
|
||||
### Changed
|
||||
### Fixed
|
||||
|
||||
## [2342.01] - 2023-11-02
|
||||
|
||||
### Added
|
||||
### Changed
|
||||
|
|
|
@ -26,15 +26,14 @@ module.exports = Self => {
|
|||
|
||||
Self.sendCheckingPresence = async(ctx, recipientId, message) => {
|
||||
if (!recipientId) return false;
|
||||
|
||||
const models = Self.app.models;
|
||||
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const sender = await models.VnUser.findById(userId, {fields: ['id']});
|
||||
const recipient = await models.VnUser.findById(recipientId, null);
|
||||
|
||||
// Prevent sending messages to yourself
|
||||
if (recipientId == userId) return false;
|
||||
|
||||
if (!recipient)
|
||||
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ module.exports = Self => {
|
|||
|
||||
Self.getTickets = async(ctx, id, print, options) => {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const origin = ctx.req.headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
const $t = ctx.req.__;
|
||||
const myOptions = {};
|
||||
|
||||
|
@ -36,7 +36,6 @@ module.exports = Self => {
|
|||
myOptions.userId = userId;
|
||||
|
||||
const promises = [];
|
||||
|
||||
const [tickets] = await Self.rawSql(`CALL vn.collection_getTickets(?)`, [id], myOptions);
|
||||
const sales = await Self.rawSql(`
|
||||
SELECT s.ticketFk,
|
||||
|
@ -86,24 +85,19 @@ module.exports = Self => {
|
|||
if (tickets && tickets.length) {
|
||||
for (const ticket of tickets) {
|
||||
const ticketId = ticket.ticketFk;
|
||||
|
||||
// SEND ROCKET
|
||||
if (ticket.observaciones != '') {
|
||||
for (observation of ticket.observaciones.split(' ')) {
|
||||
if (['#', '@'].includes(observation.charAt(0))) {
|
||||
promises.push(Self.app.models.Chat.send(ctx, observation,
|
||||
$t('The ticket is in preparation', {
|
||||
ticketId: ticketId,
|
||||
ticketUrl: `${origin}/#!/ticket/${ticketId}/summary`,
|
||||
ticketUrl: `${url}ticket/${ticketId}/summary`,
|
||||
salesPersonId: ticket.salesPersonFk
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SET COLLECTION
|
||||
if (sales && sales.length) {
|
||||
// GET BARCODES
|
||||
const barcodes = await Self.rawSql(`
|
||||
SELECT s.id saleFk, b.code, c.id
|
||||
FROM vn.sale s
|
||||
|
@ -114,13 +108,10 @@ module.exports = Self => {
|
|||
WHERE s.ticketFk = ?
|
||||
AND tr.landed >= util.VN_CURDATE() - INTERVAL 1 YEAR`,
|
||||
[ticketId], myOptions);
|
||||
|
||||
// BINDINGS
|
||||
ticket.sales = [];
|
||||
for (const sale of sales) {
|
||||
if (sale.ticketFk === ticketId) {
|
||||
sale.Barcodes = [];
|
||||
|
||||
if (barcodes && barcodes.length) {
|
||||
for (const barcode of barcodes) {
|
||||
if (barcode.saleFk === sale.saleFk) {
|
||||
|
@ -131,7 +122,6 @@ module.exports = Self => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ticket.sales.push(sale);
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +130,6 @@ module.exports = Self => {
|
|||
}
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
return collection;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -18,6 +18,14 @@ describe('setSaleQuantity()', () => {
|
|||
|
||||
it('should change quantity sale', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||
if (sqlStatement.includes('catalog_calcFromItem')) {
|
||||
sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
|
||||
SELECT 100 as available;`;
|
||||
params = null;
|
||||
}
|
||||
return models.Ticket.rawSql(sqlStatement, params, options);
|
||||
});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
module.exports = function(Self) {
|
||||
Self.remoteMethod('getByUser', {
|
||||
description: 'returns the starred modules for the current user',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'userId',
|
||||
type: 'number',
|
||||
description: 'The user id',
|
||||
required: true,
|
||||
http: {source: 'path'}
|
||||
}],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:userId/get-by-user`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getByUser = async userId => {
|
||||
const models = Self.app.models;
|
||||
const appNames = ['hedera'];
|
||||
const filter = {
|
||||
fields: ['appName', 'url'],
|
||||
where: {
|
||||
appName: {inq: appNames},
|
||||
environment: process.env.NODE_ENV ?? 'development',
|
||||
}
|
||||
};
|
||||
|
||||
const isWorker = await models.Account.findById(userId, {fields: ['id']});
|
||||
if (!isWorker)
|
||||
return models.Url.find(filter);
|
||||
|
||||
appNames.push('salix');
|
||||
return models.Url.find(filter);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('getUrl', {
|
||||
description: 'Returns the colling app name',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'app',
|
||||
type: 'string',
|
||||
required: false
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/getUrl`,
|
||||
verb: 'get'
|
||||
}
|
||||
});
|
||||
Self.getUrl = async(appName = 'salix') => {
|
||||
const {url} = await Self.app.models.Url.findOne({
|
||||
where: {
|
||||
appName,
|
||||
enviroment: process.env.NODE_ENV || 'development'
|
||||
}
|
||||
});
|
||||
return url;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('getByUser()', () => {
|
||||
const worker = 1;
|
||||
const notWorker = 2;
|
||||
it(`should return only hedera url if not is worker`, async() => {
|
||||
const urls = await models.Url.getByUser(notWorker);
|
||||
|
||||
expect(urls.length).toEqual(1);
|
||||
expect(urls[0].appName).toEqual('hedera');
|
||||
});
|
||||
|
||||
it(`should return more than hedera url`, async() => {
|
||||
const urls = await models.Url.getByUser(worker);
|
||||
|
||||
expect(urls.length).toBeGreaterThan(1);
|
||||
expect(urls.find(url => url.appName == 'salix').appName).toEqual('salix');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('updateUser', {
|
||||
description: 'Update user data',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'integer',
|
||||
description: 'The user id',
|
||||
required: true,
|
||||
http: {source: 'path'}
|
||||
}, {
|
||||
arg: 'name',
|
||||
type: 'string',
|
||||
description: 'The user name',
|
||||
}, {
|
||||
arg: 'nickname',
|
||||
type: 'string',
|
||||
description: 'The user nickname',
|
||||
}, {
|
||||
arg: 'email',
|
||||
type: 'string',
|
||||
description: 'The user email'
|
||||
}, {
|
||||
arg: 'lang',
|
||||
type: 'string',
|
||||
description: 'The user lang'
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: `/:id/update-user`,
|
||||
verb: 'PATCH'
|
||||
}
|
||||
});
|
||||
|
||||
Self.updateUser = async(ctx, id, name, nickname, email, lang) => {
|
||||
await Self.userSecurity(ctx, id);
|
||||
await Self.upsertWithWhere({id}, {name, nickname, email, lang});
|
||||
};
|
||||
};
|
|
@ -7,17 +7,14 @@ module.exports = Self => {
|
|||
|
||||
Self.observe('before save', async function(ctx) {
|
||||
if (!ctx.isNewInstance) return;
|
||||
|
||||
let {message} = ctx.instance;
|
||||
if (!message) return;
|
||||
|
||||
const parts = message.match(/(?<=\[)[a-zA-Z0-9_\-+!@#$%^&*()={};':"\\|,.<>/?\s]*(?=])/g);
|
||||
if (!parts) return;
|
||||
|
||||
const replacedParts = parts.map(part => {
|
||||
return part.replace(/[!$%^&*()={};':"\\,.<>/?]/g, '');
|
||||
});
|
||||
|
||||
for (const [index, part] of parts.entries())
|
||||
message = message.replace(part, replacedParts[index]);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const ForbiddenError = require('vn-loopback/util/forbiddenError');
|
||||
|
||||
describe('loopback model VnUser', () => {
|
||||
it('should return true if the user has the given role', async() => {
|
||||
|
@ -12,4 +13,42 @@ describe('loopback model VnUser', () => {
|
|||
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('userSecurity', () => {
|
||||
const itManagementId = 115;
|
||||
const hrId = 37;
|
||||
const employeeId = 1;
|
||||
|
||||
it('should check if you are the same user', async() => {
|
||||
const ctx = {options: {accessToken: {userId: employeeId}}};
|
||||
await models.VnUser.userSecurity(ctx, employeeId);
|
||||
});
|
||||
|
||||
it('should check for higher privileges', async() => {
|
||||
const ctx = {options: {accessToken: {userId: itManagementId}}};
|
||||
await models.VnUser.userSecurity(ctx, employeeId);
|
||||
});
|
||||
|
||||
it('should check if you have medium privileges and the user email is not verified', async() => {
|
||||
const ctx = {options: {accessToken: {userId: hrId}}};
|
||||
await models.VnUser.userSecurity(ctx, employeeId);
|
||||
});
|
||||
|
||||
it('should throw an error if you have medium privileges and the users email is verified', async() => {
|
||||
const tx = await models.VnUser.beginTransaction({});
|
||||
const ctx = {options: {accessToken: {userId: hrId}}};
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const userToUpdate = await models.VnUser.findById(1, null, options);
|
||||
userToUpdate.updateAttribute('emailVerified', 1, options);
|
||||
|
||||
await models.VnUser.userSecurity(ctx, employeeId, options);
|
||||
await tx.rollback();
|
||||
} catch (error) {
|
||||
await tx.rollback();
|
||||
|
||||
expect(error).toEqual(new ForbiddenError());
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/url/getByUser')(Self);
|
||||
require('../methods/url/getUrl')(Self);
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
const vnModel = require('vn-loopback/common/models/vn-model');
|
||||
const LoopBackContext = require('loopback-context');
|
||||
const {Email} = require('vn-print');
|
||||
const ForbiddenError = require('vn-loopback/util/forbiddenError');
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
module.exports = function(Self) {
|
||||
vnModel(Self);
|
||||
|
@ -12,6 +13,7 @@ module.exports = function(Self) {
|
|||
require('../methods/vn-user/privileges')(Self);
|
||||
require('../methods/vn-user/validate-auth')(Self);
|
||||
require('../methods/vn-user/renew-token')(Self);
|
||||
require('../methods/vn-user/update-user')(Self);
|
||||
|
||||
Self.definition.settings.acls = Self.definition.settings.acls.filter(acl => acl.property !== 'create');
|
||||
|
||||
|
@ -90,11 +92,7 @@ module.exports = function(Self) {
|
|||
};
|
||||
|
||||
Self.on('resetPasswordRequest', async function(info) {
|
||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||
const httpCtx = {req: loopBackContext.active};
|
||||
const httpRequest = httpCtx.req.http.req;
|
||||
const headers = httpRequest.headers;
|
||||
const origin = headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
|
||||
const defaultHash = '/reset-password?access_token=$token$';
|
||||
const recoverHashes = {
|
||||
|
@ -110,7 +108,7 @@ module.exports = function(Self) {
|
|||
const params = {
|
||||
recipient: info.email,
|
||||
lang: user.lang,
|
||||
url: origin + '/#!' + recoverHash
|
||||
url: url.slice(0, -1) + recoverHash
|
||||
};
|
||||
|
||||
const options = Object.assign({}, info.options);
|
||||
|
@ -178,45 +176,75 @@ module.exports = function(Self) {
|
|||
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
|
||||
.filter(acl => acl.property != 'changePassword');
|
||||
|
||||
// FIXME: https://redmine.verdnatura.es/issues/5761
|
||||
// Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => {
|
||||
// if (!ctx.args || !ctx.args.data.email) return;
|
||||
Self.userSecurity = async(ctx, userId, options) => {
|
||||
const models = Self.app.models;
|
||||
const accessToken = ctx?.options?.accessToken || LoopBackContext.getCurrentContext().active.accessToken;
|
||||
const ctxToken = {req: {accessToken}};
|
||||
|
||||
// const loopBackContext = LoopBackContext.getCurrentContext();
|
||||
// const httpCtx = {req: loopBackContext.active};
|
||||
// const httpRequest = httpCtx.req.http.req;
|
||||
// const headers = httpRequest.headers;
|
||||
// const origin = headers.origin;
|
||||
// const url = origin.split(':');
|
||||
if (userId === accessToken.userId) return;
|
||||
|
||||
// class Mailer {
|
||||
// async send(verifyOptions, cb) {
|
||||
// const params = {
|
||||
// url: verifyOptions.verifyHref,
|
||||
// recipient: verifyOptions.to,
|
||||
// lang: ctx.req.getLocale()
|
||||
// };
|
||||
const myOptions = {};
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
// const email = new Email('email-verify', params);
|
||||
// email.send();
|
||||
const hasHigherPrivileges = await models.ACL.checkAccessAcl(ctxToken, 'VnUser', 'higherPrivileges', myOptions);
|
||||
if (hasHigherPrivileges) return;
|
||||
|
||||
// cb(null, verifyOptions.to);
|
||||
// }
|
||||
// }
|
||||
const hasMediumPrivileges = await models.ACL.checkAccessAcl(ctxToken, 'VnUser', 'mediumPrivileges', myOptions);
|
||||
const user = await models.VnUser.findById(userId, {fields: ['id', 'emailVerified']}, myOptions);
|
||||
if (!user.emailVerified && hasMediumPrivileges) return;
|
||||
|
||||
// const options = {
|
||||
// type: 'email',
|
||||
// to: instance.email,
|
||||
// from: {},
|
||||
// redirect: `${origin}/#!/account/${instance.id}/basic-data?emailConfirmed`,
|
||||
// template: false,
|
||||
// mailer: new Mailer,
|
||||
// host: url[1].split('/')[2],
|
||||
// port: url[2],
|
||||
// protocol: url[0],
|
||||
// user: Self
|
||||
// };
|
||||
throw new ForbiddenError();
|
||||
};
|
||||
|
||||
// await instance.verify(options);
|
||||
// });
|
||||
Self.observe('after save', async ctx => {
|
||||
const instance = ctx?.instance;
|
||||
const newEmail = instance?.email;
|
||||
const oldEmail = ctx?.hookState?.oldInstance?.email;
|
||||
if (!ctx.isNewInstance && (!newEmail || !oldEmail || newEmail == oldEmail)) return;
|
||||
|
||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||
const httpCtx = {req: loopBackContext.active};
|
||||
const httpRequest = httpCtx.req.http.req;
|
||||
const headers = httpRequest.headers;
|
||||
const origin = headers.origin;
|
||||
const url = origin.split(':');
|
||||
|
||||
const env = process.env.NODE_ENV;
|
||||
const liliumUrl = await Self.app.models.Url.findOne({
|
||||
where: {and: [
|
||||
{appName: 'lilium'},
|
||||
{environment: env}
|
||||
]}
|
||||
});
|
||||
|
||||
class Mailer {
|
||||
async send(verifyOptions, cb) {
|
||||
const params = {
|
||||
url: verifyOptions.verifyHref,
|
||||
recipient: verifyOptions.to
|
||||
};
|
||||
|
||||
const email = new Email('email-verify', params);
|
||||
email.send();
|
||||
|
||||
cb(null, verifyOptions.to);
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
type: 'email',
|
||||
to: newEmail,
|
||||
from: {},
|
||||
redirect: `${liliumUrl.url}verifyEmail?userId=${instance.id}`,
|
||||
template: false,
|
||||
mailer: new Mailer,
|
||||
host: url[1].split('/')[2],
|
||||
port: url[2],
|
||||
protocol: url[0],
|
||||
user: Self
|
||||
};
|
||||
|
||||
await instance.verify(options, ctx.options);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -13,19 +13,12 @@
|
|||
"type": "number",
|
||||
"id": true
|
||||
},
|
||||
"name": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"mysql": {
|
||||
"columnName": "name"
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
"type": "string"
|
||||
},
|
||||
"roleFk": {
|
||||
"type": "number",
|
||||
|
@ -45,6 +38,9 @@
|
|||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailVerified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"created": {
|
||||
"type": "date"
|
||||
},
|
||||
|
@ -144,7 +140,8 @@
|
|||
"image",
|
||||
"hasGrant",
|
||||
"realm",
|
||||
"email"
|
||||
"email",
|
||||
"emailVerified"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
DELIMITER $$
|
||||
$$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canAdvance`(vDateFuture DATE, vDateToAdvance DATE, vWarehouseFk INT)
|
||||
BEGIN
|
||||
/**
|
||||
* Devuelve los tickets y la cantidad de lineas de venta que se pueden adelantar.
|
||||
*
|
||||
* @param vDateFuture Fecha de los tickets que se quieren adelantar.
|
||||
* @param vDateToAdvance Fecha a cuando se quiere adelantar.
|
||||
* @param vWarehouseFk Almacén
|
||||
*/
|
||||
DECLARE vDateInventory DATE;
|
||||
|
||||
SELECT inventoried INTO vDateInventory FROM config;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.stock;
|
||||
CREATE TEMPORARY TABLE tmp.stock
|
||||
(itemFk INT PRIMARY KEY,
|
||||
amount INT)
|
||||
ENGINE = MEMORY;
|
||||
|
||||
INSERT INTO tmp.stock(itemFk, amount)
|
||||
SELECT itemFk, SUM(quantity) amount FROM
|
||||
(
|
||||
SELECT itemFk, quantity
|
||||
FROM itemTicketOut
|
||||
WHERE shipped >= vDateInventory
|
||||
AND shipped < vDateFuture
|
||||
AND warehouseFk = vWarehouseFk
|
||||
UNION ALL
|
||||
SELECT itemFk, quantity
|
||||
FROM itemEntryIn
|
||||
WHERE landed >= vDateInventory
|
||||
AND landed < vDateFuture
|
||||
AND isVirtualStock = FALSE
|
||||
AND warehouseInFk = vWarehouseFk
|
||||
UNION ALL
|
||||
SELECT itemFk, quantity
|
||||
FROM itemEntryOut
|
||||
WHERE shipped >= vDateInventory
|
||||
AND shipped < vDateFuture
|
||||
AND warehouseOutFk = vWarehouseFk
|
||||
) t
|
||||
GROUP BY itemFk HAVING amount != 0;
|
||||
|
||||
CREATE OR REPLACE TEMPORARY TABLE tmp.filter
|
||||
(INDEX (id))
|
||||
SELECT
|
||||
origin.ticketFk futureId,
|
||||
dest.ticketFk id,
|
||||
dest.state,
|
||||
origin.futureState,
|
||||
origin.futureIpt,
|
||||
dest.ipt,
|
||||
origin.workerFk,
|
||||
origin.futureLiters,
|
||||
origin.futureLines,
|
||||
dest.shipped,
|
||||
origin.shipped futureShipped,
|
||||
dest.totalWithVat,
|
||||
origin.totalWithVat futureTotalWithVat,
|
||||
dest.agency,
|
||||
origin.futureAgency,
|
||||
dest.lines,
|
||||
dest.liters,
|
||||
origin.futureLines - origin.hasStock AS notMovableLines,
|
||||
(origin.futureLines = origin.hasStock) AS isFullMovable,
|
||||
origin.futureZoneFk,
|
||||
origin.futureZoneName,
|
||||
origin.classColor futureClassColor,
|
||||
dest.classColor
|
||||
FROM (
|
||||
SELECT
|
||||
s.ticketFk,
|
||||
c.salesPersonFk workerFk,
|
||||
t.shipped,
|
||||
t.totalWithVat,
|
||||
st.name futureState,
|
||||
t.addressFk,
|
||||
am.name futureAgency,
|
||||
count(s.id) futureLines,
|
||||
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt,
|
||||
CAST(SUM(litros) AS DECIMAL(10,0)) futureLiters,
|
||||
SUM((s.quantity <= IFNULL(st.amount,0))) hasStock,
|
||||
z.id futureZoneFk,
|
||||
z.name futureZoneName,
|
||||
st.classColor
|
||||
FROM ticket t
|
||||
JOIN client c ON c.id = t.clientFk
|
||||
JOIN sale s ON s.ticketFk = t.id
|
||||
JOIN saleVolume sv ON sv.saleFk = s.id
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
JOIN ticketState ts ON ts.ticketFk = t.id
|
||||
JOIN state st ON st.id = ts.stateFk
|
||||
JOIN agencyMode am ON t.agencyModeFk = am.id
|
||||
JOIN zone z ON t.zoneFk = z.id
|
||||
LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
|
||||
LEFT JOIN tmp.stock st ON st.itemFk = i.id
|
||||
WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture)
|
||||
AND t.warehouseFk = vWarehouseFk
|
||||
GROUP BY t.id
|
||||
) origin
|
||||
JOIN (
|
||||
SELECT
|
||||
t.id ticketFk,
|
||||
t.addressFk,
|
||||
st.name state,
|
||||
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) ipt,
|
||||
t.shipped,
|
||||
t.totalWithVat,
|
||||
am.name agency,
|
||||
CAST(SUM(litros) AS DECIMAL(10,0)) liters,
|
||||
CAST(COUNT(*) AS DECIMAL(10,0)) `lines`,
|
||||
st.classColor
|
||||
FROM ticket t
|
||||
JOIN sale s ON s.ticketFk = t.id
|
||||
JOIN saleVolume sv ON sv.saleFk = s.id
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
JOIN ticketState ts ON ts.ticketFk = t.id
|
||||
JOIN state st ON st.id = ts.stateFk
|
||||
JOIN agencyMode am ON t.agencyModeFk = am.id
|
||||
LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
|
||||
WHERE t.shipped BETWEEN vDateToAdvance AND util.dayend(vDateToAdvance)
|
||||
AND t.warehouseFk = vWarehouseFk
|
||||
AND st.order <= 5
|
||||
GROUP BY t.id
|
||||
) dest ON dest.addressFk = origin.addressFk
|
||||
WHERE origin.hasStock != 0;
|
||||
|
||||
DROP TEMPORARY TABLE tmp.stock;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||
VALUES
|
||||
('WorkerDepartment', '*', '*', 'ALLOW', 'ROLE', 'employee');
|
|
@ -0,0 +1,7 @@
|
|||
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||
VALUES
|
||||
('VnUser', 'higherPrivileges', '*', 'ALLOW', 'ROLE', 'itManagement'),
|
||||
('VnUser', 'mediumPrivileges', '*', 'ALLOW', 'ROLE', 'hr'),
|
||||
('VnUser', 'updateUser', '*', 'ALLOW', 'ROLE', 'employee');
|
||||
|
||||
ALTER TABLE `account`.`user` ADD `username` varchar(30) AS (name) VIRTUAL;
|
|
@ -0,0 +1,4 @@
|
|||
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
|
||||
VALUES ('Worker','setPassword','*','ALLOW','ROLE','employee');
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
|
||||
VALUES
|
||||
('hedera', 'test', 'https://test-shop.verdnatura.es/'),
|
||||
('hedera', 'production', 'https://shop.verdnatura.es/');
|
||||
|
||||
INSERT INTO `salix`.`ACL` ( model, property, accessType, permission, principalType, principalId)
|
||||
VALUES('Url', 'getByUser', 'READ', 'ALLOW', 'ROLE', '$everyone');
|
|
@ -0,0 +1 @@
|
|||
DROP PROCEDURE IF EXISTS `vn`.`workerCreate`;
|
|
@ -0,0 +1,5 @@
|
|||
ALTER TABLE `vn`.`buy` CHANGE `packageFk` `packagingFk` varchar(10)
|
||||
CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT '--' NULL;
|
||||
|
||||
ALTER TABLE `vn`.`buy`
|
||||
ADD COLUMN `packageFk` varchar(10) AS (`packagingFk`) VIRTUAL;
|
|
@ -0,0 +1,146 @@
|
|||
CREATE OR REPLACE DEFINER=`root`@`localhost`
|
||||
SQL SECURITY DEFINER
|
||||
VIEW `vn2008`.`Compres`
|
||||
AS SELECT `c`.`id` AS `Id_Compra`,
|
||||
`c`.`entryFk` AS `Id_Entrada`,
|
||||
`c`.`itemFk` AS `Id_Article`,
|
||||
`c`.`buyingValue` AS `Costefijo`,
|
||||
`c`.`quantity` AS `Cantidad`,
|
||||
`c`.`packagingFk` AS `Id_Cubo`,
|
||||
`c`.`stickers` AS `Etiquetas`,
|
||||
`c`.`freightValue` AS `Portefijo`,
|
||||
`c`.`packageValue` AS `Embalajefijo`,
|
||||
`c`.`comissionValue` AS `Comisionfija`,
|
||||
`c`.`packing` AS `Packing`,
|
||||
`c`.`grouping` AS `grouping`,
|
||||
`c`.`groupingMode` AS `caja`,
|
||||
`c`.`location` AS `Nicho`,
|
||||
`c`.`price1` AS `Tarifa1`,
|
||||
`c`.`price2` AS `Tarifa2`,
|
||||
`c`.`price3` AS `Tarifa3`,
|
||||
`c`.`minPrice` AS `PVP`,
|
||||
`c`.`printedStickers` AS `Vida`,
|
||||
`c`.`isChecked` AS `punteo`,
|
||||
`c`.`ektFk` AS `buy_edi_id`,
|
||||
`c`.`created` AS `odbc_date`,
|
||||
`c`.`isIgnored` AS `Novincular`,
|
||||
`c`.`isPickedOff` AS `isPickedOff`,
|
||||
`c`.`workerFk` AS `Id_Trabajador`,
|
||||
`c`.`weight` AS `weight`,
|
||||
`c`.`dispatched` AS `dispatched`,
|
||||
`c`.`containerFk` AS `container_id`,
|
||||
`c`.`itemOriginalFk` AS `itemOriginalFk`
|
||||
FROM `vn`.`buy` `c`;
|
||||
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost`
|
||||
SQL SECURITY DEFINER
|
||||
VIEW `vn2008`.`buySource`
|
||||
AS SELECT `b`.`entryFk` AS `Id_Entrada`,
|
||||
`b`.`isPickedOff` AS `isPickedOff`,
|
||||
NULL AS `tarifa0`,
|
||||
`e`.`kop` AS `kop`,
|
||||
`b`.`id` AS `Id_Compra`,
|
||||
`i`.`typeFk` AS `tipo_id`,
|
||||
`b`.`itemFk` AS `Id_Article`,
|
||||
`i`.`size` AS `Medida`,
|
||||
`i`.`stems` AS `Tallos`,
|
||||
`b`.`stickers` AS `Etiquetas`,
|
||||
`b`.`packagingFk` AS `Id_Cubo`,
|
||||
`b`.`buyingValue` AS `Costefijo`,
|
||||
`b`.`packing` AS `Packing`,
|
||||
`b`.`grouping` AS `Grouping`,
|
||||
`b`.`quantity` AS `Cantidad`,
|
||||
`b`.`price2` AS `Tarifa2`,
|
||||
`b`.`price3` AS `Tarifa3`,
|
||||
`b`.`isChecked` AS `Punteo`,
|
||||
`b`.`groupingMode` AS `Caja`,
|
||||
`i`.`isToPrint` AS `Imprimir`,
|
||||
`i`.`name` AS `Article`,
|
||||
`vn`.`ink`.`picture` AS `Tinta`,
|
||||
`i`.`originFk` AS `id_origen`,
|
||||
`i`.`minPrice` AS `PVP`,
|
||||
NULL AS `Id_Accion`,
|
||||
`s`.`company_name` AS `pro`,
|
||||
`i`.`hasMinPrice` AS `Min`,
|
||||
`b`.`isIgnored` AS `Novincular`,
|
||||
`b`.`freightValue` AS `Portefijo`,
|
||||
round(`b`.`buyingValue` * `b`.`quantity`, 2) AS `Importe`,
|
||||
`b`.`printedStickers` AS `Vida`,
|
||||
`i`.`comment` AS `reference`,
|
||||
`b`.`workerFk` AS `Id_Trabajador`,
|
||||
`e`.`s1` AS `S1`,
|
||||
`e`.`s2` AS `S2`,
|
||||
`e`.`s3` AS `S3`,
|
||||
`e`.`s4` AS `S4`,
|
||||
`e`.`s5` AS `S5`,
|
||||
`e`.`s6` AS `S6`,
|
||||
0 AS `price_fixed`,
|
||||
`i`.`producerFk` AS `producer_id`,
|
||||
`i`.`subName` AS `tag1`,
|
||||
`i`.`value5` AS `tag2`,
|
||||
`i`.`value6` AS `tag3`,
|
||||
`i`.`value7` AS `tag4`,
|
||||
`i`.`value8` AS `tag5`,
|
||||
`i`.`value9` AS `tag6`,
|
||||
`s`.`company_name` AS `company_name`,
|
||||
`b`.`weight` AS `weightPacking`,
|
||||
`i`.`packingOut` AS `packingOut`,
|
||||
`b`.`itemOriginalFk` AS `itemOriginalFk`,
|
||||
`io`.`longName` AS `itemOriginalName`,
|
||||
`it`.`gramsMax` AS `gramsMax`
|
||||
FROM (
|
||||
(
|
||||
(
|
||||
(
|
||||
(
|
||||
(
|
||||
`vn`.`item` `i`
|
||||
JOIN `vn`.`itemType` `it` ON(`it`.`id` = `i`.`typeFk`)
|
||||
)
|
||||
LEFT JOIN `vn`.`ink` ON(`vn`.`ink`.`id` = `i`.`inkFk`)
|
||||
)
|
||||
LEFT JOIN `vn`.`buy` `b` ON(`b`.`itemFk` = `i`.`id`)
|
||||
)
|
||||
LEFT JOIN `vn`.`item` `io` ON(`io`.`id` = `b`.`itemOriginalFk`)
|
||||
)
|
||||
LEFT JOIN `edi`.`ekt` `e` ON(`e`.`id` = `b`.`ektFk`)
|
||||
)
|
||||
LEFT JOIN `edi`.`supplier` `s` ON(`e`.`pro` = `s`.`supplier_id`)
|
||||
);
|
||||
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost`
|
||||
SQL SECURITY DEFINER
|
||||
VIEW `vn`.`awbVolume`
|
||||
AS SELECT `d`.`awbFk` AS `awbFk`,
|
||||
`b`.`stickers` * `i`.`density` * IF(
|
||||
`p`.`volume` > 0,
|
||||
`p`.`volume`,
|
||||
`p`.`width` * `p`.`depth` * IF(`p`.`height` = 0, `i`.`size` + 10, `p`.`height`)
|
||||
) / (`vc`.`aerealVolumetricDensity` * 1000) AS `volume`,
|
||||
`b`.`id` AS `buyFk`
|
||||
FROM (
|
||||
(
|
||||
(
|
||||
(
|
||||
(
|
||||
(
|
||||
(
|
||||
(
|
||||
`vn`.`buy` `b`
|
||||
JOIN `vn`.`item` `i` ON(`b`.`itemFk` = `i`.`id`)
|
||||
)
|
||||
JOIN `vn`.`itemType` `it` ON(`i`.`typeFk` = `it`.`id`)
|
||||
)
|
||||
JOIN `vn`.`packaging` `p` ON(`p`.`id` = `b`.`packagingFk`)
|
||||
)
|
||||
JOIN `vn`.`entry` `e` ON(`b`.`entryFk` = `e`.`id`)
|
||||
)
|
||||
JOIN `vn`.`travel` `t` ON(`t`.`id` = `e`.`travelFk`)
|
||||
)
|
||||
JOIN `vn`.`duaEntry` `de` ON(`de`.`entryFk` = `e`.`id`)
|
||||
)
|
||||
JOIN `vn`.`dua` `d` ON(`d`.`id` = `de`.`duaFk`)
|
||||
)
|
||||
JOIN `vn`.`volumeConfig` `vc`
|
||||
)
|
||||
WHERE `t`.`shipped` > makedate(year(`util`.`VN_CURDATE`()) - 1, 1);
|
|
@ -0,0 +1,26 @@
|
|||
ALTER TABLE `vn`.`zoneIncluded`
|
||||
ADD COLUMN `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
|
||||
DROP PRIMARY KEY,
|
||||
DROP FOREIGN KEY `zoneFk2`,
|
||||
DROP FOREIGN KEY `zoneGeoFk2`,
|
||||
DROP KEY `geoFk_idx`,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD CONSTRAINT `zoneIncluded_FK_1` FOREIGN KEY (zoneFk) REFERENCES `vn`.`zone`(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
ADD CONSTRAINT `zoneIncluded_FK_2` FOREIGN KEY (geoFk) REFERENCES `vn`.`zoneGeo`(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
ADD CONSTRAINT `unique_zone_geo` UNIQUE (`zoneFk`, `geoFk`);
|
||||
|
||||
DROP TRIGGER IF EXISTS `vn`.`zoneIncluded_afterDelete`;
|
||||
USE `vn`;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`zoneIncluded_afterDelete`
|
||||
AFTER DELETE ON `zoneIncluded`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO zoneLog
|
||||
SET `action` = 'delete',
|
||||
`changedModel` = 'zoneIncluded',
|
||||
`changedModelId` = OLD.zoneFk,
|
||||
`userFk` = account.myUser_getId();
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,57 @@
|
|||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`buy_afterUpdate`
|
||||
AFTER UPDATE ON `buy`
|
||||
FOR EACH ROW
|
||||
trig: BEGIN
|
||||
DECLARE vLanded DATE;
|
||||
DECLARE vBuyerFk INT;
|
||||
DECLARE vIsBuyerToBeEmailed BOOL;
|
||||
DECLARE vItemName VARCHAR(50);
|
||||
|
||||
IF @isModeInventory OR @isTriggerDisabled THEN
|
||||
LEAVE trig;
|
||||
END IF;
|
||||
|
||||
IF !(NEW.id <=> OLD.id)
|
||||
OR !(NEW.entryFk <=> OLD.entryFk)
|
||||
OR !(NEW.itemFk <=> OLD.itemFk)
|
||||
OR !(NEW.quantity <=> OLD.quantity)
|
||||
OR !(NEW.created <=> OLD.created) THEN
|
||||
CALL stock.log_add('buy', NEW.id, OLD.id);
|
||||
END IF;
|
||||
|
||||
CALL buy_afterUpsert(NEW.id);
|
||||
|
||||
SELECT w.isBuyerToBeEmailed, t.landed
|
||||
INTO vIsBuyerToBeEmailed, vLanded
|
||||
FROM entry e
|
||||
JOIN travel t ON t.id = e.travelFk
|
||||
JOIN warehouse w ON w.id = t.warehouseInFk
|
||||
WHERE e.id = NEW.entryFk;
|
||||
|
||||
SELECT it.workerFk, i.longName
|
||||
INTO vBuyerFk, vItemName
|
||||
FROM itemCategory k
|
||||
JOIN itemType it ON it.categoryFk = k.id
|
||||
JOIN item i ON i.typeFk = it.id
|
||||
WHERE i.id = OLD.itemFk;
|
||||
|
||||
IF vIsBuyerToBeEmailed AND
|
||||
vBuyerFk != account.myUser_getId() AND
|
||||
vLanded = util.VN_CURDATE() THEN
|
||||
IF !(NEW.itemFk <=> OLD.itemFk) OR
|
||||
!(NEW.quantity <=> OLD.quantity) OR
|
||||
!(NEW.packing <=> OLD.packing) OR
|
||||
!(NEW.grouping <=> OLD.grouping) OR
|
||||
!(NEW.packagingFk <=> OLD.packagingFk) OR
|
||||
!(NEW.weight <=> OLD.weight) THEN
|
||||
CALL vn.mail_insert(
|
||||
CONCAT(account.user_getNameFromId(vBuyerFk),'@verdnatura.es'),
|
||||
CONCAT(account.myUser_getName(),'@verdnatura.es'),
|
||||
CONCAT('E ', NEW.entryFk ,' Se ha modificado item ', NEW.itemFk, ' ', vItemName),
|
||||
'Este email se ha generado automáticamente'
|
||||
);
|
||||
END IF;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,6 @@
|
|||
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||
VALUES
|
||||
('CplusRectificationType', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'),
|
||||
('CplusInvoiceType477', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'),
|
||||
('InvoiceCorrectionType', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'),
|
||||
('InvoiceOut', 'transferInvoice', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue