Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2685-create_test_e2e_entryObservation

This commit is contained in:
Carlos Jimenez Ruiz 2020-12-31 16:13:40 +01:00
commit b843914cec
46 changed files with 596 additions and 65 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

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

View File

@ -831,7 +831,8 @@ export default {
firstSearchResult: 'vn-travel-index vn-tbody > a:nth-child(1)'
},
travelExtraCommunity: {
firstTravelReference: 'vn-travel-extra-community > vn-data-viewer div > vn-tbody > vn-tr > vn-td-editable',
anySearchResult: 'vn-travel-extra-community > vn-data-viewer div > vn-tbody > vn-tr',
firstTravelReference: 'vn-travel-extra-community vn-card:nth-child(1) vn-td-editable',
removeContinentFilter: 'vn-searchbar > form > vn-textfield > div.container > div.prepend > prepend > div > span:nth-child(3) > vn-icon > i'
},
travelBasicData: {
@ -863,7 +864,18 @@ export default {
travelDescriptor: {
filterByAgencyButton: 'vn-descriptor-content .quicklinks > div:nth-child(1) > vn-quick-link > a[vn-tooltip="All travels with current agency"]',
dotMenu: 'vn-travel-descriptor vn-icon-button[icon="more_vert"]',
dotMenuClone: '#clone'
dotMenuClone: '#clone',
dotMenuCloneWithEntries: '#cloneWithEntries',
acceptClonation: 'tpl-buttons > button[response="accept"]'
},
travelCreate: {
reference: 'vn-travel-create vn-textfield[ng-model="$ctrl.travel.ref"]',
agency: 'vn-travel-create vn-autocomplete[ng-model="$ctrl.travel.agencyModeFk"]',
shipped: 'vn-travel-create vn-date-picker[ng-model="$ctrl.travel.shipped"]',
landed: 'vn-travel-create vn-date-picker[ng-model="$ctrl.travel.landed"]',
warehouseOut: 'vn-travel-create vn-autocomplete[ng-model="$ctrl.travel.warehouseOutFk"]',
warehouseIn: 'vn-travel-create vn-autocomplete[ng-model="$ctrl.travel.warehouseInFk"]',
saveButton: 'vn-travel-create vn-submit[label="Save"]'
},
zoneIndex: {
searchResult: 'vn-zone-index a.vn-tr',

View File

@ -200,7 +200,7 @@ describe('Client Edit fiscalData path', () => {
it('should confirm the sageTransaction have been edited', async() => {
const result = await page.waitToGetProperty(selectors.clientFiscalData.sageTransaction, 'value');
expect(result).toEqual('Regularización de inversiones');
expect(result).toEqual('36: Regularización de inversiones');
});
it('should confirm the transferor have been edited', async() => {

View File

@ -1,7 +1,7 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('claim Summary path', () => {
describe('Claim summary path', () => {
let browser;
let page;
const claimId = '4';

View File

@ -1,7 +1,7 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('claim Descriptor path', () => {
describe('Claim descriptor path', () => {
let browser;
let page;
const claimId = '1';

View File

@ -74,6 +74,7 @@ describe('Route create path', () => {
});
it(`should clone the first route`, async() => {
await page.waitForTimeout(1000); // needs time for the index to show all items
await page.waitToClick(selectors.routeIndex.firstRouteCheckbox);
await page.waitToClick(selectors.routeIndex.cloneButton);
await page.waitToClick(selectors.routeIndex.submitClonationButton);

View File

@ -42,4 +42,48 @@ describe('Travel descriptor path', () => {
expect(state).toBe('travel.create');
});
it('should edit the data to clone and then get redirected to the cloned travel basic data', async() => {
await page.clearInput(selectors.travelCreate.reference);
await page.write(selectors.travelCreate.reference, 'reference');
await page.autocompleteSearch(selectors.travelCreate.agency, 'entanglement');
await page.pickDate(selectors.travelCreate.shipped);
await page.pickDate(selectors.travelCreate.landed);
await page.autocompleteSearch(selectors.travelCreate.warehouseOut, 'warehouse one');
await page.autocompleteSearch(selectors.travelCreate.warehouseIn, 'warehouse two');
await page.waitToClick(selectors.travelCreate.saveButton);
await page.waitForState('travel.card.basicData');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should atempt to clone the travel and its entries using the descriptor menu but receive an error', async() => {
await page.waitToClick(selectors.travelDescriptor.dotMenu);
await page.waitToClick(selectors.travelDescriptor.dotMenuCloneWithEntries);
await page.waitToClick(selectors.travelDescriptor.acceptClonation);
const message = await page.waitForSnackbar();
expect(message.text).toContain('A travel with this data already exists');
});
it('should update the landed date to a future date to enable cloneWithEntries', async() => {
const nextMonth = new Date();
nextMonth.setMonth(nextMonth.getMonth() + 1);
await page.pickDate(selectors.travelBasicData.deliveryDate, nextMonth);
await page.waitToClick(selectors.travelBasicData.save);
await page.waitForState('travel.card.basicData');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should navigate to the summary and then clone the travel and its entries using the descriptor menu to get redirected to the cloned travel basic data', async() => {
await page.waitToClick('vn-icon[icon="preview"]'); // summary icon
await page.waitForState('travel.card.summary');
await page.waitToClick(selectors.travelDescriptor.dotMenu);
await page.waitToClick(selectors.travelDescriptor.dotMenuCloneWithEntries);
await page.waitToClick(selectors.travelDescriptor.acceptClonation);
await page.waitForState('travel.card.basicData');
});
});

View File

@ -18,6 +18,7 @@ describe('Travel extra community path', () => {
it('should edit the travel reference', async() => {
await page.waitToClick(selectors.travelExtraCommunity.removeContinentFilter);
await page.waitForSpinnerLoad();
await page.writeOnEditableTD(selectors.travelExtraCommunity.firstTravelReference, 'edited reference');
});

View File

@ -23,10 +23,13 @@ describe('Supplier fiscal data path', () => {
await page.clearInput(selectors.supplierFiscalData.country);
await page.clearInput(selectors.supplierFiscalData.postCode);
await page.write(selectors.supplierFiscalData.city, 'Valencia');
await page.waitForTimeout(1000); // must repeat this action twice or fails. also #2699 may be a cool solution to this.
await page.clearInput(selectors.supplierFiscalData.city);
await page.write(selectors.supplierFiscalData.city, 'Valencia');
await page.clearInput(selectors.supplierFiscalData.socialName);
await page.write(selectors.supplierFiscalData.socialName, 'Farmer King SL');
await page.clearInput(selectors.supplierFiscalData.taxNumber);
await page.write(selectors.supplierFiscalData.taxNumber, 'invalid tax number');
await page.write(selectors.supplierFiscalData.taxNumber, 'Wrong tax number');
await page.clearInput(selectors.supplierFiscalData.account);
await page.write(selectors.supplierFiscalData.account, 'edited account number');
await page.autocompleteSearch(selectors.supplierFiscalData.sageWihholding, 'retencion estimacion objetiva');

View File

@ -145,9 +145,15 @@ module.exports = function(Self) {
rewriteDbError(replaceErrFunc) {
function replaceErr(err, replaceErrFunc) {
if (Array.isArray(err)) {
const errors = err.filter(error => {
return error != undefined && error != null;
});
let errs = [];
for (let e of err)
for (let e of errors) {
if (!(e instanceof UserError))
errs.push(replaceErrFunc(e));
else errs.push(e);
}
return errs;
}
return replaceErrFunc(err);

View File

@ -84,5 +84,6 @@
"companyFk": "Company",
"You need to fill sage information before you check verified data": "You need to fill sage information before you check verified data",
"The social name cannot be empty": "The social name cannot be empty",
"The nif cannot be empty": "The nif cannot be empty"
"The nif cannot be empty": "The nif cannot be empty",
"A travel with this data already exists": "A travel with this data already exists"
}

View File

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

View File

@ -1,8 +1,9 @@
<mg-ajax path="Claims/updateClaim/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
url="Claims/updateClaim"
data="$ctrl.claim"
form="form">
form="form"
save="patch">
</vn-watcher>
<vn-crud-model
auto-load="true"

View File

@ -1,7 +1,6 @@
const app = require('vn-loopback/server/server');
// Issue #2471
xdescribe('client sendSms()', () => {
describe('client sendSms()', () => {
let createdLog;
afterAll(async done => {

View File

@ -1,8 +1,7 @@
const app = require('vn-loopback/server/server');
const soap = require('soap');
// Issue #2471
xdescribe('sms send()', () => {
describe('sms send()', () => {
it('should return the expected message and status code', async() => {
const code = 200;
const smsConfig = await app.models.SmsConfig.findOne();

View File

@ -42,9 +42,10 @@ export default class Controller extends Section {
// Town auto complete
set town(selection) {
const oldValue = this._town;
this._town = selection;
if (!selection) return;
if (!selection || !oldValue) return;
const province = selection.province;
const postcodes = selection.postcodes;
@ -62,9 +63,10 @@ export default class Controller extends Section {
// Postcode auto complete
set postcode(selection) {
const oldValue = this._postcode;
this._postcode = selection;
if (!selection) return;
if (!selection || !oldValue) return;
const town = selection.town;
const province = town.province;

View File

@ -24,12 +24,6 @@
data="sageTaxTypes"
order="vat">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="SageTransactionTypes"
data="sageTransactionTypes"
order="transaction">
</vn-crud-model>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
@ -69,12 +63,15 @@
</vn-autocomplete>
<vn-autocomplete vn-one
ng-model="$ctrl.client.sageTransactionTypeFk"
data="sageTransactionTypes"
url="SageTransactionTypes"
show-field="transaction"
value-field="id"
label="Sage transaction type"
search-function="{or: [{id: $search}, {transaction: {like: '%'+ $search +'%'}}]}"
vn-acl="salesAssistant"
order="transaction"
rule>
<tpl-item>{{id}}: {{transaction}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete vn-one
ng-model="$ctrl.client.transferorFk"

View File

@ -106,9 +106,10 @@ export default class Controller extends Section {
// Province auto complete
set province(selection) {
const oldValue = this._province;
this._province = selection;
if (!selection) return;
if (!selection || !oldValue) return;
const country = selection.country;
@ -122,9 +123,10 @@ export default class Controller extends Section {
// Town auto complete
set town(selection) {
const oldValue = this._town;
this._town = selection;
if (!selection) return;
if (!selection || !oldValue) return;
const province = selection.province;
const country = province.country;

View File

@ -23,23 +23,27 @@ module.exports = Self => {
},
{
arg: 'account',
type: 'string'
type: 'any'
},
{
arg: 'sageTaxTypeFk',
type: 'number'
type: 'any'
},
{
arg: 'sageWithholdingFk',
type: 'number'
type: 'any'
},
{
arg: 'sageTransactionTypeFk',
type: 'number'
type: 'any'
},
{
arg: 'postCode',
type: 'string'
type: 'any'
},
{
arg: 'street',
type: 'any'
},
{
arg: 'city',
@ -47,11 +51,11 @@ module.exports = Self => {
},
{
arg: 'provinceFk',
type: 'number'
type: 'any'
},
{
arg: 'countryFk',
type: 'number'
type: 'any'
}],
returns: {
arg: 'res',

View File

@ -24,12 +24,6 @@
data="sageTaxTypes"
order="vat">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="SageTransactionTypes"
data="sageTransactionTypes"
order="transaction">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="SageWithholdings"
@ -83,11 +77,14 @@
</vn-autocomplete>
<vn-autocomplete vn-one
ng-model="$ctrl.supplier.sageTransactionTypeFk"
data="sageTransactionTypes"
url="SageTransactionTypes"
show-field="transaction"
value-field="id"
label="Sage transaction type"
search-function="{or: [{id: $search}, {transaction: {like: '%'+ $search +'%'}}]}"
order="transaction"
rule>
<tpl-item>{{id}}: {{transaction}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>

View File

@ -8,9 +8,10 @@ export default class Controller extends Section {
// Province auto complete
set province(selection) {
const oldValue = this._province;
this._province = selection;
if (!selection) return;
if (!selection || !oldValue) return;
const country = selection.country;
@ -24,9 +25,10 @@ export default class Controller extends Section {
// Town auto complete
set town(selection) {
const oldValue = this._town;
this._town = selection;
if (!selection) return;
if (!selection || !oldValue) return;
const province = selection.province;
const country = province.country;

View File

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

View File

@ -1,7 +1,6 @@
const app = require('vn-loopback/server/server');
// Issue #2471
xdescribe('ticket sendSms()', () => {
describe('ticket sendSms()', () => {
let logId;
afterAll(async done => {

View File

@ -0,0 +1,93 @@
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;
try {
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;
} catch (error) {
if (error.code === 'ER_DUP_ENTRY')
throw new UserError('A travel with this data already exists');
throw error;
}
};
};

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/updateThermograph')(Self);
require('../methods/travel/extraCommunityFilter')(Self);
require('../methods/travel/cloneWithEntries')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')

View File

@ -43,7 +43,6 @@
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button

View File

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

View File

@ -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', () => {
});
});
});

View File

@ -7,9 +7,17 @@
<vn-item
id="clone"
ng-click="clone.show()"
ng-show="::$ctrl.isBuyer"
translate>
Clone travel
</vn-item>
<vn-item
id="cloneWithEntries"
ng-click="cloneWithEntries.show()"
ng-show="::$ctrl.isBuyer"
translate>
Clone travel and his entries
</vn-item>
</vn-list>
</vn-menu>
@ -20,3 +28,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>

View File

@ -48,6 +48,10 @@ class Controller extends Section {
.then(res => this.travel = res.data);
}
get isBuyer() {
return this.aclService.hasAny(['buyer']);
}
onCloneAccept() {
const params = JSON.stringify({
ref: this.travel.ref,
@ -59,6 +63,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'];

View File

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

View File

@ -1 +1,3 @@
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
Search travels by id: Buscar envíos por identificador
New travel: Nuevo envío
travel: envio
travel: envío
# Sections
Travels: Envíos

View File

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

View File

@ -31,5 +31,8 @@
},
"ZoneEstimatedDelivery": {
"dataSource": "vn"
},
"ZoneLog": {
"dataSource": "vn"
}
}

View File

@ -1,6 +1,10 @@
{
"name": "ZoneEvent",
"base": "VnModel",
"base": "Loggable",
"log": {
"model":"ZoneLog",
"relation": "zone"
},
"options": {
"mysql": {
"table": "zoneEvent"

View File

@ -1,6 +1,11 @@
{
"name": "ZoneIncluded",
"base": "VnModel",
"base": "Loggable",
"log": {
"model": "ZoneLog",
"relation": "zone",
"showField": "isIncluded"
},
"options": {
"mysql": {
"table": "zoneIncluded"

View File

@ -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"]
}
}

View File

@ -1,6 +1,10 @@
{
"name": "ZoneWarehouse",
"base": "VnModel",
"base": "Loggable",
"log": {
"model":"ZoneLog",
"relation": "zone"
},
"options": {
"mysql": {
"table": "zoneWarehouse"

View File

@ -1,6 +1,10 @@
{
"name": "Zone",
"base": "VnModel",
"base": "Loggable",
"log": {
"model":"ZoneLog",
"showField": "name"
},
"options": {
"mysql": {
"table": "zone"
@ -39,11 +43,6 @@
}
},
"relations": {
"geolocations": {
"type": "hasMany",
"model": "ZoneGeo",
"foreignKey": "zoneFk"
},
"agencyMode": {
"type": "belongsTo",
"model": "AgencyMode",

View File

@ -16,3 +16,4 @@ import './calendar';
import './location';
import './calendar';
import './upcoming-deliveries';
import './log';

View File

@ -0,0 +1 @@
<vn-log url="ZoneLogs" origin-id="$ctrl.$params.id"></vn-log>

View File

@ -0,0 +1,7 @@
import ngModule from '../module';
import Section from 'salix/components/section';
ngModule.vnComponent('vnZoneLog', {
template: require('./index.html'),
controller: Section,
});

View File

@ -14,6 +14,7 @@
{"state": "zone.card.basicData", "icon": "settings"},
{"state": "zone.card.location", "icon": "my_location"},
{"state": "zone.card.warehouses", "icon": "home"},
{"state": "zone.card.log", "icon": "history"},
{"state": "zone.card.events", "icon": "today"}
]
},
@ -84,6 +85,11 @@
"params": {
"zone": "$ctrl.zone"
}
}, {
"url" : "/log",
"state": "zone.card.log",
"component": "vn-zone-log",
"description": "Log"
}
]
}