diff --git a/package.json b/package.json index b1e2428a03..1473040f67 100644 --- a/package.json +++ b/package.json @@ -183,7 +183,7 @@ "gulp-if": "^3.0.0", "gulp-iife": "^0.4.0", "gulp-less": "^5.0.0", - "gulp-merge-json": "^2.1.2", + "gulp-merge-json": "^2.2.0", "gulp-postcss": "^10.0.0", "gulp-rev": "^9.0.0", "gulp-rev-rewrite": "^5.0.0", diff --git a/server/controllers/payroll/configuration/index.js b/server/controllers/payroll/configuration/index.js index 1568a827f5..ac35bb3940 100644 --- a/server/controllers/payroll/configuration/index.js +++ b/server/controllers/payroll/configuration/index.js @@ -3,9 +3,10 @@ * * This controller exposes an API to the client for reading and writing Payroll configuration */ - const q = require('q'); +const moment = require('moment'); const db = require('../../../lib/db'); +const util = require('../../../lib/util'); // GET /PAYROLL_CONFIG function lookupPayrollConfig(id) { @@ -57,7 +58,8 @@ function create(req, res, next) { db.exec(sql, [data]) .then((row) => { insertedId = row.insertId; - return db.exec(`CALL UpdateStaffingIndices(?, ?, ?)`, [data.dateFrom, data.dateTo, insertedId]); + data.dateTo = moment(data.dateTo).format('YYYY-MM-DD'); + return updateEmployeesBasicIndice(insertedId, data.dateTo); }).then(() => { res.status(201).json({ id : insertedId }); }) @@ -185,6 +187,126 @@ function payrollReportElements(idPeriod, employees, employeesPaymentUuid) { ]); } +async function updateEmployeesBasicIndice(idPeriod, dateTo) { + // This query is executed when running payroll with index for the very first time or + // when dealing with employees who are configured for the very first time, + // this query searches for the date of hire, their relative base index with their rank, + // their responsibility index linked to their function, + const sqlFindNewEmployees = ` + SELECT emp.uuid, emp.date_embauche, sgi.value, sfi.value AS function_indice_value, + emp.grade_uuid, emp.fonction_id, pa.display_name + FROM employee AS emp + JOIN config_employee_item AS it ON it.employee_uuid = emp.uuid + JOIN config_employee AS conf ON conf.id = it.config_employee_id + JOIN payroll_configuration AS pay ON pay.config_employee_id = conf.id + JOIN grade AS gr ON gr.uuid = emp.grade_uuid + JOIN staffing_grade_indice AS sgi ON sgi.grade_uuid = emp.grade_uuid + LEFT JOIN staffing_function_indice AS sfi ON sfi.fonction_id = emp.fonction_id + JOIN patient AS pa ON pa.uuid = emp.patient_uuid + WHERE pay.id = ? AND emp.uuid NOT IN (SELECT stf.employee_uuid FROM staffing_indice AS stf) + ORDER BY pa.display_name ASC; + `; + + // The following query is executed when dealing with employees who have already been configured, + // this query the date of the very first increment as well as the last value of the base index, + // the responsibility index linked to the function and date of hire + const sqlFindOldEmployees = ` + SELECT emp.uuid, emp.date_embauche, lastIndice.date AS lastDateIncrease, + MAX(lastIndice.grade_indice) AS grade_indice, sfi.value AS function_indice_value, emp.grade_uuid, + emp.fonction_id, pa.display_name + FROM employee AS emp + JOIN config_employee_item AS it ON it.employee_uuid = emp.uuid + JOIN config_employee AS conf ON conf.id = it.config_employee_id + JOIN payroll_configuration AS pay ON pay.config_employee_id = conf.id + JOIN grade AS gr ON gr.uuid = emp.grade_uuid + JOIN ( + SELECT st.uuid, st.employee_uuid, st.grade_indice, st.date + FROM staffing_indice st + JOIN ( + SELECT uuid, employee_uuid, MAX(date) AS maxdate + FROM staffing_indice st + GROUP BY st.employee_uuid + ) AS currentInd ON currentInd.employee_uuid = st.employee_uuid AND currentInd.maxdate = st.date + ) AS lastIndice ON lastIndice.employee_uuid = emp.uuid + LEFT JOIN staffing_function_indice AS sfi ON sfi.fonction_id = emp.fonction_id + JOIN patient AS pa ON pa.uuid = emp.patient_uuid + WHERE pay.id = ? + GROUP BY emp.uuid + ORDER BY pa.display_name ASC; + `; + + const sqlGetBaseIndexGrowthRate = ` + SELECT base_index_growth_rate FROM enterprise_setting LIMIT 1; + `; + + const [newEmployees, oldEmployees, dataEnterprise] = await Promise.all([ + db.exec(sqlFindNewEmployees, idPeriod), + db.exec(sqlFindOldEmployees, idPeriod), + db.exec(sqlGetBaseIndexGrowthRate), + ]); + + const transaction = db.transaction(); + + const baseIndexGrowthRate = dataEnterprise[0].base_index_growth_rate; + + // Processing of new employee data + newEmployees.forEach(employee => { + employee.date_embauche = moment(employee.date_embauche).format('YYYY-MM-DD'); + const yearOfSeniority = parseInt(moment(dateTo).diff(employee.date_embauche, 'years'), 10); + + // Here we increment the base index based on the number of years + for (let i = 0; i < yearOfSeniority; i++) { + employee.value += (employee.value * (baseIndexGrowthRate / 100)); + } + + const dataStaffingIndice = { + uuid : db.uuid(), + employee_uuid : employee.uuid, + grade_uuid : employee.grade_uuid, + fonction_id : employee.fonction_id, + grade_indice : util.roundDecimal(employee.value, 0), + function_indice : employee.function_indice_value || 0, + date : new Date(), + }; + transaction.addQuery('INSERT INTO staffing_indice SET ?', dataStaffingIndice); + }); + + oldEmployees.forEach(employee => { + employee.date_embauche = moment(employee.date_embauche).format('YYYY-MM-DD'); + employee.lastDateIncrease = moment(employee.lastDateIncrease).format('YYYY-MM-DD'); + // For employees who have already been configured, we will compare the number of years of seniority + // and the difference in years between the date of the last increment of the base index, + // if this difference is greater than zero, the we will have to increment + // the base index in relation to this difference + const yearOfSeniority = parseInt(moment(dateTo).diff(employee.date_embauche, 'years'), 10); + const yearLastIncrementation = parseInt(moment(employee.lastDateIncrease).diff(employee.date_embauche, 'years'), + 10); + + const diffSeniorityIncrementation = yearOfSeniority - yearLastIncrementation; + + if ((diffSeniorityIncrementation > 0) && (baseIndexGrowthRate > 0)) { + for (let i = 0; i < diffSeniorityIncrementation; i++) { + employee.grade_indice += (employee.grade_indice * (baseIndexGrowthRate / 100)); + } + + const dataStaffingIndice = { + uuid : db.uuid(), + employee_uuid : employee.uuid, + grade_uuid : employee.grade_uuid, + fonction_id : employee.fonction_id, + grade_indice : util.roundDecimal(employee.grade_indice, 0), + function_indice : employee.function_indice_value || 0, + date : new Date(), + }; + + transaction.addQuery('INSERT INTO staffing_indice SET ?', dataStaffingIndice); + + } + }); + + return transaction.execute(); +} + // get list of Payroll configuration exports.list = list; @@ -206,3 +328,5 @@ exports.paymentStatus = paymentStatus; exports.lookupPayrollConfig = lookupPayrollConfig; exports.payrollReportElements = payrollReportElements; + +exports.updateEmployeesBasicIndice = updateEmployeesBasicIndice; diff --git a/server/models/migrations/next/migrate.sql b/server/models/migrations/next/migrate.sql index 7b200614bc..80d77e8db1 100644 --- a/server/models/migrations/next/migrate.sql +++ b/server/models/migrations/next/migrate.sql @@ -1,2 +1,8 @@ /* Release 1.31.0 */ +/* + * @author: lomamech + * @description: Problem regarding the increase in basic indices based on percentages + * @date: 2024-02-26 + */ +DROP PROCEDURE IF EXISTS `UpdateStaffingIndices`; diff --git a/test/data.sql b/test/data.sql index 7bff370aa2..c9020030b4 100644 --- a/test/data.sql +++ b/test/data.sql @@ -803,8 +803,6 @@ INSERT INTO `payroll_configuration` (`id`, `label`, `dateFrom`, `dateTo`, (1, 'FĂ©vrier 2018', '2018-02-01', '2018-02-28', 1, 1, 1, 1, 1), (2, 'Juin 2019', '2019-06-01', '2019-06-30', 2, 1, 1, 1, 1); -CALL UpdateStaffingIndices('2019-06-01', '2019-06-30', 2); - SET @paymentUuid = HUID('2a3f17b0ae3242bb9333a760825fd257'); SET @employeeUuid = HUID('75e0969465f245a1a8a28b025003d793'); -- payment DATA @@ -1145,7 +1143,7 @@ INSERT INTO `patient` (`uuid`, `project_id`, `reference`, `debtor_uuid`, `displa INSERT INTO `creditor` (`uuid`, `group_uuid`, `text`) VALUES (0x75ED6B1141764BF7821D34BA638B0647, 0xB0FA5ED204F94CB392F761D6404696E7, 'Crediteur [Employee indice 1]'); INSERT INTO `creditor` (`uuid`, `group_uuid`, `text`) VALUES (0x6150E9B14D214DC28E845BFAAB40DD5E, 0xB0FA5ED204F94CB392F761D6404696E7, 'Crediteur [Employee indice 2]'); -INSERT INTO `employee` (`uuid`, `code`, `date_embauche`, `grade_uuid`, `nb_spouse`, `nb_enfant`, `individual_salary`, `bank`, `bank_account`, `fonction_id`, `service_uuid`, `creditor_uuid`, `locked`, `patient_uuid`, `reference`) VALUES (0x10F64A41DA594962AA9A90CF0D42257C, 'IMA-ASSR-1', DATE_ADD(CURRENT_DATE, INTERVAL -2 YEAR), 0x71E9F21CD9B111E58AB778EB2F2A46E0, 0, 0, 0, NULL, NULL, 2, 0xB1816006555845F993A0C222B5EFA6CB, 0x75ED6B1141764BF7821D34BA638B0647, NULL, 0x4B23E6B9187F4CE69696542F798B5F32, 3); +INSERT INTO `employee` (`uuid`, `code`, `date_embauche`, `grade_uuid`, `nb_spouse`, `nb_enfant`, `individual_salary`, `bank`, `bank_account`, `fonction_id`, `service_uuid`, `creditor_uuid`, `locked`, `patient_uuid`, `reference`) VALUES (0x10F64A41DA594962AA9A90CF0D42257C, 'IMA-ASSR-1', DATE_ADD(CURRENT_DATE, INTERVAL -25 MONTH) , 0x71E9F21CD9B111E58AB778EB2F2A46E0, 0, 0, 0, NULL, NULL, 2, 0xB1816006555845F993A0C222B5EFA6CB, 0x75ED6B1141764BF7821D34BA638B0647, NULL, 0x4B23E6B9187F4CE69696542F798B5F32, 3); INSERT INTO `employee` (`uuid`, `code`, `date_embauche`, `grade_uuid`, `nb_spouse`, `nb_enfant`, `individual_salary`, `bank`, `bank_account`, `fonction_id`, `service_uuid`, `creditor_uuid`, `locked`, `patient_uuid`, `reference`) VALUES (0xD321099D01CD41029686B87CB70B9311, 'IMA-ASSP-2', DATE_ADD(CURRENT_DATE, INTERVAL -2 YEAR), 0x61E9F21CD9B111E48AB678EB2F2A46E0, 0, 2, 0, NULL, NULL, 1, 0xE3988489EF6641DF88FA8B8ED6AA03AC, 0x6150E9B14D214DC28E845BFAAB40DD5E, NULL, 0xA6643C4F310145BA961B54FD83D71E7C, 4); INSERT INTO `config_employee_item` (`id`, `config_employee_id`, `employee_uuid`) VALUES diff --git a/test/integration/staffingIndices.js b/test/integration/staffingIndices.js index 439cbc3867..06b5f0ce60 100644 --- a/test/integration/staffingIndices.js +++ b/test/integration/staffingIndices.js @@ -1,6 +1,6 @@ /* eslint no-unused-expressions:off */ /* global expect, agent */ - +const moment = require('moment'); const helpers = require('./helpers'); /* @@ -59,6 +59,25 @@ describe('test/integration/staffingIndices The staffing indices API', () => { payroll_configuration_id : 6, }; + const year = new Date().getFullYear(); + const datePlus2Year = moment().add(2, 'year').format('YYYY-MM-DD'); + + const datePlus2YearSub1Month = moment(datePlus2Year).clone().subtract(1, 'months').format('YYYY-MM-DD'); + const datePlus2YearSub1MonthBegin = moment(datePlus2YearSub1Month).clone().startOf('month').format('YYYY-MM-DD'); + const datePlus2YearSub1MonthEnd = moment(datePlus2YearSub1Month).clone().endOf('month').format('YYYY-MM-DD'); + + const yearPlus2 = year + 2; + + const payrollConfigYearPlus2Sub1Month = { + label : `Account Configuration ${yearPlus2} substract one month`, + dateFrom : datePlus2YearSub1MonthBegin, + dateTo : datePlus2YearSub1MonthEnd, + config_rubric_id : 2, + config_accounting_id : 1, + config_weekend_id : 1, + config_employee_id : 2, + }; + it('POST /staffing_indices add a new staffing indice', () => { return agent.post('/staffing_indices') .send(newIndice) @@ -182,4 +201,66 @@ describe('test/integration/staffingIndices The staffing indices API', () => { }) .catch(helpers.handler); }); + + // Checking the increase in base indices when creating a futuristic pay period + it(`POST /PAYROLL_CONFIG should Payroll Configuration Year+2 for Checking + the increase in base indices when creating a futuristic pay period`, () => { + return agent.post('/payroll_config') + .send(payrollConfigYearPlus2Sub1Month) + .then((res) => { + payrollConfigYearPlus2Sub1Month.id = res.body.id; + helpers.api.created(res); + + // To test the new functionality intended to increase the base indices, + // the system will first calculate the number of years of employee seniority + // in relation to the pay period as well as the difference in years between + // the last increment of the base index by in relation to the pay period, + // and if this difference is greater than zero, the basic index will be + // incremented as a percentage proportionally to the number of years. + + // + return agent.get('/staffing_indices') + .then(res2 => { + expect(res2).to.have.status(200); + expect(res2.body).to.be.an('array'); + + let checkIncrementationGradeIndice = 0; + + res2.body.forEach(element => { + // For the following example the first was hired on January 27, 2022, and + // its last base index dates from February 27, 2024 and it is 66 + // The pay period is January 2026 + // The system will first calculate their year of seniority in relation to the pay period + // Seniority = (2026-01-31) - (2022-01-27) is equal to 4 years (1) + // Last increment = (2024-02-27) - (2022-01-27) is equal to 2 years (2) + // Base Index Growth Rate being 5%, the base index of this employee will be increased in + // accordance with the difference in years (1)-(2) => 4 years - 2 years = 2 years, + // - Year 1: 66 + (66 x 0.05) = 69.03 + // - Year 2: 69.03 + (69.03 x 0.05) = 72.77 which the system will round to 73 + if (element.grade_indice === 73) { + checkIncrementationGradeIndice++; + } + + // For the following example the second was hired on february 27, 2022, and + // its last base index dates from February 27, 2024 and it is 138 + // The pay period is January 2026 + // The system will first calculate their year of seniority in relation to the pay period + // Seniority = (2026-01-31) - (2022-02-27) is equal to 3 years (1) + // Last increment = (2024-02-27) - (2022-02-27) is equal to 2 years (2) + // Base Index Growth Rate being 5%, the base index of this employee will be increased in + // accordance with the difference in years (1)-(2) => 3 years - 2 years = 1 year + // - Year 1: 138 + (138 x 0.05) = 144.9 which the system will round to 145 + if (element.grade_indice === 145) { + checkIncrementationGradeIndice++; + } + + }); + + expect(checkIncrementationGradeIndice).to.equal(2); + expect(res2.body).to.have.length(9); + }) + .catch(helpers.handler); + }) + .catch(helpers.handler); + }); }); diff --git a/yarn.lock b/yarn.lock index c0ad679236..0f22300c2a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1136,6 +1136,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +bare-events@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.2.0.tgz#a7a7263c107daf8b85adf0b64f908503454ab26e" + integrity sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -1738,7 +1743,7 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -clone@^2.1.1: +clone@^2.1.1, clone@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== @@ -4442,16 +4447,17 @@ gulp-match@^1.1.0: dependencies: minimatch "^3.0.3" -gulp-merge-json@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/gulp-merge-json/-/gulp-merge-json-2.1.2.tgz#7810dbf8f1f1a8638c18d46bad165bd44d18ea79" - integrity sha512-FysBAdHdnQvZzigVJJzlrt6TEosHxVb0mR2h/8eSnd+eJyBvb1LQF1EIrovrOCfj4HGE5p/95wGEjXsJk9qomw== +gulp-merge-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/gulp-merge-json/-/gulp-merge-json-2.2.0.tgz#b448ebf2e47195ab9972f36d7e6a8a269bcb1c7b" + integrity sha512-HEnmRRckVaLgsYRmzk14h4iD6/xNDHbLX5nuvFXHZd2Y+U9Qtvpl+IrvIh8hWs2VvpLuYaF1z8v9jkhLPYH8Mw== dependencies: - json5 "^2.2.1" + json5 "^2.2.3" + lodash.clonedeep "^4.5.0" lodash.mergewith "^4.6.1" - plugin-error "^1.0.1" + plugin-error "^2.0.1" through "^2.3.8" - vinyl "^2.2.1" + vinyl "^3.0.0" gulp-postcss@^10.0.0: version "10.0.0" @@ -5572,7 +5578,7 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.2.1: +json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -5913,6 +5919,11 @@ lodash.capitalize@^4.2.1: resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9" integrity sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw== +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + lodash.defaults@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" @@ -8799,6 +8810,16 @@ streamsearch@^1.1.0: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== +streamx@^2.12.5: + version "2.16.1" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.16.1.tgz#2b311bd34832f08aa6bb4d6a80297c9caef89614" + integrity sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ== + dependencies: + fast-fifo "^1.1.0" + queue-tick "^1.0.1" + optionalDependencies: + bare-events "^2.2.0" + streamx@^2.15.0: version "2.15.1" resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.15.1.tgz#396ad286d8bc3eeef8f5cea3f029e81237c024c6" @@ -9099,6 +9120,13 @@ tar-stream@^3.1.5: fast-fifo "^1.2.0" streamx "^2.15.0" +teex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/teex/-/teex-1.0.1.tgz#b8fa7245ef8e8effa8078281946c85ab780a0b12" + integrity sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg== + dependencies: + streamx "^2.12.5" + temp-dir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" @@ -9726,7 +9754,7 @@ vinyl-sourcemaps-apply@^0.2.0, vinyl-sourcemaps-apply@^0.2.1: dependencies: source-map "^0.5.1" -vinyl@^2.0.0, vinyl@^2.0.1, vinyl@^2.1.0, vinyl@^2.2.1: +vinyl@^2.0.0, vinyl@^2.0.1, vinyl@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== @@ -9738,6 +9766,17 @@ vinyl@^2.0.0, vinyl@^2.0.1, vinyl@^2.1.0, vinyl@^2.2.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" +vinyl@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-3.0.0.tgz#11e14732bf56e2faa98ffde5157fe6c13259ff30" + integrity sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g== + dependencies: + clone "^2.1.2" + clone-stats "^1.0.0" + remove-trailing-separator "^1.1.0" + replace-ext "^2.0.0" + teex "^1.0.1" + void-elements@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"