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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -130,7 +130,7 @@ module.exports = Self => {
|
|||
let logRecord = {
|
||||
originFk: cleanInstance.id,
|
||||
userFk: myUserId,
|
||||
action: 'create',
|
||||
action: 'insert',
|
||||
changedModel: 'Ticket',
|
||||
changedModelId: cleanInstance.id,
|
||||
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/updateThermograph')(Self);
|
||||
require('../methods/travel/extraCommunityFilter')(Self);
|
||||
require('../methods/travel/cloneWithEntries')(Self);
|
||||
|
||||
Self.rewriteDbError(function(err) {
|
||||
if (err.code === 'ER_DUP_ENTRY')
|
||||
|
|
|
@ -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})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -10,6 +10,12 @@
|
|||
translate>
|
||||
Clone travel
|
||||
</vn-item>
|
||||
<vn-item
|
||||
id="cloneWithEntries"
|
||||
ng-click="cloneWithEntries.show()"
|
||||
translate>
|
||||
Clone travel and his entries
|
||||
</vn-item>
|
||||
</vn-list>
|
||||
</vn-menu>
|
||||
|
||||
|
@ -20,3 +26,11 @@
|
|||
question="Do you want to clone this travel?"
|
||||
message="All it's properties will be copied">
|
||||
</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});
|
||||
}
|
||||
|
||||
onCloneWithEntriesAccept() {
|
||||
this.$http.post(`Travels/${this.travelId}/cloneWithEntries`)
|
||||
.then(res => this.$state.go('travel.card.basicData', {id: res.data}));
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$element', '$scope'];
|
||||
|
|
|
@ -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('<vn-travel-descriptor-menu></vn-travel-descriptor-menu>');
|
||||
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)
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
Search travels by id: Buscar envíos por identificador
|
||||
New travel: Nuevo envío
|
||||
travel: envio
|
||||
travel: envío
|
||||
|
||||
# Sections
|
||||
Travels: Envíos
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<vn-icon-button icon="launch"></vn-icon-button>
|
||||
</a>
|
||||
<span>{{$ctrl.travelData.id}} - {{$ctrl.travelData.ref}}</span>
|
||||
<vn-travel-descriptor-menu travel-id="$ctrl.travel.id"/>
|
||||
</h5>
|
||||
<vn-horizontal>
|
||||
<vn-one>
|
||||
|
|
Loading…
Reference in New Issue