Merge branch 'dev' into 5128-client.credit-management
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Vicent Llopis 2023-04-21 11:30:58 +00:00
commit ea1035311e
22 changed files with 2237 additions and 5910 deletions

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', type: 'Number',
description: 'The entity id', description: 'The entity id',
required: true required: true
}, }, {
{
arg: 'collection', arg: 'collection',
type: 'string', type: 'string',
description: 'The collection name', description: 'The collection name',
required: true required: true
}], }
],
returns: { returns: {
type: 'Object', type: 'Object',
root: true root: true

View File

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

View File

@ -53,7 +53,7 @@ async function test() {
const JunitReporter = require('jasmine-reporters'); const JunitReporter = require('jasmine-reporters');
jasmine.addReporter(new JunitReporter.JUnitXmlReporter()); jasmine.addReporter(new JunitReporter.JUnitXmlReporter());
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
jasmine.exitOnCompletion = true; jasmine.exitOnCompletion = true;
} }

View File

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

View File

@ -1,12 +1,5 @@
DROP TABLE IF EXISTS `vn`.`dmsRecover`; DROP TABLE IF EXISTS `vn`.`dmsRecover`;
DROP PROCEDURE IF EXISTS `vn`.`route_getTickets`;
ALTER TABLE `vn`.`delivery` DROP COLUMN addressFk;
ALTER TABLE `vn`.`delivery` DROP CONSTRAINT 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`);
DROP PROCEDURE IF EXISTS vn.route_getTickets;
DELIMITER $$ DELIMITER $$
$$ $$
@ -17,14 +10,14 @@ BEGIN
* de sus tickets. * de sus tickets.
* *
* @param vRouteFk * @param vRouteFk
*
* @select Información de los tickets * @select Información de los tickets
*/ */
SELECT *
SELECT FROM (
t.id Id, SELECT t.id Id,
t.clientFk Client, t.clientFk Client,
a.id Address, a.id Address,
a.nickname ClientName,
t.packages Packages, t.packages Packages,
a.street AddressName, a.street AddressName,
a.postalCode PostalCode, a.postalCode PostalCode,
@ -37,34 +30,48 @@ BEGIN
d.longitude Longitude, d.longitude Longitude,
d.latitude Latitude, d.latitude Latitude,
wm.mediaValue SalePersonPhone, wm.mediaValue SalePersonPhone,
tob.Note Note, tob.description Note,
t.isSigned Signed t.isSigned Signed,
t.priority
FROM ticket t FROM ticket t
JOIN client c ON t.clientFk = c.id JOIN client c ON t.clientFk = c.id
JOIN address a ON t.addressFk = a.id JOIN address a ON t.addressFk = a.id
LEFT JOIN delivery d ON t.id = d.ticketFk LEFT JOIN delivery d ON d.ticketFk = t.id
LEFT JOIN workerMedia wm ON wm.workerFk = c.salesPersonFk LEFT JOIN workerMedia wm ON wm.workerFk = c.salesPersonFk
LEFT JOIN LEFT JOIN(
(SELECT tob.description Note, t.id SELECT tob.description, t.id
FROM ticketObservation tob FROM ticketObservation tob
JOIN ticket t ON tob.ticketFk = t.id JOIN ticket t ON tob.ticketFk = t.id
JOIN observationType ot ON ot.id = tob.observationTypeFk JOIN observationType ot ON ot.id = tob.observationTypeFk
WHERE t.routeFk = vRouteFk WHERE t.routeFk = vRouteFk
AND ot.code = 'delivery' AND ot.code = 'delivery'
)tob ON tob.id = t.id )tob ON tob.id = t.id
LEFT JOIN LEFT JOIN(
(SELECT sub.ticketFk, SELECT sub.ticketFk,
CONCAT('(', GROUP_CONCAT(DISTINCT sub.itemPackingTypeFk ORDER BY sub.items DESC SEPARATOR ','), ') ') itemPackingTypeFk CONCAT('(',
FROM (SELECT s.ticketFk , i.itemPackingTypeFk, COUNT(*) items GROUP_CONCAT(DISTINCT sub.itemPackingTypeFk
ORDER BY sub.items DESC SEPARATOR ','),
') ') itemPackingTypeFk
FROM (
SELECT s.ticketFk, i.itemPackingTypeFk, COUNT(*) items
FROM ticket t FROM ticket t
JOIN sale s ON s.ticketFk = t.id JOIN sale s ON s.ticketFk = t.id
JOIN item i ON i.id = s.itemFk JOIN item i ON i.id = s.itemFk
WHERE t.routeFk = vRouteFk WHERE t.routeFk = vRouteFk
GROUP BY t.id,i.itemPackingTypeFk)sub GROUP BY t.id, i.itemPackingTypeFk
)sub
GROUP BY sub.ticketFk GROUP BY sub.ticketFk
)sub2 ON sub2.ticketFk = t.id )sub2 ON sub2.ticketFk = t.id
WHERE t.routeFk = vRouteFk WHERE t.routeFk = vRouteFk
GROUP BY t.id ORDER BY d.id DESC
ORDER BY t.priority; LIMIT 10000000000000000000
)sub3
GROUP BY sub3.id
ORDER BY sub3.priority;
END$$ END$$
DELIMITER ; 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'), (1, 'First sector', 1, 1, 'FIRST'),
(2, 'Second sector', 2, 0, 'SECOND'); (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`) INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`, `ipAddress`)
VALUES VALUES
(1, 'printer1', 'path1', 0, 1 , NULL), (1, 'printer1', 'path1', 0, 1 , NULL),
@ -1200,6 +1196,11 @@ INSERT INTO `vn`.`train`(`id`, `name`)
(1, 'Train1'), (1, 'Train1'),
(2, 'Train2'); (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`) INSERT INTO `vn`.`collection`(`id`, `workerFk`, `stateFk`, `created`, `trainFk`)
VALUES VALUES
(1, 1106, 5, DATE_ADD(util.VN_CURDATE(),INTERVAL +1 DAY), 1), (1, 1106, 5, DATE_ADD(util.VN_CURDATE(),INTERVAL +1 DAY), 1),

View File

@ -66,7 +66,6 @@
</vn-tr> </vn-tr>
</vn-tbody> </vn-tbody>
</vn-table> </vn-table>
<vn-pagination model="model"></vn-pagination>
</vn-card> </vn-card>
</vn-data-viewer> </vn-data-viewer>
<vn-worker-descriptor-popover vn-id="workerDescriptor"> <vn-worker-descriptor-popover vn-id="workerDescriptor">

View File

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

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}}", "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", "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", "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", "Insert a date range": "Inserte un rango de fechas",
"Added observation": "{{user}} añadió esta observacion: {{text}}", "Added observation": "{{user}} añadió esta observacion: {{text}}",
"Comment added to client": "Observación añadida al cliente {{clientFk}}", "Comment added to client": "Observación añadida al cliente {{clientFk}}",

View File

@ -15,7 +15,7 @@
"legacyUtcDateProcessing": false, "legacyUtcDateProcessing": false,
"timezone": "local", "timezone": "local",
"connectTimeout": 40000, "connectTimeout": 40000,
"acquireTimeout": 60000, "acquireTimeout": 90000,
"waitForConnections": true "waitForConnections": true
}, },
"osticket": { "osticket": {

View File

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

View File

@ -98,6 +98,7 @@ module.exports = Self => {
stmt.merge(conn.makeWhere(args.filter.where)); stmt.merge(conn.makeWhere(args.filter.where));
stmt.merge(conn.makeOrderBy(args.filter.order)); stmt.merge(conn.makeOrderBy(args.filter.order));
stmt.merge(conn.makeLimit(args.filter));
const negativeBasesIndex = stmts.push(stmt) - 1; const negativeBasesIndex = stmts.push(stmt) - 1;

View File

@ -2,7 +2,8 @@
vn-id="model" vn-id="model"
url="InvoiceIns/negativeBases" url="InvoiceIns/negativeBases"
auto-load="true" auto-load="true"
params="$ctrl.params"> params="$ctrl.params"
limit="20">
</vn-crud-model> </vn-crud-model>
<vn-portal slot="topbar"> <vn-portal slot="topbar">
</vn-portal> </vn-portal>

View File

@ -37,7 +37,7 @@ describe('Item editFixedPrice()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
try { try {
const filter = {'it.categoryFk': 1}; const filter = {where: {'it.categoryFk': 1}};
const ctx = { const ctx = {
args: { args: {
filter: filter filter: filter
@ -48,7 +48,7 @@ describe('Item editFixedPrice()', () => {
const field = 'rate2'; const field = 'rate2';
const newValue = 88; const newValue = 88;
await models.FixedPrice.editFixedPrice(ctx, field, newValue, null, filter, options); await models.FixedPrice.editFixedPrice(ctx, field, newValue, null, filter.where, options);
const [result] = await models.FixedPrice.filter(ctx, filter, options); const [result] = await models.FixedPrice.filter(ctx, filter, options);

View File

@ -68,7 +68,9 @@
<th field="stateFk"> <th field="stateFk">
<span translate>State</span> <span translate>State</span>
</th> </th>
<th field="isFragile"></th> <th field="isFragile" number>
<span translate>Fragile</span>
</th>
<th field="zoneFk"> <th field="zoneFk">
<span translate>Zone</span> <span translate>Zone</span>
</th> </th>

View File

@ -30,35 +30,47 @@ module.exports = Self => {
const ticketLogs = await models.TicketLog.find( const ticketLogs = await models.TicketLog.find(
{ {
where: { where: {
or: [
{
and: [ and: [
{originFk: id}, {originFk: id},
{action: 'update'}, {action: 'update'},
{changedModel: 'Sale'} {changedModel: 'Sale'}
] ]
}, },
{
and: [
{originFk: id},
{action: 'delete'},
{changedModel: 'Sale'}
]
}
]
},
fields: [ fields: [
'oldInstance', 'oldInstance',
'newInstance', 'newInstance',
'changedModelId' 'changedModelId',
'changedModelValue'
], ],
}, myOptions); }, myOptions);
const changes = []; const changes = [];
for (const ticketLog of ticketLogs) {
const oldQuantity = ticketLog.oldInstance.quantity; for (const log of ticketLogs) {
const newQuantity = ticketLog.newInstance.quantity; const oldQuantity = log.oldInstance.quantity;
const newQuantity = log.newInstance?.quantity || 0;
if (oldQuantity || newQuantity) { if (oldQuantity || newQuantity) {
const sale = await models.Sale.findById(ticketLog.changedModelId, null, myOptions); const changeMessage = $t('Change quantity', {
const message = $t('Change quantity', { concept: log.changedModelValue,
concept: sale.concept,
oldQuantity: oldQuantity || 0, oldQuantity: oldQuantity || 0,
newQuantity: newQuantity || 0, newQuantity: newQuantity || 0,
}); });
changes.push(changeMessage);
}
}
changes.push(message);
}
}
return changes.join('\n'); return changes.join('\n');
}; };
}; };

View File

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

7692
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<body> <body>
<table class="mainTable"> <table class="mainTable">
<tbody> <tbody>
@ -30,12 +31,16 @@
<img :src="QR" id="QR" /> <img :src="QR" id="QR" />
<div id="right"> <div id="right">
<div id="additionalInfo" class="ellipsize"><b>Pallet: </b>{{id}}</div> <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>User: </b>
<div id="additionalInfo" class="ellipsize"><b>Day: </b>{{labelData.dayName.toUpperCase() || '---'}}</div> {{ (username ? username.name : '---')}}
</div>
<div id="additionalInfo" class="ellipsize"><b>Day: </b>{{labelData.dayName.toUpperCase() ||
'---'}}</div>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</body> </body>
</html> </html>

View File

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