diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b938797e..a672e7986 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [24.20.01] - 2024-05-14 +### Fixed +- (Worker -> time-control) Corrección de errores +- (InvoiceOut -> Crear factura) Cuando falla al crear una factura, se devuelve un error + ## [24.18.01] - 2024-05-07 ## [24.16.01] - 2024-04-18 diff --git a/back/model-config.json b/back/model-config.json index db43f89b2..e64386300 100644 --- a/back/model-config.json +++ b/back/model-config.json @@ -124,6 +124,9 @@ "Postcode": { "dataSource": "vn" }, + "ReferenceRate": { + "dataSource": "vn" + }, "SageWithholding": { "dataSource": "vn" }, @@ -184,4 +187,4 @@ "AgencyWorkCenter": { "dataSource": "vn" } -} \ No newline at end of file +} diff --git a/back/models/collection.json b/back/models/collection.json index 3e428ef60..cb8dc3d7c 100644 --- a/back/models/collection.json +++ b/back/models/collection.json @@ -1,6 +1,11 @@ { "name": "Collection", "base": "VnModel", + "options": { + "mysql": { + "table": "collection" + } + }, "acls": [{ "property": "validations", "accessType": "EXECUTE", @@ -9,4 +14,3 @@ "permission": "ALLOW" }] } - \ No newline at end of file diff --git a/back/models/reference-rate.json b/back/models/reference-rate.json new file mode 100644 index 000000000..fe732f3ef --- /dev/null +++ b/back/models/reference-rate.json @@ -0,0 +1,36 @@ +{ + "name": "ReferenceRate", + "base": "PersistedModel", + "options": { + "mysql": { + "table": "referenceRate" + } + }, + "properties": { + "id": { + "type": "number", + "id": true, + "description": "Identifier" + }, + "currencyFk": { + "type": "number", + "required": true + }, + "dated": { + "type": "date", + "required": true + }, + "value": { + "type": "number", + "required": true + } + }, + "acls": [ + { + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + } + ] +} diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index ee115751e..4b106aec5 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -160,7 +160,8 @@ INSERT INTO `vn`.`currency`(`id`, `code`, `name`, `ratio`) (1, 'EUR', 'Euro', 1), (2, 'USD', 'Dollar USA', 1.4), (3, 'GBP', 'Libra', 1), - (4, 'JPY', 'Yen Japones', 1); + (4, 'JPY', 'Yen Japones', 1), + (5, 'CNY', 'Yuan Chino', 1.2); INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`, `continentFk`, `hasDailyInvoice`, `CEE`) VALUES diff --git a/db/routines/vn/procedures/ticketCalculateClon.sql b/db/routines/vn/procedures/ticketCalculateClon.sql index 7ded84f45..a56491590 100644 --- a/db/routines/vn/procedures/ticketCalculateClon.sql +++ b/db/routines/vn/procedures/ticketCalculateClon.sql @@ -29,7 +29,7 @@ BEGIN FROM sale WHERE ticketFk = vTicketNew AND price > 0; - CALL sale_recalcComponent('imbalance'); + CALL sale_recalcComponent('buyerDiscount'); DROP TEMPORARY TABLE IF EXISTS tmp.recalculateSales; diff --git a/db/routines/vn/procedures/workerTimeControl_clockIn.sql b/db/routines/vn/procedures/workerTimeControl_clockIn.sql index e58528487..a1ce53bc2 100644 --- a/db/routines/vn/procedures/workerTimeControl_clockIn.sql +++ b/db/routines/vn/procedures/workerTimeControl_clockIn.sql @@ -121,15 +121,17 @@ BEGIN CALL util.throw(vErrorCode); END IF; + -- DIRECCION CORRECTA CALL workerTimeControl_direction(vWorkerFk, vTimed); IF (SELECT - IF(IF(option1 IN ('inMiddle', 'outMiddle'), + IF((IF(option1 IN ('inMiddle', 'outMiddle'), 'middle', option1) <> vDirection AND IF(option2 IN ('inMiddle', 'outMiddle'), 'middle', - IFNULL(option2, '')) <> vDirection, + IFNULL(option2, '')) <> vDirection) + OR (option1 IS NULL AND option2 IS NULL), TRUE , FALSE) FROM tmp.workerTimeControlDirection @@ -137,12 +139,17 @@ BEGIN SET vIsError = TRUE; END IF; - DROP TEMPORARY TABLE tmp.workerTimeControlDirection; + IF vIsError THEN SET vErrorCode = 'WRONG_DIRECTION'; + IF(SELECT option1 IS NULL AND option2 IS NULL + FROM tmp.workerTimeControlDirection) THEN + + SET vErrorCode = 'DAY_MAX_TIME'; + END IF; CALL util.throw(vErrorCode); END IF; - + DROP TEMPORARY TABLE tmp.workerTimeControlDirection; -- FICHADAS IMPARES SELECT timed INTO vLastIn FROM workerTimeControl diff --git a/db/versions/10992-goldenIvy/00-acl.sql b/db/versions/10992-goldenIvy/00-acl.sql new file mode 100644 index 000000000..1d1c3ce91 --- /dev/null +++ b/db/versions/10992-goldenIvy/00-acl.sql @@ -0,0 +1,2 @@ +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES ('InvoiceIn', 'exchangeRateUpdate', '*', 'ALLOW', 'ROLE', 'employee'); diff --git a/db/versions/10992-goldenIvy/00-referenceRate.sql b/db/versions/10992-goldenIvy/00-referenceRate.sql new file mode 100644 index 000000000..db53f328f --- /dev/null +++ b/db/versions/10992-goldenIvy/00-referenceRate.sql @@ -0,0 +1,4 @@ +ALTER TABLE vn.referenceRate DROP INDEX `PRIMARY`; +ALTER TABLE vn.referenceRate ADD id INT auto_increment PRIMARY KEY; +ALTER TABLE vn.referenceRate CHANGE id id int(11) auto_increment NOT NULL FIRST; +CREATE UNIQUE INDEX referenceRate_currencyFk_IDX USING BTREE ON vn.referenceRate (currencyFk,dated); diff --git a/db/versions/11007-greenRose/00-firstScript.sql b/db/versions/11007-greenRose/00-firstScript.sql index 69959f0b4..154a75532 100644 --- a/db/versions/11007-greenRose/00-firstScript.sql +++ b/db/versions/11007-greenRose/00-firstScript.sql @@ -1,4 +1,3 @@ - CREATE OR REPLACE TABLE `vn`.`farmingDeliveryNote` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `farmingFk` int(10) unsigned NOT NULL, @@ -10,3 +9,8 @@ CREATE OR REPLACE TABLE `vn`.`farmingDeliveryNote` ( CONSTRAINT `farmingDeliveryNoteFk_FK` FOREIGN KEY (`deliveryNoteFk`) REFERENCES `deliveryNote` (`id`), CONSTRAINT `farmingDeliveryNoteFk_FK_1` FOREIGN KEY (`farmingFk`) REFERENCES `farming` (`id`) ); + +INSERT IGNORE INTO `vn`.`farmingDeliveryNote` (farmingFk, deliveryNoteFk, amount) + SELECT farmingFk, id, amount + FROM vn.deliveryNote dn + WHERE farmingFk; \ No newline at end of file diff --git a/e2e/paths/09-invoice-out/03_manualInvoice.spec.js b/e2e/paths/09-invoice-out/03_manualInvoice.spec.js index dfaa55ef9..6addbbe56 100644 --- a/e2e/paths/09-invoice-out/03_manualInvoice.spec.js +++ b/e2e/paths/09-invoice-out/03_manualInvoice.spec.js @@ -40,7 +40,7 @@ describe('InvoiceOut manual invoice path', () => { await page.waitToClick(selectors.invoiceOutIndex.createInvoice); await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm); - await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceClient, 'Max Eisenhardt'); + await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceClient, 'Bruce Wayne'); await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceSerial, 'Global nacional'); await page.autocompleteSearch(selectors.invoiceOutIndex.manualInvoiceTaxArea, 'national'); await page.waitToClick(selectors.invoiceOutIndex.saveInvoice); diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 249a46e80..d7e41233e 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -354,5 +354,7 @@ "They're not your subordinate": "No es tu subordinado/a.", "No results found": "No se han encontrado resultados", "InvoiceIn is already booked": "La factura recibida está contabilizada", - "This workCenter is already assigned to this agency": "Este centro de trabajo ya está asignado a esta agencia" -} \ No newline at end of file + "This workCenter is already assigned to this agency": "Este centro de trabajo ya está asignado a esta agencia", + "Select ticket or client": "Elija un ticket o un client", + "It was not able to create the invoice": "No se pudo crear la factura" +} diff --git a/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js b/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js new file mode 100644 index 000000000..3ad06b242 --- /dev/null +++ b/modules/invoiceIn/back/methods/invoice-in/exchangeRateUpdate.js @@ -0,0 +1,64 @@ +const axios = require('axios'); +const {DOMParser} = require('xmldom'); +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethod('exchangeRateUpdate', { + description: 'Updates the exchange rates from an XML feed', + accessType: 'WRITE', + accepts: [], + http: { + path: '/exchangeRateUpdate', + verb: 'post' + } + }); + + Self.exchangeRateUpdate = async() => { + const response = await axios.get('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml'); + const xmlData = response.data; + + const doc = new DOMParser({errorHandler: {warning: () => {}}})?.parseFromString(xmlData, 'text/xml'); + const cubes = doc?.getElementsByTagName('Cube'); + if (!cubes || cubes.length === 0) + throw new UserError('No cubes found. Exiting the method.'); + + const models = Self.app.models; + + const maxDateRecord = await models.ReferenceRate.findOne({order: 'dated DESC'}); + + const maxDate = maxDateRecord?.dated ? new Date(maxDateRecord.dated) : null; + + for (const cube of Array.from(cubes)) { + if (cube.nodeType === doc.ELEMENT_NODE && cube.attributes.getNamedItem('time')) { + const xmlDate = new Date(cube.getAttribute('time')); + const xmlDateWithoutTime = new Date(xmlDate.getFullYear(), xmlDate.getMonth(), xmlDate.getDate()); + if (!maxDate || maxDate < xmlDateWithoutTime) { + for (const rateCube of Array.from(cube.childNodes)) { + if (rateCube.nodeType === doc.ELEMENT_NODE) { + const currencyCode = rateCube.getAttribute('currency'); + const rate = rateCube.getAttribute('rate'); + if (['USD', 'CNY', 'GBP'].includes(currencyCode)) { + const currency = await models.Currency.findOne({where: {code: currencyCode}}); + if (!currency) throw new UserError(`Currency not found for code: ${currencyCode}`); + const existingRate = await models.ReferenceRate.findOne({ + where: {currencyFk: currency.id, dated: xmlDate} + }); + + if (existingRate) { + if (existingRate.value !== rate) + await existingRate.updateAttributes({value: rate}); + } else { + await models.ReferenceRate.create({ + currencyFk: currency.id, + dated: xmlDate, + value: rate + }); + } + } + } + } + } + } + } + }; +}; diff --git a/modules/invoiceIn/back/methods/invoice-in/specs/exchangeRateUpdate.spec.js b/modules/invoiceIn/back/methods/invoice-in/specs/exchangeRateUpdate.spec.js new file mode 100644 index 000000000..0fd7ea165 --- /dev/null +++ b/modules/invoiceIn/back/methods/invoice-in/specs/exchangeRateUpdate.spec.js @@ -0,0 +1,52 @@ +describe('exchangeRateUpdate functionality', function() { + const axios = require('axios'); + const models = require('vn-loopback/server/server').models; + + beforeEach(function() { + spyOn(axios, 'get').and.returnValue(Promise.resolve({ + data: ` + + + + + ` + })); + }); + + it('should process XML data and update or create rates in the database', async function() { + spyOn(models.ReferenceRate, 'findOne').and.returnValue(Promise.resolve(null)); + spyOn(models.ReferenceRate, 'create').and.returnValue(Promise.resolve()); + + await models.InvoiceIn.exchangeRateUpdate(); + + expect(models.ReferenceRate.create).toHaveBeenCalledTimes(2); + }); + + it('should not create or update rates when no XML data is available', async function() { + axios.get.and.returnValue(Promise.resolve({})); + spyOn(models.ReferenceRate, 'create'); + + let thrownError = null; + try { + await models.InvoiceIn.exchangeRateUpdate(); + } catch (error) { + thrownError = error; + } + + expect(thrownError.message).toBe('No cubes found. Exiting the method.'); + }); + + it('should handle errors gracefully', async function() { + axios.get.and.returnValue(Promise.reject(new Error('Network error'))); + let error; + + try { + await models.InvoiceIn.exchangeRateUpdate(); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error.message).toBe('Network error'); + }); +}); diff --git a/modules/invoiceIn/back/models/invoice-in.js b/modules/invoiceIn/back/models/invoice-in.js index af5efbcdf..31cdc1abe 100644 --- a/modules/invoiceIn/back/models/invoice-in.js +++ b/modules/invoiceIn/back/models/invoice-in.js @@ -10,6 +10,7 @@ module.exports = Self => { require('../methods/invoice-in/invoiceInEmail')(Self); require('../methods/invoice-in/getSerial')(Self); require('../methods/invoice-in/corrective')(Self); + require('../methods/invoice-in/exchangeRateUpdate')(Self); Self.rewriteDbError(function(err) { if (err.code === 'ER_ROW_IS_REFERENCED_2' && err.sqlMessage.includes('vehicleInvoiceIn')) diff --git a/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js b/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js index 043dfbead..c46da0ba5 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js +++ b/modules/invoiceOut/back/methods/invoiceOut/createManualInvoice.js @@ -46,12 +46,11 @@ module.exports = Self => { } }); - Self.createManualInvoice = async(ctx, options) => { + Self.createManualInvoice = async(ctx, clientFk, ticketFk, maxShipped, serial, taxArea, reference, options) => { + if (!clientFk && !ticketFk) throw new UserError(`Select ticket or client`); const models = Self.app.models; - const args = ctx.args; - - let tx; const myOptions = {userId: ctx.req.accessToken.userId}; + let tx; if (typeof options == 'object') Object.assign(myOptions, options); @@ -61,18 +60,15 @@ module.exports = Self => { myOptions.transaction = tx; } - const ticketId = args.ticketFk; - let clientId = args.clientFk; - let maxShipped = args.maxShipped; let companyId; let newInvoice; let query; try { - if (ticketId) { - const ticket = await models.Ticket.findById(ticketId, null, myOptions); + if (ticketFk) { + const ticket = await models.Ticket.findById(ticketFk, null, myOptions); const company = await models.Company.findById(ticket.companyFk, null, myOptions); - clientId = ticket.clientFk; + clientFk = ticket.clientFk; maxShipped = ticket.shipped; companyId = ticket.companyFk; @@ -85,7 +81,7 @@ module.exports = Self => { throw new UserError(`A ticket with an amount of zero can't be invoiced`); // Validates ticket nagative base - const hasNegativeBase = await getNegativeBase(maxShipped, clientId, companyId, myOptions); + const hasNegativeBase = await getNegativeBase(maxShipped, clientFk, companyId, myOptions); if (hasNegativeBase && company.code == 'VNL') throw new UserError(`A ticket with a negative base can't be invoiced`); } else { @@ -95,7 +91,7 @@ module.exports = Self => { const company = await models.Ticket.findOne({ fields: ['companyFk'], where: { - clientFk: clientId, + clientFk: clientFk, shipped: {lte: maxShipped} } }, myOptions); @@ -103,7 +99,7 @@ module.exports = Self => { } // Validate invoiceable client - const isClientInvoiceable = await isInvoiceable(clientId, myOptions); + const isClientInvoiceable = await isInvoiceable(clientFk, myOptions); if (!isClientInvoiceable) throw new UserError(`This client is not invoiceable`); @@ -114,27 +110,27 @@ module.exports = Self => { if (maxShipped >= tomorrow) throw new UserError(`Can't invoice to future`); - const maxInvoiceDate = await getMaxIssued(args.serial, companyId, myOptions); + const maxInvoiceDate = await getMaxIssued(serial, companyId, myOptions); if (Date.vnNew() < maxInvoiceDate) throw new UserError(`Can't invoice to past`); - if (ticketId) { + if (ticketFk) { query = `CALL invoiceOut_newFromTicket(?, ?, ?, ?, @newInvoiceId)`; await Self.rawSql(query, [ - ticketId, - args.serial, - args.taxArea, - args.reference + ticketFk, + serial, + taxArea, + reference ], myOptions); } else { query = `CALL invoiceOut_newFromClient(?, ?, ?, ?, ?, ?, @newInvoiceId)`; await Self.rawSql(query, [ - clientId, - args.serial, + clientFk, + serial, maxShipped, companyId, - args.taxArea, - args.reference + taxArea, + reference ], myOptions); } @@ -146,26 +142,27 @@ module.exports = Self => { throw e; } - if (newInvoice.id) - await Self.createPdf(ctx, newInvoice.id); + if (!newInvoice.id) throw new UserError('It was not able to create the invoice'); + + await Self.createPdf(ctx, newInvoice.id); return newInvoice; }; - async function isInvoiceable(clientId, options) { + async function isInvoiceable(clientFk, options) { const models = Self.app.models; const query = `SELECT (hasToInvoice AND isTaxDataChecked) AS invoiceable FROM client WHERE id = ?`; - const [result] = await models.InvoiceOut.rawSql(query, [clientId], options); + const [result] = await models.InvoiceOut.rawSql(query, [clientFk], options); return result.invoiceable; } - async function getNegativeBase(maxShipped, clientId, companyId, options) { + async function getNegativeBase(maxShipped, clientFk, companyId, options) { const models = Self.app.models; await models.InvoiceOut.rawSql('CALL invoiceOut_exportationFromClient(?,?,?)', - [maxShipped, clientId, companyId], options + [maxShipped, clientFk, companyId], options ); const query = 'SELECT vn.hasAnyNegativeBase() AS base'; const [result] = await models.InvoiceOut.rawSql(query, [], options); diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js index b166caf78..55739e570 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/createManualInvoice.spec.js @@ -1,13 +1,10 @@ -const models = require('vn-loopback/server/server').models; +const {models} = require('vn-loopback/server/server'); const LoopBackContext = require('loopback-context'); describe('InvoiceOut createManualInvoice()', () => { - const userId = 1; const ticketId = 16; const clientId = 1106; - const activeCtx = { - accessToken: {userId: userId}, - }; + const activeCtx = {accessToken: {userId: 1}}; const ctx = {req: activeCtx}; it('should throw an error trying to invoice again', async() => { @@ -18,13 +15,8 @@ describe('InvoiceOut createManualInvoice()', () => { let error; try { - ctx.args = { - ticketFk: ticketId, - serial: 'T', - taxArea: 'CEE' - }; - await models.InvoiceOut.createManualInvoice(ctx, options); - await models.InvoiceOut.createManualInvoice(ctx, options); + await createInvoice(ctx, options, undefined, ticketId); + await createInvoice(ctx, options, undefined, ticketId); await tx.rollback(); } catch (e) { @@ -47,17 +39,9 @@ describe('InvoiceOut createManualInvoice()', () => { let error; try { const ticket = await models.Ticket.findById(ticketId, null, options); - await ticket.updateAttributes({ - totalWithVat: 0 - }, options); - - ctx.args = { - ticketFk: ticketId, - serial: 'T', - taxArea: 'CEE' - }; - await models.InvoiceOut.createManualInvoice(ctx, options); + await ticket.updateAttributes({totalWithVat: 0}, options); + await createInvoice(ctx, options, undefined, ticketId); await tx.rollback(); } catch (e) { error = e; @@ -75,13 +59,7 @@ describe('InvoiceOut createManualInvoice()', () => { let error; try { - ctx.args = { - clientFk: clientId, - serial: 'T', - taxArea: 'CEE' - }; - await models.InvoiceOut.createManualInvoice(ctx, options); - + await createInvoice(ctx, options, clientId); await tx.rollback(); } catch (e) { error = e; @@ -103,16 +81,9 @@ describe('InvoiceOut createManualInvoice()', () => { let error; try { const client = await models.Client.findById(clientId, null, options); - await client.updateAttributes({ - isTaxDataChecked: false - }, options); + await client.updateAttributes({isTaxDataChecked: false}, options); - ctx.args = { - ticketFk: ticketId, - serial: 'T', - taxArea: 'CEE' - }; - await models.InvoiceOut.createManualInvoice(ctx, options); + await createInvoice(ctx, options, undefined, ticketId); await tx.rollback(); } catch (e) { @@ -130,12 +101,7 @@ describe('InvoiceOut createManualInvoice()', () => { const options = {transaction: tx}; try { - ctx.args = { - ticketFk: ticketId, - serial: 'T', - taxArea: 'CEE' - }; - const result = await models.InvoiceOut.createManualInvoice(ctx, options); + const result = await createInvoice(ctx, options, undefined, ticketId); expect(result.id).toEqual(jasmine.any(Number)); @@ -146,3 +112,18 @@ describe('InvoiceOut createManualInvoice()', () => { } }); }); + +function createInvoice( + ctx, + options, + clientFk = undefined, + ticketFk = undefined, + maxShipped = undefined, + serial = 'T', + taxArea = 'CEE', + reference = undefined +) { + return models.InvoiceOut.createManualInvoice( + ctx, clientFk, ticketFk, maxShipped, serial, taxArea, reference, options + ); +} diff --git a/modules/worker/back/methods/worker-time-control/specs/clockIn.spec.js b/modules/worker/back/methods/worker-time-control/specs/clockIn.spec.js index 343eb2a71..daf7284ac 100644 --- a/modules/worker/back/methods/worker-time-control/specs/clockIn.spec.js +++ b/modules/worker/back/methods/worker-time-control/specs/clockIn.spec.js @@ -45,7 +45,7 @@ describe('workerTimeControl clockIn()', () => { throw e; } }); - + it('should throw an error trying to change a middle hour to out not resting 12h', async() => { activeCtx.accessToken.userId = HHRRId; const workerId = teamBossId; @@ -99,6 +99,32 @@ describe('workerTimeControl clockIn()', () => { } }); + it('should throw an error trying to add an "in" entry if the last clockIn is not out', async() => { + activeCtx.accessToken.userId = HHRRId; + const workerId = teamBossId; + const tx = await models.WorkerTimeControl.beginTransaction({}); + try { + const options = {transaction: tx}; + + ctx.args = {timed: "2000-12-25T21:00:00.000Z", direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + ctx.args = {timed: "2000-12-25T22:00:00.000Z", direction: 'middle'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + ctx.args = {timed: "2000-12-25T22:30:00.000Z", direction: 'middle'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + ctx.args = {timed: "2000-12-26T01:00:00.000Z", direction: 'in'}; + await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options); + + await tx.rollback(); + } catch (e) { + expect(e.message).toBe('Dirección incorrecta'); + await tx.rollback(); + } + }); + describe('as Role errors', () => { it('should add if the current user is team boss and the target user is himself', async() => { activeCtx.accessToken.userId = teamBossId;