231801_test_to_master #1519

Merged
alexm merged 490 commits from 231801_test_to_master into master 2023-05-12 06:29:59 +00:00
17 changed files with 7555 additions and 26816 deletions
Showing only changes of commit 239685b43f - Show all commits

130
back/methods/image/scrub.js Normal file
View File

@ -0,0 +1,130 @@
const fs = require('fs-extra');
const path = require('path');
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethod('scrub', {
description: 'Deletes images without database reference',
accessType: 'WRITE',
accepts: [
{
arg: 'collection',
type: 'string',
description: 'The collection name',
required: true
}, {
arg: 'remove',
type: 'boolean',
description: 'Delete instead of move images to trash'
}, {
arg: 'limit',
type: 'integer',
description: 'Maximum number of images to clean'
}, {
arg: 'dryRun',
type: 'boolean',
description: 'Simulate actions'
}, {
arg: 'skipLock',
type: 'boolean',
description: 'Wether to skip exclusive lock'
}
],
returns: {
type: 'integer',
root: true
},
http: {
path: `/scrub`,
verb: 'POST'
}
});
Self.scrub = async function(collection, remove, limit, dryRun, skipLock) {
const $ = Self.app.models;
const env = process.env.NODE_ENV;
dryRun = dryRun || (env && env !== 'production');
const instance = await $.ImageCollection.findOne({
fields: ['id'],
where: {name: collection}
});
if (!instance)
throw new UserError('Collection does not exist');
const container = await $.ImageContainer.container(collection);
const rootPath = container.client.root;
let tx;
let opts;
const lockName = 'salix.Image.scrub';
if (!skipLock) {
tx = await Self.beginTransaction({timeout: null});
opts = {transaction: tx};
const [row] = await Self.rawSql(
`SELECT GET_LOCK(?, 10) hasLock`, [lockName], opts);
if (!row.hasLock)
throw new UserError('Cannot obtain exclusive lock');
}
try {
const now = Date.vnNew().toJSON();
const scrubDir = path.join(rootPath, '.scrub', now);
const collectionDir = path.join(rootPath, collection);
const sizes = await fs.readdir(collectionDir);
let cleanCount = 0;
mainLoop: for (const size of sizes) {
const sizeDir = path.join(collectionDir, size);
const scrubSizeDir = path.join(scrubDir, collection, size);
const images = await fs.readdir(sizeDir);
for (const image of images) {
const imageName = path.parse(image).name;
const count = await Self.count({
collectionFk: collection,
name: imageName
}, opts);
const exists = count > 0;
let scrubDirCreated = false;
if (!exists) {
const srcFile = path.join(sizeDir, image);
if (remove !== true) {
if (!scrubDirCreated) {
if (!dryRun)
await fs.mkdir(scrubSizeDir, {recursive: true});
scrubDirCreated = true;
}
const dstFile = path.join(scrubSizeDir, image);
if (!dryRun) await fs.rename(srcFile, dstFile);
} else {
try {
if (!dryRun) await fs.unlink(srcFile);
} catch (err) {
console.error(err.message);
}
}
cleanCount++;
if (limit && cleanCount == limit)
break mainLoop;
}
}
}
return cleanCount;
} finally {
if (!skipLock) {
try {
await Self.rawSql(`DO RELEASE_LOCK(?)`, [lockName], opts);
await tx.rollback();
} catch (err) {
console.error(err.message);
}
}
}
};
};

View File

@ -12,13 +12,13 @@ module.exports = Self => {
type: 'Number',
description: 'The entity id',
required: true
},
{
}, {
arg: 'collection',
type: 'string',
description: 'The collection name',
required: true
}],
}
],
returns: {
type: 'Object',
root: true

View File

@ -5,6 +5,7 @@ const gm = require('gm');
module.exports = Self => {
require('../methods/image/download')(Self);
require('../methods/image/upload')(Self);
require('../methods/image/scrub')(Self);
Self.resize = async function({collectionName, srcFile, fileName, entityId}) {
const models = Self.app.models;
@ -29,13 +30,14 @@ module.exports = Self => {
);
// Insert image row
const imageName = path.parse(fileName).name;
await models.Image.upsertWithWhere(
{
name: fileName,
name: imageName,
collectionFk: collectionName
},
{
name: fileName,
name: imageName,
collectionFk: collectionName,
updated: Date.vnNow() / 1000,
}
@ -49,7 +51,7 @@ module.exports = Self => {
if (entity) {
await entity.updateAttribute(
collection.property,
fileName
imageName
);
}

View File

@ -0,0 +1 @@
ALTER TABLE `vn`.`printQueueArgs` MODIFY COLUMN value varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NULL;

View File

@ -71,6 +71,7 @@ SELECT *
END$$
DELIMITER ;
ALTER TABLE `vn`.`delivery` DROP FOREIGN KEY delivery_ticketFk_FK;
ALTER TABLE `vn`.`delivery` DROP COLUMN ticketFk;
ALTER TABLE `vn`.`delivery` ADD ticketFk INT DEFAULT NULL;
ALTER TABLE `vn`.`delivery` ADD CONSTRAINT delivery_ticketFk_FK FOREIGN KEY (`ticketFk`) REFERENCES `vn`.`ticket`(`id`);

View File

@ -173,10 +173,6 @@ INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `isPreviousPrepare
(1, 'First sector', 1, 1, 'FIRST'),
(2, 'Second sector', 2, 0, 'SECOND');
INSERT INTO `vn`.`operator` (`workerFk`, `numberOfWagons`, `trainFk`, `itemPackingTypeFk`, `warehouseFk`, `sectorFk`, `labelerFk`)
VALUES ('1106', '1', '1', 'H', '1', '1', '1'),
('1107', '1', '1', 'V', '1', '2', '1');
INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`, `ipAddress`)
VALUES
(1, 'printer1', 'path1', 0, 1 , NULL),
@ -1200,6 +1196,11 @@ INSERT INTO `vn`.`train`(`id`, `name`)
(1, 'Train1'),
(2, 'Train2');
INSERT INTO `vn`.`operator` (`workerFk`, `numberOfWagons`, `trainFk`, `itemPackingTypeFk`, `warehouseFk`, `sectorFk`, `labelerFk`)
VALUES
('1106', '1', '1', 'H', '1', '1', '1'),
('1107', '1', '1', 'V', '1', '1', '1');
INSERT INTO `vn`.`collection`(`id`, `workerFk`, `stateFk`, `created`, `trainFk`)
VALUES
(1, 1106, 5, DATE_ADD(util.VN_CURDATE(),INTERVAL +1 DAY), 1),

View File

@ -989,7 +989,7 @@ export default {
saveButton: 'vn-worker-basic-data button[type=submit]'
},
workerNotes: {
addNoteFloatButton: 'vn-float-button',
addNoteFloatButton: 'vn-worker-note vn-float-button',
note: 'vn-textarea[ng-model="$ctrl.note.text"]',
saveButton: 'button[type=submit]',
firstNoteText: 'vn-worker-note .text'

View File

@ -7,7 +7,7 @@ describe('Worker Add notes path', () => {
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'worker');
await page.loginAndModule('hr', 'worker');
await page.accessToSearchResult('Bruce Banner');
await page.accessToSection('worker.card.note.index');
});

View File

@ -21,8 +21,8 @@ module.exports = function(Self) {
let orgBeginTransaction = this.beginTransaction;
this.beginTransaction = function(options, cb) {
options = options || {};
if (!options.timeout)
options.timeout = 120000;
if (options.timeout === undefined)
options.timeout = 120 * 1000;
return orgBeginTransaction.call(this, options, cb);
};
});

View File

@ -154,5 +154,6 @@
"Valid priorities: 1,2,3": "Valid priorities: 1,2,3",
"Warehouse inventory not set": "Almacén inventario no está establecido",
"Component cost not set": "Componente coste no está estabecido",
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2"
}
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2",
"Description cannot be blank": "Description cannot be blank"
}

View File

@ -272,6 +272,8 @@
"Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº {{id}}",
"Not exist this branch": "La rama no existe",
"This ticket cannot be signed because it has not been boxed": "Este ticket no puede firmarse porque no ha sido encajado",
"Collection does not exist": "La colección no existe",
"Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo",
"Insert a date range": "Inserte un rango de fechas",
"Added observation": "{{user}} añadió esta observacion: {{text}}",
"Comment added to client": "Observación añadida al cliente {{clientFk}}",

View File

@ -21,7 +21,7 @@ module.exports = Self => {
id,
params
FROM clientConsumptionQueue
WHERE status = ''`);
WHERE status = '' OR status IS NULL`);
for (const queue of queues) {
try {
@ -44,16 +44,23 @@ module.exports = Self => {
GROUP BY c.id`, [params.clients, params.from, params.to]);
for (const client of clients) {
const args = {
id: client.clientFk,
recipient: client.clientEmail,
replyTo: client.salesPersonEmail,
from: params.from,
to: params.to
};
try {
const args = {
id: client.clientFk,
recipient: client.clientEmail,
replyTo: client.salesPersonEmail,
from: params.from,
to: params.to
};
const email = new Email('campaign-metrics', args);
await email.send();
const email = new Email('campaign-metrics', args);
await email.send();
} catch (error) {
if (error.code === 'EENVELOPE')
continue;
throw error;
}
}
await Self.rawSql(`

View File

@ -11,8 +11,7 @@ module.exports = Self => {
http: {source: 'path'}
}, {
arg: 'userFk',
type: 'number',
required: true,
type: 'any',
description: 'The user id'
}
],

34087
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,7 @@
"mailparser": "^2.8.0",
"md5": "^2.2.1",
"node-ssh": "^11.0.0",
"npm": "^8.19.4",
"object-diff": "0.0.4",
"object.pick": "^1.3.0",
"puppeteer": "^18.0.5",

View File

@ -1,41 +1,46 @@
<!DOCTYPE html>
<html>
<body>
<table class="mainTable">
<tbody>
<tr>
<td id="truck" class="ellipsize">{{labelData.truck || '---'}}</td>
</tr>
<tr>
<td>
<div v-html="getBarcode(labelData.palletFk)" id="barcode"></div>
<table v-for="labelData in labelsData" class="zoneTable">
<thead>
<tr v-if="!labelData.isMatch" id="black">
<td id="routeFk" class="ellipsize">{{labelData.routeFk}}</td>
<td id="zone" class="ellipsize">{{labelData.zone || '---'}}</td>
<td id="labels" class="ellipsize">{{labelData.labels}}</td>
</tr>
<tr v-else>
<td id="routeFk" class="ellipsize">{{labelData.routeFk}}</td>
<td id="zone" class="ellipsize">{{labelData.zone || '---'}}</td>
<td id="labels" class="ellipsize">{{labelData.labels || '--'}}</td>
</tr>
</thead>
</table>
</td>
</tr>
<tr>
<td>
<img :src="QR" id="QR"/>
<div id="right">
<div id="additionalInfo" class="ellipsize"><b>Pallet: </b>{{id}}</div>
<div id="additionalInfo" class="ellipsize"><b>User: </b> {{username.name || '---'}}</div>
<div id="additionalInfo" class="ellipsize"><b>Day: </b>{{labelData.dayName.toUpperCase() || '---'}}</div>
<body>
<table class="mainTable">
<tbody>
<tr>
<td id="truck" class="ellipsize">{{labelData.truck || '---'}}</td>
</tr>
<tr>
<td>
<div v-html="getBarcode(labelData.palletFk)" id="barcode"></div>
<table v-for="labelData in labelsData" class="zoneTable">
<thead>
<tr v-if="!labelData.isMatch" id="black">
<td id="routeFk" class="ellipsize">{{labelData.routeFk}}</td>
<td id="zone" class="ellipsize">{{labelData.zone || '---'}}</td>
<td id="labels" class="ellipsize">{{labelData.labels}}</td>
</tr>
<tr v-else>
<td id="routeFk" class="ellipsize">{{labelData.routeFk}}</td>
<td id="zone" class="ellipsize">{{labelData.zone || '---'}}</td>
<td id="labels" class="ellipsize">{{labelData.labels || '--'}}</td>
</tr>
</thead>
</table>
</td>
</tr>
<tr>
<td>
<img :src="QR" id="QR" />
<div id="right">
<div id="additionalInfo" class="ellipsize"><b>Pallet: </b>{{id}}</div>
<div id="additionalInfo" class="ellipsize"><b>User: </b>
{{ (username ? username.name : '---')}}
</div>
</td>
</tr>
</tbody>
</table>
</body>
<div id="additionalInfo" class="ellipsize"><b>Day: </b>{{labelData.dayName.toUpperCase() ||
'---'}}</div>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>

View File

@ -14,13 +14,13 @@ module.exports = {
},
userFk: {
type: Number,
required: true,
description: 'The user id'
}
},
async serverPrefetch() {
this.username = null;
this.labelsData = await this.rawSqlFromDef('labelData', this.id);
this.username = await this.findOneFromDef('username', this.userFk);
if (this.userFk) this.username = await this.findOneFromDef('username', this.userFk);
this.labelData = this.labelsData[0];
this.checkMainEntity(this.labelData);