refs #6184 saveCmr #1788

Merged
guillermo merged 58 commits from 6184-saveCmr into dev 2024-02-13 06:47:06 +00:00
285 changed files with 5418 additions and 1949 deletions
Showing only changes of commit 05a7498219 - Show all commits

View File

@ -5,21 +5,29 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2342.01] - 2023-10-19 ## [2348.01] - 2023-11-30
### Added ### Added
### Changed ### Changed
### Fixed ### Fixed
## [2340.01] - 2023-10-05 ## [2346.01] - 2023-11-16
### Added
### Changed
### Fixed
## [2342.01] - 2023-11-02
### Added ### Added
- (Usuarios -> Foto) Se muestra la foto del trabajador - (Usuarios -> Foto) Se muestra la foto del trabajador
### Changed
### Fixed ### Fixed
- (Usuarios -> Historial) Abre el descriptor del usuario correctamente - (Usuarios -> Historial) Abre el descriptor del usuario correctamente
## [2340.01] - 2023-10-05
## [2338.01] - 2023-09-21 ## [2338.01] - 2023-09-21
### Added ### Added
@ -29,17 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- (Trabajadores -> Calendario) Icono de check arreglado cuando pulsas un tipo de dia - (Trabajadores -> Calendario) Icono de check arreglado cuando pulsas un tipo de dia
### Fixed
## [2336.01] - 2023-09-07 ## [2336.01] - 2023-09-07
### Added
### Changed
### Fixed
## [2334.01] - 2023-08-24 ## [2334.01] - 2023-08-24
### Added ### Added

View File

@ -26,15 +26,14 @@ module.exports = Self => {
Self.sendCheckingPresence = async(ctx, recipientId, message) => { Self.sendCheckingPresence = async(ctx, recipientId, message) => {
if (!recipientId) return false; if (!recipientId) return false;
const models = Self.app.models; const models = Self.app.models;
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const sender = await models.VnUser.findById(userId, {fields: ['id']}); const sender = await models.VnUser.findById(userId, {fields: ['id']});
const recipient = await models.VnUser.findById(recipientId, null); const recipient = await models.VnUser.findById(recipientId, null);
// Prevent sending messages to yourself // Prevent sending messages to yourself
if (recipientId == userId) return false; if (recipientId == userId) return false;
if (!recipient) if (!recipient)
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`); throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);

View File

@ -26,7 +26,7 @@ module.exports = Self => {
Self.getTickets = async(ctx, id, print, options) => { Self.getTickets = async(ctx, id, print, options) => {
const userId = ctx.req.accessToken.userId; 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 $t = ctx.req.__;
const myOptions = {}; const myOptions = {};
@ -36,7 +36,6 @@ module.exports = Self => {
myOptions.userId = userId; myOptions.userId = userId;
const promises = []; const promises = [];
const [tickets] = await Self.rawSql(`CALL vn.collection_getTickets(?)`, [id], myOptions); const [tickets] = await Self.rawSql(`CALL vn.collection_getTickets(?)`, [id], myOptions);
const sales = await Self.rawSql(` const sales = await Self.rawSql(`
SELECT s.ticketFk, SELECT s.ticketFk,
@ -86,24 +85,19 @@ module.exports = Self => {
if (tickets && tickets.length) { if (tickets && tickets.length) {
for (const ticket of tickets) { for (const ticket of tickets) {
const ticketId = ticket.ticketFk; const ticketId = ticket.ticketFk;
// SEND ROCKET
if (ticket.observaciones != '') { if (ticket.observaciones != '') {
for (observation of ticket.observaciones.split(' ')) { for (observation of ticket.observaciones.split(' ')) {
if (['#', '@'].includes(observation.charAt(0))) { if (['#', '@'].includes(observation.charAt(0))) {
promises.push(Self.app.models.Chat.send(ctx, observation, promises.push(Self.app.models.Chat.send(ctx, observation,
$t('The ticket is in preparation', { $t('The ticket is in preparation', {
ticketId: ticketId, ticketId: ticketId,
ticketUrl: `${origin}/#!/ticket/${ticketId}/summary`, ticketUrl: `${url}ticket/${ticketId}/summary`,
salesPersonId: ticket.salesPersonFk salesPersonId: ticket.salesPersonFk
}))); })));
} }
} }
} }
// SET COLLECTION
if (sales && sales.length) { if (sales && sales.length) {
// GET BARCODES
const barcodes = await Self.rawSql(` const barcodes = await Self.rawSql(`
SELECT s.id saleFk, b.code, c.id SELECT s.id saleFk, b.code, c.id
FROM vn.sale s FROM vn.sale s
@ -114,13 +108,10 @@ module.exports = Self => {
WHERE s.ticketFk = ? WHERE s.ticketFk = ?
AND tr.landed >= util.VN_CURDATE() - INTERVAL 1 YEAR`, AND tr.landed >= util.VN_CURDATE() - INTERVAL 1 YEAR`,
[ticketId], myOptions); [ticketId], myOptions);
// BINDINGS
ticket.sales = []; ticket.sales = [];
for (const sale of sales) { for (const sale of sales) {
if (sale.ticketFk === ticketId) { if (sale.ticketFk === ticketId) {
sale.Barcodes = []; sale.Barcodes = [];
if (barcodes && barcodes.length) { if (barcodes && barcodes.length) {
for (const barcode of barcodes) { for (const barcode of barcodes) {
if (barcode.saleFk === sale.saleFk) { if (barcode.saleFk === sale.saleFk) {
@ -131,7 +122,6 @@ module.exports = Self => {
} }
} }
} }
ticket.sales.push(sale); ticket.sales.push(sale);
} }
} }
@ -140,7 +130,6 @@ module.exports = Self => {
} }
} }
await Promise.all(promises); await Promise.all(promises);
return collection; return collection;
}; };
}; };

View File

@ -18,6 +18,14 @@ describe('setSaleQuantity()', () => {
it('should change quantity sale', async() => { it('should change quantity sale', async() => {
const tx = await models.Ticket.beginTransaction({}); 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 { try {
const options = {transaction: tx}; const options = {transaction: tx};

View File

@ -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);
};
};

View File

@ -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;
};
};

View File

@ -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');
});
});

View File

@ -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});
};
};

View File

@ -7,17 +7,14 @@ module.exports = Self => {
Self.observe('before save', async function(ctx) { Self.observe('before save', async function(ctx) {
if (!ctx.isNewInstance) return; if (!ctx.isNewInstance) return;
let {message} = ctx.instance; let {message} = ctx.instance;
if (!message) return; if (!message) return;
const parts = message.match(/(?<=\[)[a-zA-Z0-9_\-+!@#$%^&*()={};':"\\|,.<>/?\s]*(?=])/g); const parts = message.match(/(?<=\[)[a-zA-Z0-9_\-+!@#$%^&*()={};':"\\|,.<>/?\s]*(?=])/g);
if (!parts) return; if (!parts) return;
const replacedParts = parts.map(part => { const replacedParts = parts.map(part => {
return part.replace(/[!$%^&*()={};':"\\,.<>/?]/g, ''); return part.replace(/[!$%^&*()={};':"\\,.<>/?]/g, '');
}); });
for (const [index, part] of parts.entries()) for (const [index, part] of parts.entries())
message = message.replace(part, replacedParts[index]); message = message.replace(part, replacedParts[index]);

View File

@ -1,4 +1,5 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const ForbiddenError = require('vn-loopback/util/forbiddenError');
describe('loopback model VnUser', () => { describe('loopback model VnUser', () => {
it('should return true if the user has the given role', async() => { it('should return true if the user has the given role', async() => {
@ -12,4 +13,42 @@ describe('loopback model VnUser', () => {
expect(result).toBeFalsy(); 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());
}
});
});
}); });

4
back/models/url.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = Self => {
require('../methods/url/getByUser')(Self);
require('../methods/url/getUrl')(Self);
};

View File

@ -1,6 +1,7 @@
const vnModel = require('vn-loopback/common/models/vn-model'); const vnModel = require('vn-loopback/common/models/vn-model');
const LoopBackContext = require('loopback-context');
const {Email} = require('vn-print'); const {Email} = require('vn-print');
const ForbiddenError = require('vn-loopback/util/forbiddenError');
const LoopBackContext = require('loopback-context');
module.exports = function(Self) { module.exports = function(Self) {
vnModel(Self); vnModel(Self);
@ -12,6 +13,7 @@ module.exports = function(Self) {
require('../methods/vn-user/privileges')(Self); require('../methods/vn-user/privileges')(Self);
require('../methods/vn-user/validate-auth')(Self); require('../methods/vn-user/validate-auth')(Self);
require('../methods/vn-user/renew-token')(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'); 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) { Self.on('resetPasswordRequest', async function(info) {
const loopBackContext = LoopBackContext.getCurrentContext(); const url = await Self.app.models.Url.getUrl();
const httpCtx = {req: loopBackContext.active};
const httpRequest = httpCtx.req.http.req;
const headers = httpRequest.headers;
const origin = headers.origin;
const defaultHash = '/reset-password?access_token=$token$'; const defaultHash = '/reset-password?access_token=$token$';
const recoverHashes = { const recoverHashes = {
@ -110,7 +108,7 @@ module.exports = function(Self) {
const params = { const params = {
recipient: info.email, recipient: info.email,
lang: user.lang, lang: user.lang,
url: origin + '/#!' + recoverHash url: url.slice(0, -1) + recoverHash
}; };
const options = Object.assign({}, info.options); 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 Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
.filter(acl => acl.property != 'changePassword'); .filter(acl => acl.property != 'changePassword');
// FIXME: https://redmine.verdnatura.es/issues/5761 Self.userSecurity = async(ctx, userId, options) => {
// Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => { const models = Self.app.models;
// if (!ctx.args || !ctx.args.data.email) return; const accessToken = ctx?.options?.accessToken || LoopBackContext.getCurrentContext().active.accessToken;
const ctxToken = {req: {accessToken}};
// const loopBackContext = LoopBackContext.getCurrentContext(); if (userId === accessToken.userId) return;
// const httpCtx = {req: loopBackContext.active};
// const httpRequest = httpCtx.req.http.req;
// const headers = httpRequest.headers;
// const origin = headers.origin;
// const url = origin.split(':');
// class Mailer { const myOptions = {};
// async send(verifyOptions, cb) { if (typeof options == 'object')
// const params = { Object.assign(myOptions, options);
// url: verifyOptions.verifyHref,
// recipient: verifyOptions.to,
// lang: ctx.req.getLocale()
// };
// const email = new Email('email-verify', params); const hasHigherPrivileges = await models.ACL.checkAccessAcl(ctxToken, 'VnUser', 'higherPrivileges', myOptions);
// email.send(); 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 = { throw new ForbiddenError();
// 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
// };
// 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);
});
}; };

View File

@ -13,19 +13,12 @@
"type": "number", "type": "number",
"id": true "id": true
}, },
"name": { "name": {
"type": "string", "type": "string",
"required": true "required": true
}, },
"username": { "username": {
"type": "string", "type": "string"
"mysql": {
"columnName": "name"
}
},
"password": {
"type": "string",
"required": true
}, },
"roleFk": { "roleFk": {
"type": "number", "type": "number",
@ -45,6 +38,9 @@
"email": { "email": {
"type": "string" "type": "string"
}, },
"emailVerified": {
"type": "boolean"
},
"created": { "created": {
"type": "date" "type": "date"
}, },
@ -144,7 +140,8 @@
"image", "image",
"hasGrant", "hasGrant",
"realm", "realm",
"email" "email",
"emailVerified"
] ]
} }
} }

View File

@ -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 ;

View File

View File

@ -0,0 +1,41 @@
UPDATE `vn`.`workerTimeControlConfig`
SET `timeToBreakTime` = 18000;
ALTER TABLE `vn`.`workerTimeControlConfig`
DROP COLUMN IF EXISTS `maxTimeToBreak`;
ALTER TABLE `vn`.`workerTimeControlConfig`
ADD COLUMN maxTimeToBreak INT DEFAULT 3600 NULL;
ALTER TABLE `vn`.`workerTimeControlConfig`
DROP COLUMN IF EXISTS `maxWorkShortCycle`;
ALTER TABLE `vn`.`workerTimeControlConfig`
ADD COLUMN `maxWorkShortCycle` INT(10) UNSIGNED DEFAULT 561600
COMMENT 'Máximo tiempo que un trabajador puede estar trabajando con el que adquirirá el derecho a un descanso semanal corto';
ALTER TABLE `vn`.`workerTimeControlConfig`
DROP COLUMN IF EXISTS `maxWorkLongCycle`;
ALTER TABLE `vn`.`workerTimeControlConfig`
ADD COLUMN `maxWorkLongCycle` INT(10) UNSIGNED DEFAULT 950400
COMMENT 'Máximo tiempo que un trabajador puede estar trabajando con el que adquirirá el derecho a un descanso semanal largo';
CREATE TABLE IF NOT EXISTS `vn`.`workerTimeControlError` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`code` char(35) NOT NULL,
`description` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT IGNORE INTO `vn`.`workerTimeControlError` (`code`, `description`)
VALUES
('IS_NOT_ALLOWED_FUTURE', 'No se permite fichar a futuro'),
('INACTIVE_BUSINESS', 'No hay un contrato en vigor'),
('IS_NOT_ALLOWED_WORK', 'No está permitido trabajar'),
('ODD_WORKERTIMECONTROL', 'Fichadas impares'),
('DAY_MAX_TIME', 'Superado el tiempo máximo entre entrada y salida'),
('BREAK_DAY', 'Descanso diario'),
('BREAK_WEEK', 'Descanso semanal'),
('WRONG_DIRECTION', 'Dirección incorrecta'),
('UNDEFINED_ERROR', 'Error sin definir');

View File

@ -0,0 +1,194 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`timeControl_calculate`(
vDatedFrom DATETIME,
vDatedTo DATETIME)
BEGIN
/*
* Agrupa por trabajador y día, el tiempo de trabajo y descanso retribuido(si tiene).
* Los registros horarios incorrectos (tmp.timeControlError) no se considerarán.
* Si un trabajador ha trabajado más de un cierto umbral de tiempo (vTimeToBreakTime)
* y no ha tenido descansos que superen un parámetro determinado(vMaxTimeToBreak),
* se le añadirá un tiempo de descanso (vBreakTime) a sus horas trabajadas.
* El tiempo de descanso solo se añade si el trabajador realmente disfrutó del descanso.
* Si disfrutó de menos tiempo de descanso, solo se añade el tiempo que disfrutó.
*
* @param vDatedFrom
* @param vDatedTo
*
* @return tmp.timeControlCalculate
* (workerFk, dated, timeWorkSeconds, timeWorkSexagesimal, timeWorkDecimal, timed)
*/
DECLARE vHourSeconds INTEGER;
DECLARE vDatedFromYesterday DATETIME;
DECLARE vDatedToTomorrow DATETIME;
DECLARE vTimeToBreakTime INT;
DECLARE vBreakTime INT;
DECLARE vMaxTimeToBreak INT;
SELECT DATE_SUB(vDatedFrom, INTERVAL 1 DAY), DATE_ADD(vDatedTo, INTERVAL 1 DAY)
INTO vDatedFromYesterday, vDatedToTomorrow;
SELECT timeToBreakTime, breakTime, maxTimeToBreak, TIME_TO_SEC('01:00:00')
INTO vTimeToBreakTime, vBreakTime, vMaxTimeToBreak, vHourSeconds
FROM workerTimeControlConfig
LIMIT 1;
CALL timeControl_getError(vDatedFromYesterday, vDatedToTomorrow);
CREATE OR REPLACE TEMPORARY TABLE tmp.workerTimeControl
(INDEX(userFk, timed), INDEX(timed), INDEX(direction))
ENGINE = MEMORY
SELECT wtc.userFk,
wtc.timed,
DATE(wtc.timed) dated,
wtc.direction,
TRUE isReal
FROM workerTimeControl wtc
JOIN tmp.`user` u ON u.userFk = wtc.userFk
LEFT JOIN (
SELECT wtc.userFk, MIN(wtc.timed) firstIn
FROM workerTimeControl wtc
JOIN tmp.`user` u ON u.userFk = wtc.userFk
LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
WHERE wtc.timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
AND wtc.direction = 'in'
AND tce.id IS NULL
GROUP BY userFk
) fi ON wtc.userFk = fi.userFk
LEFT JOIN (
SELECT wtc.userFk, MAX(wtc.timed) lastOut
FROM workerTimeControl wtc
JOIN tmp.`user` u ON u.userFk = wtc.userFk
LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
WHERE wtc.timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
AND wtc.direction = 'out'
AND tce.id IS NULL
GROUP BY userFk
) lo ON wtc.userFk = lo.userFk
LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
WHERE wtc.timed BETWEEN fi.firstIn AND lo.lastOut
AND tce.id IS NULL
ORDER BY wtc.userFk, wtc.timed;
CREATE OR REPLACE TEMPORARY TABLE tmp.wtcToinsert
(INDEX(timed))
ENGINE = MEMORY
WITH wtc AS(
SELECT timed,
userFk,
dated,
direction,
LEAD(dated) OVER
(PARTITION BY userFk, dated ORDER BY timed) nextDay,
LEAD(userFk) OVER
(PARTITION BY userFk ORDER BY timed) nextUserFk,
ROW_NUMBER() OVER (ORDER BY userFk, timed) MOD 2 isOdd
FROM tmp.workerTimeControl
WHERE timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
ORDER BY userFk, timed
), wtcToinsert AS(
SELECT userFk,
dated,
IF(userFk = nextUserFk
AND nextDay IS NULL
AND isOdd
AND direction <> 'out', TRUE, FALSE) outNextDay,
IF(userFk = nextUserFk
AND nextDay IS NULL
AND NOT isOdd
AND direction <> 'out', TRUE, FALSE) outNextDayWhitBreak
FROM wtc
HAVING outNextDay OR outNextDayWhitBreak
)SELECT userFk, util.dayEnd(dated) timed, 'out' direction
FROM wtcToinsert
WHERE outNextDay
UNION ALL
SELECT userFk, dated + INTERVAL 1 DAY, 'in'
FROM wtcToinsert
WHERE outNextDay
UNION ALL
SELECT userFk, util.dayEnd(dated) - INTERVAL 1 SECOND, 'middle'
FROM wtcToinsert
WHERE outNextDayWhitBreak
UNION ALL
SELECT userFk, util.dayEnd(dated), 'out'
FROM wtcToinsert
WHERE outNextDayWhitBreak
UNION ALL
SELECT userFk, dated + INTERVAL 1 DAY, 'in'
FROM wtcToinsert
WHERE outNextDayWhitBreak
UNION ALL
SELECT userFk, dated + INTERVAL 1 DAY + INTERVAL 1 SECOND, 'middle'
FROM wtcToinsert
WHERE outNextDayWhitBreak;
INSERT INTO tmp.workerTimeControl (userFk, timed, dated, direction, isReal)
SELECT userFk, timed, DATE(timed), direction, FALSE
FROM tmp.wtcToinsert;
SET @accumulatedForBreakTime = 0;
SET @oldrealDay = NULL;
CREATE OR REPLACE TEMPORARY TABLE tmp.timeControlCalculate
WITH workerTimed AS (
SELECT
userFk,
dated,
timed,
(direction ='in' AND isReal) breakPoint,
SUM(CASE WHEN (direction ='in' AND isReal) THEN TRUE ELSE FALSE END)
OVER (ORDER BY userFk, timed) AS realDay,
TIMESTAMPDIFF(SECOND, LAG(timed)
OVER (PARTITION BY userFk, dated ORDER BY timed), timed) gapTime,
ROW_NUMBER()
OVER (PARTITION BY userFk, dated ORDER BY timed) MOD 2 isOdd
FROM tmp.workerTimeControl
WHERE timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
), accumulated AS (
SELECT SUM(IF(isOdd, 0, gapTime))
OVER (PARTITION BY userFk,dated ORDER BY userFk,timed) accumulatedWorkTime,
SUM(IF(NOT isOdd OR breakPoint, 0, IFNULL(gapTime, 0)))
OVER (PARTITION BY realDay ORDER BY realDay,timed) accumulatedBreakTime,
IF(realDay <> @oldrealDay OR (isOdd AND gapTime >= vMaxTimeToBreak),
@accumulatedForBreakTime := 0,
@accumulatedForBreakTime := @accumulatedForBreakTime +
IF(isOdd, 0, gapTime )) accumulatedForBreakTime,
@oldrealDay := realDay,
userFk,
dated,
realDay
FROM workerTimed
), totalWorked AS (
SELECT userFk,
dated,
MAX(accumulatedWorkTime) +
IF(MAX(accumulatedForBreakTime) >= vTimeToBreakTime,
LEAST(vBreakTime, MAX(accumulatedBreakTime)),
0) timeWorkSeconds
FROM accumulated
GROUP BY userFk, dated
)SELECT tw.userFk,
tw.dated,
timeWorkSeconds,
SEC_TO_TIME(timeWorkSeconds) timeWorkSexagesimal,
timeWorkSeconds / vHourSeconds timeWorkDecimal,
sub.tableTimed
FROM totalWorked tw
JOIN (
SELECT userFk,
dated,
GROUP_CONCAT(DATE_FORMAT(timed, "%H:%i") ORDER BY timed ASC
SEPARATOR ' - ')tableTimed
FROM tmp.workerTimeControl
WHERE timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
AND isReal
GROUP BY userFk, dated
)sub ON sub.dated = tw.dated
AND sub.userFk = tw.userFk
WHERE tw.dated BETWEEN vDatedFrom AND vDatedTo;
DROP TEMPORARY TABLE tmp.timeControlError;
DROP TEMPORARY TABLE tmp.wtcToinsert;
DROP TEMPORARY TABLE tmp.workerTimeControl;
END$$
DELIMITER ;

View File

@ -0,0 +1,286 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`workerTimeControl_clockIn`(
vWorkerFk INT,
vTimed DATETIME,
vDirection VARCHAR(10)
)
BEGIN
/**
* Verifica si el empleado puede fichar
* @param vWorkerFk Identificador del trabajador
* @param vTimed valor de la fichada, IF vTimed IS NULL vTimed = NOW
* @param vDirection solo se pueden pasa los valores del campo
* workerTimeControl.direction ENUM('in', 'out', 'middle')
* @return Si todo es correcto, retorna el número de id la tabla workerTimeControl.
* Si hay algún problema, devuelve el mesaje que se debe mostrar al usuario
* Solo retorna el primer problema, en caso de no ocurrir ningún error se añadirá
* fichada a la tabla vn.workerTimeControl
*/
DECLARE vLastIn DATETIME;
DECLARE vLastOut DATETIME;
DECLARE vNextIn DATETIME;
DECLARE vNextOut DATETIME;
DECLARE vNextDirection ENUM('in', 'out');
DECLARE vLastDirection ENUM('in', 'out');
DECLARE vDayMaxTime INTEGER;
DECLARE vDayBreak INT;
DECLARE vShortWeekBreak INT;
DECLARE vLongWeekBreak INT;
DECLARE vWeekScope INT;
DECLARE vMailTo VARCHAR(50) DEFAULT NULL;
DECLARE vUserName VARCHAR(50) DEFAULT NULL;
DECLARE vIsError BOOLEAN DEFAULT FALSE;
DECLARE vErrorMessage VARCHAR(255) DEFAULT NULL;
DECLARE vErrorCode VARCHAR(50);
DECLARE vDated DATE;
DECLARE vIsAllowedToWork VARCHAR(50);
DECLARE vIsManual BOOLEAN DEFAULT TRUE;
DECLARE vMaxWorkShortCycle INT;
DECLARE vMaxWorkLongCycle INT;
DECLARE EXIT HANDLER FOR SQLSTATE '45000'
BEGIN
SELECT CONCAT(u.name, '@verdnatura.es'),
CONCAT(w.firstName, ' ', w.lastName)
INTO vMailTo, vUserName
FROM account.user u
JOIN worker w ON w.bossFk = u.id
WHERE w.id = vWorkerFk;
SELECT `description` INTO vErrorMessage
FROM workerTimeControlError
WHERE `code` = vErrorCode;
IF vErrorMessage IS NULL THEN
SET vErrorMessage = 'Error sin definir';
END IF;
SELECT vErrorMessage `error`;
SELECT CONCAT(vUserName,
' no ha podido fichar por el siguiente problema: ',
vErrorMessage)
INTO vErrorMessage;
CALL mail_insert( vMailTo, vMailTo, 'Error al fichar', vErrorMessage);
END;
IF (vTimed IS NULL) THEN
SET vTimed = util.VN_NOW();
SET vIsManual = FALSE;
END IF;
SET vDated = DATE(vTimed);
SELECT IF(pc.name = 'Conductor +3500kg',
wc.dayBreakDriver,
wc.dayBreak),
wc.shortWeekBreak,
wc.longWeekBreak,
wc.weekScope,
wc.dayMaxTime,
wc.maxWorkShortCycle,
wc.maxWorkLongCycle
INTO vDayBreak,
vShortWeekBreak,
vLongWeekBreak,
vWeekScope,
vDayMaxTime,
vMaxWorkShortCycle,
vMaxWorkLongCycle
FROM business b
JOIN professionalCategory pc
ON pc.id = b.workerBusinessProfessionalCategoryFk
JOIN workerTimeControlConfig wc
WHERE b.workerFk = vWorkerFk
AND vDated BETWEEN b.started AND IFNULL(b.ended, vDated);
-- CONTRATO EN VIGOR
IF vDayBreak IS NULL THEN
SET vErrorCode = 'INACTIVE_BUSINESS';
CALL util.throw(vErrorCode);
END IF;
-- FICHADAS A FUTURO
IF vTimed > util.VN_NOW() + INTERVAL 1 MINUTE THEN
SET vErrorCode = 'IS_NOT_ALLOWED_FUTURE';
CALL util.throw(vErrorCode);
END IF;
-- VERIFICAR SI ESTÁ PERMITIDO TRABAJAR
CALL timeBusiness_calculateByWorker(vWorkerFk, vDated, vDated);
SELECT isAllowedToWork INTO vIsAllowedToWork
FROM tmp.timeBusinessCalculate;
DROP TEMPORARY TABLE tmp.timeBusinessCalculate;
IF NOT vIsAllowedToWork THEN
SET vErrorCode = 'IS_NOT_ALLOWED_WORK';
CALL util.throw(vErrorCode);
END IF;
-- DIRECCION CORRECTA
CALL workerTimeControl_direction(vWorkerFk, vTimed);
IF (SELECT
IF(IF(option1 IN ('inMiddle', 'outMiddle'),
'middle',
option1) <> vDirection
AND IF(option2 IN ('inMiddle', 'outMiddle'),
'middle',
IFNULL(option2, '')) <> vDirection,
TRUE ,
FALSE)
FROM tmp.workerTimeControlDirection
) THEN
SET vIsError = TRUE;
END IF;
DROP TEMPORARY TABLE tmp.workerTimeControlDirection;
IF vIsError THEN
SET vErrorCode = 'WRONG_DIRECTION';
CALL util.throw(vErrorCode);
END IF;
-- FICHADAS IMPARES
SELECT timed INTO vLastIn
FROM workerTimeControl
WHERE userFk = vWorkerFk
AND direction = 'in'
AND timed < vTimed
ORDER BY timed DESC
LIMIT 1;
IF (SELECT IF(vDirection = 'in',
MOD(COUNT(*), 2) ,
IF (vDirection = 'out', NOT MOD(COUNT(*), 2), FALSE))
FROM workerTimeControl
WHERE userFk = vWorkerFk
AND timed BETWEEN vLastIn AND vTimed
) THEN
SET vErrorCode = 'ODD_WORKERTIMECONTROL';
CALL util.throw(vErrorCode);
END IF;
-- DESCANSO DIARIO
SELECT timed INTO vLastOut
FROM workerTimeControl
WHERE userFk = vWorkerFk
AND direction = 'out'
AND timed < vTimed
ORDER BY timed DESC
LIMIT 1;
SELECT timed INTO vNextIn
FROM workerTimeControl
WHERE userFk = vWorkerFk
AND direction = 'in'
AND timed > vTimed
ORDER BY timed ASC
LIMIT 1;
CASE vDirection
WHEN 'in' THEN
IF UNIX_TIMESTAMP(vTimed) - UNIX_TIMESTAMP(vLastOut) <= vDayBreak THEN
SET vIsError = TRUE;
END IF;
WHEN 'out' THEN
IF UNIX_TIMESTAMP(vNextIn) - UNIX_TIMESTAMP(vTimed) <= vDayBreak THEN
SET vIsError = TRUE;
END IF;
ELSE BEGIN END;
END CASE;
IF vIsError THEN
SET vErrorCode = 'BREAK_DAY';
CALL util.throw(vErrorCode);
END IF;
IF (vDirection IN('in', 'out')) THEN
-- VERIFICA MAXIMO TIEMPO DESDE ENTRADA HASTA LA SALIDA
SELECT timed INTO vNextOut
FROM workerTimeControl
WHERE userFk = vWorkerFk
AND direction = 'out'
AND timed > vTimed
ORDER BY timed ASC
LIMIT 1;
SELECT direction INTO vNextDirection
FROM workerTimeControl
WHERE userFk = vWorkerFk
AND direction IN('in','out')
AND timed > vTimed
ORDER BY timed ASC
LIMIT 1;
SELECT direction INTO vLastDirection
FROM workerTimeControl
WHERE userFk = vWorkerFk
AND direction IN('in', 'out')
AND timed < vTimed
ORDER BY timed ASC
LIMIT 1;
IF (vDirection ='in'
AND vNextDirection = 'out'
AND UNIX_TIMESTAMP(vNextOut) - UNIX_TIMESTAMP(vTimed) > vDayMaxTime) OR
(vDirection ='out'
AND vLastDirection = 'in'
AND UNIX_TIMESTAMP(vTimed) -UNIX_TIMESTAMP(vLastIn) > vDayMaxTime) THEN
SET vErrorCode = 'DAY_MAX_TIME';
CALL util.throw(vErrorCode);
END IF;
-- VERIFICA DESCANSO SEMANAL
WITH wtc AS(
(SELECT timed
FROM vn.workerTimeControl
WHERE userFk = vWorkerFk
AND direction IN ('in', 'out')
AND timed BETWEEN vTimed - INTERVAL (vWeekScope * 2) SECOND
AND vTimed + INTERVAL (vWeekScope * 2) SECOND )
UNION
(SELECT vTimed)
), wtcGap AS(
SELECT timed,
TIMESTAMPDIFF(SECOND, LAG(timed) OVER (ORDER BY timed), timed) gap
FROM wtc
ORDER BY timed
), wtcBreak AS(
SELECT timed,
IF(IFNULL(gap, 0) > vShortWeekBreak, TRUE, FALSE) hasShortBreak,
IF(IFNULL(gap, 0) > vLongWeekBreak, TRUE, FALSE) hasLongBreak
FROM wtcGap
ORDER BY timed
), wtcBreakCounter AS(
SELECT timed,
SUM(hasShortBreak) OVER (ORDER BY timed) breakCounter ,
LEAD(hasLongBreak) OVER (ORDER BY timed) nextHasLongBreak
FROM wtcBreak
)SELECT TIMESTAMPDIFF(SECOND, MIN(timed), MAX(timed)) > vMaxWorkLongCycle OR
(TIMESTAMPDIFF(SECOND, MIN(timed), MAX(timed))> vMaxWorkShortCycle
AND NOT SUM(IFNULL(nextHasLongBreak, 1)))
hasError INTO vIsError
FROM wtcBreakCounter
GROUP BY breakCounter
HAVING hasError
LIMIT 1;
IF vIsError THEN
SET vErrorCode = 'BREAK_WEEK';
CALL util.throw(vErrorCode);
END IF;
END IF;
-- SE PERMITE FICHAR
INSERT INTO workerTimeControl(userFk, timed, direction, `manual`)
VALUES(vWorkerFk, vTimed, vDirection, vIsManual);
SELECT LAST_INSERT_ID() id;
END$$
DELIMITER ;

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('WorkerDepartment', '*', '*', 'ALLOW', 'ROLE', 'employee');

View File

@ -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;

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` ( model, property, accessType, permission, principalType, principalId)
VALUES('TicketCollection', '*', 'WRITE', 'ALLOW', 'ROLE', 'production');

View File

@ -0,0 +1,4 @@
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
VALUES ('Worker','setPassword','*','ALLOW','ROLE','employee');

View File

@ -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');

View File

@ -0,0 +1 @@
DROP PROCEDURE IF EXISTS `vn`.`workerCreate`;

View File

@ -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;

View File

@ -0,0 +1,150 @@
CREATE SCHEMA IF NOT EXISTS `vn2008`;
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);
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`)
);

Some files were not shown because too many files have changed in this diff Show More