Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2609-travel_index_addEntry
This commit is contained in:
commit
ccfeea8e74
|
@ -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 ;
|
||||
|
||||
|
|
@ -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;
|
|
@ -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',
|
||||
|
@ -927,6 +939,14 @@ export default {
|
|||
newEntryCompany: 'vn-entry-create vn-autocomplete[ng-model="$ctrl.entry.companyFk"]',
|
||||
saveNewEntry: 'vn-entry-create button[type="submit"]'
|
||||
},
|
||||
entryObservations: {
|
||||
addNewObservation: 'vn-entry-observation vn-icon-button[icon="add_circle"]',
|
||||
firstObservationType: 'vn-entry-observation vn-horizontal:nth-child(1) > vn-autocomplete[ng-model="observation.observationTypeFk"]',
|
||||
secondObservationType: 'vn-entry-observation vn-horizontal:nth-child(2) > vn-autocomplete[ng-model="observation.observationTypeFk"]',
|
||||
firstObservationDescription: 'vn-entry-observation vn-horizontal:nth-child(1) > vn-textfield[ng-model="observation.description"]',
|
||||
secondObservationDescription: 'vn-entry-observation vn-horizontal:nth-child(2) > vn-textfield[ng-model="observation.description"]',
|
||||
saveObservationsButton: 'vn-entry-observation vn-submit > button'
|
||||
},
|
||||
supplierSummary: {
|
||||
header: 'vn-supplier-summary > vn-card > h5',
|
||||
basicDataId: 'vn-supplier-summary vn-label-value[label="Id"]',
|
||||
|
|
|
@ -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() => {
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Entry observations path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
// await page.loginAndModule('buyer', 'entry'); // access denied, awaiting role confirmation
|
||||
await page.loginAndModule('developer', 'entry');
|
||||
await page.accessToSearchResult('2');
|
||||
await page.accessToSection('entry.card.observation');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it(`should add two new observations of the same type then fail to save as they can't be repeated`, async() => {
|
||||
await page.waitToClick(selectors.entryObservations.addNewObservation);
|
||||
await page.waitToClick(selectors.entryObservations.addNewObservation);
|
||||
await page.autocompleteSearch(selectors.entryObservations.firstObservationType, 'comercial');
|
||||
await page.autocompleteSearch(selectors.entryObservations.secondObservationType, 'comercial');
|
||||
await page.write(selectors.entryObservations.firstObservationDescription, 'first observation');
|
||||
await page.write(selectors.entryObservations.secondObservationDescription, 'second observation');
|
||||
await page.waitToClick(selectors.entryObservations.saveObservationsButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain(`The observation type can't be repeated`);
|
||||
});
|
||||
|
||||
it('should set the 2nd observation of a different one and successfully save both', async() => {
|
||||
await page.autocompleteSearch(selectors.entryObservations.secondObservationType, 'delivery');
|
||||
await page.waitToClick(selectors.entryObservations.saveObservationsButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should reload the section and make sure the first observation type was saved correctly', async() => {
|
||||
await page.reloadSection('entry.card.observation');
|
||||
const result = await page.waitToGetProperty(selectors.entryObservations.firstObservationType, 'value');
|
||||
|
||||
expect(result).toEqual('comercial');
|
||||
});
|
||||
|
||||
it('should make sure the first observation description was saved correctly', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.entryObservations.firstObservationDescription, 'value');
|
||||
|
||||
expect(result).toEqual('first observation');
|
||||
});
|
||||
|
||||
it('should make sure the second observation type was saved correctly', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.entryObservations.secondObservationType, 'value');
|
||||
|
||||
expect(result).toEqual('delivery');
|
||||
});
|
||||
|
||||
it('should make sure the second observation description was saved correctly', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.entryObservations.secondObservationDescription, 'value');
|
||||
|
||||
expect(result).toEqual('second observation');
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -84,5 +84,7 @@
|
|||
"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",
|
||||
"The observation type can't be repeated": "The observation type can't be repeated"
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
// Issue #2471
|
||||
xdescribe('client sendSms()', () => {
|
||||
describe('client sendSms()', () => {
|
||||
let createdLog;
|
||||
|
||||
afterAll(async done => {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('EntryObservation', () => {
|
||||
const entryId = 1;
|
||||
|
||||
it('should throw a user error if the observation type is duplicated.', async() => {
|
||||
const observations = [
|
||||
{
|
||||
entryFk: entryId,
|
||||
observationTypeFk: 1,
|
||||
description: 'repeated description'
|
||||
},
|
||||
{
|
||||
entryFk: entryId,
|
||||
observationTypeFk: 1,
|
||||
description: 'repeated description'
|
||||
},
|
||||
];
|
||||
|
||||
let error;
|
||||
try {
|
||||
await app.models.EntryObservation.create(observations);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error).toBeDefined();
|
||||
});
|
||||
});
|
|
@ -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',
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -130,7 +130,7 @@ module.exports = Self => {
|
|||
let logRecord = {
|
||||
originFk: cleanInstance.id,
|
||||
userFk: myUserId,
|
||||
action: 'create',
|
||||
action: 'insert',
|
||||
changedModel: 'Ticket',
|
||||
changedModelId: cleanInstance.id,
|
||||
oldInstance: {},
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
// Issue #2471
|
||||
xdescribe('ticket sendSms()', () => {
|
||||
describe('ticket sendSms()', () => {
|
||||
let logId;
|
||||
|
||||
afterAll(async done => {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -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')
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit
|
||||
disabled="!watcher.dataChanged()"
|
||||
label="Save">
|
||||
</vn-submit>
|
||||
<vn-button
|
||||
|
|
|
@ -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', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<vn-item
|
||||
id="clone"
|
||||
ng-click="clone.show()"
|
||||
ng-show="::$ctrl.isBuyer"
|
||||
translate>
|
||||
Clone travel
|
||||
</vn-item>
|
||||
|
@ -16,6 +17,13 @@
|
|||
translate>
|
||||
Add entry
|
||||
</a>
|
||||
<vn-item
|
||||
id="cloneWithEntries"
|
||||
ng-click="cloneWithEntries.show()"
|
||||
ng-show="::$ctrl.isBuyer"
|
||||
translate>
|
||||
Clone travel and his entries
|
||||
</vn-item>
|
||||
</vn-list>
|
||||
</vn-menu>
|
||||
|
||||
|
@ -26,3 +34,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>
|
||||
|
|
|
@ -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'];
|
||||
|
|
|
@ -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,2 +1,4 @@
|
|||
Clone travel: Clonar envío
|
||||
Add entry: Añadir entrada
|
||||
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>
|
||||
|
|
|
@ -31,5 +31,8 @@
|
|||
},
|
||||
"ZoneEstimatedDelivery": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"ZoneLog": {
|
||||
"dataSource": "vn"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
{
|
||||
"name": "ZoneEvent",
|
||||
"base": "VnModel",
|
||||
"base": "Loggable",
|
||||
"log": {
|
||||
"model":"ZoneLog",
|
||||
"relation": "zone"
|
||||
},
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "zoneEvent"
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
{
|
||||
"name": "ZoneIncluded",
|
||||
"base": "VnModel",
|
||||
"base": "Loggable",
|
||||
"log": {
|
||||
"model": "ZoneLog",
|
||||
"relation": "zone",
|
||||
"showField": "isIncluded"
|
||||
},
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "zoneIncluded"
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
{
|
||||
"name": "ZoneWarehouse",
|
||||
"base": "VnModel",
|
||||
"base": "Loggable",
|
||||
"log": {
|
||||
"model":"ZoneLog",
|
||||
"relation": "zone"
|
||||
},
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "zoneWarehouse"
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -16,3 +16,4 @@ import './calendar';
|
|||
import './location';
|
||||
import './calendar';
|
||||
import './upcoming-deliveries';
|
||||
import './log';
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<vn-log url="ZoneLogs" origin-id="$ctrl.$params.id"></vn-log>
|
|
@ -0,0 +1,7 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
|
||||
ngModule.vnComponent('vnZoneLog', {
|
||||
template: require('./index.html'),
|
||||
controller: Section,
|
||||
});
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue