Merge branch 'dev' into 2517-clientBalanceCompensaciones
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
0d5540b639
|
@ -69,13 +69,13 @@ pipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* stage('Backend') {
|
// stage('Backend') {
|
||||||
steps {
|
// steps {
|
||||||
nodejs('node-lts') {
|
// nodejs('node-lts') {
|
||||||
sh 'gulp launchBackTest --ci'
|
// sh 'gulp launchBackTest --ci'
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
} */
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Build') {
|
stage('Build') {
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId) VALUES ('Image', '*', 'WRITE', 'ALLOW', 'ROLE', 'employee')
|
INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId) VALUES ('Image', '*', 'WRITE', 'ALLOW', 'ROLE', 'employee');
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('PayDem', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
|
|
@ -0,0 +1,14 @@
|
||||||
|
CREATE TABLE `vn`.`entryObservation` (
|
||||||
|
id int NOT NULL AUTO_INCREMENT,
|
||||||
|
entryFk int NOT NULL,
|
||||||
|
observationTypeFk TINYINT(3) UNSIGNED,
|
||||||
|
description TEXT,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
CONSTRAINT entry_id_entryFk
|
||||||
|
FOREIGN KEY (entryFk) REFERENCES entry(id),
|
||||||
|
CONSTRAINT observationType_id_observationTypeFk
|
||||||
|
FOREIGN KEY (observationTypeFk) REFERENCES observationType(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`entryObservation`
|
||||||
|
ADD UNIQUE INDEX `entryFk_observationTypeFk_UNIQUE` (`entryFk` ASC,`observationTypeFk` ASC);
|
|
@ -0,0 +1,18 @@
|
||||||
|
CREATE TABLE `vn`.`zoneLog` (
|
||||||
|
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`originFk` int(10) NOT NULL,
|
||||||
|
`userFk` int(10) unsigned DEFAULT NULL,
|
||||||
|
`action` set('insert','update','delete') COLLATE utf8_unicode_ci NOT NULL,
|
||||||
|
`creationDate` timestamp NULL DEFAULT current_timestamp(),
|
||||||
|
`description` text CHARACTER SET utf8 DEFAULT NULL,
|
||||||
|
`changedModel` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||||
|
`oldInstance` text COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||||
|
`newInstance` text COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||||
|
`changedModelId` int(11) DEFAULT NULL,
|
||||||
|
`changedModelValue` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `originFk` (`originFk`),
|
||||||
|
KEY `userFk` (`userFk`),
|
||||||
|
CONSTRAINT `zoneLog_ibfk_1` FOREIGN KEY (`originFk`) REFERENCES `vn`.`zone` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `zoneLog_ibfk_2` FOREIGN KEY (`userFk`) REFERENCES `account`.`user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
|
@ -1,2 +0,0 @@
|
||||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
|
||||||
VALUES ('PayDem', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
|
|
|
@ -1639,7 +1639,7 @@ INSERT INTO `vn`.`orderTicket`(`orderFk`, `ticketFk`)
|
||||||
|
|
||||||
INSERT INTO `vn`.`userConfig` (`userFk`, `warehouseFk`, `companyFk`)
|
INSERT INTO `vn`.`userConfig` (`userFk`, `warehouseFk`, `companyFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 2, 69),
|
(1, 1, 69),
|
||||||
(5, 1, 442),
|
(5, 1, 442),
|
||||||
(9, 1, 442),
|
(9, 1, 442),
|
||||||
(18, 3, 567);
|
(18, 3, 567);
|
||||||
|
|
|
@ -300,9 +300,14 @@ let actions = {
|
||||||
},
|
},
|
||||||
|
|
||||||
waitForNumberOfElements: async function(selector, count) {
|
waitForNumberOfElements: async function(selector, count) {
|
||||||
return await this.waitForFunction((selector, count) => {
|
try {
|
||||||
return document.querySelectorAll(selector).length == count;
|
await this.waitForFunction((selector, count) => {
|
||||||
}, {}, selector, count);
|
return document.querySelectorAll(selector).length == count;
|
||||||
|
}, {}, selector, count);
|
||||||
|
} catch (error) {
|
||||||
|
const amount = await this.countElement(selector);
|
||||||
|
throw new Error(`actual amount of elements was: ${amount} instead of ${count}, ${error}`);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
waitForClassNotPresent: async function(selector, className) {
|
waitForClassNotPresent: async function(selector, className) {
|
||||||
|
|
|
@ -674,7 +674,14 @@ export default {
|
||||||
confirmButton: '.vn-confirm.shown button[response="accept"]',
|
confirmButton: '.vn-confirm.shown button[response="accept"]',
|
||||||
},
|
},
|
||||||
routeIndex: {
|
routeIndex: {
|
||||||
addNewRouteButton: 'vn-route-index a[ui-sref="route.create"]'
|
anyResult: 'vn-table a',
|
||||||
|
firstRouteCheckbox: 'a:nth-child(1) vn-td:nth-child(1) > vn-check',
|
||||||
|
addNewRouteButton: 'vn-route-index a[ui-sref="route.create"]',
|
||||||
|
cloneButton: 'vn-route-index button > vn-icon[icon="icon-clone"]',
|
||||||
|
submitClonationButton: 'tpl-buttons > button[response="accept"]',
|
||||||
|
openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
|
||||||
|
searchAgencyAutocomlete: 'vn-route-search-panel vn-autocomplete[ng-model="filter.agencyModeFk"]',
|
||||||
|
advancedSearchButton: 'vn-route-search-panel button[type=submit]',
|
||||||
},
|
},
|
||||||
createRouteView: {
|
createRouteView: {
|
||||||
worker: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.workerFk"]',
|
worker: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.workerFk"]',
|
||||||
|
|
|
@ -71,7 +71,7 @@ describe('User config', () => {
|
||||||
expect(expectedLocalWarehouse).toBeTruthy();
|
expect(expectedLocalWarehouse).toBeTruthy();
|
||||||
expect(expectedLocalBank).toBeTruthy();
|
expect(expectedLocalBank).toBeTruthy();
|
||||||
expect(expectedLocalCompany).toBeTruthy();
|
expect(expectedLocalCompany).toBeTruthy();
|
||||||
expect(userWarehouse).toEqual('Warehouse Two');
|
expect(userWarehouse).toEqual('Warehouse One');
|
||||||
expect(userCompany).toEqual('CCs');
|
expect(userCompany).toEqual('CCs');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -57,5 +57,36 @@ describe('Route create path', () => {
|
||||||
it(`should confirm the redirection to the created route summary`, async() => {
|
it(`should confirm the redirection to the created route summary`, async() => {
|
||||||
await page.waitForState('route.card.summary');
|
await page.waitForState('route.card.summary');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should navigate back to the route index`, async() => {
|
||||||
|
await page.waitToClick(selectors.globalItems.returnToModuleIndexButton);
|
||||||
|
await page.waitForState('route.index');
|
||||||
|
});
|
||||||
|
|
||||||
|
let count;
|
||||||
|
it(`should count the amount of routes before clonation`, async() => {
|
||||||
|
await page.waitForFunction(selector => {
|
||||||
|
return document.querySelectorAll(selector).length > 6;
|
||||||
|
}, {}, selectors.routeIndex.anyResult);
|
||||||
|
count = await page.countElement(selectors.routeIndex.anyResult);
|
||||||
|
|
||||||
|
expect(count).toBeGreaterThanOrEqual(7);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should clone the first route`, async() => {
|
||||||
|
await page.waitToClick(selectors.routeIndex.firstRouteCheckbox);
|
||||||
|
await page.waitToClick(selectors.routeIndex.cloneButton);
|
||||||
|
await page.waitToClick(selectors.routeIndex.submitClonationButton);
|
||||||
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.text).toContain('Data saved!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should reload the section and count the amount of routes after clonation`, async() => {
|
||||||
|
await page.waitForNumberOfElements(selectors.routeIndex.anyResult, count + 1);
|
||||||
|
const result = await page.countElement(selectors.routeIndex.anyResult);
|
||||||
|
|
||||||
|
expect(result).toEqual(count + 1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -4,6 +4,8 @@ import './style.scss';
|
||||||
|
|
||||||
export default class DescriptorPopover extends Popover {
|
export default class DescriptorPopover extends Popover {
|
||||||
show(parent, id) {
|
show(parent, id) {
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
super.show(parent);
|
super.show(parent);
|
||||||
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|
|
@ -145,9 +145,15 @@ module.exports = function(Self) {
|
||||||
rewriteDbError(replaceErrFunc) {
|
rewriteDbError(replaceErrFunc) {
|
||||||
function replaceErr(err, replaceErrFunc) {
|
function replaceErr(err, replaceErrFunc) {
|
||||||
if (Array.isArray(err)) {
|
if (Array.isArray(err)) {
|
||||||
|
const errors = err.filter(error => {
|
||||||
|
return error != undefined && error != null;
|
||||||
|
});
|
||||||
let errs = [];
|
let errs = [];
|
||||||
for (let e of err)
|
for (let e of errors) {
|
||||||
errs.push(replaceErrFunc(e));
|
if (!(e instanceof UserError))
|
||||||
|
errs.push(replaceErrFunc(e));
|
||||||
|
else errs.push(e);
|
||||||
|
}
|
||||||
return errs;
|
return errs;
|
||||||
}
|
}
|
||||||
return replaceErrFunc(err);
|
return replaceErrFunc(err);
|
||||||
|
|
|
@ -246,3 +246,98 @@ exports.initialize = function initialize(dataSource, callback) {
|
||||||
dataSource.connector.connect(callback);
|
dataSource.connector.connect(callback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MySQL.prototype.connect = function(callback) {
|
||||||
|
const self = this;
|
||||||
|
const options = generateOptions(this.settings);
|
||||||
|
|
||||||
|
if (this.client) {
|
||||||
|
if (callback) {
|
||||||
|
process.nextTick(function() {
|
||||||
|
callback(null, self.client);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
this.client = connectionHandler(options, callback);
|
||||||
|
|
||||||
|
function connectionHandler(options, callback) {
|
||||||
|
const client = mysql.createPool(options);
|
||||||
|
client.getConnection(function(err, connection) {
|
||||||
|
const conn = connection;
|
||||||
|
if (!err) {
|
||||||
|
if (self.debug)
|
||||||
|
debug('MySQL connection is established: ' + self.settings || {});
|
||||||
|
|
||||||
|
connection.release();
|
||||||
|
} else {
|
||||||
|
if (err.code == 'ECONNREFUSED' || err.code == 'PROTOCOL_CONNECTION_LOST') { // PROTOCOL_CONNECTION_LOST
|
||||||
|
console.error(`MySQL connection lost (${err.code}). Retrying..`);
|
||||||
|
|
||||||
|
return setTimeout(() =>
|
||||||
|
connectionHandler(options, callback), 5000);
|
||||||
|
}
|
||||||
|
if (self.debug || !callback)
|
||||||
|
console.error('MySQL connection is failed: ' + self.settings || {}, err);
|
||||||
|
}
|
||||||
|
callback && callback(err, conn);
|
||||||
|
});
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function generateOptions(settings) {
|
||||||
|
const s = settings || {};
|
||||||
|
if (s.collation) {
|
||||||
|
// Charset should be first 'chunk' of collation.
|
||||||
|
s.charset = s.collation.substr(0, s.collation.indexOf('_'));
|
||||||
|
} else {
|
||||||
|
s.collation = 'utf8_general_ci';
|
||||||
|
s.charset = 'utf8';
|
||||||
|
}
|
||||||
|
|
||||||
|
s.supportBigNumbers = (s.supportBigNumbers || false);
|
||||||
|
s.timezone = (s.timezone || 'local');
|
||||||
|
|
||||||
|
if (isNaN(s.connectionLimit))
|
||||||
|
s.connectionLimit = 10;
|
||||||
|
|
||||||
|
let options;
|
||||||
|
if (s.url) {
|
||||||
|
// use url to override other settings if url provided
|
||||||
|
options = s.url;
|
||||||
|
} else {
|
||||||
|
options = {
|
||||||
|
host: s.host || s.hostname || 'localhost',
|
||||||
|
port: s.port || 3306,
|
||||||
|
user: s.username || s.user,
|
||||||
|
password: s.password,
|
||||||
|
timezone: s.timezone,
|
||||||
|
socketPath: s.socketPath,
|
||||||
|
charset: s.collation.toUpperCase(), // Correct by docs despite seeming odd.
|
||||||
|
supportBigNumbers: s.supportBigNumbers,
|
||||||
|
connectionLimit: s.connectionLimit,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't configure the DB if the pool can be used for multiple DBs
|
||||||
|
if (!s.createDatabase)
|
||||||
|
options.database = s.database;
|
||||||
|
|
||||||
|
// Take other options for mysql driver
|
||||||
|
// See https://github.com/strongloop/loopback-connector-mysql/issues/46
|
||||||
|
for (const p in s) {
|
||||||
|
if (p === 'database' && s.createDatabase)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (options[p] === undefined)
|
||||||
|
options[p] = s[p];
|
||||||
|
}
|
||||||
|
// Legacy UTC Date Processing fallback - SHOULD BE TRANSITIONAL
|
||||||
|
if (s.legacyUtcDateProcessing === undefined)
|
||||||
|
s.legacyUtcDateProcessing = true;
|
||||||
|
|
||||||
|
if (s.legacyUtcDateProcessing)
|
||||||
|
options.timezone = 'Z';
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
|
@ -7,5 +7,8 @@
|
||||||
},
|
},
|
||||||
"EntryLog": {
|
"EntryLog": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"EntryObservation": {
|
||||||
|
"dataSource": "vn"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.rewriteDbError(function(err) {
|
||||||
|
if (err.code === 'ER_DUP_ENTRY')
|
||||||
|
return new UserError(`The observation type can't be repeated`);
|
||||||
|
return err;
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"name": "EntryObservation",
|
||||||
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model": "EntryLog",
|
||||||
|
"relation": "entry"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "entryObservation"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "Number",
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "String",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"entry": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Entry",
|
||||||
|
"foreignKey": "entryFk"
|
||||||
|
},
|
||||||
|
"observationType": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "ObservationType",
|
||||||
|
"foreignKey": "observationTypeFk",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import './latest-buys-search-panel';
|
||||||
import './descriptor';
|
import './descriptor';
|
||||||
import './descriptor-popover';
|
import './descriptor-popover';
|
||||||
import './card';
|
import './card';
|
||||||
|
import './note';
|
||||||
import './summary';
|
import './summary';
|
||||||
import './log';
|
import './log';
|
||||||
import './buy';
|
import './buy';
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
<vn-crud-model
|
||||||
|
vn-id="model"
|
||||||
|
url="EntryObservations"
|
||||||
|
fields="['id', 'entryFk', 'observationTypeFk', 'description']"
|
||||||
|
link="{entryFk: $ctrl.$params.id}"
|
||||||
|
data="observations"
|
||||||
|
auto-load="true">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-crud-model
|
||||||
|
url="ObservationTypes"
|
||||||
|
data="observationTypes"
|
||||||
|
auto-load="true">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-watcher
|
||||||
|
vn-id="watcher"
|
||||||
|
data="observations"
|
||||||
|
form="form">
|
||||||
|
</vn-watcher>
|
||||||
|
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
|
||||||
|
<vn-card class="vn-pa-lg">
|
||||||
|
<vn-one class="vn-mt-md">
|
||||||
|
<vn-horizontal ng-repeat="observation in observations track by $index">
|
||||||
|
<vn-autocomplete
|
||||||
|
ng-attr-disabled="observation.id ? true : false"
|
||||||
|
ng-model="observation.observationTypeFk"
|
||||||
|
data="observationTypes"
|
||||||
|
show-field="description"
|
||||||
|
label="Observation type"
|
||||||
|
vn-one
|
||||||
|
vn-focus>
|
||||||
|
</vn-autocomplete>
|
||||||
|
<vn-textfield
|
||||||
|
vn-two
|
||||||
|
class="vn-mr-lg"
|
||||||
|
label="Description"
|
||||||
|
ng-model="observation.description"
|
||||||
|
rule="EntryObservation">
|
||||||
|
</vn-textfield>
|
||||||
|
<vn-auto class="vn-pt-md">
|
||||||
|
<vn-icon-button
|
||||||
|
pointer
|
||||||
|
vn-tooltip="Remove note"
|
||||||
|
icon="delete"
|
||||||
|
ng-click="model.remove($index)">
|
||||||
|
</vn-icon-button>
|
||||||
|
</vn-auto>
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-one>
|
||||||
|
<vn-one>
|
||||||
|
<vn-icon-button
|
||||||
|
vn-tooltip="Add note"
|
||||||
|
icon="add_circle"
|
||||||
|
vn-bind="+"
|
||||||
|
ng-if="observationTypes.length > observations.length"
|
||||||
|
ng-click="model.insert()">
|
||||||
|
</vn-icon-button>
|
||||||
|
</vn-one>
|
||||||
|
</vn-card>
|
||||||
|
<vn-button-bar>
|
||||||
|
<vn-submit
|
||||||
|
disabled="!watcher.dataChanged()"
|
||||||
|
label="Save">
|
||||||
|
</vn-submit>
|
||||||
|
<!-- # #2680 Undo changes button bugs -->
|
||||||
|
<!-- <vn-button
|
||||||
|
class="cancel"
|
||||||
|
label="Undo changes"
|
||||||
|
disabled="!watcher.dataChanged()"
|
||||||
|
ng-click="watcher.loadOriginalData()">
|
||||||
|
</vn-button> -->
|
||||||
|
</vn-button-bar>
|
||||||
|
</form>
|
|
@ -0,0 +1,20 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
|
||||||
|
class Controller extends Section {
|
||||||
|
onSubmit() {
|
||||||
|
this.$.watcher.check();
|
||||||
|
this.$.model.save().then(() => {
|
||||||
|
this.$.watcher.notifySaved();
|
||||||
|
this.$.watcher.updateOriginalData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnEntryObservation', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
entry: '<'
|
||||||
|
}
|
||||||
|
});
|
|
@ -12,6 +12,7 @@
|
||||||
"card": [
|
"card": [
|
||||||
{"state": "entry.card.basicData", "icon": "settings"},
|
{"state": "entry.card.basicData", "icon": "settings"},
|
||||||
{"state": "entry.card.buy", "icon": "icon-lines"},
|
{"state": "entry.card.buy", "icon": "icon-lines"},
|
||||||
|
{"state": "entry.card.observation", "icon": "insert_drive_file"},
|
||||||
{"state": "entry.card.log", "icon": "history"}
|
{"state": "entry.card.log", "icon": "history"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -61,7 +62,15 @@
|
||||||
"params": {
|
"params": {
|
||||||
"entry": "$ctrl.entry"
|
"entry": "$ctrl.entry"
|
||||||
}
|
}
|
||||||
}, {
|
},{
|
||||||
|
"url": "/observation",
|
||||||
|
"state": "entry.card.observation",
|
||||||
|
"component": "vn-entry-observation",
|
||||||
|
"description": "Notes",
|
||||||
|
"params": {
|
||||||
|
"entry": "$ctrl.entry"
|
||||||
|
}
|
||||||
|
},{
|
||||||
"url" : "/log",
|
"url" : "/log",
|
||||||
"state": "entry.card.log",
|
"state": "entry.card.log",
|
||||||
"component": "vn-entry-log",
|
"component": "vn-entry-log",
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('clone', {
|
||||||
|
description: 'Clones the selected routes',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'ids',
|
||||||
|
type: ['number'],
|
||||||
|
required: true,
|
||||||
|
description: 'The routes ids to clone'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'started',
|
||||||
|
type: 'date',
|
||||||
|
required: true,
|
||||||
|
description: 'The started date for all routes'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: ['Object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/clone`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.clone = async(ids, started) => {
|
||||||
|
const tx = await Self.beginTransaction({});
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const originalRoutes = await Self.find({
|
||||||
|
where: {id: {inq: ids}},
|
||||||
|
fields: ['workerFk', 'agencyModeFk', 'vehicleFk', 'description']
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
if (ids.length != originalRoutes.length)
|
||||||
|
throw new Error(`The amount of routes found don't match`);
|
||||||
|
|
||||||
|
const routes = originalRoutes.map(route => {
|
||||||
|
route.started = started;
|
||||||
|
route.created = new Date();
|
||||||
|
return route;
|
||||||
|
});
|
||||||
|
|
||||||
|
const clones = await Self.create(routes, options);
|
||||||
|
|
||||||
|
await tx.commit();
|
||||||
|
return clones;
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,31 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
|
describe('route clone()', () => {
|
||||||
|
const startDate = new Date();
|
||||||
|
it('should throw an error if the amount of ids pased to the clone function do no match the database', async() => {
|
||||||
|
const ids = [996, 997, 998, 999];
|
||||||
|
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await app.models.Route.clone(ids, startDate);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toEqual(`The amount of routes found don't match`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clone two routes', async() => {
|
||||||
|
const ids = [1, 2];
|
||||||
|
|
||||||
|
const clones = await app.models.Route.clone(ids, startDate);
|
||||||
|
|
||||||
|
expect(clones.length).toEqual(2);
|
||||||
|
|
||||||
|
// restores
|
||||||
|
for (const clone of clones)
|
||||||
|
await app.models.Route.destroyById(clone.id);
|
||||||
|
});
|
||||||
|
});
|
|
@ -6,6 +6,7 @@ module.exports = Self => {
|
||||||
require('../methods/route/updateVolume')(Self);
|
require('../methods/route/updateVolume')(Self);
|
||||||
require('../methods/route/getDeliveryPoint')(Self);
|
require('../methods/route/getDeliveryPoint')(Self);
|
||||||
require('../methods/route/insertTicket')(Self);
|
require('../methods/route/insertTicket')(Self);
|
||||||
|
require('../methods/route/clone')(Self);
|
||||||
|
|
||||||
Self.validate('kmStart', validateDistance, {
|
Self.validate('kmStart', validateDistance, {
|
||||||
message: 'Distance must be lesser than 1000'
|
message: 'Distance must be lesser than 1000'
|
||||||
|
|
|
@ -3,3 +3,5 @@ Date started: Fecha inicio
|
||||||
Km start: Km de inicio
|
Km start: Km de inicio
|
||||||
Km end: Km de fin
|
Km end: Km de fin
|
||||||
Description: Descripción
|
Description: Descripción
|
||||||
|
Hour started: Hora inicio
|
||||||
|
Hour finished: Hora fin
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td>{{::route.agencyName | dashIfEmpty}}</vn-td>
|
<vn-td>{{::route.agencyName | dashIfEmpty}}</vn-td>
|
||||||
<vn-td>{{::route.vehiclePlateNumber | dashIfEmpty}}</vn-td>
|
<vn-td>{{::route.vehiclePlateNumber | dashIfEmpty}}</vn-td>
|
||||||
<vn-td expand>{{::route.created | date:'dd/MM/yyyy' | dashIfEmpty}}</vn-td>
|
<vn-td expand>{{::route.created | dashIfEmpty | date:'dd/MM/yyyy'}}</vn-td>
|
||||||
<vn-td number>{{::route.m3 | dashIfEmpty}}</vn-td>
|
<vn-td number>{{::route.m3 | dashIfEmpty}}</vn-td>
|
||||||
<vn-td>{{::route.description | dashIfEmpty}}</vn-td>
|
<vn-td>{{::route.description | dashIfEmpty}}</vn-td>
|
||||||
<vn-td>
|
<vn-td>
|
||||||
|
@ -59,19 +59,26 @@
|
||||||
</vn-table>
|
</vn-table>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
</vn-data-viewer>
|
</vn-data-viewer>
|
||||||
|
|
||||||
<vn-popup vn-id="summary">
|
<vn-popup vn-id="summary">
|
||||||
<vn-route-summary
|
<vn-route-summary
|
||||||
route="$ctrl.routeSelected">
|
route="$ctrl.routeSelected">
|
||||||
</vn-route-summary>
|
</vn-route-summary>
|
||||||
</vn-popup>
|
</vn-popup>
|
||||||
|
|
||||||
<vn-worker-descriptor-popover
|
<vn-worker-descriptor-popover
|
||||||
vn-id="workerDescriptor">
|
vn-id="workerDescriptor">
|
||||||
</vn-worker-descriptor-popover>
|
</vn-worker-descriptor-popover>
|
||||||
|
|
||||||
|
|
||||||
</vn-data-viewer>
|
|
||||||
<div fixed-bottom-right>
|
<div fixed-bottom-right>
|
||||||
<vn-vertical style="align-items: center;">
|
<vn-vertical style="align-items: center;">
|
||||||
|
<vn-button class="round sm vn-mb-sm"
|
||||||
|
icon="icon-clone"
|
||||||
|
ng-show="$ctrl.totalChecked > 0"
|
||||||
|
ng-click="$ctrl.openClonationDialog()"
|
||||||
|
vn-tooltip="Clone selected routes"
|
||||||
|
tooltip-position="left">
|
||||||
|
</vn-button>
|
||||||
<vn-button class="round sm vn-mb-sm"
|
<vn-button class="round sm vn-mb-sm"
|
||||||
icon="cloud_download"
|
icon="cloud_download"
|
||||||
ng-show="$ctrl.totalChecked > 0"
|
ng-show="$ctrl.totalChecked > 0"
|
||||||
|
@ -79,7 +86,6 @@
|
||||||
vn-tooltip="Download selected routes as PDF"
|
vn-tooltip="Download selected routes as PDF"
|
||||||
tooltip-position="left">
|
tooltip-position="left">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
|
|
||||||
<a ui-sref="route.create" vn-bind="+">
|
<a ui-sref="route.create" vn-bind="+">
|
||||||
<vn-button class="round md vn-mb-sm"
|
<vn-button class="round md vn-mb-sm"
|
||||||
icon="add"
|
icon="add"
|
||||||
|
@ -88,4 +94,24 @@
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</a>
|
</a>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Clonation dialog -->
|
||||||
|
<vn-dialog class="edit"
|
||||||
|
vn-id="clonationDialog"
|
||||||
|
on-accept="$ctrl.cloneSelectedRoutes()"
|
||||||
|
message="Select the starting date">
|
||||||
|
<tpl-body>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-date-picker
|
||||||
|
label="Starting date"
|
||||||
|
ng-model="$ctrl.startedDate"
|
||||||
|
required="true">
|
||||||
|
</vn-date-picker>
|
||||||
|
</vn-horizontal>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
|
<button response="accept" translate>Clone</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
|
@ -40,6 +40,22 @@ export default class Controller extends Section {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openClonationDialog() {
|
||||||
|
this.startedDate = new Date();
|
||||||
|
this.$.clonationDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
cloneSelectedRoutes() {
|
||||||
|
const routesIds = [];
|
||||||
|
for (let route of this.checked)
|
||||||
|
routesIds.push(route.id);
|
||||||
|
|
||||||
|
return this.$http.post('Routes/clone', {ids: routesIds, started: this.startedDate}).then(() => {
|
||||||
|
this.$.model.refresh();
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onDrop($event) {
|
onDrop($event) {
|
||||||
const target = $event.target;
|
const target = $event.target;
|
||||||
const droppable = target.closest(this.droppableElement);
|
const droppable = target.closest(this.droppableElement);
|
||||||
|
|
|
@ -60,6 +60,16 @@ describe('Component vnRouteIndex', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('cloneSelectedRoutes()', () => {
|
||||||
|
it('should perform an http request to Routes/clone', () => {
|
||||||
|
controller.startedDate = new Date();
|
||||||
|
|
||||||
|
$httpBackend.expect('POST', 'Routes/clone').respond();
|
||||||
|
controller.cloneSelectedRoutes();
|
||||||
|
$httpBackend.flush();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('onDrop()', () => {
|
describe('onDrop()', () => {
|
||||||
it('should call the insert method when dragging a ticket number', () => {
|
it('should call the insert method when dragging a ticket number', () => {
|
||||||
jest.spyOn(controller, 'insert');
|
jest.spyOn(controller, 'insert');
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
Vehicle: Vehículo
|
Vehicle: Vehículo
|
||||||
Download selected routes as PDF: Descargar rutas seleccionadas como PDF
|
Download selected routes as PDF: Descargar rutas seleccionadas como PDF
|
||||||
|
Clone selected routes: Clonar rutas seleccionadas
|
||||||
|
Select the starting date: Seleccione fecha de inicio
|
||||||
|
Starting date: Fecha de inicio
|
|
@ -2,15 +2,14 @@
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
url="Routes/filter"
|
url="Routes/filter"
|
||||||
limit="20"
|
limit="20"
|
||||||
order="created DESC"
|
order="created DESC">
|
||||||
auto-load="true">
|
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-portal slot="topbar">
|
<vn-portal slot="topbar">
|
||||||
<vn-searchbar
|
<vn-searchbar
|
||||||
vn-focus
|
vn-focus
|
||||||
panel="vn-route-search-panel"
|
panel="vn-route-search-panel"
|
||||||
info="Search routes by id"
|
info="Search routes by id"
|
||||||
filter="$ctrl.filter"
|
filter="$ctrl.filterParams"
|
||||||
model="model">
|
model="model">
|
||||||
</vn-searchbar>
|
</vn-searchbar>
|
||||||
</vn-portal>
|
</vn-portal>
|
||||||
|
|
|
@ -10,7 +10,8 @@ export default class Route extends ModuleMain {
|
||||||
let from = new Date();
|
let from = new Date();
|
||||||
from.setHours(0, 0, 0, 0);
|
from.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
this.filter = {from, to, warehouseFk: this.vnConfig.warehouseFk};
|
this.filterParams = {from, to, warehouseFk: this.vnConfig.warehouseFk};
|
||||||
|
this.$.model.applyFilter(null, this.filterParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
"ticket": {
|
"ticket": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Ticket",
|
"model": "Ticket",
|
||||||
"foreignKey": "ticketFk",
|
"foreignKey": "ticketFk"
|
||||||
"required": true
|
|
||||||
},
|
},
|
||||||
"observationType": {
|
"observationType": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
info="Search ticket by id or alias"
|
info="Search ticket by id or alias"
|
||||||
model="model"
|
model="model"
|
||||||
fetch-params="$ctrl.fetchParams($params)"
|
fetch-params="$ctrl.fetchParams($params)"
|
||||||
suggested-filter="$ctrl.defaultFilter">
|
suggested-filter="$ctrl.filterParams">
|
||||||
</vn-searchbar>
|
</vn-searchbar>
|
||||||
</vn-portal>
|
</vn-portal>
|
||||||
<vn-portal slot="menu">
|
<vn-portal slot="menu">
|
||||||
|
|
|
@ -5,7 +5,7 @@ export default class Ticket extends ModuleMain {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.defaultFilter = {
|
this.filterParams = {
|
||||||
scopeDays: 1
|
scopeDays: 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,9 @@
|
||||||
<vn-td>{{::tracking.state.name}}</vn-td>
|
<vn-td>{{::tracking.state.name}}</vn-td>
|
||||||
<vn-td expand>
|
<vn-td expand>
|
||||||
<span
|
<span
|
||||||
class="link"
|
ng-class="{'link': tracking.worker.id}"
|
||||||
ng-click="workerDescriptor.show($event, tracking.worker.user.id)">
|
ng-click="workerDescriptor.show($event, tracking.worker.user.id)">
|
||||||
{{::tracking.worker.user.name | dashIfEmpty}}
|
{{::tracking.worker.user.name || 'System' | translate}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td>{{::tracking.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
|
<vn-td>{{::tracking.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
panel="vn-travel-search-panel"
|
panel="vn-travel-search-panel"
|
||||||
info="Search travels by id"
|
info="Search travels by id"
|
||||||
model="model"
|
model="model"
|
||||||
fetch-params="$ctrl.fetchParams($params)">
|
fetch-params="$ctrl.fetchParams($params)"
|
||||||
|
suggested-filter="$ctrl.filterParams">
|
||||||
</vn-searchbar>
|
</vn-searchbar>
|
||||||
</vn-portal>
|
</vn-portal>
|
||||||
<vn-portal slot="menu">
|
<vn-portal slot="menu">
|
||||||
|
|
|
@ -2,6 +2,14 @@ import ngModule from '../module';
|
||||||
import ModuleMain from 'salix/components/module-main';
|
import ModuleMain from 'salix/components/module-main';
|
||||||
|
|
||||||
export default class Travel extends ModuleMain {
|
export default class Travel extends ModuleMain {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.filterParams = {
|
||||||
|
scopeDays: 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fetchParams($params) {
|
fetchParams($params) {
|
||||||
if (!Object.entries($params).length)
|
if (!Object.entries($params).length)
|
||||||
$params.scopeDays = 1;
|
$params.scopeDays = 1;
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
<span
|
<span
|
||||||
ng-class="{'link': log.user.worker.id, 'value': !log.user.worker.id}"
|
ng-class="{'link': log.user.worker.id, 'value': !log.user.worker.id}"
|
||||||
ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)"
|
ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)"
|
||||||
translate>{{::log.user.name | dashIfEmpty}}
|
translate>{{::log.user.name || 'System' | translate}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
<span
|
<span
|
||||||
ng-class="{'link': log.user.worker.id, 'value': !log.user.worker.id}"
|
ng-class="{'link': log.user.worker.id, 'value': !log.user.worker.id}"
|
||||||
ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)"
|
ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)"
|
||||||
translate>{{::log.user.name | dashIfEmpty}}
|
translate>{{::log.user.name || 'System' | translate}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td class="expendable">
|
<vn-td class="expendable">
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
<vn-one ng-repeat="old in log.oldProperties">
|
<vn-one ng-repeat="old in log.oldProperties">
|
||||||
<div>
|
<div>
|
||||||
<span translate class="label">{{::old.key}}</span><span class="label">: </span>
|
<span translate class="label">{{::old.key}}</span><span class="label">: </span>
|
||||||
<span translate class="value">{{::old.value}}</span>
|
<span translate class="value">{{::old.value | dashIfEmpty}}</span>
|
||||||
</div>
|
</div>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
id="newInstance">
|
id="newInstance">
|
||||||
<div>
|
<div>
|
||||||
<span translate class="label">{{::new.key}}</span><span class="label">: </span>
|
<span translate class="label">{{::new.key}}</span><span class="label">: </span>
|
||||||
<span translate class="value">{{::new.value}}</span>
|
<span translate class="value">{{::new.value | dashIfEmpty}}</span>
|
||||||
</div>
|
</div>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-one
|
<vn-one
|
||||||
|
|
|
@ -9,4 +9,5 @@ Name: Nombre
|
||||||
Creates: Crea
|
Creates: Crea
|
||||||
Updates: Actualiza
|
Updates: Actualiza
|
||||||
Deletes: Elimina
|
Deletes: Elimina
|
||||||
Views: Visualiza
|
Views: Visualiza
|
||||||
|
System: Sistema
|
|
@ -31,5 +31,8 @@
|
||||||
},
|
},
|
||||||
"ZoneEstimatedDelivery": {
|
"ZoneEstimatedDelivery": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"ZoneLog": {
|
||||||
|
"dataSource": "vn"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "ZoneEvent",
|
"name": "ZoneEvent",
|
||||||
"base": "VnModel",
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model":"ZoneLog",
|
||||||
|
"relation": "zone"
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "zoneEvent"
|
"table": "zoneEvent"
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "ZoneIncluded",
|
"name": "ZoneIncluded",
|
||||||
"base": "VnModel",
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model": "ZoneLog",
|
||||||
|
"relation": "zone",
|
||||||
|
"showField": "isIncluded"
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "zoneIncluded"
|
"table": "zoneIncluded"
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"name": "ZoneLog",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "zoneLog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "Number",
|
||||||
|
"forceId": false
|
||||||
|
},
|
||||||
|
"originFk": {
|
||||||
|
"type": "Number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"userFk": {
|
||||||
|
"type": "Number"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "String",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"changedModel": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"oldInstance": {
|
||||||
|
"type": "Object"
|
||||||
|
},
|
||||||
|
"newInstance": {
|
||||||
|
"type": "Object"
|
||||||
|
},
|
||||||
|
"creationDate": {
|
||||||
|
"type": "Date"
|
||||||
|
},
|
||||||
|
"changedModelId": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"changedModelValue": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"user": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Account",
|
||||||
|
"foreignKey": "userFk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"order": ["creationDate DESC", "id DESC"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "ZoneWarehouse",
|
"name": "ZoneWarehouse",
|
||||||
"base": "VnModel",
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model":"ZoneLog",
|
||||||
|
"relation": "zone"
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "zoneWarehouse"
|
"table": "zoneWarehouse"
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
{
|
{
|
||||||
"name": "Zone",
|
"name": "Zone",
|
||||||
"base": "VnModel",
|
"base": "Loggable",
|
||||||
|
"log": {
|
||||||
|
"model":"ZoneLog",
|
||||||
|
"showField": "name"
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "zone"
|
"table": "zone"
|
||||||
|
@ -39,11 +43,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
"geolocations": {
|
|
||||||
"type": "hasMany",
|
|
||||||
"model": "ZoneGeo",
|
|
||||||
"foreignKey": "zoneFk"
|
|
||||||
},
|
|
||||||
"agencyMode": {
|
"agencyMode": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "AgencyMode",
|
"model": "AgencyMode",
|
||||||
|
|
|
@ -16,3 +16,4 @@ import './calendar';
|
||||||
import './location';
|
import './location';
|
||||||
import './calendar';
|
import './calendar';
|
||||||
import './upcoming-deliveries';
|
import './upcoming-deliveries';
|
||||||
|
import './log';
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<vn-log url="ZoneLogs" origin-id="$ctrl.$params.id"></vn-log>
|
|
@ -0,0 +1,7 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnZoneLog', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Section,
|
||||||
|
});
|
|
@ -14,6 +14,7 @@
|
||||||
{"state": "zone.card.basicData", "icon": "settings"},
|
{"state": "zone.card.basicData", "icon": "settings"},
|
||||||
{"state": "zone.card.location", "icon": "my_location"},
|
{"state": "zone.card.location", "icon": "my_location"},
|
||||||
{"state": "zone.card.warehouses", "icon": "home"},
|
{"state": "zone.card.warehouses", "icon": "home"},
|
||||||
|
{"state": "zone.card.log", "icon": "history"},
|
||||||
{"state": "zone.card.events", "icon": "today"}
|
{"state": "zone.card.events", "icon": "today"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -84,6 +85,11 @@
|
||||||
"params": {
|
"params": {
|
||||||
"zone": "$ctrl.zone"
|
"zone": "$ctrl.zone"
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
"url" : "/log",
|
||||||
|
"state": "zone.card.log",
|
||||||
|
"component": "vn-zone-log",
|
||||||
|
"description": "Log"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -151,6 +151,7 @@ module.exports = app => {
|
||||||
SELECT
|
SELECT
|
||||||
t.id,
|
t.id,
|
||||||
t.clientFk,
|
t.clientFk,
|
||||||
|
c.name clientName,
|
||||||
c.email recipient,
|
c.email recipient,
|
||||||
c.salesPersonFk,
|
c.salesPersonFk,
|
||||||
c.isToBeMailed,
|
c.isToBeMailed,
|
||||||
|
@ -196,6 +197,10 @@ module.exports = app => {
|
||||||
const email = new Email('delivery-note-link', args);
|
const email = new Email('delivery-note-link', args);
|
||||||
await email.send();
|
await email.send();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// Domain not found
|
||||||
|
if (error.responseCode == 450)
|
||||||
|
return invalidEmail(ticket);
|
||||||
|
|
||||||
// Save tickets on a list of failed ids
|
// Save tickets on a list of failed ids
|
||||||
failedtickets.push({
|
failedtickets.push({
|
||||||
id: ticket.id,
|
id: ticket.id,
|
||||||
|
@ -220,4 +225,33 @@ module.exports = app => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function invalidEmail(ticket) {
|
||||||
|
await db.rawSql(`UPDATE client SET email = NULL WHERE id = :clientId`, {
|
||||||
|
clientId: ticket.clientFk
|
||||||
|
});
|
||||||
|
|
||||||
|
const oldInstance = `{"email": "${ticket.recipient}"}`;
|
||||||
|
const newInstance = `{"email": ""}`;
|
||||||
|
await db.rawSql(`
|
||||||
|
INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance)
|
||||||
|
VALUES (:clientId, :userId, 'UPDATE', 'Client', :oldInstance, :newInstance)`, {
|
||||||
|
clientId: ticket.clientFk,
|
||||||
|
userId: null,
|
||||||
|
oldInstance: oldInstance,
|
||||||
|
newInstance: newInstance
|
||||||
|
});
|
||||||
|
|
||||||
|
const body = `No se ha podido enviar el albarán <strong>${ticket.id}</strong>
|
||||||
|
al cliente <strong>${ticket.clientFk} - ${ticket.clientName}</strong>
|
||||||
|
porque la dirección de email <strong>"${ticket.recipient}"</strong> no es correcta o no está disponible.<br/><br/>
|
||||||
|
Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente.
|
||||||
|
Actualiza la dirección de email con una correcta.`;
|
||||||
|
|
||||||
|
smtp.send({
|
||||||
|
to: ticket.salesPersonEmail,
|
||||||
|
subject: 'No se ha podido enviar el albarán',
|
||||||
|
html: body
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue