Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5595-modify-loggable-tables-in-procs
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Alexandre Riera 2023-06-07 08:50:53 +02:00
commit 7121b1e516
79 changed files with 12130 additions and 13976 deletions

View File

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

View File

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

View File

@ -53,9 +53,9 @@ module.exports = Self => {
const fileNames = updatableFiles.map(file => file.name); const fileNames = updatableFiles.map(file => file.name);
const tables = await Self.rawSql(` const tables = await Self.rawSql(`
SELECT fileName, toTable, file SELECT fileName, toTable, file
FROM edi.tableConfig FROM edi.tableConfig
WHERE file IN (?)`, [fileNames], options); WHERE file IN (?)`, [fileNames], options);
for (const table of tables) { for (const table of tables) {
const fileName = table.file; const fileName = table.file;
@ -85,9 +85,9 @@ module.exports = Self => {
for (const file of updatableFiles) { for (const file of updatableFiles) {
console.log(`Updating file ${file.name} checksum...`); console.log(`Updating file ${file.name} checksum...`);
await Self.rawSql(` await Self.rawSql(`
UPDATE edi.fileConfig UPDATE edi.fileConfig
SET checksum = ? SET checksum = ?
WHERE name = ?`, WHERE name = ?`,
[file.checksum, file.name], options); [file.checksum, file.name], options);
} }
@ -121,7 +121,8 @@ module.exports = Self => {
host: ftpConfig.host, host: ftpConfig.host,
username: ftpConfig.user, username: ftpConfig.user,
password: ftpConfig.password, password: ftpConfig.password,
procotol: 'ftp' procotol: 'ftp',
additionalLftpCommands: 'set ssl:verify-certificate no'
}); });
} }
@ -132,7 +133,7 @@ module.exports = Self => {
const ftpClient = await getFtpClient(); const ftpClient = await getFtpClient();
console.debug(`Checking checksum for file ${file.name}...`); console.debug(`Checking checksum for file ${file.name}...`);
ftpClient.cat(`codes/${file.name}.txt`); ftpClient.cat(`codes/${file.name}.TXT`);
const response = await new Promise((resolve, reject) => { const response = await new Promise((resolve, reject) => {
ftpClient.exec((err, response) => { ftpClient.exec((err, response) => {
@ -227,10 +228,10 @@ module.exports = Self => {
await Self.rawSql(sqlTemplate, [filePath], options); await Self.rawSql(sqlTemplate, [filePath], options);
await Self.rawSql(` await Self.rawSql(`
UPDATE edi.tableConfig UPDATE edi.tableConfig
SET updated = ? SET updated = ?
WHERE fileName = ? WHERE fileName = ?
`, [Date.vnNew(), baseName], options); `, [Date.vnNew(), baseName], options);
} }
console.log(`Updated table ${toTable}\n`); console.log(`Updated table ${toTable}\n`);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -179,6 +179,8 @@ INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`, `ipAd
(2, 'printer2', 'path2', 1, 1 , NULL), (2, 'printer2', 'path2', 1, 1 , NULL),
(4, 'printer4', 'path4', 0, NULL, '10.1.10.4'); (4, 'printer4', 'path4', 0, NULL, '10.1.10.4');
UPDATE `vn`.`sector` SET mainPrinterFk = 1 WHERE id = 1;
INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossFk`, `phone`, `sectorFk`, `labelerFk`) INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossFk`, `phone`, `sectorFk`, `labelerFk`)
VALUES VALUES
(1106, 'LGN', 'David Charles', 'Haller', 1106, 19, 432978106, NULL, NULL), (1106, 'LGN', 'David Charles', 'Haller', 1106, 19, 432978106, NULL, NULL),
@ -905,7 +907,7 @@ INSERT INTO `vn`.`itemFamily`(`code`, `description`)
INSERT INTO `vn`.`item`(`id`, `typeFk`, `size`, `inkFk`, `stems`, `originFk`, `description`, `producerFk`, `intrastatFk`, `expenceFk`, INSERT INTO `vn`.`item`(`id`, `typeFk`, `size`, `inkFk`, `stems`, `originFk`, `description`, `producerFk`, `intrastatFk`, `expenceFk`,
`comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`, `packingShelve`, `weightByPiece`) `comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`, `packingShelve`, `weightByPiece`)
VALUES VALUES
(1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'VT', 0, NULL, 'V', 0, 15,3), (1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'EMB', 0, NULL, 'V', 0, 15,3),
(2, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H', 0, 10,2), (2, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H', 0, 10,2),
(3, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, 5,5), (3, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL, 0, 5,5),
(4, 1, 60, 'YEL', 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL,NULL), (4, 1, 60, 'YEL', 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL, 0, NULL,NULL),
@ -2729,6 +2731,7 @@ INSERT INTO `util`.`notification` (`id`, `name`, `description`)
VALUES VALUES
(1, 'print-email', 'notification fixture one'), (1, 'print-email', 'notification fixture one'),
(2, 'invoice-electronic', 'A electronic invoice has been generated'), (2, 'invoice-electronic', 'A electronic invoice has been generated'),
(3, 'not-main-printer-configured', 'A printer distinct than main has been configured'),
(4, 'supplier-pay-method-update', 'A supplier pay method has been updated'); (4, 'supplier-pay-method-update', 'A supplier pay method has been updated');
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`) INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
@ -2886,6 +2889,10 @@ INSERT INTO `vn`.`wagonTypeTray` (`id`, `typeFk`, `height`, `colorFk`)
(2, 1, 50, 2), (2, 1, 50, 2),
(3, 1, 0, 3); (3, 1, 0, 3);
INSERT INTO `vn`.`travelConfig` (`id`, `warehouseInFk`, `warehouseOutFk`, `agencyFk`, `companyFk`)
VALUES
(1, 1, 1, 1, 442);
INSERT INTO `vn`.`buyConfig` (`id`, `monthsAgo`)
VALUES
(1, 6);

View File

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

View File

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

View File

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

View File

@ -64,6 +64,6 @@ describe('SmartTable SearchBar integration', () => {
await page.reload({ await page.reload({
waitUntil: 'networkidle2' waitUntil: 'networkidle2'
}); });
await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '13'); await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '3');
}); });
}); });

View File

@ -88,7 +88,8 @@ describe('Item fixed prices path', () => {
it('should reload the section and check the created price has the expected ID', async() => { it('should reload the section and check the created price has the expected ID', async() => {
await page.goto(`http://localhost:5000/#!/item/fixed-price`); await page.goto(`http://localhost:5000/#!/item/fixed-price`);
await page.autocompleteSearch($.warehouseFilter, 'Warehouse one');
await page.click($.chip);
const result = await page.waitToGetProperty($.fourthItemID, 'value'); const result = await page.waitToGetProperty($.fourthItemID, 'value');
expect(result).toContain('13'); expect(result).toContain('13');

View File

@ -316,7 +316,7 @@ describe('Ticket Edit sale path', () => {
it('should confirm the transfered quantity is the correct one', async() => { it('should confirm the transfered quantity is the correct one', async() => {
const result = await page.waitToGetProperty(selectors.ticketSales.secondSaleQuantityCell, 'innerText'); const result = await page.waitToGetProperty(selectors.ticketSales.secondSaleQuantityCell, 'innerText');
expect(result).toContain('10'); expect(result).toContain('20');
}); });
it('should go back to the original ticket sales section', async() => { it('should go back to the original ticket sales section', async() => {

View File

@ -1,8 +1,7 @@
import selectors from '../../helpers/selectors.js'; import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
// 'https:// redmine.verdnatura.es/issues/5642' describe('Ticket Future path', () => {
xdescribe('Ticket Future path', () => {
let browser; let browser;
let page; let page;
let httpRequest; let httpRequest;
@ -22,7 +21,7 @@ xdescribe('Ticket Future path', () => {
await browser.close(); await browser.close();
}); });
it('should show errors snackbar because of the required data', async() => { it('should search with required data, check three last tickets and move to the future', async() => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.warehouseFk); await page.clearInput(selectors.ticketFuture.warehouseFk);
await page.waitToClick(selectors.ticketFuture.submit); await page.waitToClick(selectors.ticketFuture.submit);
@ -43,69 +42,58 @@ xdescribe('Ticket Future path', () => {
message = await page.waitForSnackbar(); message = await page.waitForSnackbar();
expect(message.text).toContain('originDated is a required argument'); expect(message.text).toContain('originDated is a required argument');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toBeDefined();
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketFuture.ipt, 'H');
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('ipt=H');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.ipt);
await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'H');
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('futureIpt=H');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.futureIpt);
await page.autocompleteSearch(selectors.ticketFuture.state, 'Free');
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('state=FREE');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.state);
await page.autocompleteSearch(selectors.ticketFuture.futureState, 'Free');
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('futureState=FREE');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.state);
await page.clearInput(selectors.ticketFuture.futureState);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.searchResult, 4);
await page.waitToClick(selectors.ticketFuture.multiCheck);
await page.waitToClick(selectors.ticketFuture.firstCheck);
await page.waitToClick(selectors.ticketFuture.moveButton);
await page.waitToClick(selectors.globalItems.acceptButton);
message = await page.waitForSnackbar();
expect(message.text).toContain('Tickets moved successfully!');
}); });
// it('should search with the required data', async() => {
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
// await page.waitToClick(selectors.ticketFuture.submit);
// expect(httpRequest).toBeDefined();
// });
// it('should search with the origin IPT', async() => {
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
// await page.autocompleteSearch(selectors.ticketFuture.ipt, 'H');
// await page.waitToClick(selectors.ticketFuture.submit);
// expect(httpRequest).toContain('ipt=H');
// });
// it('should search with the destination IPT', async() => {
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
// await page.clearInput(selectors.ticketFuture.ipt);
// await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'H');
// await page.waitToClick(selectors.ticketFuture.submit);
// expect(httpRequest).toContain('futureIpt=H');
// });
// it('should search with the origin grouped state', async() => {
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
// await page.clearInput(selectors.ticketFuture.futureIpt);
// await page.autocompleteSearch(selectors.ticketFuture.state, 'Free');
// await page.waitToClick(selectors.ticketFuture.submit);
// expect(httpRequest).toContain('state=FREE');
// });
// it('should search with the destination grouped state', async() => {
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
// await page.clearInput(selectors.ticketFuture.state);
// await page.autocompleteSearch(selectors.ticketFuture.futureState, 'Free');
// await page.waitToClick(selectors.ticketFuture.submit);
// expect(httpRequest).toContain('futureState=FREE');
// await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
// await page.clearInput(selectors.ticketFuture.futureState);
// await page.waitToClick(selectors.ticketFuture.submit);
// });
// it('should check the three last tickets and move to the future', async() => {
// await page.waitForNumberOfElements(selectors.ticketFuture.searchResult, 4);
// await page.waitToClick(selectors.ticketFuture.multiCheck);
// await page.waitToClick(selectors.ticketFuture.firstCheck);
// await page.waitToClick(selectors.ticketFuture.moveButton);
// await page.waitToClick(selectors.globalItems.acceptButton);
// const message = await page.waitForSnackbar();
// expect(message.text).toContain('Tickets moved successfully!');
// });
}); });

View File

@ -1,8 +1,7 @@
import selectors from '../../helpers/selectors.js'; import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
// 'https:// redmine.verdnatura.es/issues/5642' describe('Ticket Advance path', () => {
xdescribe('Ticket Advance path', () => {
let browser; let browser;
let page; let page;
let httpRequest; let httpRequest;
@ -22,7 +21,7 @@ xdescribe('Ticket Advance path', () => {
await browser.close(); await browser.close();
}); });
it('should show errors snackbar because of the required data', async() => { it('should search with the required data, check the first ticket and move to the present', async() => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.warehouseFk); await page.clearInput(selectors.ticketAdvance.warehouseFk);
@ -44,45 +43,37 @@ xdescribe('Ticket Advance path', () => {
message = await page.waitForSnackbar(); message = await page.waitForSnackbar();
expect(message.text).toContain('dateFuture is a required argument'); expect(message.text).toContain('dateFuture is a required argument');
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.submit);
expect(httpRequest).toBeDefined();
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'H');
await page.waitToClick(selectors.ticketAdvance.submit);
expect(httpRequest).toContain('futureIpt=H');
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.futureIpt);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'H');
await page.waitToClick(selectors.ticketAdvance.submit);
expect(httpRequest).toContain('ipt=H');
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.ipt);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitToClick(selectors.ticketAdvance.firstCheck);
await page.waitToClick(selectors.ticketAdvance.moveButton);
await page.waitToClick(selectors.ticketAdvance.acceptButton);
message = await page.waitForSnackbar();
expect(message.text).toContain('Tickets moved successfully!');
}); });
// it('should search with the required data', async() => {
// await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
// await page.waitToClick(selectors.ticketAdvance.submit);
// expect(httpRequest).toBeDefined();
// });
// it('should search with the origin IPT', async() => {
// await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
// await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'H');
// await page.waitToClick(selectors.ticketAdvance.submit);
// expect(httpRequest).toContain('futureIpt=H');
// await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
// await page.clearInput(selectors.ticketAdvance.futureIpt);
// await page.waitToClick(selectors.ticketAdvance.submit);
// });
// it('should search with the destination IPT', async() => {
// await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
// await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'H');
// await page.waitToClick(selectors.ticketAdvance.submit);
// expect(httpRequest).toContain('ipt=H');
// await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
// await page.clearInput(selectors.ticketAdvance.ipt);
// await page.waitToClick(selectors.ticketAdvance.submit);
// });
// it('should check the first ticket and move to the present', async() => {
// await page.waitToClick(selectors.ticketAdvance.firstCheck);
// await page.waitToClick(selectors.ticketAdvance.moveButton);
// await page.waitToClick(selectors.ticketAdvance.acceptButton);
// const message = await page.waitForSnackbar();
// expect(message.text).toContain('Tickets moved successfully!');
// });
}); });

View File

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

View File

@ -20,8 +20,6 @@ class Controller {
name: config.languages[code] ? config.languages[code] : code name: config.languages[code] ? config.languages[code] : code
}); });
} }
vnConfig.initialize();
} }
set lang(value) { set lang(value) {

View File

@ -10,6 +10,9 @@ function config($stateProvider, $urlRouterProvider) {
.state('layout', { .state('layout', {
abstract: true, abstract: true,
template: '<vn-layout></vn-layout>', template: '<vn-layout></vn-layout>',
resolve: {
config: ['vnConfig', vnConfig => vnConfig.initialize()]
}
}) })
.state('outLayout', { .state('outLayout', {
abstract: true, abstract: true,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -56,7 +56,7 @@
"type": "number", "type": "number",
"required": false "required": false
}, },
"printerFk": { "mainPrinterFk": {
"type": "number", "type": "number",
"required": false "required": false
}, },

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,62 @@
const models = require('vn-loopback/server/server').models;
describe('Operator', () => {
const authorFk = 9;
const sectorId = 1;
const mainPrinter = 1;
const notificationName = 'not-main-printer-configured';
const operator = {
workerFk: 1,
trainFk: 1,
itemPackingTypeFk: 'H',
warehouseFk: 1,
sectorFk: sectorId
};
async function createOperator(labelerFk, options) {
operator.labelerFk = labelerFk;
await models.Operator.create(operator, options);
return models.NotificationQueue.findOne({
where: {
notificationFk: notificationName
}
}, options);
}
it('should create notification when configured a not main printer in the sector', async() => {
const tx = await models.Operator.beginTransaction({});
try {
const options = {transaction: tx, accessToken: {userId: authorFk}};
const notificationQueue = await createOperator(2, options);
const params = JSON.parse(notificationQueue.params);
expect(notificationQueue.notificationFk).toEqual(notificationName);
expect(notificationQueue.authorFk).toEqual(authorFk);
expect(params.labelerId).toEqual(2);
expect(params.sectorId).toEqual(1);
expect(params.workerId).toEqual(9);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should not create notification when configured the main printer in the sector', async() => {
const tx = await models.Operator.beginTransaction({});
try {
const options = {transaction: tx, accessToken: {userId: authorFk}};
const notificationQueue = await createOperator(mainPrinter, options);
expect(notificationQueue).toEqual(null);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

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

View File

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

View File

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

View File

@ -0,0 +1,28 @@
module.exports = function(Self) {
Self.observe('after save', async function(ctx) {
const instance = ctx.instance;
const models = Self.app.models;
const options = ctx.options;
if (!instance.sectorFk || !instance.labelerFk) return;
const sector = await models.Sector.findById(instance.sectorFk, {
fields: ['mainPrinterFk']
}, options);
if (sector.mainPrinterFk && sector.mainPrinterFk != instance.labelerFk) {
const userId = ctx.options.accessToken.userId;
await models.NotificationQueue.create({
notificationFk: 'not-main-printer-configured',
authorFk: userId,
params: JSON.stringify(
{
'labelerId': instance.labelerFk,
'sectorId': instance.sectorFk,
'workerId': userId
}
)
}, options);
}
});
};

View File

@ -27,10 +27,10 @@
"type": "number", "type": "number",
"required": true "required": true
}, },
"sectorFk ": { "sectorFk": {
"type": "number" "type": "number"
}, },
"labelerFk ": { "labelerFk": {
"type": "number" "type": "number"
} }
}, },

View File

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

View File

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

24871
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -0,0 +1,11 @@
const Stylesheet = require(`vn-print/core/stylesheet`);
const path = require('path');
const vnPrintPath = path.resolve('print');
module.exports = new Stylesheet([
`${vnPrintPath}/common/css/spacing.css`,
`${vnPrintPath}/common/css/misc.css`,
`${vnPrintPath}/common/css/layout.css`,
`${vnPrintPath}/common/css/email.css`])
.mergeStyles();

View File

@ -0,0 +1,3 @@
subject: Not main printer configured
title: Not main printer configured
description: 'Printer #{0} {1} has been configured in sector #{2} {3} (the main printer for that sector is #{4} {5}). Ask the worker {6}.'

View File

@ -0,0 +1,3 @@
subject: Configurada impresora no principal
title: Configurada impresora no principal
description: 'Se ha configurado la impresora #{0} {1} en el sector #{2} {3} (la impresora principal de ese sector es la #{4} {5}). Preguntar al trabajador {6}.'

View File

@ -0,0 +1,8 @@
<email-body v-bind="$props">
<div class="grid-row">
<div class="grid-block vn-pa-ml">
<h1>{{ $t('title') }}</h1>
<p v-html="$t('description', [labeler.id, labeler.name, sector.id, sector.description, mainPrinter.id, mainPrinter.name, worker.nickname])"></p>
</div>
</div>
</email-body>

View File

@ -0,0 +1,33 @@
const Component = require(`vn-print/core/component`);
const emailBody = new Component('email-body');
module.exports = {
name: 'not-main-printer-configured',
async serverPrefetch() {
this.sector = await this.findOneFromDef('sector', [this.sectorId]);
if (!this.sector)
throw new Error('Something went wrong');
this.labeler = await this.findOneFromDef('printer', [this.labelerId]);
this.mainPrinter = await this.findOneFromDef('printer', [this.sector.mainPrinterFk]);
this.worker = await this.findOneFromDef('worker', [this.workerId]);
},
components: {
'email-body': emailBody.build(),
},
props: {
labelerId: {
type: Number,
required: true
},
sectorId: {
type: Number,
required: true
},
workerId: {
type: Number,
required: true
}
}
};

View File

@ -0,0 +1,3 @@
SELECT id, name
FROM vn.printer
WHERE id = ?

View File

@ -0,0 +1,3 @@
SELECT id, description, mainPrinterFk
FROM vn.sector
WHERE id = ?

View File

@ -0,0 +1,3 @@
SELECT nickname
FROM account.user
WHERE id = ?

View File

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