diff --git a/back/methods/collection/getTickets.js b/back/methods/collection/getTickets.js index 677c9e444..85d1111df 100644 --- a/back/methods/collection/getTickets.js +++ b/back/methods/collection/getTickets.js @@ -40,6 +40,7 @@ module.exports = Self => { const sales = await Self.rawSql(` SELECT s.ticketFk, + NULL ticketOrder, sgd.saleGroupFk, s.id saleFk, s.itemFk, @@ -83,6 +84,7 @@ module.exports = Self => { GROUP BY s.id, ish.id, p.code, p2.code UNION ALL SELECT s.ticketFk, + DENSE_RANK() OVER (ORDER BY ss.id), sgd.saleGroupFk, s.id saleFk, s.itemFk, diff --git a/back/methods/vn-user/update-user.js b/back/methods/vn-user/update-user.js index 2bb390cf9..32bad0f17 100644 --- a/back/methods/vn-user/update-user.js +++ b/back/methods/vn-user/update-user.js @@ -22,7 +22,7 @@ module.exports = Self => { description: 'The user email' }, { arg: 'lang', - type: 'string', + type: 'any', description: 'The user lang' }, { arg: 'twoFactor', diff --git a/db/routines/bs/procedures/waste_addSales.sql b/db/routines/bs/procedures/waste_addSales.sql index ea4adbc27..4a34d74b3 100644 --- a/db/routines/bs/procedures/waste_addSales.sql +++ b/db/routines/bs/procedures/waste_addSales.sql @@ -10,19 +10,27 @@ BEGIN * @param vDateFrom Fecha desde * @param vDateTo Fecha hasta */ - IF vDateFrom IS NULL THEN - SET vDateFrom = util.VN_CURDATE() - INTERVAL WEEKDAY(util.VN_CURDATE()) DAY; + DECLARE vDaysInYear INT; + SET vDaysInYear = DATEDIFF(util.lastDayOfYear(CURDATE()), util.firstDayOfYear(CURDATE())); + + SET vDateFrom = COALESCE(vDateFrom, util.VN_CURDATE()); + SET vDateTo = COALESCE(vDateTo, util.VN_CURDATE()); + + IF DATEDIFF(vDateTo, vDateFrom) > vDaysInYear THEN + CALL util.throw('The period cannot be longer than one year'); END IF; - IF vDateTo IS NULL THEN - SET vDateTo = vDateFrom + INTERVAL 6 DAY; - END IF; + -- Obtiene el primer día de la semana de esa fecha + SET vDateFrom = DATE_SUB(vDateFrom, INTERVAL ((WEEKDAY(vDateFrom) + 1) % 7) DAY); + + -- Obtiene el último día de la semana de esa fecha + SET vDateTo = DATE_ADD(vDateTo, INTERVAL (6 - ((WEEKDAY(vDateTo) + 1) % 7)) DAY); CALL cache.last_buy_refresh(FALSE); REPLACE bs.waste - SELECT YEAR(t.shipped), - WEEK(t.shipped, 4), + SELECT YEARWEEK(t.shipped, 6) DIV 100, + WEEK(t.shipped, 6), it.workerFk, it.id, s.itemFk, @@ -68,8 +76,8 @@ BEGIN JOIN cache.last_buy lb ON lb.item_id = i.id AND lb.warehouse_id = w.id JOIN vn.buy b ON b.id = lb.buy_id - WHERE t.shipped BETWEEN vDateFrom AND vDateTo + WHERE t.shipped BETWEEN vDateFrom AND util.dayEnd(vDateTo) AND w.isManaged - GROUP BY YEAR(t.shipped), WEEK(t.shipped, 4), i.id; + GROUP BY YEARWEEK(t.shipped, 6) DIV 100, WEEK(t.shipped, 6), i.id; END$$ DELIMITER ; diff --git a/db/routines/vn/triggers/workerTimeControl_afterDelete.sql b/db/routines/vn/triggers/workerTimeControl_afterDelete.sql index 27432fccb..96db381b5 100644 --- a/db/routines/vn/triggers/workerTimeControl_afterDelete.sql +++ b/db/routines/vn/triggers/workerTimeControl_afterDelete.sql @@ -3,10 +3,12 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`workerTimeControl_after AFTER DELETE ON `workerTimeControl` FOR EACH ROW BEGIN - INSERT INTO workerLog - SET `action` = 'delete', - `changedModel` = 'WorkerTimeControl', - `changedModelId` = OLD.id, - `userFk` = account.myUser_getId(); + IF account.myUser_getId() IS NOT NULL THEN + INSERT INTO workerLog + SET `action` = 'delete', + `changedModel` = 'WorkerTimeControl', + `changedModelId` = OLD.id, + `userFk` = account.myUser_getId(); + END IF; END$$ DELIMITER ; diff --git a/db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql b/db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql new file mode 100644 index 000000000..f3e0355a8 --- /dev/null +++ b/db/versions/11400-turquoiseChrysanthemum/00-firstScript.sql @@ -0,0 +1,4 @@ +DELETE FROM salix.ACL WHERE property = 'canCreateAbsenceInPast'; + +INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) + VALUES ('Worker','canModifyAbsenceInPast','WRITE','ALLOW','ROLE','hr'); diff --git a/modules/route/back/methods/route/driverRouteEmail.js b/modules/route/back/methods/route/driverRouteEmail.js index bbac2b0e8..78069683d 100644 --- a/modules/route/back/methods/route/driverRouteEmail.js +++ b/modules/route/back/methods/route/driverRouteEmail.js @@ -8,7 +8,7 @@ module.exports = Self => { arg: 'id', type: 'number', required: true, - description: 'The client id', + description: 'The route id', http: {source: 'path'} }, { arg: 'replyTo', @@ -31,26 +31,13 @@ module.exports = Self => { }); Self.driverRouteEmail = async(ctx, id) => { - const models = Self.app.models; - const {workerFk, agencyMode} = await Self.findById(id, { - fields: ['workerFk', 'agencyModeFk'], + const {agencyMode} = await Self.findById(id, { + fields: ['agencyModeFk'], include: {relation: 'agencyMode'} }); const {reportMail} = agencyMode(); - let user; - let account; - - if (workerFk) { - user = await models.VnUser.findById(workerFk, { - fields: ['active', 'id'], - include: {relation: 'emailUser'} - }); - account = await models.Account.findById(workerFk); - } - - if (user?.active && account) ctx.args.recipient = user.emailUser().email; - else ctx.args.recipient = reportMail; + ctx.args.recipient = reportMail; if (!ctx.args.recipient) throw new UserError('An email is necessary'); return Self.sendTemplate(ctx, 'driver-route'); }; diff --git a/modules/worker/back/methods/worker-time-control/sendMail.js b/modules/worker/back/methods/worker-time-control/sendMail.js index 2e1e00d83..5fd5f65ff 100644 --- a/modules/worker/back/methods/worker-time-control/sendMail.js +++ b/modules/worker/back/methods/worker-time-control/sendMail.js @@ -41,22 +41,10 @@ module.exports = Self => { const stmts = []; let stmt; - if (!args.week || !args.year) { - const from = Date.vnNew(); - const to = Date.vnNew(); + const {date: started, week, year} = Self.getMondayWeekYear(args.week, args.year); + args.week = week; + args.year = year; - const time = await models.Time.findOne({ - where: { - dated: {between: [from.setDate(from.getDate() - 10), to.setDate(to.getDate() - 4)]} - }, - order: 'week ASC' - }, myOptions); - - args.week = time.week; - args.year = time.year; - } - - const started = getStartDateOfWeekNumber(args.week, args.year); started.setHours(0, 0, 0, 0); const ended = new Date(started); @@ -388,17 +376,6 @@ module.exports = Self => { return true; }; - function getStartDateOfWeekNumber(week, year) { - const simple = new Date(year, 0, 1 + (week - 1) * 7); - const dow = simple.getDay(); - const weekStart = simple; - if (dow <= 4) - weekStart.setDate(simple.getDate() - simple.getDay() + 1); - else - weekStart.setDate(simple.getDate() + 8 - simple.getDay()); - return weekStart; - } - function getTime(timeString) { const [hours, minutes, seconds] = timeString.split(':'); return [parseInt(hours), parseInt(minutes), parseInt(seconds)]; diff --git a/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js b/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js index e352eb3cb..eadbecb00 100644 --- a/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js +++ b/modules/worker/back/methods/worker-time-control/weeklyHourRecordEmail.js @@ -55,8 +55,8 @@ module.exports = Self => { } }, myOptions); - const dated = getMondayDateFromYearWeek(args.year, args.week); - const timestamp = dated.getTime() / 1000; + const {date} = Self.getMondayWeekYear(args.week, args.year); + const timestamp = date.getTime() / 1000; const url = `${salix.url}worker/${args.workerId}/time-control?timestamp=${timestamp}`; ctx.args.url = url; @@ -64,23 +64,4 @@ module.exports = Self => { await models.WorkerTimeControl.updateMailState(ctx, ctx.args.workerId, myOptions); return Self.sendTemplate(ctx, 'weekly-hour-record'); }; - - function getMondayDateFromYearWeek(yearNumber, weekNumber) { - const yearStart = new Date(yearNumber, 0, 1); - const firstMonday = new Date(yearStart.getTime() + ((7 - yearStart.getDay() + 1) % 7) * 86400000); - const firstMondayWeekNumber = getWeekNumber(firstMonday); - - if (firstMondayWeekNumber > 1) - firstMonday.setDate(firstMonday.getDate() + 7); - - firstMonday.setDate(firstMonday.getDate() + (weekNumber - 1) * 7); - - return firstMonday; - } - - function getWeekNumber(date) { - const firstDayOfYear = new Date(date.getFullYear(), 0, 1); - const daysPassed = (date - firstDayOfYear) / 86400000; - return Math.ceil((daysPassed + firstDayOfYear.getDay() + 1) / 7); - } }; diff --git a/modules/worker/back/methods/worker/createAbsence.js b/modules/worker/back/methods/worker/createAbsence.js index 93ca7fd89..dc716c95d 100644 --- a/modules/worker/back/methods/worker/createAbsence.js +++ b/modules/worker/back/methods/worker/createAbsence.js @@ -58,12 +58,10 @@ module.exports = Self => { if (!isSubordinate || (isSubordinate && userId == id && !isTeamBoss)) throw new UserError(`You don't have enough privileges`); - const canCreateAbsenceInPast = - await models.ACL.checkAccessAcl(ctx, 'Worker', 'canCreateAbsenceInPast', 'WRITE'); const now = Date.vnNew(); const newDate = new Date(args.dated).getTime(); - if ((now.getTime() > newDate) && !canCreateAbsenceInPast) + if (!await Self.canModifyAbsenceInPast(ctx, newDate)) throw new UserError(`Holidays to past days not available`); const labour = await models.WorkerLabour.findById(args.businessFk, diff --git a/modules/worker/back/methods/worker/deleteAbsence.js b/modules/worker/back/methods/worker/deleteAbsence.js index b71d077a4..596f8f28d 100644 --- a/modules/worker/back/methods/worker/deleteAbsence.js +++ b/modules/worker/back/methods/worker/deleteAbsence.js @@ -53,6 +53,10 @@ module.exports = Self => { } } }, myOptions); + + if (!await Self.canModifyAbsenceInPast(ctx, absence.dated.getTime())) + throw new UserError(`Holidays to past days not available`); + const result = await absence.destroy(myOptions); const labour = absence.labour(); const department = labour && labour.department(); diff --git a/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js index 0f3f913dc..c0d05e4a2 100644 --- a/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js +++ b/modules/worker/back/methods/worker/specs/deleteAbsence.spec.js @@ -4,6 +4,8 @@ const LoopBackContext = require('loopback-context'); describe('Worker deleteAbsence()', () => { const businessId = 18; const workerId = 18; + const hrId = 37; + const salesBossId = 19; const activeCtx = { accessToken: {userId: 1106}, headers: {origin: 'http://localhost'} @@ -50,16 +52,16 @@ describe('Worker deleteAbsence()', () => { }); it('should successfully delete an absence', async() => { - activeCtx.accessToken.userId = 19; + activeCtx.accessToken.userId = salesBossId; const tx = await app.models.Calendar.beginTransaction({}); - + const pastDate = new Date(Date.vnNow() + 24 * 60 * 60 * 1000); try { const options = {transaction: tx}; const createdAbsence = await app.models.Calendar.create({ businessFk: businessId, dayOffTypeFk: 1, - dated: Date.vnNew() + dated: pastDate }, options); ctx.args = {absenceId: createdAbsence.id}; @@ -76,4 +78,61 @@ describe('Worker deleteAbsence()', () => { throw e; } }); + + it('should successfully delete an absence if the user is HR even if the date is in the past', async() => { + activeCtx.accessToken.userId = hrId; + const tx = await app.models.Calendar.beginTransaction({}); + + try { + const options = {transaction: tx}; + const pastDate = new Date(Date.vnNow() - 24 * 60 * 60 * 1000); // Restar un día + const createdAbsence = await app.models.Calendar.create({ + businessFk: businessId, + dayOffTypeFk: 1, + dated: pastDate + }, options); + + ctx.args = {absenceId: createdAbsence.id}; + await app.models.Worker.deleteAbsence(ctx, workerId, options); + + const deletedAbsence = await app.models.Calendar.findById(createdAbsence.id, null, options); + + expect(deletedAbsence).toBeNull(); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should throw an error if the date is in the past', async() => { + activeCtx.accessToken.userId = salesBossId; + const tx = await app.models.Calendar.beginTransaction({}); + + let error; + try { + const options = {transaction: tx}; + const pastDate = new Date(Date.vnNow() - 24 * 60 * 60 * 1000); + const createdAbsence = await app.models.Calendar.create({ + businessFk: businessId, + dayOffTypeFk: 1, + dated: pastDate + }, options); + + ctx.args = {absenceId: createdAbsence.id}; + await app.models.Worker.deleteAbsence(ctx, workerId, options); + + const deletedAbsence = await app.models.Calendar.findById(createdAbsence.id, null, options); + + expect(deletedAbsence).toBeNull(); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toBe('Holidays to past days not available'); + }); }); diff --git a/modules/worker/back/models/worker-time-control.js b/modules/worker/back/models/worker-time-control.js index 92f1bacf0..cdf51b712 100644 --- a/modules/worker/back/models/worker-time-control.js +++ b/modules/worker/back/models/worker-time-control.js @@ -1,4 +1,5 @@ const UserError = require('vn-loopback/util/user-error'); +const moment = require('moment'); module.exports = Self => { require('../methods/worker-time-control/filter')(Self); @@ -19,4 +20,15 @@ module.exports = Self => { return new UserError(`The introduced hour already exists`); return err; }); + + Self.getMondayWeekYear = (week, year) => { + if (!week || !year) { + const today = Date.vnNew(); + today.setDate(today.getDate() - 7); + week = moment(today).isoWeek(); + year = moment(today).isoWeekYear(); + } + const date = moment(year, 'YYYY').week(week).startOf('isoweek').toDate(); + return {date, year, week}; + }; }; diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js index 3351c348c..2e45b78da 100644 --- a/modules/worker/back/models/worker.js +++ b/modules/worker/back/models/worker.js @@ -26,6 +26,13 @@ module.exports = Self => { message: 'Invalid TIN' }); + Self.canModifyAbsenceInPast = async(ctx, time) => { + const hasPrivs = await Self.app.models.ACL.checkAccessAcl(ctx, 'Worker', 'canModifyAbsenceInPast', 'WRITE'); + const today = Date.vnNew(); + today.setHours(0, 0, 0, 0); + return hasPrivs || today.getTime() < time; + }; + async function tinIsValid(err, done) { const country = await Self.app.models.Country.findOne({ fields: ['code'], diff --git a/print/templates/reports/buy-label-supplier/assets/css/style.css b/print/templates/reports/buy-label-supplier/assets/css/style.css index 3b1f2f91e..f64e01688 100644 --- a/print/templates/reports/buy-label-supplier/assets/css/style.css +++ b/print/templates/reports/buy-label-supplier/assets/css/style.css @@ -1,7 +1,7 @@ html { font-family: "Roboto", "Helvetica", "Arial", sans-serif; margin-top: -7px; - font-size: 28px; + font-size: 20px; } table { border: 1px solid; @@ -10,22 +10,22 @@ table { } td { border: 1px solid; - padding: 5px; + padding: 2px; width: 100%; } span { - font-size: 48px; + font-size: 34px; font-weight: bold; } .lbl { color: gray; font-weight: lighter; - font-size: 18px; + font-size: 12px; display: block; } .cell { width: 157px; - height: 50px; + height: 35px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; diff --git a/print/templates/reports/buy-label-supplier/buy-label-supplier.js b/print/templates/reports/buy-label-supplier/buy-label-supplier.js index 5e59472eb..c8af17a5d 100755 --- a/print/templates/reports/buy-label-supplier/buy-label-supplier.js +++ b/print/templates/reports/buy-label-supplier/buy-label-supplier.js @@ -10,7 +10,7 @@ module.exports = { async serverPrefetch() { const buy = await models.Buy.findById(this.id, null); this.buys = await this.rawSqlFromDef('buy', [buy.entryFk, buy.entryFk, buy.entryFk, this.id, this.id]); - const date = new Date(); + const date = Date.vnNew(); this.weekNum = moment(date).isoWeek(); this.dayNum = moment(date).day(); }, @@ -24,7 +24,8 @@ module.exports = { format: 'code128', displayValue: false, width: 3.8, - height: 115, + height: 60, + margin: 3, }); return new XMLSerializer().serializeToString(svgNode); }, diff --git a/print/templates/reports/buy-label-supplier/options.json b/print/templates/reports/buy-label-supplier/options.json index 4ed0461b3..a2a781cbf 100644 --- a/print/templates/reports/buy-label-supplier/options.json +++ b/print/templates/reports/buy-label-supplier/options.json @@ -1,6 +1,6 @@ { "width": "10cm", - "height": "10cm", + "height": "6.5cm", "margin": { "top": "0.17cm", "right": "0.2cm",