diff --git a/.eslintrc.yml b/.eslintrc.yml index 82a1af501..d8b869d91 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -34,4 +34,5 @@ rules: no-multiple-empty-lines: ["error", { "max": 1, "maxEOF": 1 }] space-in-parens: ["error", "never"] jasmine/no-focused-tests: 0 - jasmine/prefer-toHaveBeenCalledWith: 0 \ No newline at end of file + jasmine/prefer-toHaveBeenCalledWith: 0 + arrow-spacing: ["error", { "before": true, "after": true }] \ No newline at end of file diff --git a/back/methods/dms/uploadFile.js b/back/methods/dms/uploadFile.js index c3065c3cc..d6540cbc5 100644 --- a/back/methods/dms/uploadFile.js +++ b/back/methods/dms/uploadFile.js @@ -110,11 +110,10 @@ module.exports = Self => { async function createDms(ctx, file, myOptions) { const models = Self.app.models; const myUserId = ctx.req.accessToken.userId; - const myWorker = await models.Worker.findOne({where: {userFk: myUserId}}, myOptions); const args = ctx.args; const newDms = await Self.create({ - workerFk: myWorker.id, + workerFk: myUserId, dmsTypeFk: args.dmsTypeId, companyFk: args.companyId, warehouseFk: args.warehouseId, diff --git a/back/models/bank-entity.js b/back/models/bank-entity.js new file mode 100644 index 000000000..c89e0b364 --- /dev/null +++ b/back/models/bank-entity.js @@ -0,0 +1,13 @@ +module.exports = Self => { + Self.validatesPresenceOf('name', { + message: 'Name cannot be blank' + }); + + Self.validatesPresenceOf('bic', { + message: 'Swift / BIC cannot be empty' + }); + + Self.validatesUniquenessOf('bic', { + message: 'This BIC already exist.' + }); +}; diff --git a/modules/client/back/models/bank-entity.json b/back/models/bank-entity.json similarity index 100% rename from modules/client/back/models/bank-entity.json rename to back/models/bank-entity.json diff --git a/back/models/image.js b/back/models/image.js index 78d159940..a35018814 100644 --- a/back/models/image.js +++ b/back/models/image.js @@ -1,11 +1,51 @@ const fs = require('fs-extra'); const sharp = require('sharp'); const path = require('path'); +const readChunk = require('read-chunk'); +const imageType = require('image-type'); +const bmp = require('bmp-js'); module.exports = Self => { require('../methods/image/download')(Self); require('../methods/image/upload')(Self); + // Function extracted from jimp package (utils) + function scan(image, x, y, w, h, f) { + // round input + x = Math.round(x); + y = Math.round(y); + w = Math.round(w); + h = Math.round(h); + + for (let _y = y; _y < y + h; _y++) { + for (let _x = x; _x < x + w; _x++) { + const idx = (image.bitmap.width * _y + _x) << 2; + f.call(image, _x, _y, idx); + } + } + + return image; + } + + // Function extracted from jimp package (type-bmp) + function fromAGBR(bitmap) { + return scan({bitmap}, 0, 0, bitmap.width, bitmap.height, function( + x, + y, + index + ) { + const alpha = this.bitmap.data[index + 0]; + const blue = this.bitmap.data[index + 1]; + const green = this.bitmap.data[index + 2]; + const red = this.bitmap.data[index + 3]; + + this.bitmap.data[index + 0] = red; + this.bitmap.data[index + 1] = green; + this.bitmap.data[index + 2] = blue; + this.bitmap.data[index + 3] = bitmap.is_with_alpha ? alpha : 0xff; + }).bitmap; + } + Self.registerImage = async(collectionName, srcFilePath, fileName, entityId) => { const models = Self.app.models; const tx = await Self.beginTransaction({}); @@ -48,13 +88,31 @@ module.exports = Self => { const dstDir = path.join(collectionDir, 'full'); const dstFile = path.join(dstDir, file); + const buffer = readChunk.sync(srcFilePath, 0, 12); + const type = imageType(buffer); + + let sharpOptions; + let imgSrc = srcFilePath; + if (type.mime == 'image/bmp') { + const bmpBuffer = fs.readFileSync(srcFilePath); + const bmpData = fromAGBR(bmp.decode(bmpBuffer)); + imgSrc = bmpData.data; + sharpOptions = { + raw: { + width: bmpData.width, + height: bmpData.height, + channels: 4 + } + }; + } + const resizeOpts = { withoutEnlargement: true, fit: 'inside' }; await fs.mkdir(dstDir, {recursive: true}); - await sharp(srcFilePath, {failOnError: false}) + await sharp(imgSrc, sharpOptions) .resize(collection.maxWidth, collection.maxHeight, resizeOpts) .png() .toFile(dstFile); @@ -69,7 +127,7 @@ module.exports = Self => { }; await fs.mkdir(dstDir, {recursive: true}); - await sharp(srcFilePath, {failOnError: false}) + await sharp(imgSrc, sharpOptions) .resize(size.width, size.height, resizeOpts) .png() .toFile(dstFile); diff --git a/db/changes/10281-valentineDay/00-ACL.sql b/db/changes/10281-valentineDay/00-ACL.sql index 67b8fc8c3..861db5777 100644 --- a/db/changes/10281-valentineDay/00-ACL.sql +++ b/db/changes/10281-valentineDay/00-ACL.sql @@ -1 +1,5 @@ -INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId) VALUES ('InvoiceIn', '*', '*', 'ALLOW', 'ROLE', 'administrative'); +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES + ('SupplierAccount', '*', '*', 'ALLOW', 'ROLE', 'administrative'), + ('Entry', '*', '*', 'ALLOW', 'ROLE', 'administrative'), + ('InvoiceIn', '*', '*', 'ALLOW', 'ROLE', 'administrative'); diff --git a/db/changes/10281-valentineDay/00-item_getBalance.sql b/db/changes/10281-valentineDay/00-item_getBalance.sql new file mode 100644 index 000000000..96c82cc01 --- /dev/null +++ b/db/changes/10281-valentineDay/00-item_getBalance.sql @@ -0,0 +1,137 @@ +DROP PROCEDURE `vn`.`item_getBalance`; + +DELIMITER $$ +$$ +CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`item_getBalance`(IN vItemId INT, IN vWarehouse INT) +BEGIN + DECLARE vDateInventory DATETIME; + DECLARE vCurdate DATE DEFAULT CURDATE(); + DECLARE vDayEnd DATETIME DEFAULT util.dayEnd(vCurdate); + + SELECT inventoried INTO vDateInventory FROM config; + SET @a = 0; + SET @currentLineFk = 0; + SET @shipped = ''; + + SELECT DATE(@shipped:= shipped) shipped, + alertLevel, + stateName, + origin, + reference, + clientFk, + name, + `in`, + `out`, + @a := @a + IFNULL(`in`,0) - IFNULL(`out`,0) as balance, + @currentLineFk := IF (@shipped < CURDATE() + OR (@shipped = CURDATE() AND (isPicked OR alertLevel >= 2)), + lineFk,@currentLineFk) lastPreparedLineFk, + isTicket, + lineFk, + isPicked, + clientType + FROM + ( SELECT tr.landed AS shipped, + b.quantity AS `in`, + NULL AS `out`, + al.alertLevel AS alertLevel, + st.name AS stateName, + s.name AS name, + e.ref AS reference, + e.id AS origin, + s.id AS clientFk, + IF(al.alertLevel = 3, TRUE, FALSE) isPicked, + FALSE AS isTicket, + b.id lineFk, + NULL `order`, + NULL AS clientType + FROM buy b + JOIN entry e ON e.id = b.entryFk + JOIN travel tr ON tr.id = e.travelFk + JOIN supplier s ON s.id = e.supplierFk + JOIN alertLevel al ON al.alertLevel = + CASE + WHEN tr.shipped < CURDATE() THEN 3 + WHEN tr.shipped = CURDATE() AND tr.isReceived = TRUE THEN 3 + ELSE 0 + END + JOIN state st ON st.code = al.code + WHERE tr.landed >= vDateInventory + AND vWarehouse = tr.warehouseInFk + AND b.itemFk = vItemId + AND e.isInventory = FALSE + AND e.isRaid = FALSE + UNION ALL + + SELECT tr.shipped, + NULL as `in`, + b.quantity AS `out`, + al.alertLevel AS alertLevel, + st.name AS stateName, + s.name AS name, + e.ref AS reference, + e.id AS origin, + s.id AS clientFk, + IF(al.alertLevel = 3, TRUE, FALSE) isPicked, + FALSE AS isTicket, + b.id, + NULL `order`, + NULL AS clientType + FROM buy b + JOIN entry e ON e.id = b.entryFk + JOIN travel tr ON tr.id = e.travelFk + JOIN warehouse w ON w.id = tr.warehouseOutFk + JOIN supplier s ON s.id = e.supplierFk + JOIN alertLevel al ON al.alertLevel = + CASE + WHEN tr.shipped < CURDATE() THEN 3 + WHEN tr.shipped = CURDATE() AND tr.isReceived = TRUE THEN 3 + ELSE 0 + END + JOIN state st ON st.code = al.code + WHERE tr.shipped >= vDateInventory + AND vWarehouse =tr.warehouseOutFk + AND s.id <> 4 + AND b.itemFk = vItemId + AND e.isInventory = FALSE + AND w.isFeedStock = FALSE + AND e.isRaid = FALSE + UNION ALL + + SELECT DATE(t.shipped), + NULL as `in`, + s.quantity AS `out`, + al.alertLevel AS alertLevel, + st.name AS stateName, + t.nickname AS name, + t.refFk AS reference, + t.id AS origin, + t.clientFk, + stk.id AS isPicked, + TRUE AS isTicket, + s.id, + st.`order`, + ct.code AS clientType + FROM sale s + JOIN ticket t ON t.id = s.ticketFk + LEFT JOIN ticketState ts ON ts.ticket = t.id + LEFT JOIN state st ON st.code = ts.code + JOIN client c ON c.id = t.clientFk + JOIN clientType ct ON ct.id = c.clientTypeFk + JOIN alertLevel al ON al.alertLevel = + CASE + WHEN t.shipped < curdate() THEN 3 + WHEN t.shipped > util.dayEnd(curdate()) THEN 0 + ELSE IFNULL(ts.alertLevel, 0) + END + LEFT JOIN state stPrep ON stPrep.`code` = 'PREPARED' + LEFT JOIN saleTracking stk ON stk.saleFk = s.id AND stk.stateFk = stPrep.id + WHERE t.shipped >= vDateInventory + AND s.itemFk = vItemId + AND vWarehouse =t.warehouseFk + ORDER BY shipped, alertLevel DESC, isTicket, `order` DESC, isPicked DESC, `in` DESC, `out` DESC + ) AS itemDiary; + +END$$ +DELIMITER ; + diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 86e1cc888..ec6b2a440 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -150,20 +150,20 @@ INSERT INTO `vn`.`shelving` (`code`, `parkingFk`, `isPrinted`, `priority`, `park INSERT INTO `vn`.`accountingType`(`id`, `description`, `receiptDescription`,`code`) VALUES - (1, 'CC y Polizas de crédito', NULL, NULL), - (2, 'Cash', NULL, 'cash'), - (3, 'Credit card', NULL, 'creditCard'), - (4, 'Finalcial lines', NULL, NULL), - (5, 'Other products', NULL, NULL), - (6, 'Loans', NULL, NULL), - (7, 'Leasing', NULL, NULL), - (8, 'Compensations', 'Compensations', 'Compensations'); + (1, 'CC y Polizas de crédito', NULL, NULL), + (2, 'Cash', 'Cash', 'cash'), + (3, 'Credit card', 'Credit Card', 'creditCard'), + (4, 'Finalcial lines', NULL, NULL), + (5, 'Other products', NULL, NULL), + (6, 'Loans', NULL, NULL), + (7, 'Leasing', NULL, NULL), + (8, 'Compensations', 'Compensations', 'compensation'); INSERT INTO `vn`.`bank`(`id`, `bank`, `account`, `cash`, `entityFk`, `isActive`, `currencyFk`) VALUES - (1, 'Pay on receipt', '0000000000', 3, 0, 1, 1), - (2, 'Cash', '1111111111', 2, 0, 1, 1), - (3, 'Compensation', '0000000000', 8, 0, 1, 1); + (1, 'Pay on receipt', '5720000001', 3, 0, 1, 1), + (2, 'Cash', '5700000001', 2, 0, 1, 1), + (3, 'Compensation', '4000000000', 8, 0, 1, 1); INSERT INTO `vn`.`deliveryMethod`(`id`, `code`, `description`) VALUES diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index fe35dcfe3..ca521f445 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -316,7 +316,7 @@ export default { fourthRelevancy: 'vn-item-tags vn-horizontal:nth-child(4) [ng-model="itemTag.priority"]', fourthRemoveTagButton: 'vn-item-tags vn-horizontal:nth-child(4) vn-icon-button[icon="delete"]', fifthTag: 'vn-item-tags vn-horizontal:nth-child(5) > vn-autocomplete[ng-model="itemTag.tagFk"]', - fifthValue: 'vn-item-tags vn-horizontal:nth-child(5) vn-textfield[ng-model="itemTag.value"]', + fifthValue: 'vn-item-tags vn-horizontal:nth-child(5) vn-autocomplete[ng-model="itemTag.value"]', fifthRelevancy: 'vn-item-tags vn-horizontal:nth-child(5) vn-input-number[ng-model="itemTag.priority"]', sixthTag: 'vn-item-tags vn-horizontal:nth-child(6) > vn-autocomplete[ng-model="itemTag.tagFk"]', sixthValue: 'vn-item-tags vn-horizontal:nth-child(6) vn-textfield[ng-model="itemTag.value"]', diff --git a/e2e/paths/02-client/14_balance.spec.js b/e2e/paths/02-client/14_balance.spec.js index 4917937fa..d408215ff 100644 --- a/e2e/paths/02-client/14_balance.spec.js +++ b/e2e/paths/02-client/14_balance.spec.js @@ -47,6 +47,7 @@ describe('Client balance path', () => { await page.closePopup(); await page.waitToClick(selectors.clientBalance.newPaymentButton); await page.autocompleteSearch(selectors.clientBalance.newPaymentBank, 'Cash'); + await page.clearInput(selectors.clientBalance.newDescription); await page.write(selectors.clientBalance.newDescription, 'Description'); await page.waitToClick(selectors.clientBalance.saveButton); const message = await page.waitForSnackbar(); @@ -86,6 +87,7 @@ describe('Client balance path', () => { await page.waitToClick(selectors.clientBalance.newPaymentButton); await page.write(selectors.clientBalance.newPaymentAmount, amountPaid); + await page.clearInput(selectors.clientBalance.newDescription); await page.write(selectors.clientBalance.newDescription, 'Payment'); await page.write(selectors.clientBalance.deliveredAmount, cashHanded); const refund = await page.waitToGetProperty(selectors.clientBalance.refundAmount, 'value'); @@ -107,6 +109,7 @@ describe('Client balance path', () => { await page.waitToClick(selectors.clientBalance.newPaymentButton); await page.autocompleteSearch(selectors.clientBalance.newPaymentBank, 'Pay on receipt'); await page.overwrite(selectors.clientBalance.newPaymentAmount, '-150'); + await page.clearInput(selectors.clientBalance.newDescription); await page.write(selectors.clientBalance.newDescription, 'Description'); await page.waitToClick(selectors.clientBalance.saveButton); const message = await page.waitForSnackbar(); diff --git a/e2e/paths/04-item/04_tags.spec.js b/e2e/paths/04-item/04_tags.spec.js index 9f2a8e295..574ff7562 100644 --- a/e2e/paths/04-item/04_tags.spec.js +++ b/e2e/paths/04-item/04_tags.spec.js @@ -16,7 +16,7 @@ describe('Item create tags path', () => { await browser.close(); }); - it(`should create a new tag and delete a former one`, async() => { + it('should create a new tag and delete a former one', async() => { await page.waitToClick(selectors.itemTags.fourthRemoveTagButton); await page.waitToClick(selectors.itemTags.addItemTagButton); await page.autocompleteSearch(selectors.itemTags.seventhTag, 'Ancho de la base'); @@ -29,7 +29,7 @@ describe('Item create tags path', () => { expect(message.text).toContain('Data saved!'); }); - it(`should confirm the fourth row data is the expected one`, async() => { + it('should confirm the fourth row data is the expected one', async() => { await page.reloadSection('item.card.tags'); await page.waitForSelector('vn-item-tags'); let result = await page.waitToGetProperty(selectors.itemTags.fourthTag, 'value'); @@ -47,7 +47,7 @@ describe('Item create tags path', () => { expect(result).toEqual('4'); }); - it(`should confirm the fifth row data is the expected one`, async() => { + it('should confirm the fifth row data is the expected one', async() => { let tag = await page .waitToGetProperty(selectors.itemTags.fifthTag, 'value'); @@ -62,7 +62,7 @@ describe('Item create tags path', () => { expect(relevancy).toEqual('5'); }); - it(`should confirm the sixth row data is the expected one`, async() => { + it('should confirm the sixth row data is the expected one', async() => { let tag = await page .waitToGetProperty(selectors.itemTags.sixthTag, 'value'); diff --git a/front/core/components/table/style.scss b/front/core/components/table/style.scss index 7d90d4db6..a3c339b5f 100644 --- a/front/core/components/table/style.scss +++ b/front/core/components/table/style.scss @@ -174,4 +174,9 @@ vn-table { .vn-check { margin: 0; } + .empty-rows { + color: $color-font-secondary; + font-size: 1.375rem; + text-align: center; + } } \ No newline at end of file diff --git a/front/salix/components/bank-entity/index.html b/front/salix/components/bank-entity/index.html new file mode 100644 index 000000000..3a7786607 --- /dev/null +++ b/front/salix/components/bank-entity/index.html @@ -0,0 +1,40 @@ + + +

Please, ensure you put the correct data!

+ + + + + + + + + + +
+ + + + +
diff --git a/front/salix/components/bank-entity/index.js b/front/salix/components/bank-entity/index.js new file mode 100644 index 000000000..192ebe046 --- /dev/null +++ b/front/salix/components/bank-entity/index.js @@ -0,0 +1,37 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +class Controller extends Component { + open() { + this.$.bankEntityDialog.show(); + } + + resetData() { + this.data = {}; + } + + onAccept() { + try { + if (!this.data.countryFk) + throw new Error(`The country can't be empty`); + + this.$http.post(`bankEntities`, this.data).then(res => { + this.vnApp.showMessage(this.$t('The bank entity has been created. You can save the data now')); + this.emit('response', {$response: res.data}); + }); + } catch (e) { + this.vnApp.showError(this.$t(e.message)); + return false; + } + return true; + } +} + +ngModule.vnComponent('vnNewBankEntity', { + template: require('./index.html'), + controller: Controller, + bindings: { + data: '<', + } +}); diff --git a/front/salix/components/bank-entity/index.spec.js b/front/salix/components/bank-entity/index.spec.js new file mode 100644 index 000000000..c288c3052 --- /dev/null +++ b/front/salix/components/bank-entity/index.spec.js @@ -0,0 +1,53 @@ +import './index'; + +describe('Salix Component vnNewBankEntity', () => { + let controller; + let $httpBackend; + let $scope; + let $element; + let vnApp; + + beforeEach(ngModule('salix')); + + beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _vnApp_) => { + $httpBackend = _$httpBackend_; + vnApp = _vnApp_; + jest.spyOn(vnApp, 'showError'); + $scope = $rootScope.$new(); + $element = angular.element(''); + controller = $componentController('vnNewBankEntity', {$element, $scope}); + })); + + describe('resetData()', () => { + it('should reset the location in the controller', () => { + expect(controller.data).toBeUndefined(); + + controller.resetData(); + + expect(controller.data).toEqual({}); + }); + }); + + describe('onAccept()', () => { + it('should throw an error if there is no country id in the location', () => { + jest.spyOn(controller.vnApp, 'showMessage'); + + controller.data = {}; + + controller.onAccept(); + + expect(controller.vnApp.showError).toHaveBeenCalledWith(`The country can't be empty`); + }); + + it('should do add the new bank entity', () => { + controller.data = { + countryFk: 1 + }; + + $httpBackend.expectPOST('bankEntities', controller.data).respond(200, controller.data); + + controller.onAccept(); + $httpBackend.flush(); + }); + }); +}); diff --git a/front/salix/components/bank-entity/locale/es.yml b/front/salix/components/bank-entity/locale/es.yml new file mode 100644 index 000000000..fe5160572 --- /dev/null +++ b/front/salix/components/bank-entity/locale/es.yml @@ -0,0 +1,12 @@ +New postcode: Nuevo código postal +New city: Nueva ciudad +New province: Nueva provincia +Please, ensure you put the correct data!: ¡Por favor, asegúrate de poner los datos correctos! +The postcode can't be empty: El código postal no puede quedar vacío +The town can't be empty: La población no puede quedar vacía +The province can't be empty: La provincia no puede quedar vacía +The country can't be empty: El país no puede quedar vacío +The postcode has been created. You can save the data now: Se ha creado el código postal. Ahora puedes guardar los datos +The city has been created: Se ha creado la ciudad +The province has been created: Se ha creado la provincia +The bank entity has been created. You can save the data now: Se ha creado la entidad bancaria. Puedes guardar los datos ahora \ No newline at end of file diff --git a/front/salix/components/bank-entity/style.scss b/front/salix/components/bank-entity/style.scss new file mode 100644 index 000000000..1171366da --- /dev/null +++ b/front/salix/components/bank-entity/style.scss @@ -0,0 +1,9 @@ +@import "variables"; + +vn-new-bank-entity { + vn-dialog { + p { + color: $color-alert + } + } +} \ No newline at end of file diff --git a/front/salix/components/index.js b/front/salix/components/index.js index 13f8366cd..5167b21d6 100644 --- a/front/salix/components/index.js +++ b/front/salix/components/index.js @@ -14,3 +14,4 @@ import './summary'; import './topbar/topbar'; import './user-popover'; import './upload-photo'; +import './bank-entity'; diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 44f882638..c5062c3e9 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -91,5 +91,6 @@ "The observation type can't be repeated": "The observation type can't be repeated", "New ticket request has been created with price": "New ticket request has been created *'{{description}}'* for day *{{shipped}}*, with a quantity of *{{quantity}}* and a price of *{{price}} €*", "New ticket request has been created": "New ticket request has been created *'{{description}}'* for day *{{shipped}}*, with a quantity of *{{quantity}}*", - "There's a new urgent ticket": "There's a new urgent ticket: [{{title}}](https://cau.verdnatura.es/WorkOrder.do?woMode=viewWO&woID={{issueId}})" + "There's a new urgent ticket": "There's a new urgent ticket: [{{title}}](https://cau.verdnatura.es/WorkOrder.do?woMode=viewWO&woID={{issueId}})", + "Swift / BIC cannot be empty": "Swift / BIC cannot be empty" } \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 16bd1d361..174a9e093 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -167,9 +167,12 @@ "You can't upload images on the test environment": "No puedes subir imágenes en el entorno de pruebas", "The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta", "Sorts whole route": "Reordena ruta entera", - "Invalid account": "Cuenta inválida", - "New ticket request has been created with price": "Se ha creado una nueva petición de compra *'{{description}}'* para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*", - "New ticket request has been created": "Se ha creado una nueva petición de compra *'{{description}}'* para el día *{{shipped}}*, con una cantidad de *{{quantity}}*", + "New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día {{shipped}}, con una cantidad de {{quantity}} y un precio de {{price}} €", + "New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día {{shipped}}, con una cantidad de {{quantity}}", + "Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío", + "This BIC already exist.": "Este BIC ya existe.", "That item doesn't exists": "Ese artículo no existe", - "There's a new urgent ticket": "Hay un nuevo ticket urgente: [{{title}}](https://cau.verdnatura.es/WorkOrder.do?woMode=viewWO&woID={{issueId}})" + "There's a new urgent ticket": "Hay un nuevo ticket urgente: [{{title}}](https://cau.verdnatura.es/WorkOrder.do?woMode=viewWO&woID={{issueId}})", + "Invalid account": "Cuenta inválida", + "Compensation account is empty": "La cuenta para compensar está vacia" } \ No newline at end of file diff --git a/modules/client/back/methods/client/createReceipt.js b/modules/client/back/methods/client/createReceipt.js index 75ad02373..976ff7cf2 100644 --- a/modules/client/back/methods/client/createReceipt.js +++ b/modules/client/back/methods/client/createReceipt.js @@ -62,7 +62,10 @@ module.exports = function(Self) { const bank = await models.Bank.findById(args.bankFk); const accountingType = await models.AccountingType.findById(bank.accountingTypeFk); - if (args.compensationAccount) { + if (accountingType.code == 'compensation') { + if (!args.compensationAccount) + throw new UserError('Compensation account is empty'); + const supplierCompensation = await models.Supplier.findOne({ where: { account: args.compensationAccount @@ -92,12 +95,11 @@ module.exports = function(Self) { ], options); } else { - const description = `${clientOriginal.id} : ${clientOriginal.nickname} - ${accountingType.receiptDescription}`; + const description = `${clientOriginal.id} : ${clientOriginal.socialName} - ${accountingType.receiptDescription}`; const [xdiarioNew] = await Self.rawSql( - `SELECT xdiario_new(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ledger;`, + `SELECT xdiario_new(?, CURDATE(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ledger;`, [ null, - Date(), bank.account, clientOriginal.accountingAccount, description, @@ -114,10 +116,9 @@ module.exports = function(Self) { options); await Self.rawSql( - `SELECT xdiario_new(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`, + `SELECT xdiario_new(?, CURDATE(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`, [ xdiarioNew.ledger, - Date(), clientOriginal.accountingAccount, bank.account, description, diff --git a/modules/client/back/methods/client/lastActiveTickets.js b/modules/client/back/methods/client/lastActiveTickets.js index c69c860c9..7444dff45 100644 --- a/modules/client/back/methods/client/lastActiveTickets.js +++ b/modules/client/back/methods/client/lastActiveTickets.js @@ -26,15 +26,25 @@ module.exports = Self => { Self.lastActiveTickets = async(id, ticketId) => { const ticket = await Self.app.models.Ticket.findById(ticketId); const query = ` - SELECT t.id, t.shipped, a.name AS agencyName, w.name AS warehouseName, ad.city AS address - FROM vn.ticket t - JOIN vn.ticketState ts ON t.id = ts.ticketFk - JOIN vn.agencyMode a ON t.agencyModeFk = a.id - JOIN vn.warehouse w ON t.warehouseFk = w.id - JOIN vn.address ad ON t.addressFk = ad.id - WHERE t.shipped >= CURDATE() AND t.clientFk = ? AND ts.alertLevel = 0 - AND t.id <> ? AND t.warehouseFk = ? - ORDER BY t.shipped + SELECT + t.id, + t.shipped, + a.name AS agencyName, + w.name AS warehouseName, + ad.nickname AS nickname, + ad.city AS city, + ad.postalCode AS postalCode, + ad.street AS street, + pr.name AS name + FROM ticket t + JOIN vn.ticketState ts ON t.id = ts.ticketFk + JOIN vn.agencyMode a ON t.agencyModeFk = a.id + JOIN vn.warehouse w ON t.warehouseFk = w.id + JOIN vn.address ad ON t.addressFk = ad.id + JOIN vn.province pr ON ad.provinceFk = pr.id + WHERE t.shipped >= CURDATE() AND t.clientFk = ? AND ts.alertLevel = 0 + AND t.id <> ? AND t.warehouseFk = ? + ORDER BY t.shipped LIMIT 10`; return Self.rawSql(query, [id, ticketId, ticket.warehouseFk]); diff --git a/modules/client/back/methods/client/specs/createReceipt.spec.js b/modules/client/back/methods/client/specs/createReceipt.spec.js index 59bf8f52a..8d35064a2 100644 --- a/modules/client/back/methods/client/specs/createReceipt.spec.js +++ b/modules/client/back/methods/client/specs/createReceipt.spec.js @@ -1,4 +1,5 @@ const app = require('vn-loopback/server/server'); +const LoopBackContext = require('loopback-context'); describe('Client createReceipt', () => { const clientFk = 108; @@ -6,18 +7,34 @@ describe('Client createReceipt', () => { const companyFk = 442; const amountPaid = 12.50; const description = 'Receipt description'; + const activeCtx = { + accessToken: {userId: 5}, + http: { + req: { + headers: {origin: 'http://localhost'} + } + } + }; + const ctx = {req: activeCtx}; + activeCtx.http.req.__ = value => { + return value; + }; + + beforeEach(() => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); it('should create a new receipt', async() => { const bankFk = 1; - let ctx = { - args: { - clientFk: clientFk, - payed: payed, - companyFk: companyFk, - bankFk: bankFk, - amountPaid: amountPaid, - description: description - } + ctx.args = { + clientFk: clientFk, + payed: payed, + companyFk: companyFk, + bankFk: bankFk, + amountPaid: amountPaid, + description: description }; const receipt = await app.models.Client.createReceipt(ctx); @@ -38,19 +55,39 @@ describe('Client createReceipt', () => { await till.destroy(); }); + it('should throw Compensation account is empty', async() => { + const bankFk = 3; + + ctx.args = { + clientFk: clientFk, + payed: payed, + companyFk: companyFk, + bankFk: bankFk, + amountPaid: amountPaid, + description: description + }; + + try { + await app.models.Client.createReceipt(ctx); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error.message).toEqual('Compensation account is empty'); + }); + it('should throw Invalid account if compensationAccount does not belongs to a client nor a supplier', async() => { let error; const bankFk = 3; - const ctx = { - args: { - clientFk: clientFk, - payed: payed, - companyFk: companyFk, - bankFk: bankFk, - amountPaid: amountPaid, - description: description, - compensationAccount: 'non existing account' - } + ctx.args = { + clientFk: clientFk, + payed: payed, + companyFk: companyFk, + bankFk: bankFk, + amountPaid: amountPaid, + description: description, + compensationAccount: 'non existing account' }; try { @@ -65,16 +102,15 @@ describe('Client createReceipt', () => { it('should create a new receipt with a compensation for a client', async() => { const bankFk = 3; - const ctx = { - args: { - clientFk: clientFk, - payed: payed, - companyFk: companyFk, - bankFk: bankFk, - amountPaid: amountPaid, - description: description, - compensationAccount: '4300000001' - } + + ctx.args = { + clientFk: clientFk, + payed: payed, + companyFk: companyFk, + bankFk: bankFk, + amountPaid: amountPaid, + description: description, + compensationAccount: '4300000001' }; const receipt = await app.models.Client.createReceipt(ctx); const receiptCompensated = await app.models.Receipt.findOne({ @@ -104,16 +140,16 @@ describe('Client createReceipt', () => { }); it('should create a new receipt with a compensation for a supplier', async() => { - const ctx = { - args: { - clientFk: clientFk, - payed: payed, - companyFk: companyFk, - bankFk: 3, - amountPaid: amountPaid, - description: description, - compensationAccount: '4100000001' - } + const bankFk = 3; + + ctx.args = { + clientFk: clientFk, + payed: payed, + companyFk: companyFk, + bankFk: bankFk, + amountPaid: amountPaid, + description: description, + compensationAccount: '4100000001' }; const receipt = await app.models.Client.createReceipt(ctx); diff --git a/modules/client/back/methods/client/specs/lastActiveTickets.spec.js b/modules/client/back/methods/client/specs/lastActiveTickets.spec.js new file mode 100644 index 000000000..8d06e0584 --- /dev/null +++ b/modules/client/back/methods/client/specs/lastActiveTickets.spec.js @@ -0,0 +1,18 @@ +const app = require('vn-loopback/server/server'); + +describe('Client last active tickets', () => { + it('should receive an array of last active tickets of Bruce Wayne', async() => { + const ticketId = 22; + const clientId = 109; + const warehouseId = 5; + const result = await app.models.Client.lastActiveTickets(clientId, ticketId, warehouseId); + + const length = result.length; + const anyResult = result[Math.floor(Math.random() * Math.floor(length))]; + + const properties = Object.keys(anyResult); + + expect(properties.length).toEqual(9); + expect(result.length).toEqual(3); + }); +}); diff --git a/modules/client/back/models/bank-entity.js b/modules/client/back/models/bank-entity.js deleted file mode 100644 index 4cfa7fc91..000000000 --- a/modules/client/back/models/bank-entity.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = Self => { - Self.validatesPresenceOf('name', { - message: `Name cannot be blank` - }); - Self.validatesPresenceOf('bic', { - message: `Swift / BIC can't be empty` - }); -}; diff --git a/modules/client/back/models/receipt.js b/modules/client/back/models/receipt.js index 88b7bfb5b..36a4a8952 100644 --- a/modules/client/back/models/receipt.js +++ b/modules/client/back/models/receipt.js @@ -1,3 +1,5 @@ +const LoopBackContext = require('loopback-context'); + module.exports = function(Self) { require('../methods/receipt/filter')(Self); @@ -23,13 +25,10 @@ module.exports = function(Self) { Self.observe('before save', async function(ctx) { if (ctx.isNewInstance) { - let token = ctx.options.accessToken; - let userId = token && token.userId; - - ctx.instance.workerFk = userId; - + const loopBackContext = LoopBackContext.getCurrentContext(); + ctx.instance.workerFk = loopBackContext.active.accessToken.userId; await Self.app.models.Till.create({ - workerFk: userId, + workerFk: ctx.instance.workerFk, bankFk: ctx.instance.bankFk, in: ctx.instance.amountPaid, concept: ctx.instance.description, diff --git a/modules/entry/back/methods/entry/latestBuysFilter.js b/modules/entry/back/methods/entry/latestBuysFilter.js index 10c41dd92..5d0c513df 100644 --- a/modules/entry/back/methods/entry/latestBuysFilter.js +++ b/modules/entry/back/methods/entry/latestBuysFilter.js @@ -78,6 +78,8 @@ module.exports = Self => { return {'ic.id': value}; case 'salesPersonFk': return {'it.workerFk': value}; + case 'code': + return {'it.code': value}; case 'typeFk': return {'i.typeFk': value}; case 'active': @@ -103,6 +105,7 @@ module.exports = Self => { i.id AS itemFk, i.size, i.density, + it.code, i.typeFk, i.family, i.isActive, diff --git a/modules/entry/front/latest-buys/index.html b/modules/entry/front/latest-buys/index.html index 6c4802816..34f6464fc 100644 --- a/modules/entry/front/latest-buys/index.html +++ b/modules/entry/front/latest-buys/index.html @@ -37,8 +37,8 @@ Quantity Description Size - Tags - Type + Tags + Type Intrastat Origin Density @@ -109,7 +109,7 @@ - {{::buy.type}} + {{::buy.code}} {{::buy.intrastat}} diff --git a/modules/entry/front/routes.json b/modules/entry/front/routes.json index db12e95fd..1911f721c 100644 --- a/modules/entry/front/routes.json +++ b/modules/entry/front/routes.json @@ -31,19 +31,19 @@ "state": "entry.index", "component": "vn-entry-index", "description": "Entries", - "acl": ["buyer"] + "acl": ["buyer", "administrative"] }, { "url": "/latest-buys?q", "state": "entry.latestBuys", "component": "vn-entry-latest-buys", "description": "Latest buys", - "acl": ["buyer"] + "acl": ["buyer", "administrative"] }, { "url": "/create?supplierFk&travelFk&companyFk", "state": "entry.create", "component": "vn-entry-create", "description": "New entry", - "acl": ["buyer"] + "acl": ["buyer", "administrative"] }, { "url": "/:id", "state": "entry.card", diff --git a/modules/item/back/methods/item-image-queue/downloadImages.js b/modules/item/back/methods/item-image-queue/downloadImages.js index bee5492bc..2d2e8af91 100644 --- a/modules/item/back/methods/item-image-queue/downloadImages.js +++ b/modules/item/back/methods/item-image-queue/downloadImages.js @@ -41,18 +41,13 @@ module.exports = Self => { async function download() { const image = await Self.findOne({ where: {url: {neq: null}, attempts: {lt: maxAttempts}}, - order: 'attempts, updated' + order: 'priority, attempts, updated' }); if (!image) return; - const srcFile = image.url.split('/').pop(); - const dotIndex = srcFile.lastIndexOf('.'); - - let fileName = srcFile.substring(0, dotIndex); - if (dotIndex == -1) - fileName = srcFile; - + const srcFile = image.url; + const fileName = srcFile.replace(/\.|\/|:|\?|\\|=|%/g, ''); const file = `${fileName}.png`; const filePath = path.join(tempPath, file); diff --git a/modules/item/back/methods/item/filter.js b/modules/item/back/methods/item/filter.js index f392dba87..eba0b0f91 100644 --- a/modules/item/back/methods/item/filter.js +++ b/modules/item/back/methods/item/filter.js @@ -44,6 +44,10 @@ module.exports = Self => { arg: 'description', type: 'String', description: 'The item description', + }, { + arg: 'stemMultiplier', + type: 'Integer', + description: 'The item multiplier', } ], returns: { @@ -80,16 +84,22 @@ module.exports = Self => { : {or: [{'i.name': {like: `%${value}%`}}, codeWhere]}; case 'id': return {'i.id': value}; - case 'description': - return {'i.description': {like: `%${value}%`}}; - case 'categoryFk': - return {'ic.id': value}; - case 'salesPersonFk': - return {'t.workerFk': value}; - case 'typeFk': - return {'i.typeFk': value}; case 'isActive': return {'i.isActive': value}; + case 'multiplier': + return {'i.stemMultiplier': value}; + case 'typeFk': + return {'i.typeFk': value}; + case 'category': + return {'ic.name': value}; + case 'salesPersonFk': + return {'it.workerFk': value}; + case 'origin': + return {'ori.code': value}; + case 'niche': + return {'ip.code': value}; + case 'intrastat': + return {'intr.description': value}; } }); filter = mergeFilters(filter, {where}); @@ -98,7 +108,8 @@ module.exports = Self => { let stmt; stmt = new ParameterizedSQL( - `SELECT i.id, + `SELECT + i.id, i.image, i.name, i.description, @@ -111,29 +122,30 @@ module.exports = Self => { i.tag10, i.value10, i.subName, i.isActive, - t.name type, - t.workerFk buyerFk, - u.name userName, - intr.description AS intrastat, i.stems, - ori.code AS origin, - ic.name AS category, i.density, i.stemMultiplier, + i.typeFk, + it.name AS typeName, + it.workerFk AS buyerFk, + u.name AS userName, + ori.code AS origin, + ic.name AS category, + intr.description AS intrastat, b.grouping, b.packing, - itn.code AS niche, @visibleCalc + ip.code AS niche, @visibleCalc FROM item i - LEFT JOIN itemType t ON t.id = i.typeFk - LEFT JOIN itemCategory ic ON ic.id = t.categoryFk - LEFT JOIN worker w ON w.id = t.workerFk + LEFT JOIN itemType it ON it.id = i.typeFk + LEFT JOIN itemCategory ic ON ic.id = it.categoryFk + LEFT JOIN worker w ON w.id = it.workerFk LEFT JOIN account.user u ON u.id = w.userFk LEFT JOIN intrastat intr ON intr.id = i.intrastatFk LEFT JOIN producer pr ON pr.id = i.producerFk LEFT JOIN origin ori ON ori.id = i.originFk - LEFT JOIN cache.last_buy lb ON lb.item_id = i.id AND lb.warehouse_id = t.warehouseFk + LEFT JOIN cache.last_buy lb ON lb.item_id = i.id AND lb.warehouse_id = it.warehouseFk LEFT JOIN vn.buy b ON b.id = lb.buy_id - LEFT JOIN itemPlacement itn ON itn.itemFk = i.id AND itn.warehouseFk = t.warehouseFk` + LEFT JOIN itemPlacement ip ON ip.itemFk = i.id AND ip.warehouseFk = it.warehouseFk` ); if (ctx.args.tags) { diff --git a/modules/item/back/methods/item/getBalance.js b/modules/item/back/methods/item/getBalance.js index 26fc2a7dc..916757c46 100644 --- a/modules/item/back/methods/item/getBalance.js +++ b/modules/item/back/methods/item/getBalance.js @@ -20,8 +20,12 @@ module.exports = Self => { }); Self.getBalance = async filter => { - let where = filter.where; + const where = filter.where; let [diary] = await Self.rawSql(`CALL vn.item_getBalance(?, ?)`, [where.itemFk, where.warehouseFk]); + + for (const entry of diary) + if (entry.clientType === 'loses') entry.highlighted = true; + return diary; }; }; diff --git a/modules/item/back/methods/item/specs/getBalance.spec.js b/modules/item/back/methods/item/specs/getBalance.spec.js new file mode 100644 index 000000000..671b8c103 --- /dev/null +++ b/modules/item/back/methods/item/specs/getBalance.spec.js @@ -0,0 +1,33 @@ +const app = require('vn-loopback/server/server'); +const LoopBackContext = require('loopback-context'); + +describe('item getBalance()', () => { + it('should return the balance lines of a client type loses in which one has highlighted true', async() => { + const activeCtx = { + accessToken: {userId: 9}, + }; + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + + const losesClientId = 111; + const ticket = await app.models.Ticket.findById(7); + const originalClientId = ticket.clientFk; + await ticket.updateAttribute('clientFk', losesClientId); + + const filter = { + where: { + itemFk: 1, + warehouseFk: 1 + } + }; + const results = await app.models.Item.getBalance(filter); + + const result = results.find(element => element.clientType == 'loses'); + + expect(result.highlighted).toBe(true); + + // restores + await ticket.updateAttribute('clientFk', originalClientId); + }); +}); diff --git a/modules/item/front/basic-data/index.html b/modules/item/front/basic-data/index.html index 3f049675c..cec7a063f 100644 --- a/modules/item/front/basic-data/index.html +++ b/modules/item/front/basic-data/index.html @@ -113,6 +113,12 @@ ng-model="$ctrl.item.stems" rule> + + {{::sale.stateName | dashIfEmpty}} {{::sale.reference | dashIfEmpty}} - - {{::sale.name | dashIfEmpty}} - - - {{::sale.name | dashIfEmpty}} + + + {{::sale.name | dashIfEmpty}} + + + {{::sale.name | dashIfEmpty}} + {{::sale.in | dashIfEmpty}} diff --git a/modules/item/front/index/index.html b/modules/item/front/index/index.html index 82c6d1068..590a6fdf3 100644 --- a/modules/item/front/index/index.html +++ b/modules/item/front/index/index.html @@ -5,107 +5,107 @@ model="model" class="vn-w-xl vn-mb-xl"> - - - - - Id - Grouping - Packing - Description - Stems - Size - Niche - Type - Category - Intrastat - Origin - Buyer - Density - Multiplier - Active - - - - - - - - - - - {{::item.id}} - - - {{::item.grouping | dashIfEmpty}} - {{::item.packing | dashIfEmpty}} - - {{::item.name}} - -

{{::item.subName}}

-
- - -
- {{::item.stems}} - {{::item.size}} - {{::item.niche}} - - {{::item.type}} - - - {{::item.category}} - - - {{::item.intrastat}} - - {{::item.origin}} - - - {{::item.userName}} - - - {{::item.density}} - {{::item.stemMultiplier}} - - - - - - - - - - - - -
-
-
+ + + + + Id + Grouping + Packing + Description + Stems + Size + Niche + Type + Category + Intrastat + Origin + Buyer + Density + Multiplier + Active + + + + + + + + + + + {{::item.id}} + + + {{::item.grouping | dashIfEmpty}} + {{::item.packing | dashIfEmpty}} + + {{::item.name}} + +

{{::item.subName}}

+
+ + +
+ {{::item.stems}} + {{::item.size}} + {{::item.niche}} + + {{::item.typeName}} + + + {{::item.category}} + + + {{::item.intrastat}} + + {{::item.origin}} + + + {{::item.userName}} + + + {{::item.density}} + {{::item.stemMultiplier}} + + + + + + + + + + + + +
+
+
@@ -127,4 +127,31 @@ - \ No newline at end of file + + + + + Filter by selection + + + Exclude selection + + + Remove filter + + + Remove all filters + + + \ No newline at end of file diff --git a/modules/item/front/index/index.js b/modules/item/front/index/index.js index cafa3e475..175beb88a 100644 --- a/modules/item/front/index/index.js +++ b/modules/item/front/index/index.js @@ -11,6 +11,36 @@ class Controller extends Section { }; } + exprBuilder(param, value) { + switch (param) { + case 'category': + return {'ic.name': value}; + case 'salesPersonFk': + return {'it.workerFk': value}; + case 'grouping': + return {'b.grouping': value}; + case 'packing': + return {'b.packing': value}; + case 'origin': + return {'ori.code': value}; + case 'niche': + return {'ip.code': value}; + case 'typeFk': + return {'i.typeFk': value}; + case 'intrastat': + return {'intr.description': value}; + case 'id': + case 'size': + case 'name': + case 'subname': + case 'isActive': + case 'density': + case 'stemMultiplier': + case 'stems': + return {[`i.${param}`]: value}; + } + } + onCloneAccept(itemFk) { return this.$http.post(`Items/${itemFk}/clone`) .then(res => { diff --git a/modules/item/front/request-search-panel/index.html b/modules/item/front/request-search-panel/index.html index 10d5f9762..7d3a9f1a5 100644 --- a/modules/item/front/request-search-panel/index.html +++ b/modules/item/front/request-search-panel/index.html @@ -1,6 +1,6 @@
-
- + + - + {{nickname}} - + - +
+ + ng-model="filter.from" + on-change="$ctrl.from = value"> + ng-model="filter.to" + on-change="$ctrl.to = value"> + Or + + + + - +
+ {{name}} - + diff --git a/modules/item/front/request-search-panel/index.js b/modules/item/front/request-search-panel/index.js index 823346e30..556bdd874 100644 --- a/modules/item/front/request-search-panel/index.js +++ b/modules/item/front/request-search-panel/index.js @@ -11,6 +11,35 @@ class Controller extends SearchPanel { {code: 'denied', name: this.$t('Denied')} ]; } + + get from() { + return this._from; + } + + set from(value) { + this._from = value; + this.filter.scopeDays = null; + } + + get to() { + return this._to; + } + + set to(value) { + this._to = value; + this.filter.scopeDays = null; + } + + get scopeDays() { + return this._scopeDays; + } + + set scopeDays(value) { + this._scopeDays = value; + + this.filter.from = null; + this.filter.to = null; + } } ngModule.vnComponent('vnRequestSearchPanel', { diff --git a/modules/item/front/request-search-panel/index.spec.js b/modules/item/front/request-search-panel/index.spec.js new file mode 100644 index 000000000..2fb339209 --- /dev/null +++ b/modules/item/front/request-search-panel/index.spec.js @@ -0,0 +1,48 @@ +import './index'; + +describe(' Component vnRequestSearchPanel', () => { + let controller; + + beforeEach(ngModule('item')); + + beforeEach(inject($componentController => { + controller = $componentController('vnRequestSearchPanel', {$element: null}); + controller.$t = () => {}; + controller.filter = {}; + })); + + describe('from() setter', () => { + it('should clear the scope days when setting the from property', () => { + controller.filter.scopeDays = 1; + + controller.from = new Date(); + + expect(controller.filter.scopeDays).toBeNull(); + expect(controller.from).toBeDefined(); + }); + }); + + describe('to() setter', () => { + it('should clear the scope days when setting the to property', () => { + controller.filter.scopeDays = 1; + + controller.to = new Date(); + + expect(controller.filter.scopeDays).toBeNull(); + expect(controller.to).toBeDefined(); + }); + }); + + describe('scopeDays() setter', () => { + it('should clear the date range when setting the scopeDays property', () => { + controller.filter.from = new Date(); + controller.filter.to = new Date(); + + controller.scopeDays = 1; + + expect(controller.filter.from).toBeNull(); + expect(controller.filter.to).toBeNull(); + expect(controller.scopeDays).toBeDefined(); + }); + }); +}); diff --git a/modules/item/front/request/index.html b/modules/item/front/request/index.html index 5b8f0d3fc..08a4b7b1a 100644 --- a/modules/item/front/request/index.html +++ b/modules/item/front/request/index.html @@ -10,9 +10,10 @@ diff --git a/modules/item/front/request/index.js b/modules/item/front/request/index.js index 231d5eda7..2fe08ada6 100644 --- a/modules/item/front/request/index.js +++ b/modules/item/front/request/index.js @@ -15,7 +15,6 @@ export default class Controller extends Section { nextWeek.setDate(nextWeek.getDate() + 7); this.filterParams = { - mine: true, from: today, to: nextWeek, state: 'pending' @@ -23,6 +22,24 @@ export default class Controller extends Section { } } + fetchParams($params) { + if (!Object.entries($params).length) + $params.scopeDays = 1; + + if (typeof $params.scopeDays === 'number') { + const from = new Date(); + from.setHours(0, 0, 0, 0); + + const to = new Date(from.getTime()); + to.setDate(to.getDate() + $params.scopeDays); + to.setHours(23, 59, 59, 999); + + Object.assign($params, {from, to}); + } + + return $params; + } + getState(isOk) { if (isOk === null) return 'Pending'; diff --git a/modules/item/front/routes.json b/modules/item/front/routes.json index 564eab484..4b4be3c3d 100644 --- a/modules/item/front/routes.json +++ b/modules/item/front/routes.json @@ -162,7 +162,7 @@ "acl": ["buyer"] }, { - "url" : "/fixed-price", + "url" : "/fixed-price?q", "state": "item.fixedPrice", "component": "vn-fixed-price", "description": "Fixed prices", diff --git a/modules/item/front/summary/index.html b/modules/item/front/summary/index.html index 59ceaebbe..67d3bf3c6 100644 --- a/modules/item/front/summary/index.html +++ b/modules/item/front/summary/index.html @@ -55,6 +55,9 @@ + + { + this.$http.get(query).then(res=> { if (res.data) { let client = res.data[0]; let defaultAddress = client.defaultAddress; diff --git a/modules/supplier/back/models/supplier-account.json b/modules/supplier/back/models/supplier-account.json index 237934f8c..1d989cbfb 100644 --- a/modules/supplier/back/models/supplier-account.json +++ b/modules/supplier/back/models/supplier-account.json @@ -1,6 +1,10 @@ { "name": "SupplierAccount", - "base": "VnModel", + "base": "Loggable", + "log": { + "model":"SupplierLog", + "relation": "supplier" + }, "options": { "mysql": { "table": "supplierAccount" @@ -45,6 +49,11 @@ "type": "belongsTo", "model": "Supplier", "foreignKey": "supplierFk" + }, + "bankEntity": { + "type": "belongsTo", + "model": "BankEntity", + "foreignKey": "bankEntityFk" } } } \ No newline at end of file diff --git a/modules/supplier/front/account/index.html b/modules/supplier/front/account/index.html new file mode 100644 index 000000000..577fa4e87 --- /dev/null +++ b/modules/supplier/front/account/index.html @@ -0,0 +1,67 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + \ No newline at end of file diff --git a/modules/supplier/front/account/index.js b/modules/supplier/front/account/index.js new file mode 100644 index 000000000..26f4af988 --- /dev/null +++ b/modules/supplier/front/account/index.js @@ -0,0 +1,56 @@ +import ngModule from '../module'; +import Section from 'salix/components/section'; + +class Controller extends Section { + constructor($element, $) { + super($element, $); + this.include = { + relation: 'bankEntity', + scope: { + fields: ['countryFk', 'id', 'name', 'bic'] + } + }; + } + + add() { + this.$.model.insert({ + supplierFk: this.$params.id + }); + } + + onResponse(response) { + const data = this.$.model.data; + const supplierAccount = data[this.currentRowIndex]; + supplierAccount.bankEntityFk = response.id; + } + + showBankEntity(event, $index) { + if (event.defaultPrevented) return; + event.preventDefault(); + this.currentRowIndex = $index; + this.$.bankEntity.open(); + } + + onBankEntityAccept() { + const query = `SupplierAccounts/${this.$params.id}/createBankEntity`; + return this.$http.patch(query, this.newBankEntity) + .then(res => this.supplierAccount.bankEntityFk = res.data.id); + } + + onSubmit() { + this.$.watcher.check(); + this.$.model.save().then(() => { + this.$.watcher.notifySaved(); + this.$.watcher.updateOriginalData(); + this.card.reload(); + }); + } +} + +ngModule.vnComponent('vnSupplierAccount', { + template: require('./index.html'), + controller: Controller, + require: { + card: '^vnSupplierCard' + } +}); diff --git a/modules/supplier/front/account/index.spec.js b/modules/supplier/front/account/index.spec.js new file mode 100644 index 000000000..ca1671207 --- /dev/null +++ b/modules/supplier/front/account/index.spec.js @@ -0,0 +1,71 @@ +import './index.js'; + +describe('Supplier Component vnSupplierAccount', () => { + let $scope; + let $element; + let controller; + let $httpBackend; + beforeEach(ngModule('supplier')); + + beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => { + $httpBackend = _$httpBackend_; + $scope = $rootScope.$new(); + $scope.bankEntity = { + open: () => {} + }; + $element = angular.element(''); + controller = $componentController('vnSupplierAccount', {$element, $scope}); + controller.supplierAccount = { + supplierFk: 442, + name: 'Verdnatura' + }; + })); + + describe('showBankEntity()', () => { + it('should do nothing if it default is prevented', () => { + const event = { + defaultPrevented: true, + preventDefault: () => {} + }; + jest.spyOn(event, 'preventDefault'); + jest.spyOn(controller.$.bankEntity, 'open'); + + controller.showBankEntity(event); + + expect(event.preventDefault).not.toHaveBeenCalledWith(); + expect(controller.$.bankEntity.open).not.toHaveBeenCalledWith(); + }); + + it('should call preventDefault() and open() when the default is not prevented', () => { + const event = { + defaultPrevented: false, + preventDefault: () => {} + }; + + jest.spyOn(event, 'preventDefault'); + jest.spyOn(controller.$.bankEntity, 'open'); + + controller.showBankEntity(event); + + expect(event.preventDefault).toHaveBeenCalledWith(); + expect(controller.$.bankEntity.open).toHaveBeenCalledWith(); + }); + + it('should request to create a new bank entity', () => { + controller.bankEntity = { + name: 'My new bank entity', + bic: 'ES1234', + countryFk: 1, + id: 2200 + }; + + const query = `SupplierAccounts/${controller.$.bankEntity.id}/createBankEntity`; + $httpBackend.expectPATCH(query).respond({id: 2200}); + controller.onBankEntityAccept(); + $httpBackend.flush(); + + expect(controller.supplierAccount.bankEntityFk).toEqual(controller.bankEntity.id); + }); + }); +}); + diff --git a/modules/supplier/front/account/locale/es.yml b/modules/supplier/front/account/locale/es.yml new file mode 100644 index 000000000..b5cac0207 --- /dev/null +++ b/modules/supplier/front/account/locale/es.yml @@ -0,0 +1,3 @@ +Bank entity: Entidad bancaria +swift: Swift BIC +Add account: Añadir cuenta \ No newline at end of file diff --git a/modules/supplier/front/index.js b/modules/supplier/front/index.js index 524ad733e..9c5cd4195 100644 --- a/modules/supplier/front/index.js +++ b/modules/supplier/front/index.js @@ -9,6 +9,7 @@ import './search-panel'; import './summary'; import './basic-data'; import './fiscal-data'; +import './account'; import './contact'; import './log'; import './consumption'; diff --git a/modules/supplier/front/routes.json b/modules/supplier/front/routes.json index 73ff28246..1be4df0fc 100644 --- a/modules/supplier/front/routes.json +++ b/modules/supplier/front/routes.json @@ -9,6 +9,7 @@ {"state": "supplier.index", "icon": "icon-supplier"} ], "card": [ + {"state": "supplier.card.account", "icon": "contact_support"}, {"state": "supplier.card.basicData", "icon": "settings"}, {"state": "supplier.card.fiscalData", "icon": "account_balance"}, {"state": "supplier.card.billingData", "icon": "icon-payment"}, @@ -99,6 +100,15 @@ "supplier": "$ctrl.supplier" }, "acl": ["administrative"] + },{ + "url": "/account", + "state": "supplier.card.account", + "component": "vn-supplier-account", + "description": "Account", + "params": { + "supplier": "$ctrl.supplier" + }, + "acl": ["administrative"] } ] } \ No newline at end of file diff --git a/modules/ticket/back/methods/ticket/new.js b/modules/ticket/back/methods/ticket/new.js index 8bafe5403..cec7857bd 100644 --- a/modules/ticket/back/methods/ticket/new.js +++ b/modules/ticket/back/methods/ticket/new.js @@ -101,7 +101,7 @@ module.exports = Self => { if (!shipped && landed) { const shippedResult = await models.Agency.getShipped(landed, address.id, agencyModeId, warehouseId); - shipped = shippedResult && shippedResult.shipped; + shipped = (shippedResult && shippedResult.shipped) || landed; } if (shipped && !landed) { diff --git a/modules/ticket/back/methods/ticket/specs/new.spec.js b/modules/ticket/back/methods/ticket/specs/new.spec.js index f240ce372..7ffcab68e 100644 --- a/modules/ticket/back/methods/ticket/specs/new.spec.js +++ b/modules/ticket/back/methods/ticket/specs/new.spec.js @@ -2,12 +2,13 @@ const app = require('vn-loopback/server/server'); let UserError = require('vn-loopback/util/user-error'); describe('ticket new()', () => { - let ticket; + let ticketIdsToDelete = []; let today = new Date(); let ctx = {req: {accessToken: {userId: 1}}}; afterAll(async done => { - await app.models.Ticket.destroyById(ticket.id); + for (id of ticketIdsToDelete) + await app.models.Ticket.destroyById(id); done(); }); @@ -28,7 +29,7 @@ describe('ticket new()', () => { params.shipped, params.landed, params.warehouseId, - params.companyFk, + params.companyId, params.addressId ).catch(e => { error = e; @@ -53,7 +54,7 @@ describe('ticket new()', () => { params.shipped, params.landed, params.warehouseId, - params.companyFk, + params.companyId, params.addressId ).catch(response => { expect(response.message).toEqual(`This address doesn't exist`); @@ -74,17 +75,44 @@ describe('ticket new()', () => { agencyModeId: 1 }; - ticket = await app.models.Ticket.new(ctx, + const ticket = await app.models.Ticket.new(ctx, params.clientId, params.shipped, params.landed, params.warehouseId, - params.companyFk, + params.companyId, params.addressId, params.agencyModeId); let newestTicketIdInFixtures = 21; + ticketIdsToDelete.push(ticket.id); + expect(ticket.id).toBeGreaterThan(newestTicketIdInFixtures); }); + + it('should return the set a shipped when the agency is not especified', async() => { + let params = { + clientId: 104, + landed: today, + shipped: null, + warehouseId: 2, + companyId: 442, + addressId: 4, + agencyModeId: null + }; + + const ticket = await app.models.Ticket.new(ctx, + params.clientId, + params.shipped, + params.landed, + params.warehouseId, + params.companyId, + params.addressId, + params.agencyModeId); + + ticketIdsToDelete.push(ticket.id); + + expect(ticket.shipped).toEqual(jasmine.any(Date)); + }); }); diff --git a/modules/ticket/front/create/card.html b/modules/ticket/front/create/card.html index 36e62d8e6..65c45d3dc 100644 --- a/modules/ticket/front/create/card.html +++ b/modules/ticket/front/create/card.html @@ -39,7 +39,7 @@
{ - this._availableAgencies = res.data; - - this.agencyModeId = this.defaultAddress.agencyModeFk; + this.agencies = res.data; + const defaultAgency = this.agencies.find(agency=> { + return agency.agencyModeFk == this.defaultAddress.agencyModeFk; + }); + if (defaultAgency) + this.agencyModeId = defaultAgency.agencyModeFk; }); } } diff --git a/modules/ticket/front/descriptor/index.html b/modules/ticket/front/descriptor/index.html index 2bab0b691..af625f5d4 100644 --- a/modules/ticket/front/descriptor/index.html +++ b/modules/ticket/front/descriptor/index.html @@ -116,4 +116,7 @@
- \ No newline at end of file + + + \ No newline at end of file diff --git a/modules/ticket/front/sale/index.html b/modules/ticket/front/sale/index.html index d7dfa802c..585feb063 100644 --- a/modules/ticket/front/sale/index.html +++ b/modules/ticket/front/sale/index.html @@ -326,31 +326,49 @@ icon="info"> - - - - Id - Shipped - Agency - Warehouse - Address - - - - - - + + + Id + Shipped + Agency + Address + + + + - {{::ticket.id}} - {{::ticket.shipped | date: 'dd/MM/yyyy'}} - {{::ticket.agencyName}} - {{::ticket.warehouseName}} - {{::ticket.address}} - - - + {{::ticket.id}} + {{::ticket.shipped | date: 'dd/MM/yyyy'}} + {{::ticket.agencyName}} + {{::ticket.address}} + + {{::ticket.nickname}} + {{::ticket.name}} + {{::ticket.street}} + {{::ticket.postalCode}} + {{::ticket.city}} + + + + + + No results + + + +
{ + Self.remoteMethod('getAverageDays', { + description: 'Returns the average days duration and the two warehouses of the travel.', + accessType: 'READ', + accepts: [{ + arg: 'agencyModeFk', + type: 'number', + required: true + }], + returns: { + type: 'object', + root: true + }, + http: { + path: `/getAverageDays`, + verb: 'GET' + } + }); + + Self.getAverageDays = async agencyModeFk => { + const conn = Self.dataSource.connector; + let stmts = []; + + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.travel'); + + stmt = new ParameterizedSQL(` + CREATE TEMPORARY TABLE tmp.travel ( + SELECT + t.id, + t.warehouseInFk, + t.warehouseOutFk, + t.landed, + t.shipped, + t.agencyFk + FROM travel t + WHERE t.agencyFk = ? LIMIT 50)`, [agencyModeFk]); + stmts.push(stmt); + + stmt = new ParameterizedSQL(` + SELECT + t.id, + t.warehouseInFk, + t.warehouseOutFk, + (SELECT ROUND(AVG(DATEDIFF(t.landed, t.shipped ))) + FROM tmp.travel t + WHERE t.agencyFk + ORDER BY id DESC LIMIT 50) AS dayDuration + FROM tmp.travel t + WHERE t.agencyFk + ORDER BY t.id DESC LIMIT 1`); + + const avgDaysIndex = stmts.push(stmt) - 1; + + stmts.push( + `DROP TEMPORARY TABLE + tmp.travel`); + + const sql = ParameterizedSQL.join(stmts, ';'); + const result = await conn.executeStmt(sql); + + const [avgDays] = result[avgDaysIndex]; + + return avgDays; + }; +}; diff --git a/modules/travel/back/models/travel.js b/modules/travel/back/models/travel.js index 46d33b305..046153ee2 100644 --- a/modules/travel/back/models/travel.js +++ b/modules/travel/back/models/travel.js @@ -8,6 +8,7 @@ module.exports = Self => { require('../methods/travel/deleteThermograph')(Self); require('../methods/travel/updateThermograph')(Self); require('../methods/travel/extraCommunityFilter')(Self); + require('../methods/travel/getAverageDays')(Self); require('../methods/travel/cloneWithEntries')(Self); Self.rewriteDbError(function(err) { diff --git a/modules/travel/front/create/index.html b/modules/travel/front/create/index.html index 0931c322e..6c19e0e12 100644 --- a/modules/travel/front/create/index.html +++ b/modules/travel/front/create/index.html @@ -20,6 +20,7 @@ diff --git a/modules/travel/front/create/index.js b/modules/travel/front/create/index.js index 9a9c5ce9d..a85917ca8 100644 --- a/modules/travel/front/create/index.js +++ b/modules/travel/front/create/index.js @@ -7,6 +7,34 @@ class Controller extends Section { this.travel = JSON.parse(this.$params.q); } + onShippedChange(value) { + let hasFilledProperties; + let hasAgencyMode; + if (this.travel) { + hasAgencyMode = Boolean(this.travel.agencyModeFk); + hasFilledProperties = this.travel.landed || this.travel.warehouseInFk || this.travel.warehouseOutFk; + } + if (!hasAgencyMode || hasFilledProperties) + return; + + const query = `travels/getAverageDays`; + const params = { + agencyModeFk: this.travel.agencyModeFk + }; + this.$http.get(query, {params}).then(res => { + if (!res.data) + return; + + const landed = new Date(value); + const futureDate = landed.getDate() + res.data.dayDuration; + landed.setDate(futureDate); + + this.travel.landed = landed; + this.travel.warehouseInFk = res.data.warehouseInFk; + this.travel.warehouseOutFk = res.data.warehouseOutFk; + }); + } + onSubmit() { return this.$.watcher.submit().then( res => this.$state.go('travel.card.basicData', {id: res.data.id}) diff --git a/modules/travel/front/create/index.spec.js b/modules/travel/front/create/index.spec.js index 99f52b322..e3f85d9ae 100644 --- a/modules/travel/front/create/index.spec.js +++ b/modules/travel/front/create/index.spec.js @@ -5,10 +5,12 @@ describe('Travel Component vnTravelCreate', () => { let $scope; let $state; let controller; + let $httpBackend; beforeEach(ngModule('travel')); - beforeEach(inject(($componentController, $rootScope, _$state_) => { + beforeEach(inject(($componentController, $rootScope, _$state_, _$httpBackend_) => { + $httpBackend = _$httpBackend_; $scope = $rootScope.$new(); $state = _$state_; $scope.watcher = watcher; @@ -38,4 +40,49 @@ describe('Travel Component vnTravelCreate', () => { expect(controller.travel).toEqual(json); }); }); + + describe('onShippedChange()', () => { + it(`should do nothing if there's no agencyModeFk in the travel.`, () => { + controller.travel = {}; + controller.onShippedChange(); + + expect(controller.travel.landed).toBeUndefined(); + expect(controller.travel.warehouseInFk).toBeUndefined(); + expect(controller.travel.warehouseOutFk).toBeUndefined(); + }); + + it(`should do nothing if there's no response data.`, () => { + controller.travel = {agencyModeFk: 4}; + const tomorrow = new Date(); + + const query = `travels/getAverageDays?agencyModeFk=${controller.travel.agencyModeFk}`; + $httpBackend.expectGET(query).respond(undefined); + controller.onShippedChange(tomorrow); + $httpBackend.flush(); + + expect(controller.travel.warehouseInFk).toBeUndefined(); + expect(controller.travel.warehouseOutFk).toBeUndefined(); + expect(controller.travel.dayDuration).toBeUndefined(); + }); + + it(`should fill the fields when it's selected a date and agency.`, () => { + controller.travel = {agencyModeFk: 1}; + const tomorrow = new Date(); + tomorrow.setDate(tomorrow.getDate() + 9); + const expectedResponse = { + id: 8, + dayDuration: 9, + warehouseInFk: 5, + warehouseOutFk: 1 + }; + + const query = `travels/getAverageDays?agencyModeFk=${controller.travel.agencyModeFk}`; + $httpBackend.expectGET(query).respond(expectedResponse); + controller.onShippedChange(tomorrow); + $httpBackend.flush(); + + expect(controller.travel.warehouseInFk).toEqual(expectedResponse.warehouseInFk); + expect(controller.travel.warehouseOutFk).toEqual(expectedResponse.warehouseOutFk); + }); + }); }); diff --git a/modules/worker/back/models/worker.json b/modules/worker/back/models/worker.json index 45eee23bf..cad38ac3b 100644 --- a/modules/worker/back/models/worker.json +++ b/modules/worker/back/models/worker.json @@ -42,6 +42,11 @@ "model": "Account", "foreignKey": "userFk" }, + "boss": { + "type": "belongsTo", + "model": "Account", + "foreignKey": "bossFk" + }, "client": { "type": "belongsTo", "model": "Client", diff --git a/modules/worker/front/basic-data/index.html b/modules/worker/front/basic-data/index.html index a2cbbc637..a767eccc4 100644 --- a/modules/worker/front/basic-data/index.html +++ b/modules/worker/front/basic-data/index.html @@ -29,6 +29,15 @@ ng-model="$ctrl.worker.phone" rule> + + diff --git a/modules/worker/front/locale/es.yml b/modules/worker/front/locale/es.yml index 63570ddf0..1414d089b 100644 --- a/modules/worker/front/locale/es.yml +++ b/modules/worker/front/locale/es.yml @@ -7,6 +7,7 @@ Extension: Extensión Fiscal identifier: NIF Go to client: Ir al cliente Last name: Apellidos +Boss: Jefe Log: Historial Private Branch Exchange: Centralita Role: Rol diff --git a/modules/worker/front/summary/index.html b/modules/worker/front/summary/index.html index 0a99959e4..d2a7e750b 100644 --- a/modules/worker/front/summary/index.html +++ b/modules/worker/front/summary/index.html @@ -30,6 +30,14 @@ + + + {{::worker.boss.nickname}} + + @@ -50,4 +58,7 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/modules/worker/front/summary/index.js b/modules/worker/front/summary/index.js index 6a4d87007..3cdb2c36f 100644 --- a/modules/worker/front/summary/index.js +++ b/modules/worker/front/summary/index.js @@ -11,8 +11,8 @@ class Controller extends Summary { this.$.worker = null; if (!value) return; - let query = `Workers/${value.id}`; - let filter = { + const query = `Workers/${value.id}`; + const filter = { include: [ { relation: 'user', @@ -31,13 +31,20 @@ class Controller extends Summary { } }] } - }, { + }, + { relation: 'client', scope: {fields: ['fi']} - }, { + }, + { + relation: 'boss', + scope: {fields: ['id', 'nickname']} + }, + { relation: 'sip', scope: {fields: ['extension']} - }, { + }, + { relation: 'department', scope: { include: { diff --git a/package-lock.json b/package-lock.json index dcd93f2c4..fe8ed7be0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10606,15 +10606,50 @@ "dev": true, "optional": true }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } + "bluebird": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" + }, + "bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha1-4Fpj95amwf8l9Hcex62twUjAcjM=" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + } + } }, "minizlib": { "version": "1.2.1", @@ -11677,11 +11712,14 @@ } } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + } }, "ansi-styles": { "version": "4.2.1", @@ -11693,15 +11731,19 @@ "color-convert": "^2.0.1" } }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", + "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } }, "binary-extensions": { "version": "2.0.0", @@ -14271,15 +14313,18 @@ "@types/yargs-parser": "*" } }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } + "file-type": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-10.11.0.tgz", + "integrity": "sha512-uzk64HRpUZyTGZtVuvrjP0FYxzQrBf4rojot6J65YMEbwBLB0CWm0CLojVpwpmFmxcE/lkvYICgfcGozbBq6rw==" + }, + "filed-mimefix": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/filed-mimefix/-/filed-mimefix-0.1.3.tgz", + "integrity": "sha1-Cwtn0HWmP8dPJv3znH+dQxSWe7U=", + "requires": { + "mime": "^1.4.0" + } }, "chalk": { "version": "4.1.0", @@ -14460,30 +14505,33 @@ "write-file-atomic": "^3.0.0" } }, - "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/babel__core": { - "version": "7.1.8", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.8.tgz", - "integrity": "sha512-KXBiQG2OXvaPWFPDS1rD8yV9vO0OuWIqAEqLsbfX0oU2REN5KuoMnZ1gClWcBhO5I3n6oTVAmrMufOvRqdmFTQ==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } + "fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + }, + "dependencies": { + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + } + } }, "@types/yargs": { "version": "15.0.5", @@ -15323,18 +15371,73 @@ "@jest/types": "^26.0.1" } }, - "jest-util": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.0.1.tgz", - "integrity": "sha512-byQ3n7ad1BO/WyFkYvlWQHTsomB6GIewBh8tlGtusiylAlaxQ1UpS0XYH0ngOyhZuHVLN79Qvl6/pMiDMSSG1g==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ienoopen": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.1.0.tgz", + "integrity": "sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, + "image-type": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/image-type/-/image-type-4.1.0.tgz", + "integrity": "sha512-CFJMJ8QK8lJvRlTCEgarL4ro6hfDQKif2HjSvYCdQZESaIPV4v9imrf7BQHK+sQeTeNeMpWciR9hyC/g8ybXEg==", + "requires": { + "file-type": "^10.10.0" + } + }, + "imap": { + "version": "0.8.19", + "resolved": "https://registry.npmjs.org/imap/-/imap-0.8.19.tgz", + "integrity": "sha1-NniHOTSrCc6mukh0HyhNoq9Z2NU=", + "requires": { + "readable-stream": "1.1.x", + "utf7": ">=1.0.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } }, "make-dir": { "version": "3.1.0", @@ -17243,28 +17346,53 @@ "to-regex-range": "^5.0.1" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "dependencies": { + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + } + } }, "graceful-fs": { "version": "4.2.4", @@ -17455,20 +17583,23 @@ "picomatch": "^2.0.5" } }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } + "nocache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", + "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "node-abi": { + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.19.3.tgz", + "integrity": "sha512-9xZrlyfvKhWme2EXFKQhZRp1yNWT/uI1luYPr3sFl+H4keYY4xR+1jO7mvTTijIsHf1M+QDe9uWuKeEpLInIlg==", + "requires": { + "semver": "^5.4.1" + } + }, + "node-addon-api": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz", + "integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==" }, "pretty-format": { "version": "26.0.1", @@ -17992,11 +18123,10 @@ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "is-ci": { "version": "2.0.0", @@ -18455,16 +18585,58 @@ } } }, - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "dev": true, - "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - } + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "prebuild-install": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.0.1.tgz", + "integrity": "sha512-7GOJrLuow8yeiyv75rmvZyeMGzl8mdEX5gY69d6a6bHWmiPevwqFw+tQavhK0EYMaSg3/KD24cWqeQv1EWsqDQ==", + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "requires": { + "mimic-response": "^2.0.0" + } + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" + }, + "simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + } + } }, "ws": { "version": "7.3.0", @@ -19264,15 +19436,25 @@ "path-key": "^3.0.0" } }, - "os-locale": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-5.0.0.tgz", - "integrity": "sha512-tqZcNEDAIZKBEPnHPlVDvKrp7NzgLi7jRmhKiUoa2NUmhl13FtkAGLUVR+ZsYvApBQdBfYm43A4tXXQ4IrYLBA==", - "requires": { - "execa": "^4.0.0", - "lcid": "^3.0.0", - "mem": "^5.0.0" - } + "read-chunk": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-3.2.0.tgz", + "integrity": "sha512-CEjy9LCzhmD7nUpJ1oVOE6s/hBkejlcJEgLQHVnQznOSilOPb+kpKktlLfFDK3/WP43+F80xkUTM2VOkYoSYvQ==", + "requires": { + "pify": "^4.0.1", + "with-open-file": "^0.1.6" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } }, "path-key": { "version": "3.1.1", @@ -21962,15 +22144,50 @@ "ms": "2.1.2" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } + "sharp": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.27.1.tgz", + "integrity": "sha512-IQNXWdspb4nZcJemXa6cfgz+JvKONsuqP8Mwi1Oti23Uo7+J+UF2jihJDf6I1BQbrmhcZ0lagH/1WYG+ReAzyQ==", + "requires": { + "array-flatten": "^3.0.0", + "color": "^3.1.3", + "detect-libc": "^1.0.3", + "node-addon-api": "^3.1.0", + "npmlog": "^4.1.2", + "prebuild-install": "^6.0.0", + "semver": "^7.3.4", + "simple-get": "^4.0.0", + "tar-fs": "^2.1.1", + "tunnel-agent": "^0.6.0" + }, + "dependencies": { + "array-flatten": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz", + "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } }, "https-proxy-agent": { "version": "4.0.0", @@ -24125,74 +24342,64 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - } - } - }, - "strong-globalize": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/strong-globalize/-/strong-globalize-4.1.3.tgz", - "integrity": "sha512-SJegV7w5D4AodEspZJtJ7rls3fmi+Zc0PdyJCqBsg4RN9B8TC80/uAI2fikC+s1Jp9FLvr2vDX8f0Fqc62M4OA==", - "requires": { - "accept-language": "^3.0.18", - "debug": "^4.1.1", - "globalize": "^1.4.2", - "lodash": "^4.17.4", - "md5": "^2.2.1", - "mkdirp": "^0.5.1", - "os-locale": "^3.1.0", - "yamljs": "^0.3.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "strong-remoting": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/strong-remoting/-/strong-remoting-3.16.2.tgz", - "integrity": "sha512-Kj8dZh6q0F3GQTil2rkEt6WuQoQbXFRZ2AJG36iBPmxcVEE9bVhWXUYxcJIxXaGekCprxp8kfdLLN67yp0J4Hg==", - "requires": { - "async": "^3.1.0", - "body-parser": "^1.12.4", - "debug": "^4.1.1", - "depd": "^2.0.0", - "escape-string-regexp": "^2.0.0", - "eventemitter2": "^5.0.1", - "express": "4.x", - "inflection": "^1.7.1", - "jayson": "^2.0.5", - "js2xmlparser": "^3.0.0", - "loopback-datatype-geopoint": "^1.0.0", - "loopback-phase": "^3.1.0", - "mux-demux": "^3.7.9", - "qs": "^6.2.1", - "request": "^2.83.0", - "sse": "0.0.8", - "strong-error-handler": "^3.0.0", - "strong-globalize": "^5.0.2", - "traverse": "^0.6.6", - "xml2js": "^0.4.8" - }, - "dependencies": { - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } }, "cross-spawn": { "version": "7.0.3", @@ -24609,14 +24816,38 @@ "readable-stream": "^3.4.0" } }, - "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } + "uid2": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", + "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=" + }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + }, + "dependencies": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } }, "readable-stream": { "version": "3.6.0", @@ -26380,14 +26611,41 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } + "with-open-file": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/with-open-file/-/with-open-file-0.1.7.tgz", + "integrity": "sha512-ecJS2/oHtESJ1t3ZfMI3B7KIDKyfN0O16miWxdn30zdh66Yd3LsRFebXZXq6GU4xfxLf6nVxp9kIqElb5fqczA==", + "requires": { + "p-finally": "^1.0.0", + "p-try": "^2.1.0", + "pify": "^4.0.1" + } + }, + "word-count": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/word-count/-/word-count-0.2.2.tgz", + "integrity": "sha1-aZGS/KaCn+k21Byw2V25JIxXBFE=" + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } }, "semver": { "version": "6.3.0", diff --git a/package.json b/package.json index 0e1bc8e32..1fe209cf2 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,12 @@ "node": ">=12" }, "dependencies": { + "bmp-js": "^0.1.0", "compression": "^1.7.3", "fs-extra": "^5.0.0", "helmet": "^3.21.2", "i18n": "^0.8.4", + "image-type": "^4.1.0", "imap": "^0.8.19", "ldapjs": "^2.2.0", "loopback": "^3.26.0", @@ -30,10 +32,11 @@ "node-ssh": "^11.0.0", "object-diff": "0.0.4", "object.pick": "^1.3.0", + "read-chunk": "^3.2.0", "request": "^2.88.0", "request-promise-native": "^1.0.8", "require-yaml": "0.0.1", - "sharp": "^0.25.4", + "sharp": "^0.27.1", "smbhash": "0.0.1", "soap": "^0.35.0", "strong-error-handler": "^2.3.2",