diff --git a/db/changes/10260-holidays/00-travel_cloneWithEntries.sql b/db/changes/10260-holidays/00-travel_cloneWithEntries.sql
new file mode 100644
index 000000000..1302a78ca
--- /dev/null
+++ b/db/changes/10260-holidays/00-travel_cloneWithEntries.sql
@@ -0,0 +1,135 @@
+-- DROP PROCEDURE `vn`.`clonTravelComplete`;
+
+DELIMITER $$
+USE `vn`$$
+CREATE
+ DEFINER = root@`%` PROCEDURE `vn`.`travel_cloneWithEntries`(IN vTravelFk INT, IN vDateStart DATE, IN vDateEnd DATE,
+ IN vRef VARCHAR(255), OUT vNewTravelFk INT)
+BEGIN
+ DECLARE vEntryNew INT;
+ DECLARE vDone BOOLEAN DEFAULT FALSE;
+ DECLARE vAuxEntryFk INT;
+ DECLARE vRsEntry CURSOR FOR
+ SELECT e.id
+ FROM entry e
+ JOIN travel t
+ ON t.id = e.travelFk
+ WHERE e.travelFk = vTravelFk;
+
+ DECLARE vRsBuy CURSOR FOR
+ SELECT b.*
+ FROM buy b
+ JOIN entry e
+ ON b.entryFk = e.id
+ WHERE e.travelFk = vNewTravelFk and b.entryFk=vNewTravelFk
+ ORDER BY e.id;
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
+
+ DECLARE EXIT HANDLER FOR SQLEXCEPTION
+ BEGIN
+ ROLLBACK;
+ RESIGNAL;
+ END;
+
+ START TRANSACTION;
+
+ INSERT INTO travel (shipped,landed, warehouseInFk, warehouseOutFk, agencyFk, ref, isDelivered, isReceived, m3, kg)
+ SELECT vDateStart, vDateEnd,warehouseInFk, warehouseOutFk, agencyFk, vRef, isDelivered, isReceived, m3, kg
+ FROM travel
+ WHERE id = vTravelFk;
+
+ SET vNewTravelFk = LAST_INSERT_ID();
+ SET vDone = FALSE;
+ OPEN vRsEntry ;
+ FETCH vRsEntry INTO vAuxEntryFk;
+
+ WHILE NOT vDone DO
+ INSERT INTO entry (supplierFk,
+ ref,
+ isInventory,
+ isConfirmed,
+ isOrdered,
+ isRaid,
+ commission,
+ created,
+ evaNotes,
+ travelFk,
+ currencyFk,
+ companyFk,
+ gestDocFk,
+ invoiceInFk)
+ SELECT supplierFk,
+ ref,
+ isInventory,
+ isConfirmed,
+ isOrdered,
+ isRaid,
+ commission,
+ created,
+ evaNotes,
+ vNewTravelFk,
+ currencyFk,
+ companyFk,
+ gestDocFk,
+ invoiceInFk
+ FROM entry
+ WHERE id = vAuxEntryFk;
+
+ SET vEntryNew = LAST_INSERT_ID();
+
+
+ INSERT INTO buy (entryFk,
+ itemFk,
+ quantity,
+ buyingValue,
+ packageFk,
+ stickers,
+ freightValue,
+ packageValue,
+ comissionValue,
+ packing,
+ `grouping`,
+ groupingMode,
+ location,
+ price1,
+ price2,
+ price3,
+ minPrice,
+ producer,
+ printedStickers,
+ isChecked,
+ weight)
+ SELECT vEntryNew,
+ itemFk,
+ quantity,
+ buyingValue,
+ packageFk,
+ stickers,
+ freightValue,
+ packageValue,
+ comissionValue,
+ packing,
+ `grouping`,
+ groupingMode,
+ location,
+ price1,
+ price2,
+ price3,
+ minPrice,
+ producer,
+ printedStickers,
+ isChecked,
+ weight
+ FROM buy
+ WHERE entryFk = vAuxEntryFk;
+
+
+ FETCH vRsEntry INTO vAuxEntryFk;
+ END WHILE;
+ CLOSE vRsEntry;
+ COMMIT;
+END;$$
+DELIMITER ;
+
+
diff --git a/loopback/util/log.js b/loopback/util/log.js
index baba3e827..d81fc39a0 100644
--- a/loopback/util/log.js
+++ b/loopback/util/log.js
@@ -33,6 +33,9 @@ exports.translateValues = async(instance, changes) => {
}).format(date);
}
+ if (changes instanceof instance)
+ changes = changes.__data;
+
const properties = Object.assign({}, changes);
for (let property in properties) {
const relation = getRelation(instance, property);
@@ -41,13 +44,14 @@ exports.translateValues = async(instance, changes) => {
if (relation) {
let fieldsToShow = ['alias', 'name', 'code', 'description'];
- const log = instance.definition.settings.log;
+ const modelName = relation.model;
+ const model = models[modelName];
+ const log = model.definition.settings.log;
if (log && log.showField)
- fieldsToShow = log.showField;
+ fieldsToShow = [log.showField];
- const model = relation.model;
- const row = await models[model].findById(value, {
+ const row = await model.findById(value, {
fields: fieldsToShow
});
const newValue = getValue(row);
diff --git a/modules/ticket/back/methods/ticket/new.js b/modules/ticket/back/methods/ticket/new.js
index 2763f1bd0..8bafe5403 100644
--- a/modules/ticket/back/methods/ticket/new.js
+++ b/modules/ticket/back/methods/ticket/new.js
@@ -130,7 +130,7 @@ module.exports = Self => {
let logRecord = {
originFk: cleanInstance.id,
userFk: myUserId,
- action: 'create',
+ action: 'insert',
changedModel: 'Ticket',
changedModelId: cleanInstance.id,
oldInstance: {},
diff --git a/modules/travel/back/methods/travel/cloneWithEntries.js b/modules/travel/back/methods/travel/cloneWithEntries.js
new file mode 100644
index 000000000..82d6b40e2
--- /dev/null
+++ b/modules/travel/back/methods/travel/cloneWithEntries.js
@@ -0,0 +1,87 @@
+const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
+const UserError = require('vn-loopback/util/user-error');
+const loggable = require('vn-loopback/util/log');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('cloneWithEntries', {
+ description: 'Clone travel',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'id',
+ type: 'number',
+ required: true,
+ description: 'The original travel id',
+ http: {source: 'path'}
+ }],
+ returns: {
+ type: 'Object',
+ description: 'The new cloned travel id',
+ root: true,
+ },
+ http: {
+ path: `/:id/cloneWithEntries`,
+ verb: 'post'
+ }
+ });
+
+ Self.cloneWithEntries = async(ctx, id) => {
+ const userId = ctx.req.accessToken.userId;
+ const conn = Self.dataSource.connector;
+ const models = Self.app.models;
+ const travel = await Self.findById(id, {
+ fields: [
+ 'id',
+ 'shipped',
+ 'landed',
+ 'warehouseInFk',
+ 'warehouseOutFk',
+ 'agencyFk',
+ 'ref'
+ ]
+ });
+ const started = new Date();
+ const ended = new Date();
+
+ if (!travel)
+ throw new UserError('Travel not found');
+
+ let stmts = [];
+ let stmt;
+
+ stmt = new ParameterizedSQL(
+ `CALL travel_cloneWithEntries(?, ?, ?, ?, @vTravelFk)`, [
+ id, started, ended, travel.ref]);
+
+ stmts.push(stmt);
+ const index = stmts.push('SELECT @vTravelFk AS id') - 1;
+
+ const sql = ParameterizedSQL.join(stmts, ';');
+ const result = await conn.executeStmt(sql);
+ const [lastInsert] = result[index];
+ const newTravel = await Self.findById(lastInsert.id, {
+ fields: [
+ 'id',
+ 'shipped',
+ 'landed',
+ 'warehouseInFk',
+ 'warehouseOutFk',
+ 'agencyFk',
+ 'ref'
+ ]
+ });
+
+ const oldProperties = await loggable.translateValues(Self, travel);
+ const newProperties = await loggable.translateValues(Self, newTravel);
+ await models.TravelLog.create({
+ originFk: newTravel.id,
+ userFk: userId,
+ action: 'insert',
+ changedModel: 'Travel',
+ changedModelId: newTravel.id,
+ oldInstance: oldProperties,
+ newInstance: newProperties
+ });
+
+ return newTravel.id;
+ };
+};
diff --git a/modules/travel/back/methods/travel/specs/cloneWithEntries.spec.js b/modules/travel/back/methods/travel/specs/cloneWithEntries.spec.js
new file mode 100644
index 000000000..ab7d3aa1d
--- /dev/null
+++ b/modules/travel/back/methods/travel/specs/cloneWithEntries.spec.js
@@ -0,0 +1,79 @@
+const app = require('vn-loopback/server/server');
+
+// #2687 - Cannot make a data rollback because of the triggers
+xdescribe('Travel cloneWithEntries()', () => {
+ const models = app.models;
+ const travelId = 5;
+ const currentUserId = 102;
+ const ctx = {req: {accessToken: {userId: currentUserId}}};
+ let travelBefore;
+ let newTravelId;
+
+ afterAll(async done => {
+ try {
+ const entries = await models.Entry.find({
+ where: {
+ travelFk: newTravelId
+ }
+ });
+ const entriesId = entries.map(entry => entry.id);
+
+ // Destroy all entries buys
+ await models.Buy.destroyAll({
+ where: {
+ entryFk: {inq: entriesId}
+ }
+ });
+
+ // Destroy travel entries
+ await models.Entry.destroyAll({
+ where: {
+ travelFk: newTravelId
+ }
+ });
+
+ // Destroy new travel
+ await models.Travel.destroyById(newTravelId);
+
+ // Restore original travel shipped & landed
+ const travel = await models.Travel.findById(travelId);
+ await travel.updateAttributes({
+ shipped: travelBefore.shipped,
+ landed: travelBefore.landed
+ });
+ } catch (error) {
+ console.error(error);
+ }
+
+ done();
+ });
+
+ it(`should clone the travel and the containing entries`, async() => {
+ const warehouseThree = 3;
+ const agencyModeOne = 1;
+ const yesterday = new Date();
+ yesterday.setDate(yesterday.getDate() - 1);
+
+ travelBefore = await models.Travel.findById(travelId);
+ await travelBefore.updateAttributes({
+ shipped: yesterday,
+ landed: yesterday
+ });
+
+ newTravelId = await models.Travel.cloneWithEntries(ctx, travelId);
+ const travelEntries = await models.Entry.find({
+ where: {
+ travelFk: newTravelId
+ }
+ });
+
+ const newTravel = await models.Travel.findById(travelId);
+
+ expect(newTravelId).not.toEqual(travelId);
+ expect(newTravel.ref).toEqual('fifth travel');
+ expect(newTravel.warehouseInFk).toEqual(warehouseThree);
+ expect(newTravel.warehouseOutFk).toEqual(warehouseThree);
+ expect(newTravel.agencyFk).toEqual(agencyModeOne);
+ expect(travelEntries.length).toBeGreaterThan(0);
+ });
+});
diff --git a/modules/travel/back/models/travel.js b/modules/travel/back/models/travel.js
index b8a1a24b3..46d33b305 100644
--- a/modules/travel/back/models/travel.js
+++ b/modules/travel/back/models/travel.js
@@ -8,6 +8,7 @@ module.exports = Self => {
require('../methods/travel/deleteThermograph')(Self);
require('../methods/travel/updateThermograph')(Self);
require('../methods/travel/extraCommunityFilter')(Self);
+ require('../methods/travel/cloneWithEntries')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
diff --git a/modules/travel/front/create/index.js b/modules/travel/front/create/index.js
index 7d0020034..9a9c5ce9d 100644
--- a/modules/travel/front/create/index.js
+++ b/modules/travel/front/create/index.js
@@ -9,7 +9,7 @@ class Controller extends Section {
onSubmit() {
return this.$.watcher.submit().then(
- res => this.$state.go('travel.card.summary', {id: res.data.id})
+ res => this.$state.go('travel.card.basicData', {id: res.data.id})
);
}
}
diff --git a/modules/travel/front/create/index.spec.js b/modules/travel/front/create/index.spec.js
index 4bde7747e..99f52b322 100644
--- a/modules/travel/front/create/index.spec.js
+++ b/modules/travel/front/create/index.spec.js
@@ -22,7 +22,7 @@ describe('Travel Component vnTravelCreate', () => {
controller.onSubmit();
- expect(controller.$state.go).toHaveBeenCalledWith('travel.card.summary', {id: 1234});
+ expect(controller.$state.go).toHaveBeenCalledWith('travel.card.basicData', {id: 1234});
});
});
@@ -39,4 +39,3 @@ describe('Travel Component vnTravelCreate', () => {
});
});
});
-
diff --git a/modules/travel/front/descriptor-menu/index.html b/modules/travel/front/descriptor-menu/index.html
index 1eb558008..714d3ce3f 100644
--- a/modules/travel/front/descriptor-menu/index.html
+++ b/modules/travel/front/descriptor-menu/index.html
@@ -10,6 +10,12 @@
translate>
Clone travel
+
+ Clone travel and his entries
+
@@ -20,3 +26,11 @@
question="Do you want to clone this travel?"
message="All it's properties will be copied">
+
+
+
+
diff --git a/modules/travel/front/descriptor-menu/index.js b/modules/travel/front/descriptor-menu/index.js
index 975cd9134..e83a90b97 100644
--- a/modules/travel/front/descriptor-menu/index.js
+++ b/modules/travel/front/descriptor-menu/index.js
@@ -59,6 +59,11 @@ class Controller extends Section {
});
this.$state.go('travel.create', {q: params});
}
+
+ onCloneWithEntriesAccept() {
+ this.$http.post(`Travels/${this.travelId}/cloneWithEntries`)
+ .then(res => this.$state.go('travel.card.basicData', {id: res.data}));
+ }
}
Controller.$inject = ['$element', '$scope'];
diff --git a/modules/travel/front/descriptor-menu/index.spec.js b/modules/travel/front/descriptor-menu/index.spec.js
index d66f3a435..3d94a0963 100644
--- a/modules/travel/front/descriptor-menu/index.spec.js
+++ b/modules/travel/front/descriptor-menu/index.spec.js
@@ -2,11 +2,14 @@ import './index.js';
describe('Travel Component vnTravelDescriptorMenu', () => {
let controller;
+ let $httpBackend;
beforeEach(ngModule('travel'));
- beforeEach(inject(($componentController, $state,) => {
+ beforeEach(inject(($componentController, _$httpBackend_) => {
+ $httpBackend = _$httpBackend_;
const $element = angular.element('');
controller = $componentController('vnTravelDescriptorMenu', {$element});
+ controller._travelId = 5;
}));
describe('onCloneAccept()', () => {
@@ -36,4 +39,18 @@ describe('Travel Component vnTravelDescriptorMenu', () => {
expect(controller.$state.go).toHaveBeenCalledWith('travel.create', {'q': params});
});
});
+
+ describe('onCloneWithEntriesAccept()', () => {
+ it('should make an HTTP query and then call to the $state.go method with the returned id', () => {
+ jest.spyOn(controller.$state, 'go').mockReturnValue('ok');
+
+ $httpBackend.expect('POST', `Travels/${controller.travelId}/cloneWithEntries`).respond(200, 9);
+ controller.onCloneWithEntriesAccept();
+ $httpBackend.flush();
+
+ expect(controller.$state.go).toHaveBeenCalledWith('travel.card.basicData', {
+ id: jasmine.any(Number)
+ });
+ });
+ });
});
diff --git a/modules/travel/front/descriptor-menu/locale/es.yml b/modules/travel/front/descriptor-menu/locale/es.yml
index 117611660..ca61c4e01 100644
--- a/modules/travel/front/descriptor-menu/locale/es.yml
+++ b/modules/travel/front/descriptor-menu/locale/es.yml
@@ -1 +1,3 @@
-Clone travel: Clonar envío
\ No newline at end of file
+Clone travel: Clonar envío
+Clone travel and his entries: Clonar travel y sus entradas
+Do you want to clone this travel and all containing entries?: ¿Quieres clonar este travel y todas las entradas que contiene?
\ No newline at end of file
diff --git a/modules/travel/front/locale/es.yml b/modules/travel/front/locale/es.yml
index 06fc80601..7231d37cd 100644
--- a/modules/travel/front/locale/es.yml
+++ b/modules/travel/front/locale/es.yml
@@ -13,7 +13,7 @@ Received: Recibido
Travel id: Id envío
Search travels by id: Buscar envíos por identificador
New travel: Nuevo envío
-travel: envio
+travel: envío
# Sections
Travels: Envíos
diff --git a/modules/travel/front/summary/index.html b/modules/travel/front/summary/index.html
index 8815c09e2..de6f6e979 100644
--- a/modules/travel/front/summary/index.html
+++ b/modules/travel/front/summary/index.html
@@ -7,6 +7,7 @@
{{$ctrl.travelData.id}} - {{$ctrl.travelData.ref}}
+