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/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/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.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",