Merge branch 'dev' into 5075-client_balance
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
3e15728db4
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE `vn`.`printQueueArgs` MODIFY COLUMN value varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NULL;
|
|
@ -0,0 +1,5 @@
|
||||||
|
DROP TRIGGER `vn`.`deviceProduction_afterInsert`;
|
||||||
|
DROP TRIGGER `vn`.`deviceProduction_afterUpdate`;
|
||||||
|
|
||||||
|
DROP TRIGGER `vn`.`deviceProductionUser_afterDelete`;
|
||||||
|
|
|
@ -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`);
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES ('ClientInforma', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('ClientInforma', '*', 'WRITE', 'ALLOW', 'ROLE', 'financial');
|
|
@ -0,0 +1,16 @@
|
||||||
|
ALTER TABLE `vn`.`client` ADD rating INT UNSIGNED DEFAULT NULL NULL COMMENT 'información proporcionada por Informa';
|
||||||
|
ALTER TABLE `vn`.`client` ADD recommendedCredit INT UNSIGNED DEFAULT NULL NULL COMMENT 'información proporcionada por Informa';
|
||||||
|
|
||||||
|
CREATE TABLE `vn`.`clientInforma` (
|
||||||
|
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`clientFk` int(11) NOT NULL,
|
||||||
|
`rating` int(10) unsigned DEFAULT NULL,
|
||||||
|
`recommendedCredit` int(10) unsigned DEFAULT NULL,
|
||||||
|
`workerFk` int(10) unsigned NOT NULL,
|
||||||
|
`created` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `informaWorkers_fk_idx` (`workerFk`),
|
||||||
|
KEY `informaClientFk` (`clientFk`),
|
||||||
|
CONSTRAINT `informa_ClienteFk` FOREIGN KEY (`clientFk`) REFERENCES `client` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `informa_workers_fk` FOREIGN KEY (`workerFk`) REFERENCES `worker` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='información proporcionada por Informa, se actualiza desde el hook de client (salix)';
|
|
@ -0,0 +1,64 @@
|
||||||
|
DELETE FROM `salix`.`ACL` WHERE id=7;
|
||||||
|
|
||||||
|
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||||
|
VALUES
|
||||||
|
('Client', 'setRating', 'WRITE', 'ALLOW', 'ROLE', 'financial');
|
||||||
|
|
||||||
|
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||||
|
VALUES
|
||||||
|
('Client', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'addressesPropagateRe', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'canBeInvoiced', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'canCreateTicket', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'consumption', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'createAddress', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'createWithUser', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'extendedListFilter', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'getAverageInvoiced', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'getCard', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'getDebt', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'getMana', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'transactions', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'hasCustomerRole', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'isValidClient', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'lastActiveTickets', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'sendSms', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'setPassword', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'summary', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'updateAddress', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'updateFiscalData', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'updateUser', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'uploadFile', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'campaignMetricsPdf', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'campaignMetricsEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'clientWelcomeHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'clientWelcomeEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'printerSetupHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'printerSetupEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'sepaCoreEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'letterDebtorPdf', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'letterDebtorStHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'letterDebtorStEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'letterDebtorNdHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'letterDebtorNdEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'clientDebtStatementPdf', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'clientDebtStatementHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'clientDebtStatementEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'creditRequestPdf', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'creditRequestHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'creditRequestEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'incotermsAuthorizationPdf', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'incotermsAuthorizationHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'incotermsAuthorizationEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'consumptionSendQueued', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'filter', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'getClientOrSupplierReference', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'upsert', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'create', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'replaceById', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'updateAttributes', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'updateAttributes', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'deleteById', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'replaceOrCreate', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'updateAll', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Client', 'upsertWithWhere', '*', 'ALLOW', 'ROLE', 'employee');
|
|
@ -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),
|
||||||
|
|
|
@ -156,14 +156,14 @@ let actions = {
|
||||||
await this.waitForSpinnerLoad();
|
await this.waitForSpinnerLoad();
|
||||||
},
|
},
|
||||||
|
|
||||||
accessToSection: async function(state) {
|
accessToSection: async function(state, name = 'Others') {
|
||||||
await this.waitForSelector('vn-left-menu');
|
await this.waitForSelector('vn-left-menu');
|
||||||
let nested = await this.evaluate(state => {
|
let nested = await this.evaluate(state => {
|
||||||
return document.querySelector(`vn-left-menu li li > a[ui-sref="${state}"]`) != null;
|
return document.querySelector(`vn-left-menu li li > a[ui-sref="${state}"]`) != null;
|
||||||
}, state);
|
}, state);
|
||||||
|
|
||||||
if (nested) {
|
if (nested) {
|
||||||
let selector = 'vn-left-menu vn-item-section > vn-icon[icon=keyboard_arrow_down]';
|
let selector = `vn-left-menu li[name="${name}"]`;
|
||||||
await this.evaluate(selector => {
|
await this.evaluate(selector => {
|
||||||
document.querySelector(selector).scrollIntoViewIfNeeded();
|
document.querySelector(selector).scrollIntoViewIfNeeded();
|
||||||
}, selector);
|
}, selector);
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -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}}",
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -17,6 +17,10 @@ class Controller extends Descriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendPickupOrder() {
|
sendPickupOrder() {
|
||||||
|
if (!this.claim.client.email) {
|
||||||
|
this.vnApp.showError(this.$t('The client does not have an email'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
return this.vnEmail.send(`Claims/${this.claim.id}/claim-pickup-email`, {
|
return this.vnEmail.send(`Claims/${this.claim.id}/claim-pickup-email`, {
|
||||||
recipient: this.claim.client.email,
|
recipient: this.claim.client.email,
|
||||||
recipientId: this.claim.clientFk
|
recipientId: this.claim.clientFk
|
||||||
|
|
|
@ -20,3 +20,4 @@ Photos: Fotos
|
||||||
Go to the claim: Ir a la reclamación
|
Go to the claim: Ir a la reclamación
|
||||||
Sale tracking: Líneas preparadas
|
Sale tracking: Líneas preparadas
|
||||||
Ticket tracking: Estados del ticket
|
Ticket tracking: Estados del ticket
|
||||||
|
The client does not have an email: El cliente no tiene email
|
||||||
|
|
|
@ -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(`
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('setRating', {
|
||||||
|
description: 'Change rating and recommendedCredit of a client',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The user id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'rating',
|
||||||
|
type: 'number'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'recommendedCredit',
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/:id/setRating`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.setRating = async function(ctx, id, rating, recommendedCredit, options) {
|
||||||
|
let tx;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const client = await Self.findById(id, null, myOptions);
|
||||||
|
const clientUpdated = await client.updateAttributes({
|
||||||
|
rating: rating,
|
||||||
|
recommendedCredit: recommendedCredit
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return clientUpdated;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,43 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('Client setRating()', () => {
|
||||||
|
const financialId = 73;
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: financialId},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ctx = {req: activeCtx};
|
||||||
|
|
||||||
|
beforeAll(async() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change rating and recommendedCredit', async() => {
|
||||||
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const clientId = 1101;
|
||||||
|
const newRating = 10;
|
||||||
|
const newRecommendedCredit = 20;
|
||||||
|
|
||||||
|
const updatedClient = await models.Client.setRating(ctx, clientId, newRating, newRecommendedCredit, options);
|
||||||
|
|
||||||
|
expect(updatedClient.rating).toEqual(newRating);
|
||||||
|
expect(updatedClient.recommendedCredit).toEqual(newRecommendedCredit);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -32,6 +32,9 @@
|
||||||
"ClientConsumptionQueue": {
|
"ClientConsumptionQueue": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"ClientInforma": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"ClientLog": {
|
"ClientLog": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"name": "ClientInforma",
|
||||||
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model":"ClientLog",
|
||||||
|
"relation": "client",
|
||||||
|
"showField": "clientFk"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "clientInforma"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"rating": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"recommendedCredit": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"created": {
|
||||||
|
"type": "date"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"worker": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Worker",
|
||||||
|
"foreignKey": "workerFk"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Client",
|
||||||
|
"foreignKey": "clientFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,4 +47,5 @@ module.exports = Self => {
|
||||||
require('../methods/client/consumptionSendQueued')(Self);
|
require('../methods/client/consumptionSendQueued')(Self);
|
||||||
require('../methods/client/filter')(Self);
|
require('../methods/client/filter')(Self);
|
||||||
require('../methods/client/getClientOrSupplierReference')(Self);
|
require('../methods/client/getClientOrSupplierReference')(Self);
|
||||||
|
require('../methods/client/setRating')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -280,6 +280,10 @@ module.exports = Self => {
|
||||||
if (changes.credit !== undefined)
|
if (changes.credit !== undefined)
|
||||||
await Self.changeCredit(ctx, finalState, changes);
|
await Self.changeCredit(ctx, finalState, changes);
|
||||||
|
|
||||||
|
// Credit management changes
|
||||||
|
if (orgData?.rating != changes.rating || orgData?.recommendedCredit != changes.recommendedCredit)
|
||||||
|
await Self.changeCreditManagement(ctx, finalState, changes);
|
||||||
|
|
||||||
const oldInstance = {};
|
const oldInstance = {};
|
||||||
if (!ctx.isNewInstance) {
|
if (!ctx.isNewInstance) {
|
||||||
const newProps = Object.keys(changes);
|
const newProps = Object.keys(changes);
|
||||||
|
@ -441,6 +445,19 @@ module.exports = Self => {
|
||||||
}, ctx.options);
|
}, ctx.options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Self.changeCreditManagement = async function changeCreditManagement(ctx, finalState, changes) {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||||
|
const userId = loopBackContext.active.accessToken.userId;
|
||||||
|
|
||||||
|
await models.ClientInforma.create({
|
||||||
|
clientFk: finalState.id,
|
||||||
|
rating: changes.rating,
|
||||||
|
recommendedCredit: changes.recommendedCredit,
|
||||||
|
workerFk: userId
|
||||||
|
}, ctx.options);
|
||||||
|
};
|
||||||
|
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
app.on('started', function() {
|
app.on('started', function() {
|
||||||
const VnUser = app.models.VnUser;
|
const VnUser = app.models.VnUser;
|
||||||
|
|
|
@ -141,6 +141,12 @@
|
||||||
},
|
},
|
||||||
"hasElectronicInvoice": {
|
"hasElectronicInvoice": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"rating": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"recommendedCredit": {
|
||||||
|
"type": "number"
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
<mg-ajax path="Clients/{{post.params.id}}/setRating" options="vnPost"></mg-ajax>
|
||||||
|
<vn-watcher
|
||||||
|
vn-id="watcher"
|
||||||
|
url="Clients"
|
||||||
|
data="$ctrl.client"
|
||||||
|
id-value="$ctrl.$params.id"
|
||||||
|
insert-mode="true"
|
||||||
|
form="form"
|
||||||
|
save="post">
|
||||||
|
</vn-watcher>
|
||||||
|
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
|
||||||
|
<vn-card class="vn-pa-lg">
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-input-number
|
||||||
|
vn-one
|
||||||
|
label="Rating"
|
||||||
|
ng-model="$ctrl.client.rating"
|
||||||
|
vn-focus
|
||||||
|
rule>
|
||||||
|
</vn-input-number>
|
||||||
|
<vn-input-number
|
||||||
|
vn-one
|
||||||
|
label="Recommended credit"
|
||||||
|
ng-model="$ctrl.client.recommendedCredit"
|
||||||
|
rule>
|
||||||
|
</vn-input-number>
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-card>
|
||||||
|
<vn-button-bar>
|
||||||
|
<vn-submit
|
||||||
|
disabled="!watcher.dataChanged()"
|
||||||
|
label="Save">
|
||||||
|
</vn-submit>
|
||||||
|
</vn-button-bar>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<vn-crud-model
|
||||||
|
vn-id="model"
|
||||||
|
url="ClientInformas"
|
||||||
|
filter="$ctrl.filter"
|
||||||
|
link="{clientFk: $ctrl.$params.id}"
|
||||||
|
limit="20"
|
||||||
|
data="clientInformas"
|
||||||
|
order="created DESC"
|
||||||
|
auto-load="true">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-data-viewer
|
||||||
|
model="model"
|
||||||
|
class="vn-w-md">
|
||||||
|
<vn-card>
|
||||||
|
<vn-table model="model" class="vn-mt-lg">
|
||||||
|
<vn-thead>
|
||||||
|
<vn-tr>
|
||||||
|
<vn-th shrink-date field="created">Since</vn-th>
|
||||||
|
<vn-th field="workerFk">Employee</vn-th>
|
||||||
|
<vn-th field="rating" number>Rating</vn-th>
|
||||||
|
<vn-th field="recommendedCredit" number>Recommended credit</vn-th>
|
||||||
|
</vn-tr>
|
||||||
|
</vn-thead>
|
||||||
|
<vn-tbody>
|
||||||
|
<vn-tr ng-repeat="clientInforma in clientInformas">
|
||||||
|
<vn-td shrink-datetime>{{::clientInforma.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
|
||||||
|
<vn-td shrink>
|
||||||
|
<span
|
||||||
|
ng-click="workerDescriptor.show($event, clientInforma.workerFk)"
|
||||||
|
class="link">
|
||||||
|
{{::clientInforma.worker.user.nickname}}
|
||||||
|
</span>
|
||||||
|
</vn-td>
|
||||||
|
<vn-td number>{{::clientInforma.rating}}</vn-td>
|
||||||
|
<vn-td number>{{::clientInforma.recommendedCredit}}</vn-td>
|
||||||
|
</vn-tr>
|
||||||
|
</vn-tbody>
|
||||||
|
</vn-table>
|
||||||
|
</vn-card>
|
||||||
|
</vn-data-viewer>
|
||||||
|
<vn-worker-descriptor-popover
|
||||||
|
vn-id="workerDescriptor">
|
||||||
|
</vn-worker-descriptor-popover>
|
|
@ -0,0 +1,32 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
|
||||||
|
export default class Controller extends Section {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
|
||||||
|
this.filter = {
|
||||||
|
include: [{
|
||||||
|
relation: 'worker',
|
||||||
|
scope: {
|
||||||
|
fields: ['userFk'],
|
||||||
|
include: {
|
||||||
|
relation: 'user',
|
||||||
|
scope: {
|
||||||
|
fields: ['nickname']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
onSubmit() {
|
||||||
|
this.$.watcher.submit()
|
||||||
|
.then(() => this.$state.reload());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnClientCreditManagement', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller
|
||||||
|
});
|
|
@ -0,0 +1,2 @@
|
||||||
|
Recommended credit: Crédito recomendado
|
||||||
|
Rating: Clasificación
|
|
@ -47,3 +47,5 @@ import './defaulter';
|
||||||
import './notification';
|
import './notification';
|
||||||
import './unpaid';
|
import './unpaid';
|
||||||
import './extended-list';
|
import './extended-list';
|
||||||
|
import './credit-management';
|
||||||
|
|
||||||
|
|
|
@ -64,4 +64,6 @@ Compensation Account: Cuenta para compensar
|
||||||
Amount to return: Cantidad a devolver
|
Amount to return: Cantidad a devolver
|
||||||
Delivered amount: Cantidad entregada
|
Delivered amount: Cantidad entregada
|
||||||
Unpaid: Impagado
|
Unpaid: Impagado
|
||||||
|
Credit management: Gestión de crédito
|
||||||
|
Credit opinion: Opinión de crédito
|
||||||
There is no zona: No hay zona
|
There is no zona: No hay zona
|
|
@ -23,6 +23,14 @@
|
||||||
{"state": "client.card.recovery.index", "icon": "icon-recovery"},
|
{"state": "client.card.recovery.index", "icon": "icon-recovery"},
|
||||||
{"state": "client.card.webAccess", "icon": "cloud"},
|
{"state": "client.card.webAccess", "icon": "cloud"},
|
||||||
{"state": "client.card.log", "icon": "history"},
|
{"state": "client.card.log", "icon": "history"},
|
||||||
|
{
|
||||||
|
"description": "Credit management",
|
||||||
|
"icon": "monetization_on",
|
||||||
|
"childs": [
|
||||||
|
{"state": "client.card.creditInsurance.index", "icon": "icon-solunion"},
|
||||||
|
{"state": "client.card.creditManagement", "icon": "contact_support"}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Others",
|
"description": "Others",
|
||||||
"icon": "more",
|
"icon": "more",
|
||||||
|
@ -30,7 +38,6 @@
|
||||||
{"state": "client.card.sample.index", "icon": "mail"},
|
{"state": "client.card.sample.index", "icon": "mail"},
|
||||||
{"state": "client.card.consumption", "icon": "show_chart"},
|
{"state": "client.card.consumption", "icon": "show_chart"},
|
||||||
{"state": "client.card.mandate", "icon": "pan_tool"},
|
{"state": "client.card.mandate", "icon": "pan_tool"},
|
||||||
{"state": "client.card.creditInsurance.index", "icon": "icon-solunion"},
|
|
||||||
{"state": "client.card.contact", "icon": "contact_phone"},
|
{"state": "client.card.contact", "icon": "contact_phone"},
|
||||||
{"state": "client.card.webPayment", "icon": "icon-onlinepayment"},
|
{"state": "client.card.webPayment", "icon": "icon-onlinepayment"},
|
||||||
{"state": "client.card.dms.index", "icon": "cloud_upload"},
|
{"state": "client.card.dms.index", "icon": "cloud_upload"},
|
||||||
|
@ -416,7 +423,8 @@
|
||||||
"state": "client.notification",
|
"state": "client.notification",
|
||||||
"component": "vn-client-notification",
|
"component": "vn-client-notification",
|
||||||
"description": "Notifications"
|
"description": "Notifications"
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
"url": "/unpaid",
|
"url": "/unpaid",
|
||||||
"state": "client.card.unpaid",
|
"state": "client.card.unpaid",
|
||||||
"component": "vn-client-unpaid",
|
"component": "vn-client-unpaid",
|
||||||
|
@ -428,6 +436,13 @@
|
||||||
"state": "client.extendedList",
|
"state": "client.extendedList",
|
||||||
"component": "vn-client-extended-list",
|
"component": "vn-client-extended-list",
|
||||||
"description": "Extended list"
|
"description": "Extended list"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/credit-management",
|
||||||
|
"state": "client.card.creditManagement",
|
||||||
|
"component": "vn-client-credit-management",
|
||||||
|
"acl": ["financial"],
|
||||||
|
"description": "Credit opinion"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
</head>
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="ticketsModel"
|
vn-id="ticketsModel"
|
||||||
url="Tickets"
|
url="Tickets"
|
||||||
|
@ -253,7 +256,11 @@
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<h4 translate>Financial information</h4>
|
<h4 ng-show="$ctrl.isEmployee">
|
||||||
|
<a href="https://grafana.verdnatura.es/d/40buzE4Vk/comportamiento-pagos-clientes?orgId=1&var-clientFk={{::$ctrl.client.id}}">
|
||||||
|
<span class="grafana" translate vn-tooltip="Go to grafana">Billing data</span>
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
<vn-label-value label="Risk"
|
<vn-label-value label="Risk"
|
||||||
value="{{$ctrl.summary.debt.debt | currency: 'EUR':2}}"
|
value="{{$ctrl.summary.debt.debt | currency: 'EUR':2}}"
|
||||||
ng-class="{alert: $ctrl.summary.debt.debt > $ctrl.summary.credit}"
|
ng-class="{alert: $ctrl.summary.debt.debt > $ctrl.summary.credit}"
|
||||||
|
@ -282,6 +289,10 @@
|
||||||
ng-if="$ctrl.summary.recovery.started"
|
ng-if="$ctrl.summary.recovery.started"
|
||||||
value="{{$ctrl.summary.recovery.started | date:'dd/MM/yyyy'}}">
|
value="{{$ctrl.summary.recovery.started | date:'dd/MM/yyyy'}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
|
<vn-label-value label="Rating"
|
||||||
|
value="{{$ctrl.summary.rating}}"
|
||||||
|
info="Value from 1 to 20. The higher the better value">
|
||||||
|
</vn-label-value>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
|
|
|
@ -20,3 +20,6 @@ Invoices minus payments: Facturas menos recibos
|
||||||
Deviated invoices minus payments: Facturas fuera de plazo menos recibos
|
Deviated invoices minus payments: Facturas fuera de plazo menos recibos
|
||||||
Go to the client: Ir al cliente
|
Go to the client: Ir al cliente
|
||||||
Latest tickets: Últimos tickets
|
Latest tickets: Últimos tickets
|
||||||
|
Rating: Clasificación
|
||||||
|
Value from 1 to 20. The higher the better value: Valor del 1 al 20. Cuanto más alto mejor valoración
|
||||||
|
Go to grafana: Ir a grafana
|
||||||
|
|
|
@ -6,4 +6,9 @@ vn-client-summary .summary {
|
||||||
.alert span {
|
.alert span {
|
||||||
color: $color-alert !important
|
color: $color-alert !important
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vn-horizontal h4 .grafana:after {
|
||||||
|
content: 'contact_support';
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -192,19 +192,19 @@
|
||||||
{{::buy.entryFk}}
|
{{::buy.entryFk}}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td number>{{::buy.buyingValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.buyingValue | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.freightValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.freightValue | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.comissionValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.comissionValue | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.packageValue | currency: 'EUR':2}}</td>
|
<td number>{{::buy.packageValue | currency: 'EUR':3}}</td>
|
||||||
<td>
|
<td>
|
||||||
<vn-check
|
<vn-check
|
||||||
disabled="true"
|
disabled="true"
|
||||||
ng-model="::buy.isIgnored">
|
ng-model="::buy.isIgnored">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</td>
|
</td>
|
||||||
<td number>{{::buy.price2 | currency: 'EUR':2}}</td>
|
<td number>{{::buy.price2 | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.price3 | currency: 'EUR':2}}</td>
|
<td number>{{::buy.price3 | currency: 'EUR':3}}</td>
|
||||||
<td number>{{::buy.minPrice | currency: 'EUR':2}}</td>
|
<td number>{{::buy.minPrice | currency: 'EUR':3}}</td>
|
||||||
<td>{{::buy.ektFk | dashIfEmpty}}</td>
|
<td>{{::buy.ektFk | dashIfEmpty}}</td>
|
||||||
<td>{{::buy.weight}}</td>
|
<td>{{::buy.weight}}</td>
|
||||||
<td>{{::buy.packageFk}}</td>
|
<td>{{::buy.packageFk}}</td>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.requesterFk"
|
ng-model="filter.requesterFk"
|
||||||
url="Workers/activeWithRole"
|
url="Workers/activeWithInheritedRole"
|
||||||
search-function="{firstName: $search}"
|
search-function="{firstName: $search}"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
where="{role: 'salesPerson'}"
|
where="{role: 'salesPerson'}"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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');
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -100,7 +100,7 @@ module.exports = Self => {
|
||||||
dmsTypeId: dmsType.id,
|
dmsTypeId: dmsType.id,
|
||||||
reference: '',
|
reference: '',
|
||||||
description: `Firma del cliente - Ruta ${ticket.route().id}`,
|
description: `Firma del cliente - Ruta ${ticket.route().id}`,
|
||||||
hasFile: true
|
hasFile: false
|
||||||
};
|
};
|
||||||
dms = await models.Dms.uploadFile(ctxUploadFile, myOptions);
|
dms = await models.Dms.uploadFile(ctxUploadFile, myOptions);
|
||||||
gestDocCreated = true;
|
gestDocCreated = true;
|
||||||
|
|
|
@ -23,6 +23,9 @@
|
||||||
"DeviceProduction": {
|
"DeviceProduction": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"DeviceProductionLog": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"DeviceProductionModels": {
|
"DeviceProductionModels": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"name": "DeviceProductionLog",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "deviceProductionLog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number",
|
||||||
|
"forceId": false
|
||||||
|
},
|
||||||
|
"originFk": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"userFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"deviceProduction": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"created": {
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
"oldInstance": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"newInstance": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"changedModel": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"changedModelId": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"user": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Account",
|
||||||
|
"foreignKey": "userFk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"order": ["created DESC", "id DESC"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "DeviceProductionUser",
|
"name": "DeviceProductionUser",
|
||||||
"base": "VnModel",
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model": "DeviceProductionLog",
|
||||||
|
"relation": "deviceProduction"
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "deviceProductionUser"
|
"table": "deviceProductionUser"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "DeviceProduction",
|
"name": "DeviceProduction",
|
||||||
"base": "VnModel",
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model": "DeviceProductionLog"
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "deviceProduction"
|
"table": "deviceProduction"
|
||||||
|
|
|
@ -31,11 +31,12 @@
|
||||||
value-field="id"
|
value-field="id"
|
||||||
show-field="serialNumber">
|
show-field="serialNumber">
|
||||||
<tpl-item>
|
<tpl-item>
|
||||||
<span>ID: {{id}}</span>
|
<div>
|
||||||
<span class="separator"></span>
|
ID: {{id}}
|
||||||
<span>{{'Model' | translate}}: {{modelFk}}</span>
|
</div>
|
||||||
<span class="separator"></span>
|
<div class="text-caption text-grey">
|
||||||
<span>{{'Serial Number' | translate}}: {{serialNumber}}</span>
|
{{modelFk}}, {{serialNumber}}
|
||||||
|
</div>
|
||||||
</tpl-item>
|
</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
span.separator{
|
@import "./variables";
|
||||||
border-left: 1px solid black;
|
|
||||||
height: 100%;
|
.text-grey {
|
||||||
margin: 0 10px;
|
color: $color-font-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -24,9 +24,11 @@ sections:
|
||||||
dicho stock puede variar en función de la fecha seleccionada al configurar el
|
dicho stock puede variar en función de la fecha seleccionada al configurar el
|
||||||
pedido. Es importante CONFIRMAR los pedidos para que la mercancía quede reservada.
|
pedido. Es importante CONFIRMAR los pedidos para que la mercancía quede reservada.
|
||||||
delivery: El reparto se realiza de lunes a sábado según la zona en la que te encuentres.
|
delivery: El reparto se realiza de lunes a sábado según la zona en la que te encuentres.
|
||||||
Por regla general, los pedidos que se entregan por agencia, deben estar confirmados
|
Los pedidos que se entregan por agencia o por reparto Verdnatura deben estar confirmados y pagados
|
||||||
y pagados antes de las 17h del día en que se preparan (el día anterior a recibirlos),
|
antes del cierre de la correspondiente ruta el mismo día de preparación del pedido. Este horario
|
||||||
aunque esto puede variar si el pedido se envía a través de nuestro reparto y
|
puede variar mucho en función de la ruta y del volumen de pedidos para ese día, por lo que es
|
||||||
|
recomendable no apurar el tiempo y dejar todo listo a primera hora del día de preparación del pedido.
|
||||||
|
Aunque esto puede variar si el pedido se envía a través de nuestro reparto y
|
||||||
según la zona.
|
según la zona.
|
||||||
howToPay:
|
howToPay:
|
||||||
title: Cómo pagar
|
title: Cómo pagar
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
subject: Bienvenue chez Verdnatura
|
||||||
|
title: "Nous vous souhaitons la bienvenue!"
|
||||||
|
dearClient: Cher client
|
||||||
|
clientData: 'Vos données pour pouvoir acheter sur le site de Verdnatura (<a href="https://shop.verdnatura.es"
|
||||||
|
title="Visiter Verdnatura" target="_blank" style="color: #8dba25">https://shop.verdnatura.es</a>)
|
||||||
|
ou sur nos applications pour <a href="https://goo.gl/3hC2mG" title="App Store"
|
||||||
|
target="_blank" style="color: #8dba25">iOS</a> et <a href="https://goo.gl/8obvLc"
|
||||||
|
title="Google Play" target="_blank" style="color: #8dba25">Android</a>, sont'
|
||||||
|
clientId: Identifiant du client
|
||||||
|
user: Utilisateur
|
||||||
|
password: Mot de passe
|
||||||
|
passwordResetText: Cliquez sur "Vous avez oublié votre mot de passe?"
|
||||||
|
sections:
|
||||||
|
howToBuy:
|
||||||
|
title: Comment passer une commande
|
||||||
|
description: 'Pour passer une commande sur notre site, vous devez configurer celle-ci en indiquant :'
|
||||||
|
requeriments:
|
||||||
|
- Si vous souhaitez recevoir la commande (par agence ou par notre propre livraison)
|
||||||
|
ou si vous préférez la récupérer dans l'un de nos entrepôts.
|
||||||
|
- La date à laquelle vous souhaitez recevoir la commande (elle sera préparée la veille).
|
||||||
|
- L'adresse de livraison ou l'entrepôt où vous souhaitez récupérer la commande.
|
||||||
|
stock: Sur notre site et nos applications, vous pouvez visualiser le stock disponible de
|
||||||
|
fleurs coupées, feuillages, plantes, accessoires et artificiels. Veuillez noter que ce
|
||||||
|
stock peut varier en fonction de la date sélectionnée lors de la configuration de la
|
||||||
|
commande. Il est important de CONFIRMER les commandes pour que la marchandise soit réservée.
|
||||||
|
delivery: La livraison est effectuée du lundi au samedi selon la zone dans laquelle vous
|
||||||
|
vous trouvez. Les commandes livrées par agence ou par notre propre livraison doivent
|
||||||
|
être confirmées et payées avant la fermeture de la route correspondante le jour même
|
||||||
|
de la préparation de la commande. Cet horaire peut varier considérablement en fonction
|
||||||
|
de la route et du volume de commandes pour cette journée, il est donc recommandé de ne
|
||||||
|
pas attendre la dernière minute et de tout préparer tôt le matin le jour de la
|
||||||
|
préparation de la commande. Cela peut toutefois varier si la commande est envoyée par
|
||||||
|
notre propre livraison et en fonction de la zone.
|
||||||
|
howToPay:
|
||||||
|
title: Comment payer
|
||||||
|
description: 'Les moyens de paiement acceptés chez Verdnatura sont les suivants :'
|
||||||
|
options:
|
||||||
|
- Par <strong>carte de crédit</strong> via notre plateforme web (lors de la confirmation de la commande).
|
||||||
|
- Par <strong>virement bancaire mensuel</strong>, modalité à demander et à gérer.
|
||||||
|
toConsider:
|
||||||
|
title: Choses à prendre en compte
|
||||||
|
description: Verdnatura vend EXCLUSIVEMENT aux professionnels, vous devez donc nous envoyer
|
||||||
|
le modèle 036 ou 037 pour vérifier que vous êtes bien inscrit dans la catégorie du commerce de fleurs.
|
||||||
|
claimsPolicy:
|
||||||
|
title: POLITIQUE DE RÉCLAMATION
|
||||||
|
description: Verdnatura acceptera les réclamations effectuées dans les deux jours civils suivant
|
||||||
|
la réception de la commande (y compris le jour même de la réception). Passé ce délai, aucune réclamation ne sera acceptée.
|
||||||
|
help: Si vous avez des questions, n'hésitez pas à nous contacter, <strong>nous sommes là pour vous aider !</strong>
|
||||||
|
salesPersonName: Je suis votre commercial et mon nom est
|
||||||
|
salesPersonPhone: Téléphone et Whatsapp
|
||||||
|
salesPersonEmail: Adresse e-mail
|
|
@ -0,0 +1,50 @@
|
||||||
|
subject: Bem-Vindo à Verdnatura
|
||||||
|
title: "Damos-te as boas-vindas!"
|
||||||
|
dearClient: Estimado cliente
|
||||||
|
clientData: 'seus dados para poder comprar na loja online da Verdnatura (<a href="https://shop.verdnatura.es"
|
||||||
|
title="Visitar Verdnatura" target="_blank" style="color: #8dba25">https://shop.verdnatura.es</a>)
|
||||||
|
ou em nossos aplicativos para <a href="https://goo.gl/3hC2mG" title="App Store" target="_blank" style="color: #8dba25">iOS</a>
|
||||||
|
e <a href="https://goo.gl/8obvLc" title="Google Play" target="_blank" style="color: #8dba25">Android</a>, são'
|
||||||
|
clientId: Identificador de cliente
|
||||||
|
user: Utilizador
|
||||||
|
password: Palavra-passe
|
||||||
|
passwordResetText: Clique em 'Esqueceu a sua palavra-passe?'
|
||||||
|
sections:
|
||||||
|
howToBuy:
|
||||||
|
title: Como fazer uma encomenda
|
||||||
|
description: 'Para realizar uma encomenda no nosso site, deves configurá-la indicando:'
|
||||||
|
requeriments:
|
||||||
|
- Se queres receber a encomenda (por agência ou o nosso próprio transporte) ou se preferes levantá-lo em algum dos nossos armazéns.
|
||||||
|
- A data que queres receber a encomenda (se preparará no dia anterior).
|
||||||
|
- A morada de entrega ou armazém aonde queres levantar a encomenda.
|
||||||
|
stock: No nosso site e apps podes visualizar o estoque disponível de
|
||||||
|
flor-de-corte, verduras, plantas, acessórios e artificial. Tenha presente que
|
||||||
|
dito estoque pode variar em função da data escolhida ao configurar a
|
||||||
|
encomenda. É importante confirmar as encomendas para que a mercadoria fique reservada.
|
||||||
|
delivery: O transporte se realiza de terça a sabado. As encomendas que se entreguem por agências ou transporte Verdnatura, devem estar confirmadas e pagas até
|
||||||
|
antes do horário de encerre da correspondente rota do dia de preparação da mesma. Este horario
|
||||||
|
pode variar muito em função da rota e o volume de encomendas deste dia, pelo qual é
|
||||||
|
recomendável não esperar à última hora e deixar tudo pronto à primeira hora do dia de preparação. Ainda que isto possa variar se a encomenda se envia através do nosso transporte
|
||||||
|
dependendo da zona.
|
||||||
|
howToPay:
|
||||||
|
title: Como pagar
|
||||||
|
description: 'As formas de pagamentos admitidas na Verdnatura são:'
|
||||||
|
options:
|
||||||
|
- Com <strong>cartão</strong> através da plataforma de pagamentos (ao confirmar a encomenda ou entrando em Encomendas > Confirmadas).
|
||||||
|
- Mediante <strong>débito automatico mensual</strong>, modalidade que deve-se solicitar e tramitar.
|
||||||
|
toConsider:
|
||||||
|
title: Coisas a ter em conta
|
||||||
|
description: A Verdnatura vende EXCLUSIVAMENTE a profissionais, pelo qual deves
|
||||||
|
remetir-nos o documento de Inicio de Actividade, para comprobar-mos que o vosso CAE
|
||||||
|
esteja relacionado com o mundo das flores.
|
||||||
|
claimsPolicy:
|
||||||
|
title: POLÍTICA DE RECLAMAÇÕES
|
||||||
|
description: A Verdnatura aceitará as reclamações que se realizem dentro dos
|
||||||
|
dois dias naturais seguintes à recepção da encomenda (incluindo o mesmo
|
||||||
|
dia da receção). Passado este prazo não se aceitará nenhuma reclamação.
|
||||||
|
help: Qualquer dúvida que lhe surja, não hesite em consultá-la <strong>estamos
|
||||||
|
para atender-te!</strong>
|
||||||
|
salesPersonName: Sou o seu asesor comercial e o meu nome é
|
||||||
|
salesPersonPhone: Telemovel e whatsapp
|
||||||
|
salesPersonEmail: Correio eletrônico
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<body>
|
|
||||||
|
<body>
|
||||||
<table class="mainTable">
|
<table class="mainTable">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -27,15 +28,19 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<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>
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue