2661 - Added clone options
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Joan Sanchez 2020-12-28 14:53:26 +01:00
parent c192a0e125
commit 910caf5689
14 changed files with 355 additions and 11 deletions

View File

@ -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 ;

View File

@ -33,6 +33,9 @@ exports.translateValues = async(instance, changes) => {
}).format(date); }).format(date);
} }
if (changes instanceof instance)
changes = changes.__data;
const properties = Object.assign({}, changes); const properties = Object.assign({}, changes);
for (let property in properties) { for (let property in properties) {
const relation = getRelation(instance, property); const relation = getRelation(instance, property);
@ -41,13 +44,14 @@ exports.translateValues = async(instance, changes) => {
if (relation) { if (relation) {
let fieldsToShow = ['alias', 'name', 'code', 'description']; 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) if (log && log.showField)
fieldsToShow = log.showField; fieldsToShow = [log.showField];
const model = relation.model; const row = await model.findById(value, {
const row = await models[model].findById(value, {
fields: fieldsToShow fields: fieldsToShow
}); });
const newValue = getValue(row); const newValue = getValue(row);

View File

@ -130,7 +130,7 @@ module.exports = Self => {
let logRecord = { let logRecord = {
originFk: cleanInstance.id, originFk: cleanInstance.id,
userFk: myUserId, userFk: myUserId,
action: 'create', action: 'insert',
changedModel: 'Ticket', changedModel: 'Ticket',
changedModelId: cleanInstance.id, changedModelId: cleanInstance.id,
oldInstance: {}, oldInstance: {},

View File

@ -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;
};
};

View File

@ -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);
});
});

View File

@ -8,6 +8,7 @@ module.exports = Self => {
require('../methods/travel/deleteThermograph')(Self); require('../methods/travel/deleteThermograph')(Self);
require('../methods/travel/updateThermograph')(Self); require('../methods/travel/updateThermograph')(Self);
require('../methods/travel/extraCommunityFilter')(Self); require('../methods/travel/extraCommunityFilter')(Self);
require('../methods/travel/cloneWithEntries')(Self);
Self.rewriteDbError(function(err) { Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY') if (err.code === 'ER_DUP_ENTRY')

View File

@ -9,7 +9,7 @@ class Controller extends Section {
onSubmit() { onSubmit() {
return this.$.watcher.submit().then( 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})
); );
} }
} }

View File

@ -22,7 +22,7 @@ describe('Travel Component vnTravelCreate', () => {
controller.onSubmit(); 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', () => {
}); });
}); });
}); });

View File

@ -10,6 +10,12 @@
translate> translate>
Clone travel Clone travel
</vn-item> </vn-item>
<vn-item
id="cloneWithEntries"
ng-click="cloneWithEntries.show()"
translate>
Clone travel and his entries
</vn-item>
</vn-list> </vn-list>
</vn-menu> </vn-menu>
@ -20,3 +26,11 @@
question="Do you want to clone this travel?" question="Do you want to clone this travel?"
message="All it's properties will be copied"> message="All it's properties will be copied">
</vn-confirm> </vn-confirm>
<!-- Clone travel popup -->
<vn-confirm
vn-id="cloneWithEntries"
on-accept="$ctrl.onCloneWithEntriesAccept()"
question="Do you want to clone this travel and all containing entries?"
message="All it's properties will be copied">
</vn-confirm>

View File

@ -59,6 +59,11 @@ class Controller extends Section {
}); });
this.$state.go('travel.create', {q: params}); 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']; Controller.$inject = ['$element', '$scope'];

View File

@ -2,11 +2,14 @@ import './index.js';
describe('Travel Component vnTravelDescriptorMenu', () => { describe('Travel Component vnTravelDescriptorMenu', () => {
let controller; let controller;
let $httpBackend;
beforeEach(ngModule('travel')); beforeEach(ngModule('travel'));
beforeEach(inject(($componentController, $state,) => { beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-travel-descriptor-menu></vn-travel-descriptor-menu>'); const $element = angular.element('<vn-travel-descriptor-menu></vn-travel-descriptor-menu>');
controller = $componentController('vnTravelDescriptorMenu', {$element}); controller = $componentController('vnTravelDescriptorMenu', {$element});
controller._travelId = 5;
})); }));
describe('onCloneAccept()', () => { describe('onCloneAccept()', () => {
@ -36,4 +39,18 @@ describe('Travel Component vnTravelDescriptorMenu', () => {
expect(controller.$state.go).toHaveBeenCalledWith('travel.create', {'q': params}); 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)
});
});
});
}); });

View File

@ -1 +1,3 @@
Clone travel: Clonar envío 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?

View File

@ -13,7 +13,7 @@ Received: Recibido
Travel id: Id envío Travel id: Id envío
Search travels by id: Buscar envíos por identificador Search travels by id: Buscar envíos por identificador
New travel: Nuevo envío New travel: Nuevo envío
travel: envio travel: envío
# Sections # Sections
Travels: Envíos Travels: Envíos

View File

@ -7,6 +7,7 @@
<vn-icon-button icon="launch"></vn-icon-button> <vn-icon-button icon="launch"></vn-icon-button>
</a> </a>
<span>{{$ctrl.travelData.id}} - {{$ctrl.travelData.ref}}</span> <span>{{$ctrl.travelData.id}} - {{$ctrl.travelData.ref}}</span>
<vn-travel-descriptor-menu travel-id="$ctrl.travel.id"/>
</h5> </h5>
<vn-horizontal> <vn-horizontal>
<vn-one> <vn-one>