Merge branch 'dev' into 7731-clientViesCode
gitea/salix/pipeline/pr-dev This commit looks good Details

This commit is contained in:
Robert Ferrús 2025-01-10 12:36:25 +00:00
commit 36cfff13ca
16 changed files with 135 additions and 93 deletions

View File

@ -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,

View File

@ -22,7 +22,7 @@ module.exports = Self => {
description: 'The user email'
}, {
arg: 'lang',
type: 'string',
type: 'any',
description: 'The user lang'
}, {
arg: 'twoFactor',

View File

@ -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 ;

View File

@ -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 ;

View File

@ -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');

View File

@ -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');
};

View File

@ -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)];

View File

@ -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);
}
};

View File

@ -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,

View File

@ -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();

View File

@ -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');
});
});

View File

@ -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};
};
};

View File

@ -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'],

View File

@ -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;

View File

@ -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);
},

View File

@ -1,6 +1,6 @@
{
"width": "10cm",
"height": "10cm",
"height": "6.5cm",
"margin": {
"top": "0.17cm",
"right": "0.2cm",