Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5252-canRefund_withOutWarehouse2
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
e2c7a38e82
|
@ -24,13 +24,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Added
|
||||
- (Tickets -> Crear Factura) Al facturar se envia automáticamente el pdf al cliente
|
||||
- (Artículos -> Histórico) Filtro para mostrar lo anterior al inventario
|
||||
|
||||
- (Trabajadores -> Nuevo trabajador) Permite elegir el método de pago
|
||||
|
||||
### Changed
|
||||
- (Trabajadores -> Nuevo trabajador) Los clientes se crean sin 'TR' pero se añade tipo de negocio 'Trabajador'
|
||||
|
||||
### Fixed
|
||||
- (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
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`item`
|
||||
CHARACTER SET ascii
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9, @col10, @col11, @col12)
|
||||
SET
|
||||
id = @col2,
|
||||
SET id = @col2,
|
||||
product_name = @col4,
|
||||
name = @col5,
|
||||
plant_id = @col7,
|
||||
|
@ -11,3 +11,4 @@ LOAD DATA LOCAL INFILE ?
|
|||
entry_date = STR_TO_DATE(@col10, '%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')
|
||||
|
|
@ -3,236 +3,237 @@ const path = require('path');
|
|||
const fs = require('fs-extra');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('updateData', {
|
||||
description: 'Updates schema data from external provider',
|
||||
accessType: 'WRITE',
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/updateData`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
Self.remoteMethodCtx('updateData', {
|
||||
description: 'Updates schema data from external provider',
|
||||
accessType: 'WRITE',
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/updateData`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.updateData = async() => {
|
||||
const models = Self.app.models;
|
||||
Self.updateData = async() => {
|
||||
const models = Self.app.models;
|
||||
|
||||
// Get files checksum
|
||||
const tx = await Self.beginTransaction({});
|
||||
// Get files checksum
|
||||
const tx = await Self.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const files = await Self.rawSql('SELECT name, checksum, keyValue FROM edi.fileConfig', null, options);
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const files = await Self.rawSql('SELECT name, checksum, keyValue FROM edi.fileConfig', null, options);
|
||||
|
||||
const updatableFiles = [];
|
||||
for (const file of files) {
|
||||
const fileChecksum = await getChecksum(file);
|
||||
const updatableFiles = [];
|
||||
for (const file of files) {
|
||||
const fileChecksum = await getChecksum(file);
|
||||
|
||||
if (file.checksum != fileChecksum) {
|
||||
updatableFiles.push({
|
||||
name: file.name,
|
||||
checksum: fileChecksum
|
||||
});
|
||||
} else
|
||||
console.debug(`File already updated, skipping...`);
|
||||
}
|
||||
if (file.checksum != fileChecksum) {
|
||||
updatableFiles.push({
|
||||
name: file.name,
|
||||
checksum: fileChecksum
|
||||
});
|
||||
} else
|
||||
console.debug(`File already updated, skipping...`);
|
||||
}
|
||||
|
||||
if (updatableFiles.length === 0)
|
||||
return false;
|
||||
if (updatableFiles.length === 0)
|
||||
return false;
|
||||
|
||||
// Download files
|
||||
const container = await models.TempContainer.container('edi');
|
||||
const tempPath = path.join(container.client.root, container.name);
|
||||
// Download files
|
||||
const container = await models.TempContainer.container('edi');
|
||||
const tempPath = path.join(container.client.root, container.name);
|
||||
|
||||
let remoteFile;
|
||||
let tempDir;
|
||||
let tempFile;
|
||||
let remoteFile;
|
||||
let tempDir;
|
||||
let tempFile;
|
||||
|
||||
const fileNames = updatableFiles.map(file => file.name);
|
||||
const fileNames = updatableFiles.map(file => file.name);
|
||||
|
||||
const tables = await Self.rawSql(`
|
||||
SELECT fileName, toTable, file
|
||||
FROM edi.tableConfig
|
||||
WHERE file IN (?)`, [fileNames], options);
|
||||
const tables = await Self.rawSql(`
|
||||
SELECT fileName, toTable, file
|
||||
FROM edi.tableConfig
|
||||
WHERE file IN (?)`, [fileNames], options);
|
||||
|
||||
for (const table of tables) {
|
||||
const fileName = table.file;
|
||||
for (const table of tables) {
|
||||
const fileName = table.file;
|
||||
|
||||
remoteFile = `codes/${fileName}.ZIP`;
|
||||
tempDir = `${tempPath}/${fileName}`;
|
||||
tempFile = `${tempPath}/${fileName}.zip`;
|
||||
remoteFile = `codes/${fileName}.ZIP`;
|
||||
tempDir = `${tempPath}/${fileName}`;
|
||||
tempFile = `${tempPath}/${fileName}.zip`;
|
||||
|
||||
try {
|
||||
await fs.readFile(tempFile);
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
console.debug(`Downloading file ${fileName}...`);
|
||||
const downloadOutput = await downloadFile(remoteFile, tempFile);
|
||||
if (downloadOutput.error)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
try {
|
||||
await fs.readFile(tempFile);
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
console.debug(`Downloading file ${fileName}...`);
|
||||
const downloadOutput = await downloadFile(remoteFile, tempFile);
|
||||
if (downloadOutput.error)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
await extractFile(fileName, tempFile, tempDir);
|
||||
await extractFile(fileName, tempFile, tempDir);
|
||||
|
||||
console.debug(`Updating table ${table.toTable}...`);
|
||||
await dumpData(tempDir, table, options);
|
||||
}
|
||||
console.debug(`Updating table ${table.toTable}...`);
|
||||
await dumpData(tempDir, table, options);
|
||||
}
|
||||
|
||||
// Update files checksum
|
||||
for (const file of updatableFiles) {
|
||||
console.log(`Updating file ${file.name} checksum...`);
|
||||
await Self.rawSql(`
|
||||
UPDATE edi.fileConfig
|
||||
SET checksum = ?
|
||||
WHERE name = ?`,
|
||||
[file.checksum, file.name], options);
|
||||
}
|
||||
// Update files checksum
|
||||
for (const file of updatableFiles) {
|
||||
console.log(`Updating file ${file.name} checksum...`);
|
||||
await Self.rawSql(`
|
||||
UPDATE edi.fileConfig
|
||||
SET checksum = ?
|
||||
WHERE name = ?`,
|
||||
[file.checksum, file.name], options);
|
||||
}
|
||||
|
||||
await tx.commit();
|
||||
await tx.commit();
|
||||
|
||||
// Clean files
|
||||
try {
|
||||
console.debug(`Cleaning files...`);
|
||||
await fs.remove(tempPath);
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT')
|
||||
throw e;
|
||||
}
|
||||
// Clean files
|
||||
try {
|
||||
console.debug(`Cleaning files...`);
|
||||
await fs.remove(tempPath);
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT')
|
||||
throw e;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
await tx.rollback();
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
return true;
|
||||
} catch (error) {
|
||||
await tx.rollback();
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
let ftpClient;
|
||||
async function getFtpClient() {
|
||||
if (!ftpClient) {
|
||||
const [ftpConfig] = await Self.rawSql('SELECT host, user, password FROM edi.ftpConfig');
|
||||
console.debug(`Openning FTP connection to ${ftpConfig.host}...\n`);
|
||||
let ftpClient;
|
||||
async function getFtpClient() {
|
||||
if (!ftpClient) {
|
||||
const [ftpConfig] = await Self.rawSql('SELECT host, user, password FROM edi.ftpConfig');
|
||||
console.debug(`Openning FTP connection to ${ftpConfig.host}...\n`);
|
||||
|
||||
const FtpClient = require('ftps');
|
||||
const FtpClient = require('ftps');
|
||||
|
||||
ftpClient = new FtpClient({
|
||||
host: ftpConfig.host,
|
||||
username: ftpConfig.user,
|
||||
password: ftpConfig.password,
|
||||
procotol: 'ftp'
|
||||
});
|
||||
}
|
||||
ftpClient = new FtpClient({
|
||||
host: ftpConfig.host,
|
||||
username: ftpConfig.user,
|
||||
password: ftpConfig.password,
|
||||
procotol: 'ftp',
|
||||
additionalLftpCommands: 'set ssl:verify-certificate no'
|
||||
});
|
||||
}
|
||||
|
||||
return ftpClient;
|
||||
}
|
||||
return ftpClient;
|
||||
}
|
||||
|
||||
async function getChecksum(file) {
|
||||
const ftpClient = await getFtpClient();
|
||||
console.debug(`Checking checksum for file ${file.name}...`);
|
||||
async function getChecksum(file) {
|
||||
const ftpClient = await getFtpClient();
|
||||
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) => {
|
||||
ftpClient.exec((err, response) => {
|
||||
if (err || response.error) {
|
||||
console.debug(`Error downloading checksum file... ${response.error}`);
|
||||
return reject(err);
|
||||
}
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
ftpClient.exec((err, response) => {
|
||||
if (err || response.error) {
|
||||
console.debug(`Error downloading checksum file... ${response.error}`);
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
|
||||
if (response && response.data) {
|
||||
const fileContents = response.data;
|
||||
const rows = fileContents.split('\n');
|
||||
const row = rows[4];
|
||||
const columns = row.split(/\s+/);
|
||||
if (response && response.data) {
|
||||
const fileContents = response.data;
|
||||
const rows = fileContents.split('\n');
|
||||
const row = rows[4];
|
||||
const columns = row.split(/\s+/);
|
||||
|
||||
let fileChecksum;
|
||||
if (file.keyValue)
|
||||
fileChecksum = columns[1];
|
||||
let fileChecksum;
|
||||
if (file.keyValue)
|
||||
fileChecksum = columns[1];
|
||||
|
||||
if (!file.keyValue)
|
||||
fileChecksum = columns[0];
|
||||
if (!file.keyValue)
|
||||
fileChecksum = columns[0];
|
||||
|
||||
return fileChecksum;
|
||||
}
|
||||
}
|
||||
return fileChecksum;
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadFile(remoteFile, tempFile) {
|
||||
const ftpClient = await getFtpClient();
|
||||
async function downloadFile(remoteFile, tempFile) {
|
||||
const ftpClient = await getFtpClient();
|
||||
|
||||
ftpClient.get(remoteFile, tempFile);
|
||||
ftpClient.get(remoteFile, tempFile);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
ftpClient.exec((err, response) => {
|
||||
if (err || response.error) {
|
||||
console.debug(`Error downloading file... ${response.error}`);
|
||||
return reject(err);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
ftpClient.exec((err, response) => {
|
||||
if (err || response.error) {
|
||||
console.debug(`Error downloading file... ${response.error}`);
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
}
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function extractFile(fileName, tempFile, tempDir) {
|
||||
const JSZip = require('jszip');
|
||||
async function extractFile(fileName, tempFile, tempDir) {
|
||||
const JSZip = require('jszip');
|
||||
|
||||
try {
|
||||
await fs.mkdir(tempDir);
|
||||
console.debug(`Extracting file ${fileName}...`);
|
||||
} catch (error) {
|
||||
if (error.code !== 'EEXIST')
|
||||
throw e;
|
||||
}
|
||||
try {
|
||||
await fs.mkdir(tempDir);
|
||||
console.debug(`Extracting file ${fileName}...`);
|
||||
} catch (error) {
|
||||
if (error.code !== 'EEXIST')
|
||||
throw e;
|
||||
}
|
||||
|
||||
const fileStream = await fs.readFile(tempFile);
|
||||
if (fileStream) {
|
||||
const zip = new JSZip();
|
||||
const zipContents = await zip.loadAsync(fileStream);
|
||||
const fileStream = await fs.readFile(tempFile);
|
||||
if (fileStream) {
|
||||
const zip = new JSZip();
|
||||
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) {
|
||||
const fileContent = await zip.file(fileName).async('nodebuffer');
|
||||
const dest = path.join(tempDir, fileName);
|
||||
await fs.writeFile(dest, fileContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const fileName of fileNames) {
|
||||
const fileContent = await zip.file(fileName).async('nodebuffer');
|
||||
const dest = path.join(tempDir, fileName);
|
||||
await fs.writeFile(dest, fileContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function dumpData(tempDir, table, options) {
|
||||
const toTable = table.toTable;
|
||||
const baseName = table.fileName;
|
||||
async function dumpData(tempDir, table, options) {
|
||||
const toTable = table.toTable;
|
||||
const baseName = table.fileName;
|
||||
|
||||
console.log(`Emptying table ${toTable}...`);
|
||||
const tableName = `edi.${toTable}`;
|
||||
await Self.rawSql(`DELETE FROM ??`, [tableName]);
|
||||
console.log(`Emptying table ${toTable}...`);
|
||||
const tableName = `edi.${toTable}`;
|
||||
await Self.rawSql(`DELETE FROM ??`, [tableName]);
|
||||
|
||||
const dirFiles = await fs.readdir(tempDir);
|
||||
const files = dirFiles.filter(file => file.startsWith(baseName));
|
||||
const dirFiles = await fs.readdir(tempDir);
|
||||
const files = dirFiles.filter(file => file.startsWith(baseName));
|
||||
|
||||
for (const file of files) {
|
||||
console.log(`Dumping data from file ${file}...`);
|
||||
for (const file of files) {
|
||||
console.log(`Dumping data from file ${file}...`);
|
||||
|
||||
const templatePath = path.join(__dirname, `./sql/${toTable}.sql`);
|
||||
const sqlTemplate = await fs.readFile(templatePath, 'utf8');
|
||||
const filePath = path.join(tempDir, file);
|
||||
const templatePath = path.join(__dirname, `./sql/${toTable}.sql`);
|
||||
const sqlTemplate = await fs.readFile(templatePath, 'utf8');
|
||||
const filePath = path.join(tempDir, file);
|
||||
|
||||
await Self.rawSql(sqlTemplate, [filePath], options);
|
||||
await Self.rawSql(`
|
||||
UPDATE edi.tableConfig
|
||||
SET updated = ?
|
||||
WHERE fileName = ?
|
||||
`, [Date.vnNew(), baseName], options);
|
||||
}
|
||||
await Self.rawSql(sqlTemplate, [filePath], options);
|
||||
await Self.rawSql(`
|
||||
UPDATE edi.tableConfig
|
||||
SET updated = ?
|
||||
WHERE fileName = ?
|
||||
`, [Date.vnNew(), baseName], options);
|
||||
}
|
||||
|
||||
console.log(`Updated table ${toTable}\n`);
|
||||
}
|
||||
console.log(`Updated table ${toTable}\n`);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -67,7 +67,7 @@ module.exports = Self => {
|
|||
|
||||
if (!image) return false;
|
||||
|
||||
const hasReadRole = models.ImageCollection.hasReadRole(ctx, collection);
|
||||
const hasReadRole = await models.ImageCollection.hasReadRole(ctx, collection);
|
||||
if (!hasReadRole)
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
||||
|
|
|
@ -44,4 +44,4 @@
|
|||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,11 @@ module.exports = Self => {
|
|||
* @return {boolean} True for user with read privileges
|
||||
*/
|
||||
Self.hasReadRole = async(ctx, name, options) => {
|
||||
const collection = await Self.findOne({where: {name}}, {
|
||||
const collection = await Self.findOne({
|
||||
include: {
|
||||
relation: 'readRole'
|
||||
}
|
||||
},
|
||||
where: {name}
|
||||
}, options);
|
||||
|
||||
return await hasRole(ctx, collection, options);
|
||||
|
|
|
@ -56,8 +56,6 @@ CREATE TABLE `vn`.`collectionWagonTicket` (
|
|||
|
||||
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 CONSTRAINT `wagon_type` FOREIGN KEY (`typeFk`) REFERENCES `wagonType` (`id`) ON UPDATE CASCADE;
|
||||
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES
|
||||
('WagonType', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||
|
@ -70,3 +68,4 @@ INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `pri
|
|||
('WagonType', 'createWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||
('WagonType', 'deleteWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||
('WagonType', 'editWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi');
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalTyp
|
|||
('Client', 'summary', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'updateAddress', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'updateFiscalData', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'updateUser', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'uploadFile', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'campaignMetricsPdf', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'campaignMetricsEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
|
|
|
@ -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 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;
|
||||
-- Cuando se apruebe el PR quitar y poner en redmine para hacerse manualmente
|
||||
|
||||
UPDATE `vn`.`workerConfig`
|
||||
SET payMethodFk = 4
|
||||
WHERE id=1;
|
||||
|
|
|
@ -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,121 @@
|
|||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES
|
||||
('Ticket', 'editDiscount', 'WRITE', 'ALLOW', 'ROLE', 'claimManager'),
|
||||
('Ticket', 'editDiscount', 'WRITE', 'ALLOW', 'ROLE', 'salesPerson'),
|
||||
('Ticket', 'isRoleAdvanced', '*', 'ALLOW', 'ROLE', 'salesAssistant'),
|
||||
('Ticket', 'isRoleAdvanced', '*', 'ALLOW', 'ROLE', 'deliveryBoss'),
|
||||
('Ticket', 'isRoleAdvanced', '*', 'ALLOW', 'ROLE', 'buyer'),
|
||||
('Ticket', 'isRoleAdvanced', '*', 'ALLOW', 'ROLE', 'claimManager'),
|
||||
('Ticket', 'deleteTicketWithPartPrepared', 'WRITE', 'ALLOW', 'ROLE', 'salesAssistant'),
|
||||
('Ticket', 'editZone', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss'),
|
||||
('State', 'editableStates', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('State', 'seeEditableStates', 'READ', 'ALLOW', 'ROLE', 'administrative'),
|
||||
('State', 'seeEditableStates', 'READ', 'ALLOW', 'ROLE', 'production'),
|
||||
('State', 'isSomeEditable', 'READ', 'ALLOW', 'ROLE', 'salesPerson'),
|
||||
('State', 'isAllEditable', 'READ', 'ALLOW', 'ROLE', 'production'),
|
||||
('State', 'isAllEditable', 'READ', 'ALLOW', 'ROLE', 'administrative'),
|
||||
('Agency', 'seeExpired', 'READ', 'ALLOW', 'ROLE', 'administrative'),
|
||||
('Agency', 'seeExpired', 'READ', 'ALLOW', 'ROLE', 'productionBoss'),
|
||||
('Claim', 'createAfterDeadline', 'WRITE', 'ALLOW', 'ROLE', 'claimManager'),
|
||||
('Client', 'editAddressLogifloraAllowed', 'WRITE', 'ALLOW', 'ROLE', 'salesAssistant'),
|
||||
('Client', 'editFiscalDataWithoutTaxDataCheck', 'WRITE', 'ALLOW', 'ROLE', 'salesAssistant'),
|
||||
('Client', 'editVerifiedDataWithoutTaxDataCheck', 'WRITE', 'ALLOW', 'ROLE', 'salesAssistant'),
|
||||
('Client', 'editCredit', 'WRITE', 'ALLOW', 'ROLE', 'financialBoss'),
|
||||
('Client', 'isNotEditableCredit', 'WRITE', 'ALLOW', 'ROLE', 'financialBoss'),
|
||||
('InvoiceOut', 'canCreatePdf', 'WRITE', 'ALLOW', 'ROLE', 'invoicing'),
|
||||
('Supplier', 'editPayMethodCheck', 'WRITE', 'ALLOW', 'ROLE', 'financial'),
|
||||
('Worker', 'isTeamBoss', 'WRITE', 'ALLOW', 'ROLE', 'teamBoss'),
|
||||
('Worker', 'forceIsSubordinate', 'READ', 'ALLOW', 'ROLE', 'hr'),
|
||||
('Claim', 'editState', 'WRITE', 'ALLOW', 'ROLE', 'claimManager');
|
||||
|
||||
DELETE FROM `salix`.`ACL`
|
||||
WHERE
|
||||
model = 'Claim'
|
||||
AND property = '*'
|
||||
AND accessType = '*';
|
||||
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES
|
||||
('Claim', 'find', 'READ', 'ALLOW', 'ROLE', 'salesPerson'),
|
||||
('Claim', 'findById', 'READ', 'ALLOW', 'ROLE', 'salesPerson'),
|
||||
('Claim', 'findOne', 'READ', 'ALLOW', 'ROLE', 'salesPerson'),
|
||||
('Claim', 'getSummary', 'READ', 'ALLOW', 'ROLE', 'salesPerson'),
|
||||
('Claim', 'updateClaim', 'WRITE', 'ALLOW', 'ROLE', 'salesPerson'),
|
||||
('Claim', 'regularizeClaim', 'WRITE', 'ALLOW', 'ROLE', 'claimManager'),
|
||||
('Claim', 'updateClaimDestination', 'WRITE', 'ALLOW', 'ROLE', 'claimManager'),
|
||||
('Claim', 'downloadFile', 'READ', 'ALLOW', 'ROLE', 'claimManager'),
|
||||
('Claim', 'deleteById', 'WRITE', 'ALLOW', 'ROLE', 'claimManager'),
|
||||
('Claim', 'filter', 'READ', 'ALLOW', 'ROLE', 'salesPerson'),
|
||||
('Claim', 'logs', 'READ', 'ALLOW', 'ROLE', 'claimManager');
|
||||
|
||||
DELETE FROM `salix`.`ACL`
|
||||
WHERE
|
||||
model = 'Ticket'
|
||||
AND property = '*'
|
||||
AND accessType = '*';
|
||||
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES
|
||||
('Ticket', 'find', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'findById', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'findOne', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'getVolume', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'getTotalVolume', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'summary', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'priceDifference', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'componentUpdate', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'new', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'isEditable', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'setDeleted', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'restore', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'getSales', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'getSalesPersonMana', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'filter', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'makeInvoice', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'updateEditableTicket', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'updateDiscount', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'transferSales', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'sendSms', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'isLocked', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'freightCost', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'getComponentsSum', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Ticket', 'updateAttributes', 'WRITE', 'ALLOW', 'ROLE', 'delivery'), -- Change Priority in Route tickets
|
||||
('Ticket', 'deliveryNoteCsv', 'READ', 'ALLOW', 'ROLE', 'employee');
|
||||
|
||||
DELETE FROM `salix`.`ACL`
|
||||
WHERE
|
||||
model = 'State'
|
||||
AND property = '*'
|
||||
AND accessType = 'READ';
|
||||
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES
|
||||
('State', 'find', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('State', 'findById', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('State', 'findOne', 'READ', 'ALLOW', 'ROLE', 'employee');
|
||||
|
||||
DELETE FROM `salix`.`ACL`
|
||||
WHERE
|
||||
model = 'Worker'
|
||||
AND property = '*'
|
||||
AND accessType = 'READ';
|
||||
|
||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES
|
||||
('Worker', 'find', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Worker', 'findById', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Worker', 'findOne', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Worker', 'filter', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Worker', 'getWorkedHours', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Worker', 'active', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Worker', 'activeWithRole', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Worker', 'uploadFile', 'WRITE', 'ALLOW', 'ROLE', 'hr'),
|
||||
('Worker', 'contracts', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Worker', 'holidays', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Worker', 'activeContract', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Worker', 'activeWithInheritedRole', 'READ', 'ALLOW', 'ROLE', 'employee');
|
||||
|
||||
DELETE FROM `salix`.`ACL`
|
||||
WHERE model = 'Client'
|
||||
AND property = 'updateUser'
|
||||
AND accessType = '*';
|
|
@ -179,6 +179,8 @@ INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`, `ipAd
|
|||
(2, 'printer2', 'path2', 1, 1 , NULL),
|
||||
(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`)
|
||||
VALUES
|
||||
(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`,
|
||||
`comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`, `packingShelve`, `weightByPiece`)
|
||||
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),
|
||||
(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),
|
||||
|
@ -2332,26 +2334,26 @@ INSERT INTO `vn`.`workerTimeControl`(`userFk`, `timed`, `manual`, `direction`, `
|
|||
|
||||
INSERT INTO `vn`.`dmsType`(`id`, `name`, `path`, `readRoleFk`, `writeRoleFk`, `code`)
|
||||
VALUES
|
||||
(1, 'Facturas Recibidas', 'recibidas', NULL, NULL, 'invoiceIn'),
|
||||
(2, 'Doc oficial', 'oficial', NULL, NULL, 'officialDoc'),
|
||||
(3, 'Laboral', 'laboral', 37, 37, 'hhrrData'),
|
||||
(4, 'Albaranes recibidos', 'entradas', NULL, NULL, 'deliveryNote'),
|
||||
(5, 'Otros', 'otros', 1, 1, 'miscellaneous'),
|
||||
(6, 'Pruebas', 'pruebas', NULL, NULL, 'tests'),
|
||||
(7, 'IAE Clientes', 'IAE_Clientes', 1, 1, 'economicActivitiesTax'),
|
||||
(8, 'Fiscal', 'fiscal', NULL, NULL, 'fiscal'),
|
||||
(9, 'Vehiculos', 'vehiculos', NULL, NULL, 'vehicles'),
|
||||
(10, 'Plantillas', 'plantillas', NULL, NULL, 'templates'),
|
||||
(11, 'Contratos', 'contratos', NULL, NULL, 'contracts'),
|
||||
(12, 'ley de pagos', 'ley pagos', 1, 1, 'paymentsLaw'),
|
||||
(13, 'Basura', 'basura', 1, 1, 'trash'),
|
||||
(14, 'Ticket', 'tickets', 1, 1, 'ticket'),
|
||||
(15, 'Presupuestos', 'Presupuestos', NULL, NULL, 'budgets'),
|
||||
(16, 'Logistica', 'logistica', NULL, NULL, 'logistics'),
|
||||
(17, 'cmr', 'cmr', NULL, NULL, 'cmr'),
|
||||
(18, 'dua', 'dua', NULL, NULL, 'dua'),
|
||||
(19, 'inmovilizado', 'inmovilizado', NULL, NULL, 'fixedAssets'),
|
||||
(20, 'Reclamación', 'reclamacion', 1, 1, 'claim');
|
||||
(1, 'Facturas Recibidas', 'recibidas', NULL, NULL, 'invoiceIn'),
|
||||
(2, 'Doc oficial', 'oficial', NULL, NULL, 'officialDoc'),
|
||||
(3, 'Laboral', 'laboral', 37, 37, 'hhrrData'),
|
||||
(4, 'Albaranes recibidos', 'entradas', NULL, NULL, 'deliveryNote'),
|
||||
(5, 'Otros', 'otros', 1, 1, 'miscellaneous'),
|
||||
(6, 'Pruebas', 'pruebas', NULL, NULL, 'tests'),
|
||||
(7, 'IAE Clientes', 'IAE_Clientes', 1, 1, 'economicActivitiesTax'),
|
||||
(8, 'Fiscal', 'fiscal', NULL, NULL, 'fiscal'),
|
||||
(9, 'Vehiculos', 'vehiculos', NULL, NULL, 'vehicles'),
|
||||
(10, 'Plantillas', 'plantillas', NULL, NULL, 'templates'),
|
||||
(11, 'Contratos', 'contratos', NULL, NULL, 'contracts'),
|
||||
(12, 'ley de pagos', 'ley pagos', 1, 1, 'paymentsLaw'),
|
||||
(13, 'Basura', 'basura', 1, 1, 'trash'),
|
||||
(14, 'Ticket', 'tickets', 1, 1, 'ticket'),
|
||||
(15, 'Presupuestos', 'Presupuestos', NULL, NULL, 'budgets'),
|
||||
(16, 'Logistica', 'logistica', NULL, NULL, 'logistics'),
|
||||
(17, 'cmr', 'cmr', NULL, NULL, 'cmr'),
|
||||
(18, 'dua', 'dua', NULL, NULL, 'dua'),
|
||||
(19, 'inmovilizado', 'inmovilizado', NULL, NULL, 'fixedAssets'),
|
||||
(20, 'Reclamación', 'reclamacion', 1, 1, 'claim');
|
||||
|
||||
INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `warehouseFk`, `companyFk`, `hardCopyNumber`, `hasFile`, `reference`, `description`, `created`)
|
||||
VALUES
|
||||
|
@ -2729,6 +2731,7 @@ INSERT INTO `util`.`notification` (`id`, `name`, `description`)
|
|||
VALUES
|
||||
(1, 'print-email', 'notification fixture one'),
|
||||
(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');
|
||||
|
||||
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
|
||||
|
@ -2886,6 +2889,10 @@ INSERT INTO `vn`.`wagonTypeTray` (`id`, `typeFk`, `height`, `colorFk`)
|
|||
(2, 1, 50, 2),
|
||||
(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
|
||||
* @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 vInvCalculated INT;
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ services:
|
|||
- node.role == worker
|
||||
resources:
|
||||
limits:
|
||||
memory: 4G
|
||||
memory: 8G
|
||||
configs:
|
||||
datasources:
|
||||
external: true
|
||||
|
|
|
@ -22,7 +22,8 @@ export async function getBrowser() {
|
|||
env.E2E_SHOW = true;
|
||||
}
|
||||
|
||||
const headless = !env.E2E_SHOW;
|
||||
const headless = env.E2E_SHOW ? false : 'new';
|
||||
|
||||
const browser = await Puppeteer.launch({
|
||||
args,
|
||||
defaultViewport: null,
|
||||
|
|
|
@ -627,6 +627,7 @@ export default {
|
|||
selectAllSalesCheckbox: 'vn-ticket-sale vn-thead vn-check',
|
||||
secondSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(2) vn-check[ng-model="sale.checked"]',
|
||||
thirdSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(3) vn-check[ng-model="sale.checked"]',
|
||||
fourthSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(4) vn-check[ng-model="sale.checked"]',
|
||||
deleteSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="delete"]',
|
||||
transferSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="call_split"]',
|
||||
moveToTicketInput: 'form vn-input-number[ng-model="$ctrl.transfer.ticketId"] input',
|
||||
|
|
|
@ -64,6 +64,6 @@ describe('SmartTable SearchBar integration', () => {
|
|||
await page.reload({
|
||||
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() => {
|
||||
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');
|
||||
|
||||
expect(result).toContain('13');
|
||||
|
|
|
@ -261,7 +261,6 @@ describe('Ticket Edit sale path', () => {
|
|||
await page.waitToClick(selectors.ticketSales.moreMenu);
|
||||
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
|
||||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
await page.waitForState('claim.card.basicData');
|
||||
});
|
||||
|
||||
|
@ -327,7 +326,7 @@ describe('Ticket Edit sale path', () => {
|
|||
it('should confirm the transfered quantity is the correct one', async() => {
|
||||
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() => {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
// 'https:// redmine.verdnatura.es/issues/5642'
|
||||
xdescribe('Ticket Future path', () => {
|
||||
describe('Ticket Future path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
let httpRequest;
|
||||
|
@ -22,7 +21,7 @@ xdescribe('Ticket Future path', () => {
|
|||
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.clearInput(selectors.ticketFuture.warehouseFk);
|
||||
await page.waitToClick(selectors.ticketFuture.submit);
|
||||
|
@ -43,69 +42,58 @@ xdescribe('Ticket Future path', () => {
|
|||
message = await page.waitForSnackbar();
|
||||
|
||||
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 getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
// 'https:// redmine.verdnatura.es/issues/5642'
|
||||
xdescribe('Ticket Advance path', () => {
|
||||
describe('Ticket Advance path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
let httpRequest;
|
||||
|
@ -22,7 +21,7 @@ xdescribe('Ticket Advance path', () => {
|
|||
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.clearInput(selectors.ticketAdvance.warehouseFk);
|
||||
|
||||
|
@ -44,45 +43,37 @@ xdescribe('Ticket Advance path', () => {
|
|||
message = await page.waitForSnackbar();
|
||||
|
||||
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!');
|
||||
// });
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('Claim summary path', () => {
|
|||
});
|
||||
|
||||
it('should navigate to the target claim summary section', async() => {
|
||||
await page.loginAndModule('employee', 'claim');
|
||||
await page.loginAndModule('salesPerson', 'claim');
|
||||
await page.accessToSearchResult(claimId);
|
||||
await page.waitForState('claim.card.summary');
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@ describe('Claim descriptor path', () => {
|
|||
});
|
||||
|
||||
it('should now navigate to the target claim summary section', async() => {
|
||||
await page.loginAndModule('employee', 'claim');
|
||||
await page.loginAndModule('salesPerson', 'claim');
|
||||
await page.accessToSearchResult(claimId);
|
||||
await page.waitForState('claim.card.summary');
|
||||
});
|
||||
|
|
|
@ -7,22 +7,24 @@ vn-descriptor-content {
|
|||
|
||||
.photo {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
|
||||
& > img[ng-src] {
|
||||
min-height: 16em;
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: 256px;
|
||||
}
|
||||
|
||||
vn-float-button {
|
||||
position: absolute;
|
||||
margin: 1em;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
margin: 1em;
|
||||
bottom: 0;
|
||||
right: 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
& > vn-spinner {
|
||||
display: block;
|
||||
height: 40px;
|
||||
|
@ -39,7 +41,7 @@ vn-descriptor-content {
|
|||
align-items: stretch;
|
||||
color: $color-font-dark;
|
||||
|
||||
& > a,
|
||||
& > a,
|
||||
& > vn-icon-button {
|
||||
display: flex;
|
||||
min-width: 45px;
|
||||
|
@ -89,7 +91,7 @@ vn-descriptor-content {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
|
||||
& > vn-icon {
|
||||
padding: $spacing-xs $spacing-sm;
|
||||
font-size: 1.5rem;
|
||||
|
|
|
@ -150,10 +150,10 @@ export default class Controller extends Section {
|
|||
if (value == null || value == '') return null;
|
||||
switch (prop) {
|
||||
case 'search':
|
||||
const or = [{changedModelId: value}];
|
||||
if (!/^[0-9]+$/.test(value))
|
||||
or.push({changedModelValue: {like: `%${value}%`}});
|
||||
return {or};
|
||||
if (/^[0-9]+$/.test(value))
|
||||
return {changedModelId: value};
|
||||
else
|
||||
return {changedModelValue: {like: `%${value}%`}};
|
||||
case 'changes':
|
||||
return {or: [
|
||||
{oldInstance: {like: `%${value}%`}},
|
||||
|
|
|
@ -20,8 +20,6 @@ class Controller {
|
|||
name: config.languages[code] ? config.languages[code] : code
|
||||
});
|
||||
}
|
||||
|
||||
vnConfig.initialize();
|
||||
}
|
||||
|
||||
set lang(value) {
|
||||
|
|
|
@ -10,6 +10,9 @@ function config($stateProvider, $urlRouterProvider) {
|
|||
.state('layout', {
|
||||
abstract: true,
|
||||
template: '<vn-layout></vn-layout>',
|
||||
resolve: {
|
||||
config: ['vnConfig', vnConfig => vnConfig.initialize()]
|
||||
}
|
||||
})
|
||||
.state('outLayout', {
|
||||
abstract: true,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
const UserError = require('vn-loopback/util/user-error');
|
||||
const utils = require('loopback/lib/utils');
|
||||
const {util} = require('webpack');
|
||||
|
||||
module.exports = function(Self) {
|
||||
Self.ParameterizedSQL = ParameterizedSQL;
|
||||
|
@ -164,23 +166,21 @@ module.exports = function(Self) {
|
|||
|
||||
function rewriteMethod(methodName) {
|
||||
const realMethod = this[methodName];
|
||||
return async(data, options, cb) => {
|
||||
if (options instanceof Function) {
|
||||
cb = options;
|
||||
options = null;
|
||||
}
|
||||
return function(...args) {
|
||||
let cb;
|
||||
const lastArg = args[args.length - 1];
|
||||
if (lastArg instanceof Function) {
|
||||
cb = lastArg;
|
||||
args.pop();
|
||||
} else
|
||||
cb = utils.createPromiseCallback();
|
||||
|
||||
try {
|
||||
const result = await realMethod.call(this, data, options);
|
||||
|
||||
if (cb) cb(null, result);
|
||||
else return result;
|
||||
} catch (err) {
|
||||
let myErr = replaceErr(err, replaceErrFunc);
|
||||
if (cb) cb(myErr);
|
||||
else
|
||||
throw myErr;
|
||||
}
|
||||
args.push(function(err, res) {
|
||||
if (err) err = replaceErr(err, replaceErrFunc);
|
||||
cb(err, res);
|
||||
});
|
||||
realMethod.apply(this, args);
|
||||
return cb.promise;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -171,5 +171,7 @@
|
|||
"Added observation": "Added observation",
|
||||
"Comment added to client": "Comment added to client",
|
||||
"This ticket is already a refund": "This ticket is already a refund",
|
||||
"A claim with that sale already exists": "A claim with that sale already exists"
|
||||
"A claim with that sale already exists": "A claim with that sale already exists",
|
||||
"Can't transfer claimed sales": "Can't transfer claimed sales",
|
||||
"Invalid quantity": "Invalid quantity"
|
||||
}
|
||||
|
|
|
@ -15,3 +15,7 @@ columns:
|
|||
image: image
|
||||
hasGrant: has grant
|
||||
userFk: user
|
||||
recoverPass: recover password
|
||||
role: role
|
||||
sync: pending sync
|
||||
lastPassChange: password changed
|
||||
|
|
|
@ -15,3 +15,7 @@ columns:
|
|||
image: imagen
|
||||
hasGrant: puede delegar
|
||||
userFk: usuario
|
||||
recoverPass: recuperar contraseña
|
||||
role: rol
|
||||
sync: Pendiente de sincronizar
|
||||
lastPassChange: contraseña modificada
|
||||
|
|
|
@ -59,12 +59,14 @@ module.exports = Self => {
|
|||
|
||||
const landedPlusWeek = new Date(ticket.landed);
|
||||
landedPlusWeek.setDate(landedPlusWeek.getDate() + 7);
|
||||
const hasClaimManagerRole = await models.VnUser.hasRole(userId, 'claimManager', myOptions);
|
||||
const isClaimable = landedPlusWeek >= Date.vnNew();
|
||||
|
||||
const canCreateClaimAfterDeadline =
|
||||
await models.ACL.checkAccessAcl(ctx, 'Claim', 'createAfterDeadline', 'WRITE');
|
||||
|
||||
if (ticket.isDeleted)
|
||||
throw new UserError(`You can't create a claim for a removed ticket`);
|
||||
if (!isClaimable && !hasClaimManagerRole)
|
||||
if (!isClaimable && !canCreateClaimAfterDeadline)
|
||||
throw new UserError(`You can't create a claim from a ticket delivered more than seven days ago`);
|
||||
|
||||
const newClaim = await Self.create({
|
||||
|
|
|
@ -46,7 +46,6 @@ module.exports = Self => {
|
|||
|
||||
Self.updateClaim = async(ctx, id, options) => {
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const args = ctx.args;
|
||||
let tx;
|
||||
const myOptions = {};
|
||||
|
@ -81,9 +80,9 @@ module.exports = Self => {
|
|||
if (args.claimStateFk) {
|
||||
const canEditOldState = await models.ClaimState.isEditable(ctx, claim.claimStateFk, myOptions);
|
||||
const canEditNewState = await models.ClaimState.isEditable(ctx, args.claimStateFk, myOptions);
|
||||
const isClaimManager = await models.VnUser.hasRole(userId, 'claimManager', myOptions);
|
||||
const canEditState = await models.ACL.checkAccessAcl(ctx, 'Claim', 'editState', 'WRITE');
|
||||
|
||||
if (!canEditOldState || !canEditNewState || changedHasToPickUp && !isClaimManager)
|
||||
if (!canEditOldState || !canEditNewState || changedHasToPickUp && !canEditState)
|
||||
throw new UserError(`You don't have enough privileges to change that field`);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,12 @@ module.exports = function(Self) {
|
|||
fields: ['id', 'name']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'businessType',
|
||||
scope: {
|
||||
fields: ['description']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'account',
|
||||
scope: {
|
||||
|
|
|
@ -87,15 +87,15 @@ module.exports = function(Self) {
|
|||
Self.updateAddress = async(ctx, clientId, addressId, options) => {
|
||||
const models = Self.app.models;
|
||||
const args = ctx.args;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const isSalesAssistant = await models.VnUser.hasRole(userId, 'salesAssistant', myOptions);
|
||||
const canEditAddressLogifloraAllowed =
|
||||
await models.ACL.checkAccessAcl(ctx, 'Client', 'editAddressLogifloraAllowed');
|
||||
|
||||
if (args.isLogifloraAllowed && !isSalesAssistant)
|
||||
if (args.isLogifloraAllowed && !canEditAddressLogifloraAllowed)
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
||||
const address = await models.Address.findOne({
|
||||
|
|
|
@ -131,9 +131,10 @@ module.exports = Self => {
|
|||
myOptions.transaction = tx;
|
||||
}
|
||||
try {
|
||||
const isSalesAssistant = await models.VnUser.hasRole(userId, 'salesAssistant', myOptions);
|
||||
const canEditNotTaxDataChecked =
|
||||
await models.ACL.checkAccessAcl(ctx, 'Client', 'editFiscalDataWithoutTaxDataCheck', 'WRITE');
|
||||
const client = await models.Client.findById(clientId, null, myOptions);
|
||||
if (!isSalesAssistant && client.isTaxDataChecked)
|
||||
if (!canEditNotTaxDataChecked && client.isTaxDataChecked)
|
||||
throw new UserError(`Not enough privileges to edit a client with verified data`);
|
||||
// Sage data validation
|
||||
const taxDataChecked = args.isTaxDataChecked;
|
||||
|
|
|
@ -2,6 +2,7 @@ const UserError = require('vn-loopback/util/user-error');
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('updateUser', {
|
||||
description: 'Updates the user information',
|
||||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
|
@ -32,7 +33,6 @@ module.exports = Self => {
|
|||
|
||||
Self.updateUser = async function(ctx, id, options) {
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
let tx;
|
||||
const myOptions = {};
|
||||
|
||||
|
@ -45,9 +45,8 @@ module.exports = Self => {
|
|||
}
|
||||
|
||||
try {
|
||||
const isSalesPerson = await models.VnUser.hasRole(userId, 'salesPerson', myOptions);
|
||||
|
||||
if (!isSalesPerson)
|
||||
const canEdit = await models.ACL.checkAccessAcl(ctx, 'Client', 'updateUser', 'WRITE');
|
||||
if (!canEdit)
|
||||
throw new UserError(`Not enough privileges to edit a client`);
|
||||
|
||||
const isClient = await models.Client.findById(id, null, myOptions);
|
||||
|
|
|
@ -218,9 +218,9 @@ module.exports = Self => {
|
|||
const models = Self.app.models;
|
||||
|
||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||
const userId = loopBackContext.active.accessToken.userId;
|
||||
const accessToken = {req: loopBackContext.active.accessToken};
|
||||
|
||||
const isSalesAssistant = await models.VnUser.hasRole(userId, 'salesAssistant', ctx.options);
|
||||
const editVerifiedDataWithoutTaxDataChecked = models.ACL.checkAccessAcl(accessToken, 'Client', 'editVerifiedDataWithoutTaxDataCheck', 'WRITE');
|
||||
const hasChanges = orgData && changes;
|
||||
|
||||
const isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked);
|
||||
|
@ -232,8 +232,8 @@ module.exports = Self => {
|
|||
const sageTransactionType = hasChanges && (changes.sageTransactionTypeFk || orgData.sageTransactionTypeFk);
|
||||
const sageTransactionTypeChanged = hasChanges && orgData.sageTransactionTypeFk != sageTransactionType;
|
||||
|
||||
const cantEditVerifiedData = isTaxDataCheckedChanged && !isSalesAssistant;
|
||||
const cantChangeSageData = (sageTaxTypeChanged || sageTransactionTypeChanged) && !isSalesAssistant;
|
||||
const cantEditVerifiedData = isTaxDataCheckedChanged && !editVerifiedDataWithoutTaxDataChecked;
|
||||
const cantChangeSageData = (sageTaxTypeChanged || sageTransactionTypeChanged) && !editVerifiedDataWithoutTaxDataChecked;
|
||||
|
||||
if (cantEditVerifiedData || cantChangeSageData)
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
@ -401,9 +401,10 @@ module.exports = Self => {
|
|||
Self.changeCredit = async function changeCredit(ctx, finalState, changes) {
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.options.accessToken.userId;
|
||||
const accessToken = {req: {accessToken: ctx.options.accessToken} };
|
||||
|
||||
const isFinancialBoss = await models.VnUser.hasRole(userId, 'financialBoss', ctx.options);
|
||||
if (!isFinancialBoss) {
|
||||
const canEditCredit = await models.ACL.checkAccessAcl(accessToken, 'Client', 'editCredit', 'WRITE');
|
||||
if (!canEditCredit) {
|
||||
const lastCredit = await models.ClientCredit.findOne({
|
||||
where: {
|
||||
clientFk: finalState.id
|
||||
|
@ -412,10 +413,9 @@ module.exports = Self => {
|
|||
}, ctx.options);
|
||||
|
||||
const lastAmount = lastCredit && lastCredit.amount;
|
||||
const lastWorkerId = lastCredit && lastCredit.workerFk;
|
||||
const lastWorkerIsFinancialBoss = await models.VnUser.hasRole(lastWorkerId, 'financialBoss', ctx.options);
|
||||
const lastCreditIsNotEditable = !await models.ACL.checkAccessAcl(accessToken, 'Client', 'isNotEditableCredit', 'WRITE');
|
||||
|
||||
if (lastAmount == 0 && lastWorkerIsFinancialBoss)
|
||||
if (lastAmount == 0 && lastCreditIsNotEditable)
|
||||
throw new UserError(`You can't change the credit set to zero from a financialBoss`);
|
||||
|
||||
const creditLimits = await models.ClientCreditLimit.find({
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "payMethod"
|
||||
"table": "payMethod"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
|
|
|
@ -43,6 +43,10 @@
|
|||
{{$ctrl.client.salesPersonUser.name}}
|
||||
</span>
|
||||
</vn-label-value>
|
||||
<vn-label-value
|
||||
label="Business type"
|
||||
value="{{$ctrl.client.businessType.description}}">
|
||||
</vn-label-value>
|
||||
</div>
|
||||
<div class="icons">
|
||||
<vn-icon
|
||||
|
|
|
@ -8,3 +8,4 @@ Client invoices list: Listado de facturas del cliente
|
|||
Pay method: Forma de pago
|
||||
Unpaid Dated: "Fecha: {{dated | date:'dd/MM/yyyy'}}"
|
||||
Unpaid Amount: "Importe: {{amount | currency: 'EUR':2}}"
|
||||
Business type: Tipo de negocio
|
|
@ -2,6 +2,11 @@
|
|||
vn-id="watcher"
|
||||
data="$ctrl.dms">
|
||||
</vn-watcher>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form
|
||||
name="form"
|
||||
ng-submit="$ctrl.onSubmit()"
|
||||
|
@ -29,7 +34,7 @@
|
|||
<vn-autocomplete vn-one required="true"
|
||||
label="Warehouse"
|
||||
ng-model="$ctrl.dms.warehouseId"
|
||||
url="Warehouses"
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
|
|
|
@ -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": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"BuyConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"ItemMatchProperties": {
|
||||
"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": {
|
||||
"type": "number"
|
||||
},
|
||||
"price1": {
|
||||
"type": "number"
|
||||
},
|
||||
"price2": {
|
||||
"type": "number"
|
||||
},
|
||||
|
@ -47,7 +50,44 @@
|
|||
},
|
||||
"weight": {
|
||||
"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": {
|
||||
"entry": {
|
||||
|
@ -64,6 +104,16 @@
|
|||
"type": "belongsTo",
|
||||
"model": "Packaging",
|
||||
"foreignKey": "packageFk"
|
||||
},
|
||||
"worker": {
|
||||
"type": "belongsTo",
|
||||
"model": "Worker",
|
||||
"foreignKey": "workerFk"
|
||||
},
|
||||
"delivery": {
|
||||
"type": "belongsTo",
|
||||
"model": "Delivery",
|
||||
"foreignKey": "deliveryFk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ module.exports = Self => {
|
|||
require('../methods/entry/importBuysPreview')(Self);
|
||||
require('../methods/entry/lastItemBuys')(Self);
|
||||
require('../methods/entry/entryOrderPdf')(Self);
|
||||
require('../methods/entry/addFromPackaging')(Self);
|
||||
require('../methods/entry/addFromBuy')(Self);
|
||||
|
||||
Self.observe('before save', async function(ctx, options) {
|
||||
if (ctx.isNewInstance) return;
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
form="form"
|
||||
save="patch">
|
||||
</vn-watcher>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form name="form" ng-submit="watcher.submit()" class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
|
@ -152,14 +157,14 @@
|
|||
<vn-autocomplete
|
||||
label="Warehouse Out"
|
||||
ng-model="$ctrl.travelFilterParams.warehouseOutFk"
|
||||
url="Warehouses"
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
label="Warehouse In"
|
||||
ng-model="$ctrl.travelFilterParams.warehouseInFk"
|
||||
url="Warehouses"
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
|
|
|
@ -25,7 +25,6 @@ module.exports = Self => {
|
|||
|
||||
Self.createPdf = async function(ctx, id, options) {
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
|
||||
if (process.env.NODE_ENV == 'test')
|
||||
throw new UserError(`Action not allowed on the test environment`);
|
||||
|
@ -43,9 +42,9 @@ module.exports = Self => {
|
|||
|
||||
try {
|
||||
const invoiceOut = await Self.findById(id, null, myOptions);
|
||||
const hasInvoicing = await models.VnUser.hasRole(userId, 'invoicing', myOptions);
|
||||
const canCreatePdf = await models.ACL.checkAccessAcl(ctx, 'InvoiceOut', 'canCreatePdf', 'WRITE');
|
||||
|
||||
if (invoiceOut.hasPdf && !hasInvoicing)
|
||||
if (invoiceOut.hasPdf && !canCreatePdf)
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
||||
await invoiceOut.updateAttributes({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name: botanical
|
||||
name: botanical data
|
||||
columns:
|
||||
itemFk: item
|
||||
genusFk: genus
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name: botánico
|
||||
name: datos botánicos
|
||||
columns:
|
||||
itemFk: artículo
|
||||
genusFk: género
|
||||
|
|
|
@ -35,7 +35,7 @@ columns:
|
|||
packingOut: packing out
|
||||
hasMinPrice: has min price
|
||||
isFragile: fragile
|
||||
isFloramondo: is floramondo
|
||||
isFloramondo: floramondo
|
||||
packingShelve: packing shelve
|
||||
isLaid: laid
|
||||
inkFk: ink
|
||||
|
|
|
@ -35,7 +35,7 @@ columns:
|
|||
packingOut: empaquetar
|
||||
hasMinPrice: tiene precio mínimo
|
||||
isFragile: frágil
|
||||
isFloramondo: es floramondo
|
||||
isFloramondo: floramondo
|
||||
packingShelve: estantería embalaje
|
||||
isLaid: puesto
|
||||
inkFk: tinta
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<vn-descriptor-content
|
||||
module="item"
|
||||
description="$ctrl.item.name"
|
||||
|
@ -108,7 +113,9 @@
|
|||
<vn-autocomplete
|
||||
label="Warehouse"
|
||||
ng-model="$ctrl.warehouseFk"
|
||||
url="Warehouses">
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value="id">
|
||||
</vn-autocomplete>
|
||||
</tpl-body>
|
||||
<tpl-buttons>
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<div class="search-panel">
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal class="vn-px-lg vn-pt-lg">
|
||||
<vn-textfield
|
||||
|
@ -35,7 +40,9 @@
|
|||
vn-one
|
||||
label="Warehouse"
|
||||
ng-model="filter.warehouseFk"
|
||||
url="Warehouses">
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal class="vn-px-lg">
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<div class="search-panel">
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal class="vn-px-lg vn-pt-lg">
|
||||
<vn-textfield
|
||||
|
@ -83,7 +88,7 @@
|
|||
vn-one
|
||||
label="Warehouse"
|
||||
ng-model="filter.warehouseFk"
|
||||
url="Warehouses">
|
||||
data="warehouses">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<div class="search-panel">
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Agencies"
|
||||
data="agencies">
|
||||
</vn-crud-model>
|
||||
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal class="vn-px-lg vn-pt-lg">
|
||||
<vn-textfield
|
||||
|
@ -17,7 +22,7 @@
|
|||
ng-model="filter.agencyModeFk">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
url="Agencies"
|
||||
data="agencies"
|
||||
label="Agency Agreement"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<div class="search-panel">
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal class="vn-px-lg vn-pt-lg">
|
||||
<vn-textfield
|
||||
|
@ -79,7 +84,7 @@
|
|||
vn-one
|
||||
label="Warehouse"
|
||||
ng-model="filter.warehouseFk"
|
||||
url="Warehouses"
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
|
|
|
@ -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",
|
||||
"required": false
|
||||
},
|
||||
"printerFk": {
|
||||
"mainPrinterFk": {
|
||||
"type": "number",
|
||||
"required": false
|
||||
},
|
||||
|
@ -69,4 +69,4 @@
|
|||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -11,6 +11,7 @@ module.exports = Self => {
|
|||
require('../methods/supplier/campaignMetricsPdf')(Self);
|
||||
require('../methods/supplier/campaignMetricsEmail')(Self);
|
||||
require('../methods/supplier/newSupplier')(Self);
|
||||
require('../methods/supplier/getItemsPackaging')(Self);
|
||||
|
||||
Self.validatesPresenceOf('name', {
|
||||
message: 'The social name cannot be empty'
|
||||
|
@ -99,18 +100,20 @@ module.exports = Self => {
|
|||
|
||||
Self.observe('before save', async function(ctx) {
|
||||
if (ctx.isNewInstance) return;
|
||||
const loopbackContext = LoopBackContext.getCurrentContext();
|
||||
const changes = ctx.data || ctx.instance;
|
||||
const orgData = ctx.currentInstance;
|
||||
const userId = loopbackContext.active.accessToken.userId;
|
||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||
const accessToken = {req: loopBackContext.active.accessToken};
|
||||
|
||||
const editPayMethodCheck =
|
||||
await Self.app.models.ACL.checkAccessAcl(accessToken, 'Supplier', 'editPayMethodCheck', 'WRITE');
|
||||
|
||||
const isNotFinancial = !await Self.app.models.VnUser.hasRole(userId, 'financial');
|
||||
const isPayMethodChecked = changes.isPayMethodChecked || orgData.isPayMethodChecked;
|
||||
const hasChanges = orgData && changes;
|
||||
const isPayMethodCheckedChanged = hasChanges
|
||||
&& orgData.isPayMethodChecked != isPayMethodChecked;
|
||||
|
||||
if (isNotFinancial && isPayMethodCheckedChanged)
|
||||
if (!editPayMethodCheck && isPayMethodCheckedChanged)
|
||||
throw new UserError('You can not modify is pay method checked');
|
||||
});
|
||||
|
||||
|
|
|
@ -12,3 +12,5 @@ columns:
|
|||
hostFk: PC
|
||||
isBox: box
|
||||
itemPackingTypeFk: packing type
|
||||
externalId: external id
|
||||
stateTypeFk: status
|
||||
|
|
|
@ -12,3 +12,5 @@ columns:
|
|||
hostFk: PC
|
||||
isBox: caja
|
||||
itemPackingTypeFk: tipo empaquetado
|
||||
externalId: id externo
|
||||
stateTypeFk: estado
|
||||
|
|
|
@ -106,6 +106,9 @@ module.exports = Self => {
|
|||
}
|
||||
}
|
||||
|
||||
const query = `CALL vn.ticket_recalc(?)`;
|
||||
await Self.rawSql(query, [refundTicket.id], myOptions);
|
||||
|
||||
if (tx) await tx.commit();
|
||||
|
||||
return refundTicket;
|
||||
|
|
|
@ -64,7 +64,7 @@ describe('sale updateQuantity()', () => {
|
|||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const isRoleAdvanced = await models.Ticket.isRoleAdvanced(ctx, options);
|
||||
const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
|
||||
|
||||
expect(isRoleAdvanced).toEqual(true);
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ module.exports = Self => {
|
|||
|
||||
const sale = await models.Sale.findById(id, filter, myOptions);
|
||||
|
||||
const isRoleAdvanced = await models.Ticket.isRoleAdvanced(ctx, myOptions);
|
||||
const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
|
||||
if (newQuantity > sale.quantity && !isRoleAdvanced)
|
||||
throw new UserError('The new quantity should be smaller than the old one');
|
||||
|
||||
|
|
|
@ -19,13 +19,11 @@ module.exports = Self => {
|
|||
|
||||
Self.editableStates = async(ctx, filter, options) => {
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const myOptions = {...(options || {})};
|
||||
|
||||
const isProduction = await models.VnUser.hasRole(userId, 'production', myOptions);
|
||||
const isAdministrative = await models.VnUser.hasRole(userId, 'administrative', myOptions);
|
||||
const seeEditableStates = await models.ACL.checkAccessAcl(ctx, 'State', 'seeEditableStates', 'READ');
|
||||
|
||||
if (!isProduction && !isAdministrative)
|
||||
if (!seeEditableStates)
|
||||
filter = mergeFilters(filter, {where: {alertLevel: 0}});
|
||||
|
||||
const states = await models.State.find(filter, myOptions);
|
||||
|
|
|
@ -19,22 +19,23 @@ module.exports = Self => {
|
|||
});
|
||||
|
||||
Self.isEditable = async(ctx, stateId, options) => {
|
||||
const accessToken = ctx.req.accessToken;
|
||||
const models = Self.app.models;
|
||||
const userId = accessToken.userId;
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const isProduction = await models.VnUser.hasRole(userId, 'production', myOptions);
|
||||
const isSalesPerson = await models.VnUser.hasRole(userId, 'salesPerson', myOptions);
|
||||
const isAdministrative = await models.VnUser.hasRole(userId, 'administrative', myOptions);
|
||||
const isAllEditable = await models.ACL.checkAccessAcl(ctx, 'State', 'isAllEditable', 'READ');
|
||||
|
||||
const state = await models.State.findById(stateId, null, myOptions);
|
||||
const isSomeEditable = (
|
||||
await models.ACL.checkAccessAcl(ctx, 'State', 'isSomeEditable', 'READ')
|
||||
&& (
|
||||
state.code == 'PICKER_DESIGNED' || state.code == 'PRINTED'
|
||||
)
|
||||
);
|
||||
|
||||
const salesPersonAllowed = (isSalesPerson && (state.code == 'PICKER_DESIGNED' || state.code == 'PRINTED'));
|
||||
|
||||
const isAllowed = isProduction || isAdministrative || salesPersonAllowed || state.alertLevel == 0;
|
||||
const isAllowed = isAllEditable || isSomeEditable || state.alertLevel == 0;
|
||||
return isAllowed;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -121,8 +121,8 @@ module.exports = Self => {
|
|||
if (!isEditable)
|
||||
throw new UserError(`The sales of this ticket can't be modified`);
|
||||
|
||||
const isDeliveryBoss = await models.VnUser.hasRole(userId, 'deliveryBoss', myOptions);
|
||||
if (!isDeliveryBoss) {
|
||||
const editZone = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'editZone', 'WRITE');
|
||||
if (!editZone) {
|
||||
const zoneShipped = await models.Agency.getShipped(
|
||||
ctx,
|
||||
args.landed,
|
||||
|
|
|
@ -30,7 +30,7 @@ module.exports = Self => {
|
|||
where: {ticketFk: id}
|
||||
}, myOptions);
|
||||
|
||||
const isRoleAdvanced = await models.Ticket.isRoleAdvanced(ctx, myOptions);
|
||||
const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
|
||||
|
||||
const alertLevel = state ? state.alertLevel : null;
|
||||
const ticket = await models.Ticket.findById(id, {
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('isRoleAdvanced', {
|
||||
description: 'Check if a ticket is editable',
|
||||
accessType: 'READ',
|
||||
returns: {
|
||||
type: 'boolean',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/isRoleAdvanced`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.isRoleAdvanced = async(ctx, options) => {
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const isSalesAssistant = await models.VnUser.hasRole(userId, 'salesAssistant', myOptions);
|
||||
const isDeliveryBoss = await models.VnUser.hasRole(userId, 'deliveryBoss', myOptions);
|
||||
const isBuyer = await models.VnUser.hasRole(userId, 'buyer', myOptions);
|
||||
const isClaimManager = await models.VnUser.hasRole(userId, 'claimManager', myOptions);
|
||||
|
||||
const isRoleAdvanced = isSalesAssistant || isDeliveryBoss || isBuyer || isClaimManager;
|
||||
|
||||
return isRoleAdvanced;
|
||||
};
|
||||
};
|
|
@ -60,7 +60,6 @@ module.exports = Self => {
|
|||
Self.priceDifference = async(ctx, options) => {
|
||||
const args = ctx.args;
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const myOptions = {};
|
||||
let tx;
|
||||
|
||||
|
@ -78,8 +77,8 @@ module.exports = Self => {
|
|||
if (!isEditable)
|
||||
throw new UserError(`The sales of this ticket can't be modified`);
|
||||
|
||||
const isDeliveryBoss = await models.VnUser.hasRole(userId, 'deliveryBoss', myOptions);
|
||||
if (!isDeliveryBoss) {
|
||||
const editZone = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'editZone', 'WRITE');
|
||||
if (!editZone) {
|
||||
const zoneShipped = await models.Agency.getShipped(
|
||||
ctx,
|
||||
args.landed,
|
||||
|
|
|
@ -39,7 +39,6 @@ module.exports = Self => {
|
|||
const ticketToDelete = await models.Ticket.findById(id, {fields: ['isDeleted']}, myOptions);
|
||||
if (ticketToDelete.isDeleted) return false;
|
||||
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const isEditable = await Self.isEditable(ctx, id, myOptions);
|
||||
|
||||
if (!isEditable)
|
||||
|
@ -54,7 +53,8 @@ module.exports = Self => {
|
|||
throw new UserError($t('Tickets with associated refunds', {id: ticketRefunds[0].id}));
|
||||
|
||||
// Check if has sales with shelving
|
||||
const isSalesAssistant = await models.VnUser.hasRole(userId, 'salesAssistant', myOptions);
|
||||
const canDeleteTicketWithPartPrepared =
|
||||
await models.ACL.checkAccessAcl(ctx, 'Ticket', 'deleteTicketWithPartPrepared', 'WRITE');
|
||||
const sales = await models.Sale.find({
|
||||
include: {relation: 'itemShelvingSale'},
|
||||
where: {ticketFk: id}
|
||||
|
@ -63,7 +63,7 @@ module.exports = Self => {
|
|||
return sale.itemShelvingSale();
|
||||
});
|
||||
|
||||
if (hasItemShelvingSales && !isSalesAssistant)
|
||||
if (hasItemShelvingSales && !canDeleteTicketWithPartPrepared)
|
||||
throw new UserError(`You cannot delete a ticket that part of it is being prepared`);
|
||||
|
||||
// Check for existing claim
|
||||
|
|
|
@ -3,6 +3,7 @@ let UserError = require('vn-loopback/util/user-error');
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('transferSales', {
|
||||
description: 'Transfer sales to a new or a given ticket',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
|
|
|
@ -85,17 +85,14 @@ module.exports = Self => {
|
|||
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const isLocked = await models.Ticket.isLocked(id, myOptions);
|
||||
const roles = await models.VnUser.getRoles(userId, myOptions);
|
||||
const hasAllowedRoles = roles.filter(role =>
|
||||
role == 'salesPerson' || role == 'claimManager'
|
||||
);
|
||||
const canEditDiscount = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'editDiscount');
|
||||
|
||||
const state = await Self.app.models.TicketState.findOne({
|
||||
where: {ticketFk: id}
|
||||
}, myOptions);
|
||||
const alertLevel = state ? state.alertLevel : null;
|
||||
|
||||
if (isLocked || (!hasAllowedRoles && alertLevel > 0))
|
||||
if (isLocked || (!canEditDiscount && alertLevel > 0))
|
||||
throw new UserError(`The sales of this ticket can't be modified`);
|
||||
|
||||
const usesMana = await models.Sale.usesMana(ctx, myOptions);
|
||||
|
|
|
@ -36,7 +36,6 @@ module.exports = function(Self) {
|
|||
require('../methods/ticket/getTicketsFuture')(Self);
|
||||
require('../methods/ticket/merge')(Self);
|
||||
require('../methods/ticket/getTicketsAdvance')(Self);
|
||||
require('../methods/ticket/isRoleAdvanced')(Self);
|
||||
require('../methods/ticket/collectionLabel')(Self);
|
||||
require('../methods/ticket/expeditionPalletLabel')(Self);
|
||||
require('../methods/ticket/saveSign')(Self);
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<div class="search-panel">
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal class="vn-px-lg vn-pt-lg">
|
||||
<vn-date-picker
|
||||
|
@ -49,7 +54,9 @@
|
|||
vn-one
|
||||
label="Warehouse"
|
||||
ng-model="filter.warehouseFk"
|
||||
url="Warehouses"
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
required="true">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
order="name"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form name="form">
|
||||
<vn-card class="vn-w-md vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
|
@ -25,7 +30,7 @@
|
|||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
required="true"
|
||||
url="Warehouses"
|
||||
data="warehouses"
|
||||
label="Warehouse"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
vn-id="watcher"
|
||||
data="$ctrl.dms">
|
||||
</vn-watcher>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form
|
||||
name="form"
|
||||
ng-submit="$ctrl.onSubmit()"
|
||||
|
@ -29,7 +34,7 @@
|
|||
<vn-autocomplete vn-one required="true"
|
||||
label="Warehouse"
|
||||
ng-model="$ctrl.dms.warehouseId"
|
||||
url="Warehouses"
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<div class="search-panel">
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal class="vn-px-lg vn-pt-lg">
|
||||
<vn-date-picker
|
||||
|
@ -83,7 +88,9 @@
|
|||
vn-one
|
||||
label="Warehouse"
|
||||
ng-model="filter.warehouseFk"
|
||||
url="Warehouses"
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
required="true">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
|
|
|
@ -61,20 +61,20 @@
|
|||
</vn-th>
|
||||
<vn-th shrink></vn-th>
|
||||
<vn-th shrink></vn-th>
|
||||
<vn-th shrink>Visible</vn-th>
|
||||
<vn-th shrink>Available</vn-th>
|
||||
<vn-th>Id</vn-th>
|
||||
<vn-th shrink>Quantity</vn-th>
|
||||
<vn-th expand>Item</vn-th>
|
||||
<vn-th number>Price</vn-th>
|
||||
<vn-th number>Disc</vn-th>
|
||||
<vn-th number>Amount</vn-th>
|
||||
<vn-th shrink>Packaging</vn-th>
|
||||
<vn-th ng-click="$ctrl.sortBy('visible')" field="visible" shrink>Visible</vn-th>
|
||||
<vn-th ng-click="$ctrl.sortBy('available')" field="available" shrink>Available</vn-th>
|
||||
<vn-th ng-click="$ctrl.sortBy('itemFk')" field="itemFk">Id</vn-th>
|
||||
<vn-th ng-click="$ctrl.sortBy('quantity')" field="quantity" shrink>Quantity</vn-th>
|
||||
<vn-th ng-click="$ctrl.sortBy('concept')" field="concept" expand>Item</vn-th>
|
||||
<vn-th ng-click="$ctrl.sortBy('price')" field="price" number>Price</vn-th>
|
||||
<vn-th ng-click="$ctrl.sortBy('discount')" field="discount" number>Disc</vn-th>
|
||||
<vn-th ng-click="$ctrl.sortBy('amount')" field="amount" number>Amount</vn-th>
|
||||
<vn-th ng-click="$ctrl.sortBy('itemPackingTypeFk')" field="itemPackingTypeFk" shrink>Packaging</vn-th>
|
||||
<vn-th shrink></vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="sale in $ctrl.sales">
|
||||
<vn-tr ng-repeat="sale in $ctrl.sales | orderBy: $ctrl.propertyName:$ctrl.reverse">
|
||||
<vn-td shrink>
|
||||
<vn-check tabindex="-1"
|
||||
ng-model="sale.checked">
|
||||
|
@ -491,7 +491,7 @@
|
|||
<vn-confirm
|
||||
vn-id="claimSure"
|
||||
message="Do you want to create a claim?"
|
||||
on-accept="$ctrl.createClaim()">
|
||||
on-accept="$ctrl.onCreateClaimAccepted()">
|
||||
</vn-confirm>
|
||||
|
||||
<vn-menu vn-id="moreOptions">
|
||||
|
@ -513,7 +513,7 @@
|
|||
</vn-item>
|
||||
<vn-item translate
|
||||
name="claim"
|
||||
ng-click="claimSure.show()"
|
||||
ng-click="$ctrl.createClaim()"
|
||||
ng-if="$ctrl.isClaimable">
|
||||
Add claim
|
||||
</vn-item>
|
||||
|
|
|
@ -10,6 +10,11 @@ class Controller extends Section {
|
|||
this.getConfig();
|
||||
}
|
||||
|
||||
sortBy(propertyName) {
|
||||
this.reverse = (this.propertyName === propertyName) ? !this.reverse : false;
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
get manaCode() {
|
||||
return this._manaCode;
|
||||
}
|
||||
|
@ -203,7 +208,7 @@ class Controller extends Section {
|
|||
if (pastDays >= this.ticketConfig[0].daysForWarningClaim)
|
||||
this.$.claimConfirm.show();
|
||||
else
|
||||
this.onCreateClaimAccepted();
|
||||
this.$.claimSure.show();
|
||||
}
|
||||
|
||||
onCreateClaimAccepted() {
|
||||
|
|
|
@ -758,5 +758,15 @@ describe('Ticket', () => {
|
|||
expect(controller.$.editDiscount.hide).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('sortBy()', () => {
|
||||
it('should set reverse and propertyName properties', () => {
|
||||
const propertyName = 'id';
|
||||
controller.sortBy(propertyName);
|
||||
|
||||
expect(controller.propertyName).toEqual(propertyName);
|
||||
expect(controller.reverse).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<div class="search-panel">
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal class="vn-px-lg vn-pt-lg">
|
||||
<vn-textfield
|
||||
|
@ -102,7 +107,7 @@
|
|||
vn-one
|
||||
label="Warehouse"
|
||||
ng-model="filter.warehouseFk"
|
||||
url="Warehouses">
|
||||
data="warehouses">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
"TravelThermograph": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"TravelConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Temperature": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,9 @@
|
|||
<div class="search-panel">
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="Warehouses"
|
||||
data="warehouses">
|
||||
</vn-crud-model>
|
||||
<form ng-submit="$ctrl.onSearch()">
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
|
@ -52,14 +57,14 @@
|
|||
<vn-autocomplete vn-one
|
||||
label="Warehouse Out"
|
||||
ng-model="filter.warehouseOutFk"
|
||||
url="Warehouses"
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
label="Warehouse In"
|
||||
ng-model="filter.warehouseInFk"
|
||||
url="Warehouses"
|
||||
data="warehouses"
|
||||
show-field="name"
|
||||
value-field="id">
|
||||
</vn-autocomplete>
|
||||
|
|
|
@ -4,9 +4,20 @@ columns:
|
|||
companyCodeFk: company
|
||||
started: started
|
||||
ended: ended
|
||||
workerBusiness: business
|
||||
reasonEndFk: ending reason
|
||||
payedHolidays: payed holidays
|
||||
occupationCodeFk: occupation
|
||||
workerFk: worker
|
||||
notes: notes
|
||||
departmentFk: department
|
||||
workerBusinessProfessionalCategoryFk: professional category
|
||||
calendarTypeFk: calendar type
|
||||
isHourlyLabor: hourly labor
|
||||
workcenterFk: workcenter
|
||||
rate: rate
|
||||
workerBusinessCategoryFk: category
|
||||
workerBusinessTypeFk: type
|
||||
amount: amount
|
||||
workerBusinessAgreementFk: agreement
|
||||
basicSalary: salary
|
||||
|
|
|
@ -4,9 +4,20 @@ columns:
|
|||
companyCodeFk: empresa
|
||||
started: iniciado
|
||||
ended: finalizado
|
||||
workerBusiness: negocio
|
||||
reasonEndFk: motivo finalización
|
||||
payedHolidays: vacaciones pagadas
|
||||
occupationCodeFk: ocupación
|
||||
workerFk: trabajador
|
||||
notes: notas
|
||||
departmentFk: departamento
|
||||
workerBusinessProfessionalCategoryFk: categoría profesional
|
||||
calendarTypeFk: tipo calendario
|
||||
isHourlyLabor: horario laboral
|
||||
workcenterFk: centro de trabajo
|
||||
rate: tarifa
|
||||
workerBusinessCategoryFk: categoría
|
||||
workerBusinessTypeFk: tipo
|
||||
amount: salario
|
||||
workerBusinessAgreementFk: acuerdo
|
||||
basicSalary: salario base
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: document
|
||||
columns:
|
||||
id: id
|
||||
dmsFk: dms
|
||||
workerFk: worker
|
||||
worker: worker
|
||||
document: document
|
||||
isReadableByWorker: readable by worker
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue