2661 - Added clone options
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
parent
c192a0e125
commit
910caf5689
|
@ -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 ;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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: {},
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -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')
|
||||||
|
|
|
@ -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})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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'];
|
||||||
|
|
|
@ -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)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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?
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue