Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5066-vehicleSorted

This commit is contained in:
Carlos Satorres 2023-06-07 08:15:50 +02:00
commit 738b04af4c
68 changed files with 12049 additions and 14039 deletions

View File

@ -24,13 +24,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- (Tickets -> Crear Factura) Al facturar se envia automáticamente el pdf al cliente - (Tickets -> Crear Factura) Al facturar se envia automáticamente el pdf al cliente
- (Artículos -> Histórico) Filtro para mostrar lo anterior al inventario - (Artículos -> Histórico) Filtro para mostrar lo anterior al inventario
- (Trabajadores -> Nuevo trabajador) Permite elegir el método de pago
### Changed ### Changed
- (Trabajadores -> Nuevo trabajador) Los clientes se crean sin 'TR' pero se añade tipo de negocio 'Trabajador' - (Trabajadores -> Nuevo trabajador) Los clientes se crean sin 'TR' pero se añade tipo de negocio 'Trabajador'
### Fixed ### Fixed
- (Tickets -> Líneas) Se permite hacer split de líneas al mismo ticket - (Tickets -> Líneas) Se permite hacer split de líneas al mismo ticket
- (Tickets -> Cambiar estado) Ahora muestra la lista completa de todos los estados

View File

@ -1,9 +1,9 @@
LOAD DATA LOCAL INFILE ? LOAD DATA LOCAL INFILE ?
INTO TABLE `edi`.`item` INTO TABLE `edi`.`item`
CHARACTER SET ascii
FIELDS TERMINATED BY ';' FIELDS TERMINATED BY ';'
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9, @col10, @col11, @col12) LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9, @col10, @col11, @col12)
SET SET id = @col2,
id = @col2,
product_name = @col4, product_name = @col4,
name = @col5, name = @col5,
plant_id = @col7, plant_id = @col7,
@ -11,3 +11,4 @@ LOAD DATA LOCAL INFILE ?
entry_date = STR_TO_DATE(@col10, '%Y%m%d'), entry_date = STR_TO_DATE(@col10, '%Y%m%d'),
expiry_date = IFNULL(NULL,STR_TO_DATE(@col11, '%Y%m%d')), expiry_date = IFNULL(NULL,STR_TO_DATE(@col11, '%Y%m%d')),
change_date_time = STR_TO_DATE(@col12, '%Y%m%d%H%i') change_date_time = STR_TO_DATE(@col12, '%Y%m%d%H%i')

View File

@ -3,236 +3,237 @@ const path = require('path');
const fs = require('fs-extra'); const fs = require('fs-extra');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('updateData', { Self.remoteMethodCtx('updateData', {
description: 'Updates schema data from external provider', description: 'Updates schema data from external provider',
accessType: 'WRITE', accessType: 'WRITE',
returns: { returns: {
type: 'object', type: 'object',
root: true root: true
}, },
http: { http: {
path: `/updateData`, path: `/updateData`,
verb: 'POST' verb: 'POST'
} }
}); });
Self.updateData = async() => { Self.updateData = async() => {
const models = Self.app.models; const models = Self.app.models;
// Get files checksum // Get files checksum
const tx = await Self.beginTransaction({}); const tx = await Self.beginTransaction({});
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
const files = await Self.rawSql('SELECT name, checksum, keyValue FROM edi.fileConfig', null, options); const files = await Self.rawSql('SELECT name, checksum, keyValue FROM edi.fileConfig', null, options);
const updatableFiles = []; const updatableFiles = [];
for (const file of files) { for (const file of files) {
const fileChecksum = await getChecksum(file); const fileChecksum = await getChecksum(file);
if (file.checksum != fileChecksum) { if (file.checksum != fileChecksum) {
updatableFiles.push({ updatableFiles.push({
name: file.name, name: file.name,
checksum: fileChecksum checksum: fileChecksum
}); });
} else } else
console.debug(`File already updated, skipping...`); console.debug(`File already updated, skipping...`);
} }
if (updatableFiles.length === 0) if (updatableFiles.length === 0)
return false; return false;
// Download files // Download files
const container = await models.TempContainer.container('edi'); const container = await models.TempContainer.container('edi');
const tempPath = path.join(container.client.root, container.name); const tempPath = path.join(container.client.root, container.name);
let remoteFile; let remoteFile;
let tempDir; let tempDir;
let tempFile; let tempFile;
const fileNames = updatableFiles.map(file => file.name); const fileNames = updatableFiles.map(file => file.name);
const tables = await Self.rawSql(` const tables = await Self.rawSql(`
SELECT fileName, toTable, file SELECT fileName, toTable, file
FROM edi.tableConfig FROM edi.tableConfig
WHERE file IN (?)`, [fileNames], options); WHERE file IN (?)`, [fileNames], options);
for (const table of tables) { for (const table of tables) {
const fileName = table.file; const fileName = table.file;
remoteFile = `codes/${fileName}.ZIP`; remoteFile = `codes/${fileName}.ZIP`;
tempDir = `${tempPath}/${fileName}`; tempDir = `${tempPath}/${fileName}`;
tempFile = `${tempPath}/${fileName}.zip`; tempFile = `${tempPath}/${fileName}.zip`;
try { try {
await fs.readFile(tempFile); await fs.readFile(tempFile);
} catch (error) { } catch (error) {
if (error.code === 'ENOENT') { if (error.code === 'ENOENT') {
console.debug(`Downloading file ${fileName}...`); console.debug(`Downloading file ${fileName}...`);
const downloadOutput = await downloadFile(remoteFile, tempFile); const downloadOutput = await downloadFile(remoteFile, tempFile);
if (downloadOutput.error) if (downloadOutput.error)
continue; continue;
} }
} }
await extractFile(fileName, tempFile, tempDir); await extractFile(fileName, tempFile, tempDir);
console.debug(`Updating table ${table.toTable}...`); console.debug(`Updating table ${table.toTable}...`);
await dumpData(tempDir, table, options); await dumpData(tempDir, table, options);
} }
// Update files checksum // Update files checksum
for (const file of updatableFiles) { for (const file of updatableFiles) {
console.log(`Updating file ${file.name} checksum...`); console.log(`Updating file ${file.name} checksum...`);
await Self.rawSql(` await Self.rawSql(`
UPDATE edi.fileConfig UPDATE edi.fileConfig
SET checksum = ? SET checksum = ?
WHERE name = ?`, WHERE name = ?`,
[file.checksum, file.name], options); [file.checksum, file.name], options);
} }
await tx.commit(); await tx.commit();
// Clean files // Clean files
try { try {
console.debug(`Cleaning files...`); console.debug(`Cleaning files...`);
await fs.remove(tempPath); await fs.remove(tempPath);
} catch (error) { } catch (error) {
if (error.code !== 'ENOENT') if (error.code !== 'ENOENT')
throw e; throw e;
} }
return true; return true;
} catch (error) { } catch (error) {
await tx.rollback(); await tx.rollback();
throw error; throw error;
} }
}; };
let ftpClient; let ftpClient;
async function getFtpClient() { async function getFtpClient() {
if (!ftpClient) { if (!ftpClient) {
const [ftpConfig] = await Self.rawSql('SELECT host, user, password FROM edi.ftpConfig'); const [ftpConfig] = await Self.rawSql('SELECT host, user, password FROM edi.ftpConfig');
console.debug(`Openning FTP connection to ${ftpConfig.host}...\n`); console.debug(`Openning FTP connection to ${ftpConfig.host}...\n`);
const FtpClient = require('ftps'); const FtpClient = require('ftps');
ftpClient = new FtpClient({ ftpClient = new FtpClient({
host: ftpConfig.host, host: ftpConfig.host,
username: ftpConfig.user, username: ftpConfig.user,
password: ftpConfig.password, password: ftpConfig.password,
procotol: 'ftp' procotol: 'ftp',
}); additionalLftpCommands: 'set ssl:verify-certificate no'
} });
}
return ftpClient; return ftpClient;
} }
async function getChecksum(file) { async function getChecksum(file) {
const ftpClient = await getFtpClient(); const ftpClient = await getFtpClient();
console.debug(`Checking checksum for file ${file.name}...`); console.debug(`Checking checksum for file ${file.name}...`);
ftpClient.cat(`codes/${file.name}.txt`); ftpClient.cat(`codes/${file.name}.TXT`);
const response = await new Promise((resolve, reject) => { const response = await new Promise((resolve, reject) => {
ftpClient.exec((err, response) => { ftpClient.exec((err, response) => {
if (err || response.error) { if (err || response.error) {
console.debug(`Error downloading checksum file... ${response.error}`); console.debug(`Error downloading checksum file... ${response.error}`);
return reject(err); return reject(err);
} }
resolve(response); resolve(response);
}); });
}); });
if (response && response.data) { if (response && response.data) {
const fileContents = response.data; const fileContents = response.data;
const rows = fileContents.split('\n'); const rows = fileContents.split('\n');
const row = rows[4]; const row = rows[4];
const columns = row.split(/\s+/); const columns = row.split(/\s+/);
let fileChecksum; let fileChecksum;
if (file.keyValue) if (file.keyValue)
fileChecksum = columns[1]; fileChecksum = columns[1];
if (!file.keyValue) if (!file.keyValue)
fileChecksum = columns[0]; fileChecksum = columns[0];
return fileChecksum; return fileChecksum;
} }
} }
async function downloadFile(remoteFile, tempFile) { async function downloadFile(remoteFile, tempFile) {
const ftpClient = await getFtpClient(); const ftpClient = await getFtpClient();
ftpClient.get(remoteFile, tempFile); ftpClient.get(remoteFile, tempFile);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
ftpClient.exec((err, response) => { ftpClient.exec((err, response) => {
if (err || response.error) { if (err || response.error) {
console.debug(`Error downloading file... ${response.error}`); console.debug(`Error downloading file... ${response.error}`);
return reject(err); return reject(err);
} }
resolve(response); resolve(response);
}); });
}); });
} }
async function extractFile(fileName, tempFile, tempDir) { async function extractFile(fileName, tempFile, tempDir) {
const JSZip = require('jszip'); const JSZip = require('jszip');
try { try {
await fs.mkdir(tempDir); await fs.mkdir(tempDir);
console.debug(`Extracting file ${fileName}...`); console.debug(`Extracting file ${fileName}...`);
} catch (error) { } catch (error) {
if (error.code !== 'EEXIST') if (error.code !== 'EEXIST')
throw e; throw e;
} }
const fileStream = await fs.readFile(tempFile); const fileStream = await fs.readFile(tempFile);
if (fileStream) { if (fileStream) {
const zip = new JSZip(); const zip = new JSZip();
const zipContents = await zip.loadAsync(fileStream); const zipContents = await zip.loadAsync(fileStream);
if (!zipContents) return; if (!zipContents) return;
const fileNames = Object.keys(zipContents.files); const fileNames = Object.keys(zipContents.files);
for (const fileName of fileNames) { for (const fileName of fileNames) {
const fileContent = await zip.file(fileName).async('nodebuffer'); const fileContent = await zip.file(fileName).async('nodebuffer');
const dest = path.join(tempDir, fileName); const dest = path.join(tempDir, fileName);
await fs.writeFile(dest, fileContent); await fs.writeFile(dest, fileContent);
} }
} }
} }
async function dumpData(tempDir, table, options) { async function dumpData(tempDir, table, options) {
const toTable = table.toTable; const toTable = table.toTable;
const baseName = table.fileName; const baseName = table.fileName;
console.log(`Emptying table ${toTable}...`); console.log(`Emptying table ${toTable}...`);
const tableName = `edi.${toTable}`; const tableName = `edi.${toTable}`;
await Self.rawSql(`DELETE FROM ??`, [tableName]); await Self.rawSql(`DELETE FROM ??`, [tableName]);
const dirFiles = await fs.readdir(tempDir); const dirFiles = await fs.readdir(tempDir);
const files = dirFiles.filter(file => file.startsWith(baseName)); const files = dirFiles.filter(file => file.startsWith(baseName));
for (const file of files) { for (const file of files) {
console.log(`Dumping data from file ${file}...`); console.log(`Dumping data from file ${file}...`);
const templatePath = path.join(__dirname, `./sql/${toTable}.sql`); const templatePath = path.join(__dirname, `./sql/${toTable}.sql`);
const sqlTemplate = await fs.readFile(templatePath, 'utf8'); const sqlTemplate = await fs.readFile(templatePath, 'utf8');
const filePath = path.join(tempDir, file); const filePath = path.join(tempDir, file);
await Self.rawSql(sqlTemplate, [filePath], options); await Self.rawSql(sqlTemplate, [filePath], options);
await Self.rawSql(` await Self.rawSql(`
UPDATE edi.tableConfig UPDATE edi.tableConfig
SET updated = ? SET updated = ?
WHERE fileName = ? WHERE fileName = ?
`, [Date.vnNew(), baseName], options); `, [Date.vnNew(), baseName], options);
} }
console.log(`Updated table ${toTable}\n`); console.log(`Updated table ${toTable}\n`);
} }
}; };

View File

@ -56,8 +56,6 @@ CREATE TABLE `vn`.`collectionWagonTicket` (
ALTER TABLE `vn`.`wagon` ADD `typeFk` int(11) unsigned NOT NULL; ALTER TABLE `vn`.`wagon` ADD `typeFk` int(11) unsigned NOT NULL;
ALTER TABLE `vn`.`wagon` ADD `label` int(11) unsigned NOT NULL; ALTER TABLE `vn`.`wagon` ADD `label` int(11) unsigned NOT NULL;
ALTER TABLE `vn`.`wagon` ADD CONSTRAINT `wagon_type` FOREIGN KEY (`typeFk`) REFERENCES `wagonType` (`id`) ON UPDATE CASCADE;
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES VALUES
('WagonType', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'), ('WagonType', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
@ -70,3 +68,4 @@ INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `pri
('WagonType', 'createWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'), ('WagonType', 'createWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('WagonType', 'deleteWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'), ('WagonType', 'deleteWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('WagonType', 'editWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'); ('WagonType', 'editWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi');

View File

@ -0,0 +1 @@
ALTER TABLE `vn`.`wagon` ADD CONSTRAINT `wagon_type` FOREIGN KEY (`typeFk`) REFERENCES `wagonType` (`id`) ON UPDATE CASCADE;

View File

@ -1,7 +1,7 @@
ALTER TABLE `vn`.`workerConfig` ADD payMethodFk tinyint(3) unsigned NULL; ALTER TABLE `vn`.`workerConfig` ADD payMethodFk tinyint(3) unsigned NULL;
ALTER TABLE `vn`.`workerConfig` ADD CONSTRAINT workerConfig_FK FOREIGN KEY (roleFk) REFERENCES account.`role`(id) ON DELETE RESTRICT ON UPDATE CASCADE; ALTER TABLE `vn`.`workerConfig` ADD CONSTRAINT workerConfig_FK FOREIGN KEY (roleFk) REFERENCES account.`role`(id) ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE `vn`.`workerConfig` ADD CONSTRAINT workerConfig_FK_1 FOREIGN KEY (payMethodFk) REFERENCES `vn`.`payMethod`(id) ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE `vn`.`workerConfig` ADD CONSTRAINT workerConfig_FK_1 FOREIGN KEY (payMethodFk) REFERENCES `vn`.`payMethod`(id) ON DELETE SET NULL ON UPDATE CASCADE;
-- Cuando se apruebe el PR quitar y poner en redmine para hacerse manualmente
UPDATE `vn`.`workerConfig` UPDATE `vn`.`workerConfig`
SET payMethodFk = 4 SET payMethodFk = 4
WHERE id=1; WHERE id=1;

View File

@ -0,0 +1,28 @@
CREATE TABLE `vn`.`buyConfig` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`monthsAgo` int(11) NOT NULL DEFAULT 6 COMMENT 'Meses desde la última compra',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
CREATE TABLE `vn`.`travelConfig` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`warehouseInFk` smallint(6) unsigned NOT NULL DEFAULT 8 COMMENT 'Warehouse de origen',
`warehouseOutFk` smallint(6) unsigned NOT NULL DEFAULT 60 COMMENT 'Warehouse destino',
`agencyFk` int(11) NOT NULL DEFAULT 1378 COMMENT 'Agencia por defecto',
`companyFk` smallint(5) unsigned NOT NULL DEFAULT 442 COMMENT 'Compañía por defecto',
PRIMARY KEY (`id`),
KEY `travelConfig_FK` (`warehouseInFk`),
KEY `travelConfig_FK_1` (`warehouseOutFk`),
KEY `travelConfig_FK_2` (`agencyFk`),
KEY `travelConfig_FK_3` (`companyFk`),
CONSTRAINT `travelConfig_FK` FOREIGN KEY (`warehouseInFk`) REFERENCES `warehouse` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `travelConfig_FK_1` FOREIGN KEY (`warehouseOutFk`) REFERENCES `warehouse` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `travelConfig_FK_2` FOREIGN KEY (`agencyFk`) REFERENCES `agencyMode` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `travelConfig_FK_3` FOREIGN KEY (`companyFk`) REFERENCES `company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('Entry', 'addFromPackaging', 'WRITE', 'ALLOW', 'ROLE', 'production'),
('Entry', 'addFromBuy', 'WRITE', 'ALLOW', 'ROLE', 'production'),
('Supplier', 'getItemsPackaging', 'READ', 'ALLOW', 'ROLE', 'production');

View File

@ -905,7 +905,7 @@ INSERT INTO `vn`.`itemFamily`(`code`, `description`)
INSERT INTO `vn`.`item`(`id`, `typeFk`, `size`, `inkFk`, `stems`, `originFk`, `description`, `producerFk`, `intrastatFk`, `expenceFk`, 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`, `packingShelve`, `weightByPiece`) `comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`, `packingShelve`, `weightByPiece`)
VALUES VALUES
(1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'VT', 0, NULL, 'V', 0, 15,3), (1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'EMB', 0, NULL, 'V', 0, 15,3),
(2, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H', 0, 10,2), (2, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H', 0, 10,2),
(3, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, 5,5), (3, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, 5,5),
(4, 1, 60, 'YEL', 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL,NULL), (4, 1, 60, 'YEL', 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL,NULL),
@ -2886,6 +2886,10 @@ INSERT INTO `vn`.`wagonTypeTray` (`id`, `typeFk`, `height`, `colorFk`)
(2, 1, 50, 2), (2, 1, 50, 2),
(3, 1, 0, 3); (3, 1, 0, 3);
INSERT INTO `vn`.`travelConfig` (`id`, `warehouseInFk`, `warehouseOutFk`, `agencyFk`, `companyFk`)
VALUES
(1, 1, 1, 1, 442);
INSERT INTO `vn`.`buyConfig` (`id`, `monthsAgo`)
VALUES
(1, 6);

View File

@ -61950,7 +61950,6 @@ BEGIN
* @vWarehouseFk almacen donde buscar * @vWarehouseFk almacen donde buscar
* @vDate Si la fecha es null, muestra el histórico desde el inventario. Si la fecha no es null, muestra histórico desde la fecha pasada. * @vDate Si la fecha es null, muestra el histórico desde el inventario. Si la fecha no es null, muestra histórico desde la fecha pasada.
*/ */
DECLARE vDateInventory DATETIME; DECLARE vDateInventory DATETIME;
DECLARE vInvCalculated INT; DECLARE vInvCalculated INT;

View File

@ -43,7 +43,7 @@ services:
- node.role == worker - node.role == worker
resources: resources:
limits: limits:
memory: 4G memory: 8G
configs: configs:
datasources: datasources:
external: true external: true

View File

@ -22,7 +22,8 @@ export async function getBrowser() {
env.E2E_SHOW = true; env.E2E_SHOW = true;
} }
const headless = !env.E2E_SHOW; const headless = env.E2E_SHOW ? false : 'new';
const browser = await Puppeteer.launch({ const browser = await Puppeteer.launch({
args, args,
defaultViewport: null, defaultViewport: null,

View File

@ -150,10 +150,10 @@ export default class Controller extends Section {
if (value == null || value == '') return null; if (value == null || value == '') return null;
switch (prop) { switch (prop) {
case 'search': case 'search':
const or = [{changedModelId: value}]; if (/^[0-9]+$/.test(value))
if (!/^[0-9]+$/.test(value)) return {changedModelId: value};
or.push({changedModelValue: {like: `%${value}%`}}); else
return {or}; return {changedModelValue: {like: `%${value}%`}};
case 'changes': case 'changes':
return {or: [ return {or: [
{oldInstance: {like: `%${value}%`}}, {oldInstance: {like: `%${value}%`}},

View File

@ -1,6 +1,8 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
const utils = require('loopback/lib/utils');
const {util} = require('webpack');
module.exports = function(Self) { module.exports = function(Self) {
Self.ParameterizedSQL = ParameterizedSQL; Self.ParameterizedSQL = ParameterizedSQL;
@ -164,23 +166,21 @@ module.exports = function(Self) {
function rewriteMethod(methodName) { function rewriteMethod(methodName) {
const realMethod = this[methodName]; const realMethod = this[methodName];
return async(data, options, cb) => { return function(...args) {
if (options instanceof Function) { let cb;
cb = options; const lastArg = args[args.length - 1];
options = null; if (lastArg instanceof Function) {
} cb = lastArg;
args.pop();
} else
cb = utils.createPromiseCallback();
try { args.push(function(err, res) {
const result = await realMethod.call(this, data, options); if (err) err = replaceErr(err, replaceErrFunc);
cb(err, res);
if (cb) cb(null, result); });
else return result; realMethod.apply(this, args);
} catch (err) { return cb.promise;
let myErr = replaceErr(err, replaceErrFunc);
if (cb) cb(myErr);
else
throw myErr;
}
}; };
} }

View File

@ -15,3 +15,7 @@ columns:
image: image image: image
hasGrant: has grant hasGrant: has grant
userFk: user userFk: user
recoverPass: recover password
role: role
sync: pending sync
lastPassChange: password changed

View File

@ -15,3 +15,7 @@ columns:
image: imagen image: imagen
hasGrant: puede delegar hasGrant: puede delegar
userFk: usuario userFk: usuario
recoverPass: recuperar contraseña
role: rol
sync: Pendiente de sincronizar
lastPassChange: contraseña modificada

View File

@ -59,6 +59,12 @@ module.exports = function(Self) {
fields: ['id', 'name'] fields: ['id', 'name']
} }
}, },
{
relation: 'businessType',
scope: {
fields: ['description']
}
},
{ {
relation: 'account', relation: 'account',
scope: { scope: {

View File

@ -3,7 +3,7 @@
"base": "VnModel", "base": "VnModel",
"options": { "options": {
"mysql": { "mysql": {
"table": "payMethod" "table": "payMethod"
} }
}, },
"properties": { "properties": {

View File

@ -43,6 +43,10 @@
{{$ctrl.client.salesPersonUser.name}} {{$ctrl.client.salesPersonUser.name}}
</span> </span>
</vn-label-value> </vn-label-value>
<vn-label-value
label="Business type"
value="{{$ctrl.client.businessType.description}}">
</vn-label-value>
</div> </div>
<div class="icons"> <div class="icons">
<vn-icon <vn-icon

View File

@ -8,3 +8,4 @@ Client invoices list: Listado de facturas del cliente
Pay method: Forma de pago Pay method: Forma de pago
Unpaid Dated: "Fecha: {{dated | date:'dd/MM/yyyy'}}" Unpaid Dated: "Fecha: {{dated | date:'dd/MM/yyyy'}}"
Unpaid Amount: "Importe: {{amount | currency: 'EUR':2}}" Unpaid Amount: "Importe: {{amount | currency: 'EUR':2}}"
Business type: Tipo de negocio

View File

@ -34,7 +34,7 @@
<vn-autocomplete vn-one required="true" <vn-autocomplete vn-one required="true"
label="Warehouse" label="Warehouse"
ng-model="$ctrl.dms.warehouseId" ng-model="$ctrl.dms.warehouseId"
data="Warehouses" data="warehouses"
show-field="name" show-field="name"
value-field="id"> value-field="id">
</vn-autocomplete> </vn-autocomplete>

View File

@ -0,0 +1,107 @@
module.exports = Self => {
Self.remoteMethodCtx('addFromBuy', {
description: 'Modify a field of a buy or creates a new one with default values',
accessType: 'WRITE',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The entry id',
http: {source: 'path'}
}, {
arg: 'item',
type: 'number',
required: true,
description: 'The item id',
}, {
arg: 'printedStickers',
type: 'number',
required: true,
description: 'The field to modify',
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/addFromBuy`,
verb: 'POST'
}
});
Self.addFromBuy = async(ctx, options) => {
const args = ctx.args;
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const myOptions = {userId};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
let buy = await models.Buy.findOne({where: {entryFk: args.id}}, myOptions);
if (buy)
await buy.updateAttribute('printedStickers', args.printedStickers);
else {
const userConfig = await models.UserConfig.findById(userId, {fields: ['warehouseFk']}, myOptions);
await Self.rawSql(
'CALL vn.buyUltimate(?,?)',
[userConfig.warehouseFk, null],
myOptions
);
let buyUltimate = await Self.rawSql(
`SELECT buyFk
FROM tmp.buyUltimate
WHERE itemFk = ?`,
[args.item],
myOptions
);
buyUltimate = await models.Buy.findById(buyUltimate[0].buyFk, null, myOptions);
buy = await models.Buy.create({
entryFk: args.id,
itemFk: args.item,
quantity: 0,
dispatched: buyUltimate.dispatched,
buyingValue: buyUltimate.buyingValue,
freightValue: buyUltimate.freightValue,
isIgnored: buyUltimate.isIgnored,
stickers: buyUltimate.stickers,
packing: buyUltimate.packing,
grouping: buyUltimate.grouping,
groupingMode: buyUltimate.groupingMode,
containerFk: buyUltimate.containerFk,
comissionValue: buyUltimate.comissionValue,
packageValue: buyUltimate.packageValue,
location: buyUltimate.location,
packageFk: buyUltimate.packageFk,
price1: buyUltimate.price1,
price2: buyUltimate.price2,
price3: buyUltimate.price3,
minPrice: buyUltimate.minPrice,
printedStickers: args.printedStickers,
workerFk: buyUltimate.workerFk,
isChecked: buyUltimate.isChecked,
isPickedOff: buyUltimate.isPickedOff,
created: buyUltimate.created,
ektFk: buyUltimate.ektFk,
weight: buyUltimate.weight,
deliveryFk: buyUltimate.deliveryFk,
itemOriginalFk: buyUltimate.itemOriginalFk
}, myOptions);
}
if (tx) await tx.commit();
return buy;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,72 @@
module.exports = Self => {
Self.remoteMethodCtx('addFromPackaging', {
description: 'Create a receipt or return entry for a supplier with a specific travel',
accessType: 'WRITE',
accepts: [{
arg: 'supplier',
type: 'number',
required: true,
description: 'The supplier id',
},
{
arg: 'isTravelReception',
type: 'boolean',
required: true,
description: 'Indicates if the travel associated with the entry is a return or receipt travel'
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/addFromPackaging`,
verb: 'POST'
}
});
Self.addFromPackaging = async(ctx, options) => {
const args = ctx.args;
const models = Self.app.models;
let tx;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const travelConfig = await models.TravelConfig.findOne({}, myOptions);
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(today.getDate() - 1);
const tomorrow = new Date(today);
tomorrow.setDate(today.getDate() + 1);
const travel = await models.Travel.create({
shipped: args.isTravelReception ? yesterday : today,
landed: args.isTravelReception ? today : tomorrow,
agencyModeFk: travelConfig.agencyFk,
warehouseInFk: travelConfig.warehouseOutFk,
warehouseOutFk: travelConfig.warehouseInFk
}, myOptions);
const entry = await models.Entry.create({
supplierFk: args.supplier,
travelFk: travel.id,
companyFk: travelConfig.companyFk
}, myOptions);
if (tx) await tx.commit();
return entry;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
};
};

View File

@ -0,0 +1,51 @@
const models = require('vn-loopback/server/server').models;
describe('entry addFromBuy()', () => {
const ctx = {req: {accessToken: {userId: 18}}};
it('should change the printedStickers of an existent buy', async() => {
const id = 1;
const item = 1;
const buy = 1;
const tx = await models.Entry.beginTransaction({});
const options = {transaction: tx};
try {
const currentBuy = await models.Buy.findById(buy, {fields: ['printedStickers']}, options);
const printedStickers = currentBuy.printedStickers + 10;
ctx.args = {id, item, printedStickers};
const newBuy = await models.Entry.addFromBuy(ctx, options);
expect(newBuy.printedStickers).toEqual(printedStickers);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should create for an entry without a concrete item a new buy', async() => {
const id = 8;
const item = 1;
const printedStickers = 10;
const tx = await models.Entry.beginTransaction({});
const options = {transaction: tx};
try {
const emptyBuy = await models.Buy.findOne({where: {entryFk: id}}, options);
ctx.args = {id, item, printedStickers};
const newBuy = await models.Entry.addFromBuy(ctx, options);
expect(emptyBuy).toEqual(null);
expect(newBuy.entryFk).toEqual(id);
expect(newBuy.printedStickers).toEqual(printedStickers);
expect(newBuy.itemFk).toEqual(item);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -0,0 +1,49 @@
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('entry addFromPackaging()', () => {
const supplier = 442;
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(today.getDate() - 1);
beforeAll(async() => {
const activeCtx = {
accessToken: {userId: 49},
http: {
req: {
headers: {origin: 'http://localhost'},
},
},
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx,
});
});
it('should create an incoming travel', async() => {
const ctx = {args: {isTravelReception: true, supplier}};
const tx = await models.Entry.beginTransaction({});
const options = {transaction: tx};
try {
const entry = await models.Entry.addFromPackaging(ctx, options);
const travelConfig = await models.TravelConfig.findOne({}, options);
const travel = await models.Travel.findOne({order: 'id DESC'}, options);
expect(new Date(travel.shipped).getDate()).toEqual(yesterday.getDate());
expect(new Date(travel.landed).getDate()).toEqual(today.getDate());
expect(travel.agencyModeFk).toEqual(travelConfig.agencyFk);
expect(travel.warehouseInFk).toEqual(travelConfig.warehouseOutFk);
expect(travel.warehouseOutFk).toEqual(travelConfig.warehouseInFk);
expect(entry.supplierFk).toEqual(supplier);
expect(entry.travelFk).toEqual(travel.id);
expect(entry.companyFk).toEqual(travelConfig.companyFk);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -5,6 +5,9 @@
"Buy": { "Buy": {
"dataSource": "vn" "dataSource": "vn"
}, },
"BuyConfig": {
"dataSource": "vn"
},
"ItemMatchProperties": { "ItemMatchProperties": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -0,0 +1,18 @@
{
"name": "BuyConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "buyConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true
},
"showLastBuy": {
"type": "number"
}
}
}

View File

@ -39,6 +39,9 @@
"packageValue": { "packageValue": {
"type": "number" "type": "number"
}, },
"price1": {
"type": "number"
},
"price2": { "price2": {
"type": "number" "type": "number"
}, },
@ -47,7 +50,44 @@
}, },
"weight": { "weight": {
"type": "number" "type": "number"
},
"printedStickers": {
"type": "number"
},
"dispatched": {
"type": "number"
},
"isIgnored": {
"type": "boolean"
},
"containerFk": {
"type": "number"
},
"location": {
"type": "number"
},
"minPrice": {
"type": "number"
},
"isChecked": {
"type": "boolean"
},
"isPickedOff": {
"type": "boolean"
},
"created": {
"type": "date"
},
"ektFk": {
"type": "number"
},
"itemOriginalFk": {
"type": "number"
},
"editorFk": {
"type": "number"
} }
}, },
"relations": { "relations": {
"entry": { "entry": {
@ -64,6 +104,16 @@
"type": "belongsTo", "type": "belongsTo",
"model": "Packaging", "model": "Packaging",
"foreignKey": "packageFk" "foreignKey": "packageFk"
},
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
},
"delivery": {
"type": "belongsTo",
"model": "Delivery",
"foreignKey": "deliveryFk"
} }
} }
} }

View File

@ -8,6 +8,8 @@ module.exports = Self => {
require('../methods/entry/importBuysPreview')(Self); require('../methods/entry/importBuysPreview')(Self);
require('../methods/entry/lastItemBuys')(Self); require('../methods/entry/lastItemBuys')(Self);
require('../methods/entry/entryOrderPdf')(Self); require('../methods/entry/entryOrderPdf')(Self);
require('../methods/entry/addFromPackaging')(Self);
require('../methods/entry/addFromBuy')(Self);
Self.observe('before save', async function(ctx, options) { Self.observe('before save', async function(ctx, options) {
if (ctx.isNewInstance) return; if (ctx.isNewInstance) return;

View File

@ -157,14 +157,14 @@
<vn-autocomplete <vn-autocomplete
label="Warehouse Out" label="Warehouse Out"
ng-model="$ctrl.travelFilterParams.warehouseOutFk" ng-model="$ctrl.travelFilterParams.warehouseOutFk"
data="Warehouses" data="warehouses"
show-field="name" show-field="name"
value-field="id"> value-field="id">
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete <vn-autocomplete
label="Warehouse In" label="Warehouse In"
ng-model="$ctrl.travelFilterParams.warehouseInFk" ng-model="$ctrl.travelFilterParams.warehouseInFk"
data="Warehouses" data="warehouses"
show-field="name" show-field="name"
value-field="id"> value-field="id">
</vn-autocomplete> </vn-autocomplete>

View File

@ -1,4 +1,4 @@
name: botanical name: botanical data
columns: columns:
itemFk: item itemFk: item
genusFk: genus genusFk: genus

View File

@ -1,4 +1,4 @@
name: botánico name: datos botánicos
columns: columns:
itemFk: artículo itemFk: artículo
genusFk: género genusFk: género

View File

@ -35,7 +35,7 @@ columns:
packingOut: packing out packingOut: packing out
hasMinPrice: has min price hasMinPrice: has min price
isFragile: fragile isFragile: fragile
isFloramondo: is floramondo isFloramondo: floramondo
packingShelve: packing shelve packingShelve: packing shelve
isLaid: laid isLaid: laid
inkFk: ink inkFk: ink

View File

@ -35,7 +35,7 @@ columns:
packingOut: empaquetar packingOut: empaquetar
hasMinPrice: tiene precio mínimo hasMinPrice: tiene precio mínimo
isFragile: frágil isFragile: frágil
isFloramondo: es floramondo isFloramondo: floramondo
packingShelve: estantería embalaje packingShelve: estantería embalaje
isLaid: puesto isLaid: puesto
inkFk: tinta inkFk: tinta

View File

@ -88,7 +88,7 @@
vn-one vn-one
label="Warehouse" label="Warehouse"
ng-model="filter.warehouseFk" ng-model="filter.warehouseFk"
data="Warehouses"> data="warehouses">
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete <vn-autocomplete
vn-one vn-one

View File

@ -22,7 +22,7 @@
ng-model="filter.agencyModeFk"> ng-model="filter.agencyModeFk">
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete vn-one <vn-autocomplete vn-one
url="Agencies" data="agencies"
label="Agency Agreement" label="Agency Agreement"
show-field="name" show-field="name"
value-field="id" value-field="id"

View File

@ -84,7 +84,7 @@
vn-one vn-one
label="Warehouse" label="Warehouse"
ng-model="filter.warehouseFk" ng-model="filter.warehouseFk"
data="Warehouses" data="warehouses"
show-field="name" show-field="name"
value-field="id"> value-field="id">
</vn-autocomplete> </vn-autocomplete>

View File

@ -0,0 +1,11 @@
name: shelving
columns:
id: id
code: code
parkingFk: parking
isPrinted: printed
priority: priority
parked: parked
userFk: user
isSpam: SPAM
isRecyclable: recyclable

View File

@ -0,0 +1,11 @@
name: estantería
columns:
id: id
code: código
parkingFk: parking
isPrinted: impreso
priority: prioridad
parked: aparcado
userFk: usuario
isSpam: SPAM
isRecyclable: reciclable

View File

@ -0,0 +1,50 @@
module.exports = Self => {
Self.remoteMethod('getItemsPackaging', {
description: 'Returns the list of items from the supplier of type packing',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The supplier id',
http: {source: 'path'}
}, {
arg: 'entry',
type: 'number',
required: true,
description: 'The entry id',
}],
returns: {
type: 'object',
root: true
},
http: {
path: `/:id/getItemsPackaging`,
verb: 'GET'
}
});
Self.getItemsPackaging = async(id, entry) => {
return Self.rawSql(`
WITH entryTmp AS (
SELECT i.id, SUM(b.quantity) quantity
FROM vn.entry e
JOIN vn.buy b ON b.entryFk = e.id
JOIN vn.supplier s ON s.id = e.supplierFk
JOIN vn.item i ON i.id = b.itemFk
WHERE e.id = ? AND e.supplierFk = ?
GROUP BY i.id
) SELECT i.id, i.name, et.quantity, SUM(b.quantity) quantityTotal
FROM vn.buy b
JOIN vn.item i ON i.id = b.itemFk
JOIN vn.entry e ON e.id = b.entryFk
JOIN vn.supplier s ON s.id = e.supplierFk
JOIN vn.buyConfig bc ON bc.monthsAgo
JOIN vn.travel t ON t.id = e.travelFk
LEFT JOIN entryTmp et ON et.id = i.id
WHERE e.supplierFk = ?
AND i.family IN ('EMB', 'CONT')
AND b.created > (util.VN_CURDATE() - INTERVAL bc.monthsAgo MONTH)
GROUP BY b.itemFk
ORDER BY et.quantity DESC, quantityTotal DESC`, [entry, id, id]);
};
};

View File

@ -0,0 +1,12 @@
const app = require('vn-loopback/server/server');
describe('Supplier getItemsPackaging()', () => {
it('should return a summary of the list of items from a specific supplier', async() => {
const [item] = await app.models.Supplier.getItemsPackaging(1, 1);
expect(item.id).toEqual(1);
expect(item.name).toEqual('Ranged weapon longbow 2m');
expect(item.quantity).toEqual(5000);
expect(item.quantityTotal).toEqual(5100);
});
});

View File

@ -11,6 +11,7 @@ module.exports = Self => {
require('../methods/supplier/campaignMetricsPdf')(Self); require('../methods/supplier/campaignMetricsPdf')(Self);
require('../methods/supplier/campaignMetricsEmail')(Self); require('../methods/supplier/campaignMetricsEmail')(Self);
require('../methods/supplier/newSupplier')(Self); require('../methods/supplier/newSupplier')(Self);
require('../methods/supplier/getItemsPackaging')(Self);
Self.validatesPresenceOf('name', { Self.validatesPresenceOf('name', {
message: 'The social name cannot be empty' message: 'The social name cannot be empty'

View File

@ -12,3 +12,5 @@ columns:
hostFk: PC hostFk: PC
isBox: box isBox: box
itemPackingTypeFk: packing type itemPackingTypeFk: packing type
externalId: external id
stateTypeFk: status

View File

@ -12,3 +12,5 @@ columns:
hostFk: PC hostFk: PC
isBox: caja isBox: caja
itemPackingTypeFk: tipo empaquetado itemPackingTypeFk: tipo empaquetado
externalId: id externo
stateTypeFk: estado

View File

@ -30,7 +30,7 @@
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete vn-one <vn-autocomplete vn-one
required="true" required="true"
data="Warehouses" data="warehouses"
label="Warehouse" label="Warehouse"
show-field="name" show-field="name"
value-field="id" value-field="id"

View File

@ -34,7 +34,7 @@
<vn-autocomplete vn-one required="true" <vn-autocomplete vn-one required="true"
label="Warehouse" label="Warehouse"
ng-model="$ctrl.dms.warehouseId" ng-model="$ctrl.dms.warehouseId"
data="Warehouses" data="warehouses"
show-field="name" show-field="name"
value-field="id"> value-field="id">
</vn-autocomplete> </vn-autocomplete>

View File

@ -107,7 +107,7 @@
vn-one vn-one
label="Warehouse" label="Warehouse"
ng-model="filter.warehouseFk" ng-model="filter.warehouseFk"
data="Warehouses"> data="warehouses">
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete <vn-autocomplete
vn-one vn-one

View File

@ -14,6 +14,9 @@
"TravelThermograph": { "TravelThermograph": {
"dataSource": "vn" "dataSource": "vn"
}, },
"TravelConfig": {
"dataSource": "vn"
},
"Temperature": { "Temperature": {
"dataSource": "vn" "dataSource": "vn"
} }

View File

@ -0,0 +1,49 @@
{
"name": "TravelConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "travelConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true
},
"warehouseInFk": {
"type": "number"
},
"warehouseOutFk": {
"type": "number"
},
"agencyFk": {
"type": "number"
},
"companyFk": {
"type": "number"
}
},
"relations": {
"warehouseIn": {
"type": "belongsTo",
"model": "Warehouse",
"foreignKey": "warehouseInFk"
},
"warehouseOut": {
"type": "belongsTo",
"model": "Warehouse",
"foreignKey": "warehouseOutFk"
},
"agency": {
"type": "belongsTo",
"model": "AgencyMode",
"foreignKey": "agencyFk"
},
"company": {
"type": "belongsTo",
"model": "Company",
"foreignKey": "companyFk"
}
}
}

View File

@ -57,14 +57,14 @@
<vn-autocomplete vn-one <vn-autocomplete vn-one
label="Warehouse Out" label="Warehouse Out"
ng-model="filter.warehouseOutFk" ng-model="filter.warehouseOutFk"
data="Warehouses" data="warehouses"
show-field="name" show-field="name"
value-field="id"> value-field="id">
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete vn-one <vn-autocomplete vn-one
label="Warehouse In" label="Warehouse In"
ng-model="filter.warehouseInFk" ng-model="filter.warehouseInFk"
data="Warehouses" data="warehouses"
show-field="name" show-field="name"
value-field="id"> value-field="id">
</vn-autocomplete> </vn-autocomplete>

View File

@ -4,9 +4,20 @@ columns:
companyCodeFk: company companyCodeFk: company
started: started started: started
ended: ended ended: ended
workerBusiness: business
reasonEndFk: ending reason
payedHolidays: payed holidays payedHolidays: payed holidays
occupationCodeFk: occupation
workerFk: worker workerFk: worker
notes: notes
departmentFk: department
workerBusinessProfessionalCategoryFk: professional category
calendarTypeFk: calendar type calendarTypeFk: calendar type
isHourlyLabor: hourly labor isHourlyLabor: hourly labor
workcenterFk: workcenter workcenterFk: workcenter
rate: rate
workerBusinessCategoryFk: category
workerBusinessTypeFk: type
amount: amount amount: amount
workerBusinessAgreementFk: agreement
basicSalary: salary

View File

@ -4,9 +4,20 @@ columns:
companyCodeFk: empresa companyCodeFk: empresa
started: iniciado started: iniciado
ended: finalizado ended: finalizado
workerBusiness: negocio
reasonEndFk: motivo finalización
payedHolidays: vacaciones pagadas payedHolidays: vacaciones pagadas
occupationCodeFk: ocupación
workerFk: trabajador workerFk: trabajador
notes: notas
departmentFk: departamento
workerBusinessProfessionalCategoryFk: categoría profesional
calendarTypeFk: tipo calendario calendarTypeFk: tipo calendario
isHourlyLabor: horario laboral isHourlyLabor: horario laboral
workcenterFk: centro de trabajo workcenterFk: centro de trabajo
rate: tarifa
workerBusinessCategoryFk: categoría
workerBusinessTypeFk: tipo
amount: salario amount: salario
workerBusinessAgreementFk: acuerdo
basicSalary: salario base

View File

@ -1,6 +1,6 @@
name: document name: document
columns: columns:
id: id id: id
dmsFk: dms worker: worker
workerFk: worker document: document
isReadableByWorker: readable by worker isReadableByWorker: readable by worker

View File

@ -1,6 +1,6 @@
name: documento name: documento
columns: columns:
id: id id: id
dmsFk: dms worker: trabajador
workerFk: trabajador document: documento
isReadableByWorker: legible por trabajador isReadableByWorker: accesible por trabajador

View File

@ -1,20 +1,32 @@
name: worker name: worker
columns: columns:
id: id id: id
code: code
firstName: first name firstName: first name
lastName: last name lastName: last name
sub: buyer number
photo: photo
phone: phone phone: phone
mobileExtension: mobile extension
userFk: user userFk: user
bossFk: boss bossFk: boss
fiDueDate: FI due date
hasMachineryAuthorized: machinery authorized
seniority: seniority
isTodayRelative: today relative
isF11Allowed: F11 allowed
sectorFk: sector
maritalStatus: marital status maritalStatus: marital status
labelerFk: labeler
originCountryFk: origin country originCountryFk: origin country
educationLevelFk: education level educationLevelFk: education level
SSN: SSN SSN: SSN
labelerFk: labeler fi: fiscal identifier
mobileExtension: mobile extension birth: birth date
code: code isDisable: disabled
isFreelance: freelance
isSsDiscounted: SS discounted
sex: sex
businessFk: current contract
balance: balance
locker: locker locker: locker
workerFk: worker
sectorFk: sector

View File

@ -1,20 +1,32 @@
name: trabajador name: trabajador
columns: columns:
id: id id: id
code: código
firstName: nombre firstName: nombre
lastName: apellido lastName: apellido
sub: número comprador
photo: foto
phone: teléfono phone: teléfono
mobileExtension: extensión móvil
userFk: usuario userFk: usuario
bossFk: jefe bossFk: jefe
fiDueDate: caducidad DNI
hasMachineryAuthorized: maquinaria autorizada
seniority: antigüedad
isTodayRelative: relativo hoy
isF11Allowed: F11 autorizado
sectorFk: sector
maritalStatus: estado civil maritalStatus: estado civil
labelerFk: etiquetadora
originCountryFk: país origen originCountryFk: país origen
educationLevelFk: nivel educativo educationLevelFk: nivel educativo
SSN: SSN SSN: SSN
labelerFk: etiquetadora fi: identificador fiscal
mobileExtension: extensión móvil birth: nacimiento
code: código isDisable: deshabilitado
locker: casillero isFreelance: autónomo
workerFk: trabajador isSsDiscounted: descuento SS
sectorFk: sector sex: sexo
businessFk: contrato actual
balance: saldo
locker: taquilla

View File

@ -230,21 +230,21 @@ module.exports = Self => {
}, myOptions); }, myOptions);
} else { } else {
const weekDay = day.dated.getDay(); const weekDay = day.dated.getDay();
const journeys = await models.Journey.find({ const journeys = await models.BusinessSchedule.find({
where: { where: {
business_id: day.businessFk, businessFk: day.businessFk,
day_id: weekDay weekday: weekDay
} }
}, myOptions); }, myOptions);
let timeTableDecimalInSeconds = 0; let timeTableDecimalInSeconds = 0;
for (let journey of journeys) { for (let journey of journeys) {
const start = Date.vnNew(); const start = Date.vnNew();
const [startHours, startMinutes, startSeconds] = getTime(journey.start); const [startHours, startMinutes, startSeconds] = getTime(journey.started);
start.setHours(startHours, startMinutes, startSeconds, 0); start.setHours(startHours, startMinutes, startSeconds, 0);
const end = Date.vnNew(); const end = Date.vnNew();
const [endHours, endMinutes, endSeconds] = getTime(journey.end); const [endHours, endMinutes, endSeconds] = getTime(journey.ended);
end.setHours(endHours, endMinutes, endSeconds, 0); end.setHours(endHours, endMinutes, endSeconds, 0);
const result = (end - start) / 1000; const result = (end - start) / 1000;
@ -255,7 +255,7 @@ module.exports = Self => {
const timeTableDecimal = timeTableDecimalInSeconds / 3600; const timeTableDecimal = timeTableDecimalInSeconds / 3600;
if (day.timeWorkDecimal == timeTableDecimal) { if (day.timeWorkDecimal == timeTableDecimal) {
const timed = new Date(day.dated); const timed = new Date(day.dated);
const [startHours, startMinutes, startSeconds] = getTime(journey.start); const [startHours, startMinutes, startSeconds] = getTime(journey.started);
await models.WorkerTimeControl.create({ await models.WorkerTimeControl.create({
userFk: day.workerFk, userFk: day.workerFk,
timed: timed.setHours(startHours, startMinutes, startSeconds), timed: timed.setHours(startHours, startMinutes, startSeconds),
@ -263,7 +263,7 @@ module.exports = Self => {
isSendMail: true isSendMail: true
}, myOptions); }, myOptions);
const [endHours, endMinutes, endSeconds] = getTime(journey.end); const [endHours, endMinutes, endSeconds] = getTime(journey.ended);
await models.WorkerTimeControl.create({ await models.WorkerTimeControl.create({
userFk: day.workerFk, userFk: day.workerFk,
timed: timed.setHours(endHours, endMinutes, endSeconds), timed: timed.setHours(endHours, endMinutes, endSeconds),
@ -276,7 +276,7 @@ module.exports = Self => {
}); });
if (journey == minStart) { if (journey == minStart) {
const timed = new Date(day.dated); const timed = new Date(day.dated);
const [startHours, startMinutes, startSeconds] = getTime(journey.start); const [startHours, startMinutes, startSeconds] = getTime(journey.started);
await models.WorkerTimeControl.create({ await models.WorkerTimeControl.create({
userFk: day.workerFk, userFk: day.workerFk,
timed: timed.setHours(startHours, startMinutes, startSeconds), timed: timed.setHours(startHours, startMinutes, startSeconds),
@ -304,7 +304,7 @@ module.exports = Self => {
}); });
if (journey == minStart) { if (journey == minStart) {
const timed = new Date(day.dated); const timed = new Date(day.dated);
const [startHours, startMinutes, startSeconds] = getTime(journey.start); const [startHours, startMinutes, startSeconds] = getTime(journey.started);
await models.WorkerTimeControl.create({ await models.WorkerTimeControl.create({
userFk: day.workerFk, userFk: day.workerFk,
timed: timed.setHours(startHours + 1, startMinutes, startSeconds), timed: timed.setHours(startHours + 1, startMinutes, startSeconds),

View File

@ -2,6 +2,9 @@
"AbsenceType": { "AbsenceType": {
"dataSource": "vn" "dataSource": "vn"
}, },
"BusinessSchedule": {
"dataSource": "vn"
},
"Calendar": { "Calendar": {
"dataSource": "vn" "dataSource": "vn"
}, },
@ -38,9 +41,6 @@
"EducationLevel": { "EducationLevel": {
"dataSource": "vn" "dataSource": "vn"
}, },
"Journey": {
"dataSource": "vn"
},
"ProfileType":{ "ProfileType":{
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -1,26 +1,26 @@
{ {
"name": "Journey", "name": "BusinessSchedule",
"base": "VnModel", "base": "VnModel",
"options": { "options": {
"mysql": { "mysql": {
"table": "postgresql.journey" "table": "businessSchedule"
} }
}, },
"properties": { "properties": {
"journey_id": { "id": {
"id": true, "id": true,
"type": "number" "type": "number"
}, },
"day_id": { "weekday": {
"type": "number" "type": "number"
}, },
"start": { "started": {
"type": "date" "type": "date"
}, },
"end": { "ended": {
"type": "date" "type": "date"
}, },
"business_id": { "businessFk": {
"type": "number" "type": "number"
} }
} }

View File

@ -35,7 +35,7 @@
<vn-autocomplete vn-one <vn-autocomplete vn-one
label="Warehouse" label="Warehouse"
ng-model="$ctrl.dms.warehouseId" ng-model="$ctrl.dms.warehouseId"
url="Warehouses" data="warehouses"
show-field="name" show-field="name"
value-field="id"> value-field="id">
</vn-autocomplete> </vn-autocomplete>

View File

@ -34,7 +34,7 @@
<vn-autocomplete vn-one required="true" <vn-autocomplete vn-one required="true"
label="Warehouse" label="Warehouse"
ng-model="$ctrl.dms.warehouseId" ng-model="$ctrl.dms.warehouseId"
data="Warehouses" data="warehouses"
show-field="name" show-field="name"
value-field="id"> value-field="id">
</vn-autocomplete> </vn-autocomplete>

View File

@ -72,7 +72,7 @@
"state": "worker.card.workerLog", "state": "worker.card.workerLog",
"component": "vn-worker-log", "component": "vn-worker-log",
"description": "Log", "description": "Log",
"acl": ["salesAssistant"] "acl": ["hr"]
}, { }, {
"url": "/note", "url": "/note",
"state": "worker.card.note", "state": "worker.card.note",

24871
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
"url": "https://gitea.verdnatura.es/verdnatura/salix" "url": "https://gitea.verdnatura.es/verdnatura/salix"
}, },
"engines": { "engines": {
"node": ">=14" "node": ">=20"
}, },
"dependencies": { "dependencies": {
"axios": "^1.2.2", "axios": "^1.2.2",
@ -33,19 +33,20 @@
"loopback-boot": "3.3.1", "loopback-boot": "3.3.1",
"loopback-component-explorer": "^6.5.0", "loopback-component-explorer": "^6.5.0",
"loopback-component-storage": "3.6.1", "loopback-component-storage": "3.6.1",
"loopback-connector-mysql": "^5.4.3", "loopback-connector-mysql": "^6.2.0",
"loopback-connector-remote": "^3.4.1", "loopback-connector-remote": "^3.4.1",
"loopback-context": "^3.4.0", "loopback-context": "^3.5.2",
"mailparser": "^2.8.0", "mailparser": "^2.8.0",
"md5": "^2.2.1", "md5": "^2.2.1",
"node-ssh": "^11.0.0", "node-ssh": "^11.0.0",
"object-diff": "0.0.4", "object-diff": "0.0.4",
"object.pick": "^1.3.0", "object.pick": "^1.3.0",
"puppeteer": "^18.0.5", "puppeteer": "^20.3.0",
"read-chunk": "^3.2.0", "read-chunk": "^3.2.0",
"require-yaml": "0.0.1", "require-yaml": "0.0.1",
"smbhash": "0.0.1", "smbhash": "0.0.1",
"strong-error-handler": "^2.3.2", "strong-error-handler": "^2.3.2",
"url-loader": "^4.1.1",
"uuid": "^3.3.3", "uuid": "^3.3.3",
"vn-loopback": "file:./loopback", "vn-loopback": "file:./loopback",
"vn-print": "file:./print", "vn-print": "file:./print",
@ -58,15 +59,15 @@
"@babel/register": "^7.7.7", "@babel/register": "^7.7.7",
"angular-mocks": "^1.7.9", "angular-mocks": "^1.7.9",
"babel-jest": "^26.0.1", "babel-jest": "^26.0.1",
"babel-loader": "^8.0.6", "babel-loader": "^8.2.4",
"core-js": "^3.9.1", "core-js": "^3.30.1",
"css-loader": "^2.1.0", "css-loader": "^6.7.4",
"del": "^2.2.2", "del": "^2.2.2",
"eslint": "^7.11.0", "eslint": "^7.11.0",
"eslint-config-google": "^0.11.0", "eslint-config-google": "^0.11.0",
"eslint-plugin-jasmine": "^2.10.1", "eslint-plugin-jasmine": "^2.10.1",
"fancy-log": "^1.3.2", "fancy-log": "^1.3.2",
"file-loader": "^1.1.11", "file-loader": "^6.2.0",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-concat": "^2.6.1", "gulp-concat": "^2.6.1",
"gulp-env": "^0.4.0", "gulp-env": "^0.4.0",
@ -79,9 +80,9 @@
"gulp-yaml": "^1.0.1", "gulp-yaml": "^1.0.1",
"html-loader": "^0.4.5", "html-loader": "^0.4.5",
"html-loader-jest": "^0.2.1", "html-loader-jest": "^0.2.1",
"html-webpack-plugin": "^4.0.0-beta.11", "html-webpack-plugin": "^5.5.1",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"jasmine": "^4.5.0", "jasmine": "^5.0.0",
"jasmine-reporters": "^2.4.0", "jasmine-reporters": "^2.4.0",
"jasmine-spec-reporter": "^7.0.0", "jasmine-spec-reporter": "^7.0.0",
"jest": "^26.0.1", "jest": "^26.0.1",
@ -90,15 +91,16 @@
"merge-stream": "^1.0.1", "merge-stream": "^1.0.1",
"minimist": "^1.2.5", "minimist": "^1.2.5",
"mysql2": "^1.7.0", "mysql2": "^1.7.0",
"node-sass": "^4.14.1", "node-sass": "^9.0.0",
"nodemon": "^2.0.16", "nodemon": "^2.0.16",
"plugin-error": "^1.0.1", "plugin-error": "^1.0.1",
"raw-loader": "^1.0.0", "raw-loader": "^4.0.2",
"regenerator-runtime": "^0.13.7", "regenerator-runtime": "^0.13.7",
"sass-loader": "^7.3.1", "sass": "^1.62.1",
"style-loader": "^0.23.1", "sass-loader": "^13.3.0",
"webpack": "^4.41.5", "style-loader": "^3.3.3",
"webpack-cli": "^3.3.10", "webpack": "^5.83.1",
"webpack-cli": "^5.1.1",
"webpack-dev-server": "^3.11.0", "webpack-dev-server": "^3.11.0",
"webpack-merge": "^4.2.2", "webpack-merge": "^4.2.2",
"yaml-loader": "^0.5.0" "yaml-loader": "^0.5.0"

View File

@ -9,7 +9,7 @@ module.exports = {
concurrency: Cluster.CONCURRENCY_CONTEXT, concurrency: Cluster.CONCURRENCY_CONTEXT,
maxConcurrency: cpus().length, maxConcurrency: cpus().length,
puppeteerOptions: { puppeteerOptions: {
headless: true, headless: 'new',
args: [ args: [
'--no-sandbox', '--no-sandbox',
'--disable-setuid-sandbox', '--disable-setuid-sandbox',

View File

@ -2,7 +2,6 @@
"name": "vn-print", "name": "vn-print",
"version": "2.0.0", "version": "2.0.0",
"description": "Print service", "description": "Print service",
"main": "server/server.js",
"scripts": { "scripts": {
"start": "node server/server.js", "start": "node server/server.js",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"

View File

@ -20,13 +20,13 @@ let baseConfig = {
test: /\.js$/, test: /\.js$/,
loader: 'babel-loader', loader: 'babel-loader',
exclude: /node_modules/, exclude: /node_modules/,
query: { options: {
presets: ['@babel/preset-env'], presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-syntax-dynamic-import'] plugins: ['@babel/plugin-syntax-dynamic-import']
} }
}, { }, {
test: /\.yml$/, test: /\.yml$/,
loader: 'json-loader!yaml-loader' use: ['json-loader!yaml-loader']
}, { }, {
test: /\.html$/, test: /\.html$/,
loader: 'html-loader', loader: 'html-loader',
@ -38,39 +38,34 @@ let baseConfig = {
} }
}, { }, {
test: /\.css$/, test: /\.css$/,
use: [ use: ['style-loader', 'css-loader']
{
loader: 'style-loader'
}, {
loader: 'css-loader'
}
]
}, { }, {
test: /\.scss$/, test: /\.scss$/,
use: [ use: [
{ 'style-loader', 'css-loader', {
loader: 'style-loader'
}, {
loader: 'css-loader'
}, {
loader: 'sass-loader', loader: 'sass-loader',
options: { options: {
// XXX: Don't work in Firefox // XXX: Don't work in Firefox
// https://github.com/webpack-contrib/style-loader/issues/303 // https://github.com/webpack-contrib/style-loader/issues/303
// sourceMap: true, // sourceMap: true,
includePaths: [ sassOptions: {
path.resolve(__dirname, 'front/core/styles') includePaths: [
] path.resolve(__dirname, 'front/core/styles/')
]
}
} }
} }
] ]
}, { }, {
test: /\.(svg|png|ttf|woff|woff2)$/, test: /\.(woff(2)?|ttf|eot|svg|png)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file-loader' type: 'asset/resource',
}, { }, {
test: /manifest\.json$/, test: /manifest\.json$/,
type: 'javascript/auto', type: 'javascript/auto',
loader: 'file-loader' loader: 'file-loader',
options: {
esModule: false,
}
} }
] ]
}, },
@ -105,7 +100,7 @@ let baseConfig = {
chunks: ['salix'] chunks: ['salix']
}), }),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) 'process.env.NODE_ENV': JSON.stringify(env)
}) })
], ],
devtool: 'source-map', devtool: 'source-map',
@ -124,7 +119,7 @@ let prodConfig = {
chunkFilename: '[id].[chunkhash].js' chunkFilename: '[id].[chunkhash].js'
}, },
plugins: [ plugins: [
new webpack.HashedModuleIdsPlugin() new webpack.ids.HashedModuleIdsPlugin()
], ],
performance: { performance: {
maxEntrypointSize: 2000000, maxEntrypointSize: 2000000,