5244-component_workerAutocomplete #1679
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -5,14 +5,23 @@ 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).
|
||||||
|
|
||||||
|
## [2326.01] - 2023-06-29
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
-
|
||||||
|
|
||||||
## [2324.01] - 2023-06-08
|
## [2324.01] - 2023-06-08
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
-
|
- (Tickets -> Abono) Al abonar permite crear el ticket abono con almacén o sin almmacén
|
||||||
|
- (General -> Desplegables) Mejorada eficiencia de carga de datos
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
-
|
- (General -> Permisos) Mejorada seguridad
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
-
|
-
|
||||||
|
@ -24,13 +33,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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@ RUN apt-get update \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
gnupg2 \
|
gnupg2 \
|
||||||
graphicsmagick \
|
graphicsmagick \
|
||||||
&& curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
|
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||||
&& apt-get install -y --no-install-recommends nodejs \
|
&& apt-get install -y --no-install-recommends nodejs \
|
||||||
&& npm install -g npm@8.19.2
|
&& npm install -g npm@9.6.6
|
||||||
|
|
||||||
# Puppeteer
|
# Puppeteer
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ pipeline {
|
||||||
NODE_ENV = ""
|
NODE_ENV = ""
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
nodejs('node-v14') {
|
nodejs('node-v20') {
|
||||||
sh 'npm install --no-audit --prefer-offline'
|
sh 'npm install --no-audit --prefer-offline'
|
||||||
sh 'gulp install --ci'
|
sh 'gulp install --ci'
|
||||||
}
|
}
|
||||||
|
@ -57,14 +57,14 @@ pipeline {
|
||||||
parallel {
|
parallel {
|
||||||
stage('Frontend') {
|
stage('Frontend') {
|
||||||
steps {
|
steps {
|
||||||
nodejs('node-v14') {
|
nodejs('node-v20') {
|
||||||
sh 'jest --ci --reporters=default --reporters=jest-junit --maxWorkers=2'
|
sh 'jest --ci --reporters=default --reporters=jest-junit --maxWorkers=2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Backend') {
|
stage('Backend') {
|
||||||
steps {
|
steps {
|
||||||
nodejs('node-v14') {
|
nodejs('node-v20') {
|
||||||
sh 'npm run test:back:ci'
|
sh 'npm run test:back:ci'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ pipeline {
|
||||||
CREDENTIALS = credentials('docker-registry')
|
CREDENTIALS = credentials('docker-registry')
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
nodejs('node-v14') {
|
nodejs('node-v20') {
|
||||||
sh 'gulp build'
|
sh 'gulp build'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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');
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE `vn`.`wagon` ADD CONSTRAINT `wagon_type` FOREIGN KEY (`typeFk`) REFERENCES `wagonType` (`id`) ON UPDATE CASCADE;
|
|
@ -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;
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.filter;
|
||||||
|
CREATE 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.classColor futureClassColor,
|
||||||
|
dest.classColor
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
s.ticketFk,
|
||||||
|
t.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,
|
||||||
|
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
|
||||||
|
LEFT JOIN tmp.stock st ON st.itemFk = i.id
|
||||||
|
WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture)
|
||||||
|
AND t.warehouseFk = vWarehouseFk
|
||||||
|
GROUP BY t.id
|
||||||
|
) origin
|
||||||
|
JOIN (
|
||||||
|
SELECT
|
||||||
|
t.id ticketFk,
|
||||||
|
t.addressFk,
|
||||||
|
st.name state,
|
||||||
|
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) ipt,
|
||||||
|
t.shipped,
|
||||||
|
t.totalWithVat,
|
||||||
|
am.name agency,
|
||||||
|
CAST(SUM(litros) AS DECIMAL(10,0)) liters,
|
||||||
|
CAST(COUNT(*) AS DECIMAL(10,0)) `lines`,
|
||||||
|
st.classColor
|
||||||
|
FROM ticket t
|
||||||
|
JOIN sale s ON s.ticketFk = t.id
|
||||||
|
JOIN saleVolume sv ON sv.saleFk = s.id
|
||||||
|
JOIN item i ON i.id = s.itemFk
|
||||||
|
JOIN ticketState ts ON ts.ticketFk = t.id
|
||||||
|
JOIN state st ON st.id = ts.stateFk
|
||||||
|
JOIN agencyMode am ON t.agencyModeFk = am.id
|
||||||
|
LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
|
||||||
|
WHERE t.shipped BETWEEN vDateToAdvance AND util.dayend(vDateToAdvance)
|
||||||
|
AND t.warehouseFk = vWarehouseFk
|
||||||
|
AND st.order <= 5
|
||||||
|
GROUP BY t.id
|
||||||
|
) dest ON dest.addressFk = origin.addressFk
|
||||||
|
WHERE origin.hasStock != 0;
|
||||||
|
DROP TEMPORARY TABLE tmp.stock;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,74 @@
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canbePostponed`(vOriginDated DATE, vFutureDated DATE, vWarehouseFk INT)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Devuelve un listado de tickets susceptibles de fusionarse con otros tickets en el futuro
|
||||||
|
*
|
||||||
|
* @param vOriginDated Fecha en cuestión
|
||||||
|
* @param vFutureDated Fecha en el futuro a sondear
|
||||||
|
* @param vWarehouseFk Identificador de vn.warehouse
|
||||||
|
*/
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.filter;
|
||||||
|
CREATE TEMPORARY TABLE tmp.filter
|
||||||
|
(INDEX (id))
|
||||||
|
SELECT sv.ticketFk id,
|
||||||
|
sub2.id futureId,
|
||||||
|
GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) ipt,
|
||||||
|
CAST(sum(litros) AS DECIMAL(10,0)) liters,
|
||||||
|
CAST(count(*) AS DECIMAL(10,0)) `lines`,
|
||||||
|
st.name state,
|
||||||
|
sub2.iptd futureIpt,
|
||||||
|
sub2.state futureState,
|
||||||
|
t.clientFk,
|
||||||
|
t.warehouseFk,
|
||||||
|
ts.alertLevel,
|
||||||
|
t.shipped,
|
||||||
|
sub2.shipped futureShipped,
|
||||||
|
t.workerFk,
|
||||||
|
st.code stateCode,
|
||||||
|
sub2.code futureStateCode,
|
||||||
|
st.classColor,
|
||||||
|
sub2.classColor futureClassColor
|
||||||
|
FROM vn.saleVolume sv
|
||||||
|
JOIN vn.sale s ON s.id = sv.saleFk
|
||||||
|
JOIN vn.item i ON i.id = s.itemFk
|
||||||
|
JOIN vn.ticket t ON t.id = sv.ticketFk
|
||||||
|
JOIN vn.address a ON a.id = t.addressFk
|
||||||
|
JOIN vn.province p ON p.id = a.provinceFk
|
||||||
|
JOIN vn.country c ON c.id = p.countryFk
|
||||||
|
JOIN vn.ticketState ts ON ts.ticketFk = t.id
|
||||||
|
JOIN vn.state st ON st.id = ts.stateFk
|
||||||
|
JOIN vn.alertLevel al ON al.id = ts.alertLevel
|
||||||
|
LEFT JOIN vn.ticketParking tp ON tp.ticketFk = t.id
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT *
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
t.addressFk,
|
||||||
|
t.id,
|
||||||
|
t.shipped,
|
||||||
|
st.name state,
|
||||||
|
st.code,
|
||||||
|
st.classColor,
|
||||||
|
GROUP_CONCAT(DISTINCT i.itemPackingTypeFk ORDER BY i.itemPackingTypeFk) iptd
|
||||||
|
FROM vn.ticket t
|
||||||
|
JOIN vn.ticketState ts ON ts.ticketFk = t.id
|
||||||
|
JOIN vn.state st ON st.id = ts.stateFk
|
||||||
|
JOIN vn.sale s ON s.ticketFk = t.id
|
||||||
|
JOIN vn.item i ON i.id = s.itemFk
|
||||||
|
WHERE t.shipped BETWEEN vFutureDated
|
||||||
|
AND util.dayend(vFutureDated)
|
||||||
|
AND t.warehouseFk = vWarehouseFk
|
||||||
|
GROUP BY t.id
|
||||||
|
) sub
|
||||||
|
GROUP BY sub.addressFk
|
||||||
|
) sub2 ON sub2.addressFk = t.addressFk AND t.id != sub2.id
|
||||||
|
WHERE t.shipped BETWEEN vOriginDated AND util.dayend(vOriginDated)
|
||||||
|
AND t.warehouseFk = vWarehouseFk
|
||||||
|
AND al.code = 'FREE'
|
||||||
|
AND tp.ticketFk IS NULL
|
||||||
|
GROUP BY sv.ticketFk
|
||||||
|
HAVING futureId;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -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');
|
|
@ -0,0 +1,3 @@
|
||||||
|
ALTER TABLE `vn`.`sector` DROP COLUMN `printerFk`;
|
||||||
|
ALTER TABLE `vn`.`sector` ADD COLUMN `mainPrinterFk` tinyint(3) unsigned;
|
||||||
|
ALTER TABLE `vn`.`sector` ADD CONSTRAINT sector_FK_1 FOREIGN KEY (mainPrinterFk) REFERENCES vn.printer(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE `vn`.`ticket` MODIFY COLUMN warehouseFk smallint(6) unsigned DEFAULT NULL NULL;
|
|
@ -179,6 +179,8 @@ INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`, `ipAd
|
||||||
(2, 'printer2', 'path2', 1, 1 , NULL),
|
(2, 'printer2', 'path2', 1, 1 , NULL),
|
||||||
(4, 'printer4', 'path4', 0, NULL, '10.1.10.4');
|
(4, 'printer4', 'path4', 0, NULL, '10.1.10.4');
|
||||||
|
|
||||||
|
UPDATE `vn`.`sector` SET mainPrinterFk = 1 WHERE id = 1;
|
||||||
|
|
||||||
INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossFk`, `phone`, `sectorFk`, `labelerFk`)
|
INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossFk`, `phone`, `sectorFk`, `labelerFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1106, 'LGN', 'David Charles', 'Haller', 1106, 19, 432978106, NULL, NULL),
|
(1106, 'LGN', 'David Charles', 'Haller', 1106, 19, 432978106, NULL, NULL),
|
||||||
|
@ -905,7 +907,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),
|
||||||
|
@ -2729,6 +2731,7 @@ INSERT INTO `util`.`notification` (`id`, `name`, `description`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'print-email', 'notification fixture one'),
|
(1, 'print-email', 'notification fixture one'),
|
||||||
(2, 'invoice-electronic', 'A electronic invoice has been generated'),
|
(2, 'invoice-electronic', 'A electronic invoice has been generated'),
|
||||||
|
(3, 'not-main-printer-configured', 'A printer distinct than main has been configured'),
|
||||||
(4, 'supplier-pay-method-update', 'A supplier pay method has been updated');
|
(4, 'supplier-pay-method-update', 'A supplier pay method has been updated');
|
||||||
|
|
||||||
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
|
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
|
||||||
|
@ -2886,6 +2889,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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -595,6 +595,8 @@ export default {
|
||||||
moreMenuUpdateDiscount: 'vn-item[name="discount"]',
|
moreMenuUpdateDiscount: 'vn-item[name="discount"]',
|
||||||
moreMenuRecalculatePrice: 'vn-item[name="calculatePrice"]',
|
moreMenuRecalculatePrice: 'vn-item[name="calculatePrice"]',
|
||||||
moreMenuRefund: 'vn-item[name="refund"]',
|
moreMenuRefund: 'vn-item[name="refund"]',
|
||||||
|
refundWithWarehouse: 'vn-item[name="refundWithWarehouse"]',
|
||||||
|
refundWithoutWarehouse: 'vn-item[name="refundWithoutWarehouse"]',
|
||||||
moreMenuUpdateDiscountInput: 'vn-input-number[ng-model="$ctrl.edit.discount"] input',
|
moreMenuUpdateDiscountInput: 'vn-input-number[ng-model="$ctrl.edit.discount"] input',
|
||||||
transferQuantityInput: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable > span > text',
|
transferQuantityInput: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable > span > text',
|
||||||
transferQuantityCell: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable',
|
transferQuantityCell: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable',
|
||||||
|
|
|
@ -64,6 +64,6 @@ describe('SmartTable SearchBar integration', () => {
|
||||||
await page.reload({
|
await page.reload({
|
||||||
waitUntil: 'networkidle2'
|
waitUntil: 'networkidle2'
|
||||||
});
|
});
|
||||||
await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '13');
|
await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '3');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -88,7 +88,8 @@ describe('Item fixed prices path', () => {
|
||||||
|
|
||||||
it('should reload the section and check the created price has the expected ID', async() => {
|
it('should reload the section and check the created price has the expected ID', async() => {
|
||||||
await page.goto(`http://localhost:5000/#!/item/fixed-price`);
|
await page.goto(`http://localhost:5000/#!/item/fixed-price`);
|
||||||
|
await page.autocompleteSearch($.warehouseFilter, 'Warehouse one');
|
||||||
|
await page.click($.chip);
|
||||||
const result = await page.waitToGetProperty($.fourthItemID, 'value');
|
const result = await page.waitToGetProperty($.fourthItemID, 'value');
|
||||||
|
|
||||||
expect(result).toContain('13');
|
expect(result).toContain('13');
|
||||||
|
|
|
@ -220,14 +220,25 @@ describe('Ticket Edit sale path', () => {
|
||||||
|
|
||||||
it('should log in as salesAssistant and navigate to ticket sales', async() => {
|
it('should log in as salesAssistant and navigate to ticket sales', async() => {
|
||||||
await page.loginAndModule('salesAssistant', 'ticket');
|
await page.loginAndModule('salesAssistant', 'ticket');
|
||||||
await page.accessToSearchResult('16');
|
await page.accessToSearchResult('17');
|
||||||
await page.accessToSection('ticket.card.sale');
|
await page.accessToSection('ticket.card.sale');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select the third sale and create a refund', async() => {
|
it('should select the first sale and create a refund with warehouse', async() => {
|
||||||
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
|
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
|
||||||
await page.waitToClick(selectors.ticketSales.moreMenu);
|
await page.waitToClick(selectors.ticketSales.moreMenu);
|
||||||
await page.waitToClick(selectors.ticketSales.moreMenuRefund);
|
await page.waitToClick(selectors.ticketSales.moreMenuRefund);
|
||||||
|
await page.waitToClick(selectors.ticketSales.refundWithWarehouse);
|
||||||
|
await page.waitForSnackbar();
|
||||||
|
await page.waitForState('ticket.card.sale');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select the first sale and create a refund without warehouse', async() => {
|
||||||
|
await page.accessToSearchResult('18');
|
||||||
|
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
|
||||||
|
await page.waitToClick(selectors.ticketSales.moreMenu);
|
||||||
|
await page.waitToClick(selectors.ticketSales.moreMenuRefund);
|
||||||
|
await page.waitToClick(selectors.ticketSales.refundWithoutWarehouse);
|
||||||
await page.waitForSnackbar();
|
await page.waitForSnackbar();
|
||||||
await page.waitForState('ticket.card.sale');
|
await page.waitForState('ticket.card.sale');
|
||||||
});
|
});
|
||||||
|
@ -246,7 +257,6 @@ describe('Ticket Edit sale path', () => {
|
||||||
it('should select the third sale and create a claim of it', async() => {
|
it('should select the third sale and create a claim of it', async() => {
|
||||||
await page.accessToSearchResult('16');
|
await page.accessToSearchResult('16');
|
||||||
await page.accessToSection('ticket.card.sale');
|
await page.accessToSection('ticket.card.sale');
|
||||||
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
|
|
||||||
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
|
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
|
||||||
await page.waitToClick(selectors.ticketSales.moreMenu);
|
await page.waitToClick(selectors.ticketSales.moreMenu);
|
||||||
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
|
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
|
||||||
|
@ -316,7 +326,7 @@ describe('Ticket Edit sale path', () => {
|
||||||
it('should confirm the transfered quantity is the correct one', async() => {
|
it('should confirm the transfered quantity is the correct one', async() => {
|
||||||
const result = await page.waitToGetProperty(selectors.ticketSales.secondSaleQuantityCell, 'innerText');
|
const result = await page.waitToGetProperty(selectors.ticketSales.secondSaleQuantityCell, 'innerText');
|
||||||
|
|
||||||
expect(result).toContain('10');
|
expect(result).toContain('20');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should go back to the original ticket sales section', async() => {
|
it('should go back to the original ticket sales section', async() => {
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import selectors from '../../helpers/selectors.js';
|
import selectors from '../../helpers/selectors.js';
|
||||||
import getBrowser from '../../helpers/puppeteer';
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
// 'https:// redmine.verdnatura.es/issues/5642'
|
describe('Ticket Future path', () => {
|
||||||
xdescribe('Ticket Future path', () => {
|
|
||||||
let browser;
|
let browser;
|
||||||
let page;
|
let page;
|
||||||
let httpRequest;
|
let httpRequest;
|
||||||
|
@ -22,7 +21,7 @@ xdescribe('Ticket Future path', () => {
|
||||||
await browser.close();
|
await browser.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show errors snackbar because of the required data', async() => {
|
it('should search with required data, check three last tickets and move to the future', async() => {
|
||||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
await page.clearInput(selectors.ticketFuture.warehouseFk);
|
await page.clearInput(selectors.ticketFuture.warehouseFk);
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
|
@ -43,69 +42,58 @@ xdescribe('Ticket Future path', () => {
|
||||||
message = await page.waitForSnackbar();
|
message = await page.waitForSnackbar();
|
||||||
|
|
||||||
expect(message.text).toContain('originDated is a required argument');
|
expect(message.text).toContain('originDated is a required argument');
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
|
|
||||||
|
expect(httpRequest).toBeDefined();
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
|
|
||||||
|
await page.autocompleteSearch(selectors.ticketFuture.ipt, 'H');
|
||||||
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('ipt=H');
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
|
|
||||||
|
await page.clearInput(selectors.ticketFuture.ipt);
|
||||||
|
|
||||||
|
await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'H');
|
||||||
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('futureIpt=H');
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
|
|
||||||
|
await page.clearInput(selectors.ticketFuture.futureIpt);
|
||||||
|
|
||||||
|
await page.autocompleteSearch(selectors.ticketFuture.state, 'Free');
|
||||||
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('state=FREE');
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
|
|
||||||
|
await page.clearInput(selectors.ticketFuture.state);
|
||||||
|
|
||||||
|
await page.autocompleteSearch(selectors.ticketFuture.futureState, 'Free');
|
||||||
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('futureState=FREE');
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||||
|
await page.clearInput(selectors.ticketFuture.state);
|
||||||
|
await page.clearInput(selectors.ticketFuture.futureState);
|
||||||
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
|
|
||||||
|
await page.waitForNumberOfElements(selectors.ticketFuture.searchResult, 4);
|
||||||
|
await page.waitToClick(selectors.ticketFuture.multiCheck);
|
||||||
|
await page.waitToClick(selectors.ticketFuture.firstCheck);
|
||||||
|
await page.waitToClick(selectors.ticketFuture.moveButton);
|
||||||
|
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||||
|
message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.text).toContain('Tickets moved successfully!');
|
||||||
});
|
});
|
||||||
|
|
||||||
// it('should search with the required data', async() => {
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.submit);
|
|
||||||
|
|
||||||
// expect(httpRequest).toBeDefined();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should search with the origin IPT', async() => {
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
|
||||||
|
|
||||||
// await page.autocompleteSearch(selectors.ticketFuture.ipt, 'H');
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.submit);
|
|
||||||
|
|
||||||
// expect(httpRequest).toContain('ipt=H');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should search with the destination IPT', async() => {
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
|
||||||
|
|
||||||
// await page.clearInput(selectors.ticketFuture.ipt);
|
|
||||||
|
|
||||||
// await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'H');
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.submit);
|
|
||||||
|
|
||||||
// expect(httpRequest).toContain('futureIpt=H');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should search with the origin grouped state', async() => {
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
|
||||||
|
|
||||||
// await page.clearInput(selectors.ticketFuture.futureIpt);
|
|
||||||
|
|
||||||
// await page.autocompleteSearch(selectors.ticketFuture.state, 'Free');
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.submit);
|
|
||||||
|
|
||||||
// expect(httpRequest).toContain('state=FREE');
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should search with the destination grouped state', async() => {
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
|
||||||
|
|
||||||
// await page.clearInput(selectors.ticketFuture.state);
|
|
||||||
|
|
||||||
// await page.autocompleteSearch(selectors.ticketFuture.futureState, 'Free');
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.submit);
|
|
||||||
|
|
||||||
// expect(httpRequest).toContain('futureState=FREE');
|
|
||||||
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
|
||||||
// await page.clearInput(selectors.ticketFuture.futureState);
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.submit);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should check the three last tickets and move to the future', async() => {
|
|
||||||
// await page.waitForNumberOfElements(selectors.ticketFuture.searchResult, 4);
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.multiCheck);
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.firstCheck);
|
|
||||||
// await page.waitToClick(selectors.ticketFuture.moveButton);
|
|
||||||
// await page.waitToClick(selectors.globalItems.acceptButton);
|
|
||||||
// const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
// expect(message.text).toContain('Tickets moved successfully!');
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import selectors from '../../helpers/selectors.js';
|
import selectors from '../../helpers/selectors.js';
|
||||||
import getBrowser from '../../helpers/puppeteer';
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
// 'https:// redmine.verdnatura.es/issues/5642'
|
describe('Ticket Advance path', () => {
|
||||||
xdescribe('Ticket Advance path', () => {
|
|
||||||
let browser;
|
let browser;
|
||||||
let page;
|
let page;
|
||||||
let httpRequest;
|
let httpRequest;
|
||||||
|
@ -22,7 +21,7 @@ xdescribe('Ticket Advance path', () => {
|
||||||
await browser.close();
|
await browser.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show errors snackbar because of the required data', async() => {
|
it('should search with the required data, check the first ticket and move to the present', async() => {
|
||||||
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||||
await page.clearInput(selectors.ticketAdvance.warehouseFk);
|
await page.clearInput(selectors.ticketAdvance.warehouseFk);
|
||||||
|
|
||||||
|
@ -44,45 +43,37 @@ xdescribe('Ticket Advance path', () => {
|
||||||
message = await page.waitForSnackbar();
|
message = await page.waitForSnackbar();
|
||||||
|
|
||||||
expect(message.text).toContain('dateFuture is a required argument');
|
expect(message.text).toContain('dateFuture is a required argument');
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.submit);
|
||||||
|
|
||||||
|
expect(httpRequest).toBeDefined();
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||||
|
await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'H');
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.submit);
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('futureIpt=H');
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||||
|
await page.clearInput(selectors.ticketAdvance.futureIpt);
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.submit);
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||||
|
await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'H');
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.submit);
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('ipt=H');
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||||
|
await page.clearInput(selectors.ticketAdvance.ipt);
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.submit);
|
||||||
|
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.firstCheck);
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.moveButton);
|
||||||
|
await page.waitToClick(selectors.ticketAdvance.acceptButton);
|
||||||
|
message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.text).toContain('Tickets moved successfully!');
|
||||||
});
|
});
|
||||||
|
|
||||||
// it('should search with the required data', async() => {
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.submit);
|
|
||||||
|
|
||||||
// expect(httpRequest).toBeDefined();
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should search with the origin IPT', async() => {
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
|
||||||
// await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'H');
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.submit);
|
|
||||||
|
|
||||||
// expect(httpRequest).toContain('futureIpt=H');
|
|
||||||
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
|
||||||
// await page.clearInput(selectors.ticketAdvance.futureIpt);
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.submit);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should search with the destination IPT', async() => {
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
|
||||||
// await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'H');
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.submit);
|
|
||||||
|
|
||||||
// expect(httpRequest).toContain('ipt=H');
|
|
||||||
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
|
||||||
// await page.clearInput(selectors.ticketAdvance.ipt);
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.submit);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it('should check the first ticket and move to the present', async() => {
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.firstCheck);
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.moveButton);
|
|
||||||
// await page.waitToClick(selectors.ticketAdvance.acceptButton);
|
|
||||||
// const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
// expect(message.text).toContain('Tickets moved successfully!');
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -49,7 +49,11 @@ describe('Claim summary path', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should click on the first sale ID making the item descriptor visible`, async() => {
|
it(`should click on the first sale ID making the item descriptor visible`, async() => {
|
||||||
await page.waitToClick(selectors.claimSummary.firstSaleItemId);
|
const firstItem = selectors.claimSummary.firstSaleItemId;
|
||||||
|
await page.evaluate(selectors => {
|
||||||
|
document.querySelector(selectors).scrollIntoView();
|
||||||
|
}, firstItem);
|
||||||
|
await page.click(firstItem);
|
||||||
await page.waitImgLoad(selectors.claimSummary.firstSaleDescriptorImage);
|
await page.waitImgLoad(selectors.claimSummary.firstSaleDescriptorImage);
|
||||||
const visible = await page.isVisible(selectors.claimSummary.itemDescriptorPopover);
|
const visible = await page.isVisible(selectors.claimSummary.itemDescriptorPopover);
|
||||||
|
|
||||||
|
|
|
@ -53,12 +53,10 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="model vn-pb-sm vn-px-sm"
|
||||||
class="model vn-pb-sm vn-px-sm"
|
|
||||||
title="{{::log.changedModelValue}}"
|
|
||||||
ng-if="::log.changedModelId || log.changedModelValue">
|
ng-if="::log.changedModelId || log.changedModelValue">
|
||||||
<span class="model-id" ng-if="::log.changedModelId">#{{::log.changedModelId}}</span>
|
<span class="model-id" ng-if="::log.changedModelId">#{{::log.changedModelId}}</span>
|
||||||
<span class="model-value">{{::log.changedModelValue}}</span>
|
<span class="model-value" title="{{::log.changedModelValue}}">{{::log.changedModelValue}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="changes vn-pa-sm"
|
<div class="changes vn-pa-sm"
|
||||||
ng-class="{expanded: log.expand}"
|
ng-class="{expanded: log.expand}"
|
||||||
|
@ -75,16 +73,16 @@
|
||||||
<span class="json-field" title="{{::prop.name}}">
|
<span class="json-field" title="{{::prop.name}}">
|
||||||
{{::prop.nameI18n}}:
|
{{::prop.nameI18n}}:
|
||||||
</span>
|
</span>
|
||||||
<vn-json-value value="::$ctrl.mainVal(prop, log.action)"></vn-json-value><span ng-if="::!$last">,</span>
|
<vn-json-value value="::prop.val.val"></vn-json-value><span ng-if="::!$last">,</span>
|
||||||
</span>
|
</span>
|
||||||
<div ng-if="log.expand" class="expanded-json">
|
<div ng-if="log.expand" class="expanded-json">
|
||||||
<div ng-repeat="prop in ::log.props">
|
<div ng-repeat="prop in ::log.props">
|
||||||
<span class="json-field" title="{{::prop.name}}">
|
<span class="json-field" title="{{::prop.name}}">
|
||||||
{{::prop.nameI18n}}:
|
{{::prop.nameI18n}}:
|
||||||
</span>
|
</span>
|
||||||
<vn-json-value value="::$ctrl.mainVal(prop, log.action)"></vn-json-value>
|
<vn-log-value val="::prop.val"></vn-log-value>
|
||||||
<span ng-if="::log.action == 'update'">
|
<span ng-if="::log.action == 'update'">
|
||||||
← <vn-json-value value="::prop.old"></vn-json-value>
|
← <vn-log-value val="::prop.old"></vn-log-value>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -163,12 +161,17 @@
|
||||||
data="$ctrl.models"
|
data="$ctrl.models"
|
||||||
class="changed-model">
|
class="changed-model">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<!-- FIXME: Cannot use LIKE with JSON columns
|
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Changes"
|
label="Changes"
|
||||||
ng-model="filter.changes">
|
ng-model="filter.changes">
|
||||||
|
<append>
|
||||||
|
<vn-icon
|
||||||
|
icon="info_outline"
|
||||||
|
vn-tooltip="Search by changes"
|
||||||
|
pointer>
|
||||||
|
</vn-icon>
|
||||||
|
</append>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
-->
|
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Creates"
|
label="Creates"
|
||||||
|
|
|
@ -64,29 +64,47 @@ export default class Controller extends Section {
|
||||||
set logs(value) {
|
set logs(value) {
|
||||||
this._logs = value;
|
this._logs = value;
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
|
|
||||||
const empty = {};
|
const empty = {};
|
||||||
const validations = window.validations;
|
const validations = window.validations;
|
||||||
|
const castJsonValue = this.castJsonValue;
|
||||||
|
|
||||||
for (const log of value) {
|
for (const log of value) {
|
||||||
const oldValues = log.oldInstance || empty;
|
const notDelete = log.action != 'delete';
|
||||||
const newValues = log.newInstance || empty;
|
const olds = (notDelete ? log.oldInstance : null) || empty;
|
||||||
|
const vals = (notDelete ? log.newInstance : log.oldInstance) || empty;
|
||||||
const locale = validations[log.changedModel]?.locale || empty;
|
const locale = validations[log.changedModel]?.locale || empty;
|
||||||
log.changedModelI18n = firstUpper(locale.name) || log.changedModel;
|
log.changedModelI18n = firstUpper(locale.name) || log.changedModel;
|
||||||
|
|
||||||
let props = Object.keys(oldValues).concat(Object.keys(newValues));
|
let props = Object.keys(olds).concat(Object.keys(vals));
|
||||||
props = [...new Set(props)];
|
props = [...new Set(props)];
|
||||||
|
|
||||||
log.props = [];
|
log.props = [];
|
||||||
for (const prop of props) {
|
for (const prop of props) {
|
||||||
|
if (prop.endsWith('$')) continue;
|
||||||
log.props.push({
|
log.props.push({
|
||||||
name: prop,
|
name: prop,
|
||||||
nameI18n: firstUpper(locale.columns?.[prop]) || prop,
|
nameI18n: firstUpper(locale.columns?.[prop]) || prop,
|
||||||
old: this.castJsonValue(oldValues[prop]),
|
old: getVal(olds, prop),
|
||||||
new: this.castJsonValue(newValues[prop])
|
val: getVal(vals, prop)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
log.props.sort(
|
log.props.sort(
|
||||||
(a, b) => a.nameI18n.localeCompare(b.nameI18n));
|
(a, b) => a.nameI18n.localeCompare(b.nameI18n));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getVal(vals, prop) {
|
||||||
|
let val, id;
|
||||||
|
const showProp = `${prop}$`;
|
||||||
|
|
||||||
|
if (vals[showProp] != null) {
|
||||||
|
val = vals[showProp];
|
||||||
|
id = vals[prop];
|
||||||
|
} else
|
||||||
|
val = vals[prop];
|
||||||
|
|
||||||
|
return {val: castJsonValue(val), id};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get models() {
|
get models() {
|
||||||
|
@ -113,10 +131,6 @@ export default class Controller extends Section {
|
||||||
: value;
|
: value;
|
||||||
}
|
}
|
||||||
|
|
||||||
mainVal(prop, action) {
|
|
||||||
return action == 'delete' ? prop.old : prop.new;
|
|
||||||
}
|
|
||||||
|
|
||||||
relativeDate(dateVal) {
|
relativeDate(dateVal) {
|
||||||
if (dateVal == null) return '';
|
if (dateVal == null) return '';
|
||||||
const date = new Date(dateVal);
|
const date = new Date(dateVal);
|
||||||
|
@ -150,14 +164,16 @@ 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}];
|
const or = [];
|
||||||
|
if (/^[\w_-]+$/.test(value))
|
||||||
|
or.push({changedModelId: value});
|
||||||
if (!/^[0-9]+$/.test(value))
|
if (!/^[0-9]+$/.test(value))
|
||||||
or.push({changedModelValue: {like: `%${value}%`}});
|
or.push({changedModelValue: {like: `%${value}%`}});
|
||||||
return {or};
|
return or.length ? {or} : null;
|
||||||
case 'changes':
|
case 'changes':
|
||||||
return {or: [
|
return {or: [
|
||||||
{oldInstance: {like: `%${value}%`}},
|
{oldJson: {like: `%${value}%`}},
|
||||||
{newInstance: {like: `%${value}%`}},
|
{newJson: {like: `%${value}%`}},
|
||||||
{description: {like: `%${value}%`}}
|
{description: {like: `%${value}%`}}
|
||||||
]};
|
]};
|
||||||
case 'who':
|
case 'who':
|
||||||
|
@ -238,3 +254,12 @@ ngModule.vnComponent('vnLog', {
|
||||||
url: '@'
|
url: '@'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ngModule.component('vnLogValue', {
|
||||||
|
template:
|
||||||
|
'<vn-json-value value="::$ctrl.val.val"></vn-json-value>' +
|
||||||
|
'<span ng-if="::$ctrl.val.id" class="id-value"> #{{::$ctrl.val.id}}</span>',
|
||||||
|
bindings: {
|
||||||
|
val: '<?',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -2,6 +2,9 @@ Date: Fecha
|
||||||
Concept: Concepto
|
Concept: Concepto
|
||||||
Search: Buscar
|
Search: Buscar
|
||||||
Search by id or concept: Buscar por identificador o concepto
|
Search by id or concept: Buscar por identificador o concepto
|
||||||
|
Search by changes: |
|
||||||
|
Buscar por cambios realizados. Los atributos deben buscarse por su nombre
|
||||||
|
interno, para obtenerlo situar el cursor sobre el nombre.
|
||||||
Entity: Entidad
|
Entity: Entidad
|
||||||
Action: Acción
|
Action: Acción
|
||||||
Author: Autor
|
Author: Autor
|
||||||
|
|
|
@ -105,6 +105,7 @@ vn-log {
|
||||||
& > .model-id {
|
& > .model-id {
|
||||||
color: $color-font-secondary;
|
color: $color-font-secondary;
|
||||||
font-size: .9rem;
|
font-size: .9rem;
|
||||||
|
float: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,3 +145,7 @@ vn-log {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
vn-log-value > .id-value {
|
||||||
|
font-size: .9rem;
|
||||||
|
color: $color-font-secondary;
|
||||||
|
}
|
||||||
|
|
|
@ -20,8 +20,6 @@ class Controller {
|
||||||
name: config.languages[code] ? config.languages[code] : code
|
name: config.languages[code] ? config.languages[code] : code
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
vnConfig.initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set lang(value) {
|
set lang(value) {
|
||||||
|
|
|
@ -10,6 +10,9 @@ function config($stateProvider, $urlRouterProvider) {
|
||||||
.state('layout', {
|
.state('layout', {
|
||||||
abstract: true,
|
abstract: true,
|
||||||
template: '<vn-layout></vn-layout>',
|
template: '<vn-layout></vn-layout>',
|
||||||
|
resolve: {
|
||||||
|
config: ['vnConfig', vnConfig => vnConfig.initialize()]
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.state('outLayout', {
|
.state('outLayout', {
|
||||||
abstract: true,
|
abstract: true,
|
||||||
|
|
|
@ -1,4 +1,61 @@
|
||||||
{
|
{
|
||||||
"name": "Log",
|
"name": "Log",
|
||||||
"base": "VnModel"
|
"base": "VnModel",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number",
|
||||||
|
"forceId": false
|
||||||
|
},
|
||||||
|
"originFk": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"userFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"changedModel": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"oldInstance": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"newInstance": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"oldJson": {
|
||||||
|
"type": "String",
|
||||||
|
"mysql": {"columnName": "oldInstance"}
|
||||||
|
},
|
||||||
|
"newJson": {
|
||||||
|
"type": "String",
|
||||||
|
"mysql": {"columnName": "newInstance"}
|
||||||
|
},
|
||||||
|
"creationDate": {
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
"changedModelId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"changedModelValue": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"user": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "VnUser",
|
||||||
|
"foreignKey": "userFk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"order": ["creationDate DESC", "id DESC"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
name: subrole
|
name: subrole
|
||||||
columns:
|
columns:
|
||||||
|
id: id
|
||||||
role: rol
|
role: rol
|
||||||
inheritsFrom: inherits
|
inheritsFrom: inherits
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
name: subrol
|
name: subrol
|
||||||
columns:
|
columns:
|
||||||
|
id: id
|
||||||
role: rol
|
role: rol
|
||||||
inheritsFrom: hereda
|
inheritsFrom: hereda
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -5,54 +5,5 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "account.roleLog"
|
"table": "account.roleLog"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "Account",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": ["creationDate DESC", "id DESC"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,54 +5,5 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "account.userLog"
|
"table": "account.userLog"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "VnUser",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": ["creationDate DESC", "id DESC"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,54 +5,5 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "claimLog"
|
"table": "claimLog"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "VnUser",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": ["creationDate DESC", "id DESC"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
name: SMS
|
||||||
|
columns:
|
||||||
|
id: id
|
||||||
|
senderFk: sender
|
||||||
|
sender: sender number
|
||||||
|
destination: destination
|
||||||
|
message: message
|
||||||
|
statusCode: status code
|
||||||
|
status: status
|
||||||
|
created: created
|
|
@ -0,0 +1,10 @@
|
||||||
|
name: SMS
|
||||||
|
columns:
|
||||||
|
id: id
|
||||||
|
senderFk: remitente
|
||||||
|
sender: número remitente
|
||||||
|
destination: destinatario
|
||||||
|
message: mensaje
|
||||||
|
statusCode: código estado
|
||||||
|
status: estado
|
||||||
|
created: creado
|
|
@ -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: {
|
||||||
|
|
|
@ -5,54 +5,5 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "clientLog"
|
"table": "clientLog"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "VnUser",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": ["creationDate DESC", "id DESC"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"base": "VnModel",
|
"base": "VnModel",
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "payMethod"
|
"table": "payMethod"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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, myOptions);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -5,6 +5,9 @@
|
||||||
"Buy": {
|
"Buy": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"BuyConfig": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"ItemMatchProperties": {
|
"ItemMatchProperties": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "BuyConfig",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "buyConfig"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true
|
||||||
|
},
|
||||||
|
"showLastBuy": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,54 +5,5 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "entryLog"
|
"table": "entryLog"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "VnUser",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": ["creationDate DESC", "id DESC"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -5,57 +5,5 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "invoiceInLog"
|
"table": "invoiceInLog"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "VnUser",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": [
|
|
||||||
"creationDate DESC",
|
|
||||||
"id DESC"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,19 @@ module.exports = Self => {
|
||||||
Self.remoteMethod('refund', {
|
Self.remoteMethod('refund', {
|
||||||
description: 'Create refund tickets with sales and services if provided',
|
description: 'Create refund tickets with sales and services if provided',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [{
|
accepts: [
|
||||||
arg: 'ref',
|
{
|
||||||
type: 'string',
|
arg: 'ref',
|
||||||
description: 'The invoice reference'
|
type: 'string',
|
||||||
}],
|
description: 'The invoice reference',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'withWarehouse',
|
||||||
|
type: 'boolean',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
],
|
||||||
returns: {
|
returns: {
|
||||||
type: ['number'],
|
type: ['number'],
|
||||||
root: true
|
root: true
|
||||||
|
@ -17,7 +25,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.refund = async(ref, options) => {
|
Self.refund = async(ref, withWarehouse, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
let tx;
|
let tx;
|
||||||
|
@ -35,7 +43,7 @@ module.exports = Self => {
|
||||||
const tickets = await models.Ticket.find(filter, myOptions);
|
const tickets = await models.Ticket.find(filter, myOptions);
|
||||||
|
|
||||||
const ticketsIds = tickets.map(ticket => ticket.id);
|
const ticketsIds = tickets.map(ticket => ticket.id);
|
||||||
const refundedTickets = await models.Ticket.refund(ticketsIds, myOptions);
|
const refundedTickets = await models.Ticket.refund(ticketsIds, withWarehouse, myOptions);
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('InvoiceOut refund()', () => {
|
describe('InvoiceOut refund()', () => {
|
||||||
const userId = 5;
|
const userId = 5;
|
||||||
|
const withWarehouse = true;
|
||||||
const activeCtx = {
|
const activeCtx = {
|
||||||
accessToken: {userId: userId},
|
accessToken: {userId: userId},
|
||||||
};
|
};
|
||||||
|
@ -15,7 +16,7 @@ describe('InvoiceOut refund()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await models.InvoiceOut.refund('T1111111', options);
|
const result = await models.InvoiceOut.refund('T1111111', withWarehouse, options);
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
|
|
@ -76,14 +76,27 @@
|
||||||
translate>
|
translate>
|
||||||
Show CITES letter
|
Show CITES letter
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item class="dropdown"
|
||||||
ng-click="refundConfirmation.show()"
|
vn-click-stop="refundMenu.show($event, 'left')"
|
||||||
name="refundInvoice"
|
|
||||||
vn-tooltip="Create a single ticket with all the content of the current invoice"
|
vn-tooltip="Create a single ticket with all the content of the current invoice"
|
||||||
vn-acl="invoicing, claimManager, salesAssistant"
|
vn-acl="invoicing, claimManager, salesAssistant"
|
||||||
vn-acl-action="remove"
|
vn-acl-action="remove"
|
||||||
translate>
|
translate>
|
||||||
Refund
|
Refund...
|
||||||
|
<vn-menu vn-id="refundMenu">
|
||||||
|
<vn-list>
|
||||||
|
<vn-item
|
||||||
|
ng-click="$ctrl.refundInvoiceOut(true)"
|
||||||
|
translate>
|
||||||
|
with warehouse
|
||||||
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="$ctrl.refundInvoiceOut(false)"
|
||||||
|
translate>
|
||||||
|
without warehouse
|
||||||
|
</vn-item>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
||||||
</vn-item>
|
</vn-item>
|
||||||
</vn-list>
|
</vn-list>
|
||||||
</vn-menu>
|
</vn-menu>
|
||||||
|
@ -97,12 +110,7 @@
|
||||||
on-accept="$ctrl.bookInvoiceOut()"
|
on-accept="$ctrl.bookInvoiceOut()"
|
||||||
question="Are you sure you want to book this invoice?">
|
question="Are you sure you want to book this invoice?">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
<vn-confirm
|
<vn-client-descriptor-popover
|
||||||
vn-id="refundConfirmation"
|
|
||||||
on-accept="$ctrl.refundInvoiceOut()"
|
|
||||||
question="Are you sure you want to refund this invoice?">
|
|
||||||
</vn-confirm>
|
|
||||||
<vn-client-descriptor-popover
|
|
||||||
vn-id="clientDescriptor">
|
vn-id="clientDescriptor">
|
||||||
</vn-client-descriptor-popover>
|
</vn-client-descriptor-popover>
|
||||||
|
|
||||||
|
@ -148,4 +156,4 @@
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
<button response="accept" translate>Confirm</button>
|
<button response="accept" translate>Confirm</button>
|
||||||
</tpl-buttons>
|
</tpl-buttons>
|
||||||
</vn-dialog>
|
</vn-dialog>
|
||||||
|
|
|
@ -114,9 +114,9 @@ class Controller extends Section {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
refundInvoiceOut() {
|
refundInvoiceOut(withWarehouse) {
|
||||||
const query = 'InvoiceOuts/refund';
|
const query = 'InvoiceOuts/refund';
|
||||||
const params = {ref: this.invoiceOut.ref};
|
const params = {ref: this.invoiceOut.ref, withWarehouse: withWarehouse};
|
||||||
this.$http.post(query, params).then(res => {
|
this.$http.post(query, params).then(res => {
|
||||||
const refundTicket = res.data;
|
const refundTicket = res.data;
|
||||||
this.vnApp.showSuccess(this.$t('The following refund ticket have been created', {
|
this.vnApp.showSuccess(this.$t('The following refund ticket have been created', {
|
||||||
|
|
|
@ -13,10 +13,11 @@ InvoiceOut deleted: Factura eliminada
|
||||||
Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura?
|
Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura?
|
||||||
Are you sure you want to clone this invoice?: Estas seguro de clonar esta factura?
|
Are you sure you want to clone this invoice?: Estas seguro de clonar esta factura?
|
||||||
InvoiceOut booked: Factura asentada
|
InvoiceOut booked: Factura asentada
|
||||||
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
|
Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
|
||||||
Are you sure you want to refund this invoice?: Estas seguro de querer abonar esta factura?
|
Are you sure you want to refund this invoice?: Estas seguro de querer abonar esta factura?
|
||||||
Create a single ticket with all the content of the current invoice: Crear un ticket unico con todo el contenido de la factura actual
|
Create a single ticket with all the content of the current invoice: Crear un ticket unico con todo el contenido de la factura actual
|
||||||
Regenerate PDF invoice: Regenerar PDF factura
|
Regenerate PDF invoice: Regenerar PDF factura
|
||||||
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
|
The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
|
||||||
The email can't be empty: El correo no puede estar vacío
|
The email can't be empty: El correo no puede estar vacío
|
||||||
The following refund tickets have been created: "Se han creado los siguientes tickets de abono: {{ticketIds}}"
|
The following refund tickets have been created: "Se han creado los siguientes tickets de abono: {{ticketIds}}"
|
||||||
|
Refund...: Abono...
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
name: botanical
|
name: botanical data
|
||||||
columns:
|
columns:
|
||||||
itemFk: item
|
itemFk: item
|
||||||
genusFk: genus
|
genusFk: genus
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -5,54 +5,5 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "itemLog"
|
"table": "itemLog"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "VnUser",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": ["creationDate DESC", "id DESC"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ class Controller extends SearchPanel {
|
||||||
|
|
||||||
addValue() {
|
addValue() {
|
||||||
this.filter.values.push({});
|
this.filter.values.push({});
|
||||||
setTimeout(() => this.popover.relocate());
|
setTimeout(() => this.parentPopover.relocate());
|
||||||
}
|
}
|
||||||
|
|
||||||
changeTag() {
|
changeTag() {
|
||||||
|
@ -36,7 +36,7 @@ ngModule.vnComponent('vnOrderCatalogSearchPanel', {
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
bindings: {
|
bindings: {
|
||||||
onSubmit: '&?',
|
onSubmit: '&?',
|
||||||
popover: '<?',
|
parentPopover: '<?',
|
||||||
resultTags: '<?'
|
resultTags: '<?'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
</vn-searchbar>
|
</vn-searchbar>
|
||||||
</vn-portal>
|
</vn-portal>
|
||||||
<vn-order-catalog-view
|
<vn-order-catalog-view
|
||||||
model="model"
|
model="model"
|
||||||
order="$ctrl.order">
|
order="$ctrl.order">
|
||||||
</vn-order-catalog-view>
|
</vn-order-catalog-view>
|
||||||
<vn-side-menu side="right">
|
<vn-side-menu side="right">
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
label="Category">
|
label="Category">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-one ng-repeat="category in categories">
|
<vn-one ng-repeat="category in categories">
|
||||||
<vn-icon
|
<vn-icon
|
||||||
ng-class="{'active': $ctrl.categoryId == category.id}"
|
ng-class="{'active': $ctrl.categoryId == category.id}"
|
||||||
icon="{{::category.icon}}"
|
icon="{{::category.icon}}"
|
||||||
vn-tooltip="{{::category.name}}"
|
vn-tooltip="{{::category.name}}"
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
</div>
|
</div>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
<vn-vertical class="input vn-pt-md">
|
<vn-vertical class="input vn-pt-md">
|
||||||
<vn-textfield vn-one
|
<vn-textfield vn-one
|
||||||
vn-id="search"
|
vn-id="search"
|
||||||
ng-keyUp="$ctrl.onSearchByTag($event)"
|
ng-keyUp="$ctrl.onSearchByTag($event)"
|
||||||
label="Search tag">
|
label="Search tag">
|
||||||
|
@ -104,20 +104,20 @@
|
||||||
on-close="$ctrl.onPopoverClose()">
|
on-close="$ctrl.onPopoverClose()">
|
||||||
<vn-order-catalog-search-panel
|
<vn-order-catalog-search-panel
|
||||||
on-submit="$ctrl.onPanelSubmit($filter)"
|
on-submit="$ctrl.onPanelSubmit($filter)"
|
||||||
popover="popover"
|
parent-popover="popover"
|
||||||
result-tags="$ctrl.resultTags">
|
result-tags="$ctrl.resultTags">
|
||||||
</vn-order-catalog-search-panel>
|
</vn-order-catalog-search-panel>
|
||||||
</vn-popover>
|
</vn-popover>
|
||||||
<div class="chips">
|
<div class="chips">
|
||||||
<vn-chip
|
<vn-chip
|
||||||
ng-if="$ctrl.itemId"
|
ng-if="$ctrl.itemId"
|
||||||
removable="true"
|
removable="true"
|
||||||
vn-tooltip="Item id"
|
vn-tooltip="Item id"
|
||||||
on-remove="$ctrl.removeItemId()"
|
on-remove="$ctrl.removeItemId()"
|
||||||
class="colored">
|
class="colored">
|
||||||
<span>Id: {{$ctrl.itemId}}</span>
|
<span>Id: {{$ctrl.itemId}}</span>
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
<vn-chip
|
<vn-chip
|
||||||
ng-if="$ctrl.itemName"
|
ng-if="$ctrl.itemName"
|
||||||
removable="true"
|
removable="true"
|
||||||
vn-tooltip="Item"
|
vn-tooltip="Item"
|
||||||
|
@ -130,20 +130,20 @@
|
||||||
<span>{{$ctrl.itemName}}</span>
|
<span>{{$ctrl.itemName}}</span>
|
||||||
</div>
|
</div>
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
<vn-chip
|
<vn-chip
|
||||||
ng-if="category.selection"
|
ng-if="category.selection"
|
||||||
removable="true"
|
removable="true"
|
||||||
vn-tooltip="Category"
|
vn-tooltip="Category"
|
||||||
on-remove="$ctrl.categoryId = null"
|
on-remove="$ctrl.categoryId = null"
|
||||||
class="colored">
|
class="colored">
|
||||||
<span translate>{{category.selection.name}}</span>
|
<span translate>{{category.selection.name}}</span>
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
<vn-chip
|
<vn-chip
|
||||||
ng-if="type.selection"
|
ng-if="type.selection"
|
||||||
removable="true"
|
removable="true"
|
||||||
vn-tooltip="Type"
|
vn-tooltip="Type"
|
||||||
on-remove="$ctrl.typeId = null"
|
on-remove="$ctrl.typeId = null"
|
||||||
class="colored">
|
class="colored">
|
||||||
<span translate>{{type.selection.name}}</span>
|
<span translate>{{type.selection.name}}</span>
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
<vn-chip
|
<vn-chip
|
||||||
|
@ -151,7 +151,7 @@
|
||||||
removable="true"
|
removable="true"
|
||||||
on-remove="$ctrl.remove($index)"
|
on-remove="$ctrl.remove($index)"
|
||||||
vn-tooltip="{{::$ctrl.formatTooltip(tagGroup)}}"
|
vn-tooltip="{{::$ctrl.formatTooltip(tagGroup)}}"
|
||||||
class="colored">
|
class="colored">
|
||||||
<div>
|
<div>
|
||||||
<span ng-if="::tagGroup.tagFk">
|
<span ng-if="::tagGroup.tagFk">
|
||||||
<span translate>{{::tagGroup.tagSelection.name}}</span>:
|
<span translate>{{::tagGroup.tagSelection.name}}</span>:
|
||||||
|
@ -163,4 +163,4 @@
|
||||||
</div>
|
</div>
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
</div>
|
</div>
|
||||||
</vn-side-menu>
|
</vn-side-menu>
|
||||||
|
|
|
@ -5,54 +5,5 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "routeLog"
|
"table": "routeLog"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "VnUser",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": ["creationDate DESC", "id DESC"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -56,7 +56,7 @@
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
"printerFk": {
|
"mainPrinterFk": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
|
@ -69,4 +69,4 @@
|
||||||
"required": true
|
"required": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,58 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "ShelvingLog",
|
"name": "ShelvingLog",
|
||||||
"base": "Log",
|
"base": "Log",
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "shelvingLog"
|
"table": "shelvingLog"
|
||||||
}
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "VnUser",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": ["creationDate DESC", "id DESC"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]);
|
||||||
|
};
|
||||||
|
};
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -5,54 +5,5 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "supplierLog"
|
"table": "supplierLog"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "VnUser",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": ["creationDate DESC", "id DESC"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -12,3 +12,5 @@ columns:
|
||||||
hostFk: PC
|
hostFk: PC
|
||||||
isBox: box
|
isBox: box
|
||||||
itemPackingTypeFk: packing type
|
itemPackingTypeFk: packing type
|
||||||
|
externalId: external id
|
||||||
|
stateTypeFk: status
|
||||||
|
|
|
@ -12,3 +12,5 @@ columns:
|
||||||
hostFk: PC
|
hostFk: PC
|
||||||
isBox: caja
|
isBox: caja
|
||||||
itemPackingTypeFk: tipo empaquetado
|
itemPackingTypeFk: tipo empaquetado
|
||||||
|
externalId: id externo
|
||||||
|
stateTypeFk: estado
|
||||||
|
|
|
@ -11,6 +11,11 @@ module.exports = Self => {
|
||||||
{
|
{
|
||||||
arg: 'servicesIds',
|
arg: 'servicesIds',
|
||||||
type: ['number']
|
type: ['number']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'withWarehouse',
|
||||||
|
type: 'boolean',
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
|
@ -23,7 +28,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.refund = async(salesIds, servicesIds, options) => {
|
Self.refund = async(salesIds, servicesIds, withWarehouse, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
let tx;
|
let tx;
|
||||||
|
@ -65,7 +70,7 @@ module.exports = Self => {
|
||||||
const now = Date.vnNew();
|
const now = Date.vnNew();
|
||||||
const [firstTicketId] = ticketsIds;
|
const [firstTicketId] = ticketsIds;
|
||||||
|
|
||||||
const refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, myOptions);
|
const refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions);
|
||||||
|
|
||||||
for (const sale of sales) {
|
for (const sale of sales) {
|
||||||
const createdSale = await models.Sale.create({
|
const createdSale = await models.Sale.create({
|
||||||
|
@ -113,7 +118,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function createTicketRefund(ticketId, now, refundAgencyMode, refoundZoneId, myOptions) {
|
async function createTicketRefund(ticketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
||||||
const filter = {include: {relation: 'address'}};
|
const filter = {include: {relation: 'address'}};
|
||||||
|
@ -125,7 +130,7 @@ module.exports = Self => {
|
||||||
addressFk: ticket.address().id,
|
addressFk: ticket.address().id,
|
||||||
agencyModeFk: refundAgencyMode.id,
|
agencyModeFk: refundAgencyMode.id,
|
||||||
nickname: ticket.address().nickname,
|
nickname: ticket.address().nickname,
|
||||||
warehouseFk: ticket.warehouseFk,
|
warehouseFk: withWarehouse ? ticket.warehouseFk : null,
|
||||||
companyFk: ticket.companyFk,
|
companyFk: ticket.companyFk,
|
||||||
landed: now,
|
landed: now,
|
||||||
zoneFk: refoundZoneId
|
zoneFk: refoundZoneId
|
||||||
|
|
|
@ -6,8 +6,8 @@ describe('Sale refund()', () => {
|
||||||
const activeCtx = {
|
const activeCtx = {
|
||||||
accessToken: {userId: userId},
|
accessToken: {userId: userId},
|
||||||
};
|
};
|
||||||
|
|
||||||
const servicesIds = [3];
|
const servicesIds = [3];
|
||||||
|
const withWarehouse = true;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
@ -22,7 +22,7 @@ describe('Sale refund()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const refundedTicket = await models.Sale.refund(salesIds, servicesIds, options);
|
const refundedTicket = await models.Sale.refund(salesIds, servicesIds, withWarehouse, options);
|
||||||
|
|
||||||
expect(refundedTicket).toBeDefined();
|
expect(refundedTicket).toBeDefined();
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ describe('Sale refund()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const ticket = await models.Sale.refund(salesIds, servicesIds, options);
|
const ticket = await models.Sale.refund(salesIds, servicesIds, withWarehouse, options);
|
||||||
|
|
||||||
const refundedTicket = await models.Ticket.findOne({
|
const refundedTicket = await models.Ticket.findOne({
|
||||||
where: {
|
where: {
|
||||||
|
|
|
@ -7,6 +7,11 @@ module.exports = Self => {
|
||||||
arg: 'ticketsIds',
|
arg: 'ticketsIds',
|
||||||
type: ['number'],
|
type: ['number'],
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'withWarehouse',
|
||||||
|
type: 'boolean',
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
|
@ -19,7 +24,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.refund = async(ticketsIds, options) => {
|
Self.refund = async(ticketsIds, withWarehouse, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
let tx;
|
let tx;
|
||||||
|
@ -41,7 +46,7 @@ module.exports = Self => {
|
||||||
const services = await models.TicketService.find(filter, myOptions);
|
const services = await models.TicketService.find(filter, myOptions);
|
||||||
const servicesIds = services.map(service => service.id);
|
const servicesIds = services.map(service => service.id);
|
||||||
|
|
||||||
const refundedTickets = await models.Sale.refund(salesIds, servicesIds, myOptions);
|
const refundedTickets = await models.Sale.refund(salesIds, servicesIds, withWarehouse, myOptions);
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
|
|
@ -1,58 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "TicketLog",
|
"name": "TicketLog",
|
||||||
"base": "Log",
|
"base": "Log",
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "ticketLog"
|
"table": "ticketLog"
|
||||||
}
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "VnUser",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": ["creationDate DESC", "id DESC"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,7 @@
|
||||||
<td>{{::ticket.futureIpt | dashIfEmpty}}</td>
|
<td>{{::ticket.futureIpt | dashIfEmpty}}</td>
|
||||||
<td>
|
<td>
|
||||||
<span
|
<span
|
||||||
class="chip {{ticket.classColor}}">
|
class="chip {{ticket.futureClassColor}}">
|
||||||
{{::ticket.futureState | dashIfEmpty}}
|
{{::ticket.futureState | dashIfEmpty}}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -102,13 +102,6 @@ export default class Controller extends Section {
|
||||||
return checkedLines;
|
return checkedLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
stateColor(state) {
|
|
||||||
if (state === 'OK')
|
|
||||||
return 'success';
|
|
||||||
else if (state === 'Libre')
|
|
||||||
return 'notice';
|
|
||||||
}
|
|
||||||
|
|
||||||
dateRange(value) {
|
dateRange(value) {
|
||||||
const minHour = new Date(value);
|
const minHour = new Date(value);
|
||||||
minHour.setHours(0, 0, 0, 0);
|
minHour.setHours(0, 0, 0, 0);
|
||||||
|
|
|
@ -61,24 +61,6 @@ describe('Component vnTicketAdvance', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('stateColor()', () => {
|
|
||||||
it('should return success to the OK tickets', () => {
|
|
||||||
const ok = controller.stateColor(controller.$.model.data[0].state);
|
|
||||||
const notOk = controller.stateColor(controller.$.model.data[1].state);
|
|
||||||
|
|
||||||
expect(ok).toEqual('success');
|
|
||||||
expect(notOk).not.toEqual('success');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return success to the FREE tickets', () => {
|
|
||||||
const notFree = controller.stateColor(controller.$.model.data[0].state);
|
|
||||||
const free = controller.stateColor(controller.$.model.data[1].state);
|
|
||||||
|
|
||||||
expect(free).toEqual('notice');
|
|
||||||
expect(notFree).not.toEqual('notice');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('dateRange()', () => {
|
describe('dateRange()', () => {
|
||||||
it('should return two dates with the hours at the start and end of the given date', () => {
|
it('should return two dates with the hours at the start and end of the given date', () => {
|
||||||
const now = Date.vnNew();
|
const now = Date.vnNew();
|
||||||
|
|
|
@ -141,12 +141,27 @@
|
||||||
translate>
|
translate>
|
||||||
Recalculate components
|
Recalculate components
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item class="dropdown"
|
||||||
ng-click="refundAllConfirmation.show()"
|
vn-click-stop="refundMenu.show($event, 'left')"
|
||||||
vn-acl="invoicing, claimManager, salesAssistant"
|
vn-acl="invoicing, claimManager, salesAssistant"
|
||||||
vn-acl-action="remove"
|
vn-acl-action="remove"
|
||||||
|
vn-tooltip="Create a single ticket with all the content of the current ticket"
|
||||||
translate>
|
translate>
|
||||||
Refund all
|
Refund all...
|
||||||
|
<vn-menu vn-id="refundMenu">
|
||||||
|
<vn-list>
|
||||||
|
<vn-item
|
||||||
|
ng-click="$ctrl.refund(true)"
|
||||||
|
translate>
|
||||||
|
with warehouse
|
||||||
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="$ctrl.refund(false)"
|
||||||
|
translate>
|
||||||
|
without warehouse
|
||||||
|
</vn-item>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
||||||
</vn-item>
|
</vn-item>
|
||||||
</vn-list>
|
</vn-list>
|
||||||
</vn-menu>
|
</vn-menu>
|
||||||
|
@ -319,14 +334,6 @@
|
||||||
message="Recalculate components">
|
message="Recalculate components">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
|
|
||||||
<!-- Refund all confirmation dialog -->
|
|
||||||
<vn-confirm
|
|
||||||
vn-id="refundAllConfirmation"
|
|
||||||
on-accept="$ctrl.refund()"
|
|
||||||
question="Are you sure you want to refund all?"
|
|
||||||
message="Refund all">
|
|
||||||
</vn-confirm>
|
|
||||||
|
|
||||||
<!-- Client balance popup-->
|
<!-- Client balance popup-->
|
||||||
<vn-client-balance-create
|
<vn-client-balance-create
|
||||||
vn-id="balance-create"
|
vn-id="balance-create"
|
||||||
|
|
|
@ -297,16 +297,17 @@ class Controller extends Section {
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
|
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
|
||||||
}
|
}
|
||||||
|
|
||||||
async refund() {
|
refund(withWarehouse) {
|
||||||
const params = {ticketsIds: [this.id]};
|
const params = {ticketsIds: [this.id], withWarehouse: withWarehouse};
|
||||||
const query = 'Tickets/refund';
|
const query = 'Tickets/refund';
|
||||||
return this.$http.post(query, params).then(res => {
|
return this.$http.post(query, params)
|
||||||
const refundTicket = res.data;
|
.then(res => {
|
||||||
this.vnApp.showSuccess(this.$t('The following refund ticket have been created', {
|
const refundTicket = res.data;
|
||||||
ticketId: refundTicket.id
|
this.vnApp.showSuccess(this.$t('The following refund ticket have been created', {
|
||||||
}));
|
ticketId: refundTicket.id
|
||||||
this.$state.go('ticket.card.sale', {id: refundTicket.id});
|
}));
|
||||||
});
|
this.$state.go('ticket.card.sale', {id: refundTicket.id});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSmsSend(sms) {
|
onSmsSend(sms) {
|
||||||
|
|
|
@ -10,7 +10,9 @@ Send CSV: Enviar CSV
|
||||||
Send CSV Delivery Note: Enviar albarán en CSV
|
Send CSV Delivery Note: Enviar albarán en CSV
|
||||||
Send PDF Delivery Note: Enviar albarán en PDF
|
Send PDF Delivery Note: Enviar albarán en PDF
|
||||||
Show Proforma: Ver proforma
|
Show Proforma: Ver proforma
|
||||||
Refund all: Abonar todo
|
Refund all...: Abonar todo...
|
||||||
|
with warehouse: con almacén
|
||||||
|
without warehouse: sin almacén
|
||||||
Invoice sent: Factura enviada
|
Invoice sent: Factura enviada
|
||||||
The following refund ticket have been created: "Se ha creado siguiente ticket de abono: {{ticketId}}"
|
The following refund ticket have been created: "Se ha creado siguiente ticket de abono: {{ticketId}}"
|
||||||
Transfer client: Transferir cliente
|
Transfer client: Transferir cliente
|
||||||
|
@ -18,3 +20,4 @@ SMS Notify changes: SMS Notificar cambios
|
||||||
PDF sent!: ¡PDF enviado!
|
PDF sent!: ¡PDF enviado!
|
||||||
Already exist signed delivery note: Ya existe albarán de entrega firmado
|
Already exist signed delivery note: Ya existe albarán de entrega firmado
|
||||||
Are you sure you want to replace this delivery note?: ¿Seguro que quieres reemplazar este albarán de entrega?
|
Are you sure you want to replace this delivery note?: ¿Seguro que quieres reemplazar este albarán de entrega?
|
||||||
|
Create a single ticket with all the content of the current ticket: Crea un ticket único con todo el contenido del ticket actual
|
||||||
|
|
|
@ -158,7 +158,7 @@
|
||||||
<td>{{::ticket.futureIpt | dashIfEmpty}}</td>
|
<td>{{::ticket.futureIpt | dashIfEmpty}}</td>
|
||||||
<td>
|
<td>
|
||||||
<span
|
<span
|
||||||
class="chip {{ticket.classColor}}">
|
class="chip {{ticket.futureClassColor}}">
|
||||||
{{::ticket.futureState}}
|
{{::ticket.futureState}}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -529,11 +529,28 @@
|
||||||
ng-if="$ctrl.isEditable && $ctrl.hasReserves()">
|
ng-if="$ctrl.isEditable && $ctrl.hasReserves()">
|
||||||
Unmark as reserved
|
Unmark as reserved
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item translate
|
<vn-item class="dropdown"
|
||||||
name="refund"
|
name="refund"
|
||||||
ng-click="$ctrl.createRefund()"
|
vn-click-stop="refundMenu.show($event, 'left')"
|
||||||
vn-acl="invoicing, claimManager, salesAssistant"
|
vn-acl="invoicing, claimManager, salesAssistant"
|
||||||
vn-acl-action="remove">
|
vn-acl-action="remove"
|
||||||
Refund
|
translate>
|
||||||
</vn-item>
|
Refund...
|
||||||
|
<vn-menu vn-id="refundMenu">
|
||||||
|
<vn-list>
|
||||||
|
<vn-item
|
||||||
|
name="refundWithWarehouse"
|
||||||
|
ng-click="$ctrl.createRefund(true)"
|
||||||
|
translate>
|
||||||
|
with warehouse
|
||||||
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
name="refundWithoutWarehouse"
|
||||||
|
ng-click="$ctrl.createRefund(false)"
|
||||||
|
translate>
|
||||||
|
without warehouse
|
||||||
|
</vn-item>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
||||||
|
</vn-item>
|
||||||
</vn-menu>
|
</vn-menu>
|
||||||
|
|
|
@ -520,13 +520,12 @@ class Controller extends Section {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createRefund() {
|
createRefund(withWarehouse) {
|
||||||
const sales = this.selectedValidSales();
|
const sales = this.selectedValidSales();
|
||||||
if (!sales) return;
|
if (!sales) return;
|
||||||
|
|
||||||
const salesIds = sales.map(sale => sale.id);
|
const salesIds = sales.map(sale => sale.id);
|
||||||
|
const params = {salesIds: salesIds, withWarehouse: withWarehouse};
|
||||||
const params = {salesIds: salesIds};
|
|
||||||
const query = 'Sales/refund';
|
const query = 'Sales/refund';
|
||||||
this.$http.post(query, params).then(res => {
|
this.$http.post(query, params).then(res => {
|
||||||
const refundTicket = res.data;
|
const refundTicket = res.data;
|
||||||
|
|
|
@ -36,10 +36,10 @@ Warehouse: Almacen
|
||||||
Agency: Agencia
|
Agency: Agencia
|
||||||
Shipped: F. envio
|
Shipped: F. envio
|
||||||
Packaging: Encajado
|
Packaging: Encajado
|
||||||
Refund: Abono
|
Refund...: Abono...
|
||||||
Promotion mana: Maná promoción
|
Promotion mana: Maná promoción
|
||||||
Claim mana: Maná reclamación
|
Claim mana: Maná reclamación
|
||||||
History: Historial
|
History: Historial
|
||||||
Do you want to continue?: ¿Desea continuar?
|
Do you want to continue?: ¿Desea continuar?
|
||||||
Claim out of time: Reclamación fuera de plazo
|
Claim out of time: Reclamación fuera de plazo
|
||||||
Do you want to create a claim?: ¿Quieres crear una reclamación?
|
Do you want to create a claim?: ¿Quieres crear una reclamación?
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
"TravelThermograph": {
|
"TravelThermograph": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"TravelConfig": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"Temperature": {
|
"Temperature": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,54 +5,5 @@
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "travelLog"
|
"table": "travelLog"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"id": true,
|
|
||||||
"type": "number",
|
|
||||||
"forceId": false
|
|
||||||
},
|
|
||||||
"originFk": {
|
|
||||||
"type": "number",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"userFk": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"changedModel": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"oldInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"newInstance": {
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"creationDate": {
|
|
||||||
"type": "date"
|
|
||||||
},
|
|
||||||
"changedModelId": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"changedModelValue": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"relations": {
|
|
||||||
"user": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "VnUser",
|
|
||||||
"foreignKey": "userFk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scope": {
|
|
||||||
"order": ["creationDate DESC", "id DESC"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue