Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5092-listado-ticketsSinFacturar
gitea/salix/pipeline/head There was a failure building this commit
Details
gitea/salix/pipeline/head There was a failure building this commit
Details
This commit is contained in:
commit
24f576a4b8
|
@ -6,5 +6,9 @@
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": true
|
||||||
},
|
},
|
||||||
"search.useIgnoreFiles": false,
|
"search.useIgnoreFiles": false,
|
||||||
|
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
||||||
|
"eslint.format.enable": true,
|
||||||
|
"[javascript]": {
|
||||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -5,16 +5,26 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2314.01] - 2023-04-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
-
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
-
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
-
|
||||||
|
|
||||||
## [2312.01] - 2023-04-06
|
## [2312.01] - 2023-04-06
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- (Monitor tickets) Muestra un icono al lado de la zona, si el ticket es frágil y se envía por agencia
|
- (Monitor tickets) Muestra un icono al lado de la zona, si el ticket es frágil y se envía por agencia
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- (Monitor tickets) Cuando se filtra por 'Pendiente' ya no muestra los estados de 'Previa'
|
||||||
- (Envíos -> Extra comunitarios) Se agrupan las entradas del mismo travel. Añadidos campos Referencia y Importe.
|
- (Envíos -> Extra comunitarios) Se agrupan las entradas del mismo travel. Añadidos campos Referencia y Importe.
|
||||||
|
- (Envíos -> Índice) Cambiado el buscador superior por uno lateral
|
||||||
### Fixed
|
|
||||||
-
|
|
||||||
|
|
||||||
## [2310.01] - 2023-03-23
|
## [2310.01] - 2023-03-23
|
||||||
|
|
||||||
|
|
|
@ -69,15 +69,15 @@ module.exports = Self => {
|
||||||
|
|
||||||
const result = response.headers.get('set-cookie');
|
const result = response.headers.get('set-cookie');
|
||||||
const [firtHeader] = result.split(' ');
|
const [firtHeader] = result.split(' ');
|
||||||
const firtCookie = firtHeader.substring(0, firtHeader.length - 1);
|
const cookie = firtHeader.substring(0, firtHeader.length - 1);
|
||||||
const body = await response.text();
|
const body = await response.text();
|
||||||
const dom = new jsdom.JSDOM(body);
|
const dom = new jsdom.JSDOM(body);
|
||||||
const token = dom.window.document.querySelector('[name="__CSRFToken__"]').value;
|
const token = dom.window.document.querySelector('[name="__CSRFToken__"]').value;
|
||||||
|
|
||||||
await login(token, firtCookie);
|
await login(token, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function login(token, firtCookie) {
|
async function login(token, cookie) {
|
||||||
const data = {
|
const data = {
|
||||||
__CSRFToken__: token,
|
__CSRFToken__: token,
|
||||||
do: 'scplogin',
|
do: 'scplogin',
|
||||||
|
@ -90,21 +90,18 @@ module.exports = Self => {
|
||||||
body: new URLSearchParams(data),
|
body: new URLSearchParams(data),
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
'Cookie': firtCookie
|
'Cookie': cookie
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const response = await fetch(ostUri, params);
|
await fetch(ostUri, params);
|
||||||
const result = response.headers.get('set-cookie');
|
|
||||||
const [firtHeader] = result.split(' ');
|
|
||||||
const secondCookie = firtHeader.substring(0, firtHeader.length - 1);
|
|
||||||
|
|
||||||
await close(token, secondCookie);
|
await close(token, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function close(token, secondCookie) {
|
async function close(token, cookie) {
|
||||||
for (const ticketId of ticketsId) {
|
for (const ticketId of ticketsId) {
|
||||||
try {
|
try {
|
||||||
const lock = await getLockCode(token, secondCookie, ticketId);
|
const lock = await getLockCode(token, cookie, ticketId);
|
||||||
if (!lock.code) {
|
if (!lock.code) {
|
||||||
let error = `Can't get lock code`;
|
let error = `Can't get lock code`;
|
||||||
if (lock.msg) error += `: ${lock.msg}`;
|
if (lock.msg) error += `: ${lock.msg}`;
|
||||||
|
@ -127,7 +124,7 @@ module.exports = Self => {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: form,
|
body: form,
|
||||||
headers: {
|
headers: {
|
||||||
'Cookie': secondCookie
|
'Cookie': cookie
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
await fetch(ostUri, params);
|
await fetch(ostUri, params);
|
||||||
|
@ -139,13 +136,13 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLockCode(token, secondCookie, ticketId) {
|
async function getLockCode(token, cookie, ticketId) {
|
||||||
const ostUri = `${config.host}/ajax.php/lock/ticket/${ticketId}`;
|
const ostUri = `${config.host}/ajax.php/lock/ticket/${ticketId}`;
|
||||||
const params = {
|
const params = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-CSRFToken': token,
|
'X-CSRFToken': token,
|
||||||
'Cookie': secondCookie
|
'Cookie': cookie
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const response = await fetch(ostUri, params);
|
const response = await fetch(ostUri, params);
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
ALTER TABLE `vn`.`invoiceInConfig` ADD daysAgo INT UNSIGNED DEFAULT 45 COMMENT 'Días en el pasado para mostrar facturas en invoiceIn series en salix';
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES
|
||||||
|
('InvoiceIn', 'getSerial', 'READ', 'ALLOW', 'ROLE', 'administrative');
|
|
@ -8,7 +8,7 @@ WHERE code IN ('ZKA', 'ZKE');
|
||||||
UPDATE `vn`.`itemType`
|
UPDATE `vn`.`itemType`
|
||||||
SET isFragile = 1
|
SET isFragile = 1
|
||||||
WHERE id IN (SELECT it.id
|
WHERE id IN (SELECT it.id
|
||||||
FROM itemCategory ic
|
FROM `vn`.`itemCategory` ic
|
||||||
JOIN itemType it ON it.categoryFk = ic.id
|
JOIN `vn`.`itemType` it ON it.categoryFk = ic.id
|
||||||
WHERE ic.code = 'plant');
|
WHERE ic.code = 'plant');
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
DROP TRIGGER `vn`.`supplierAccount_afterInsert`;
|
||||||
|
DROP TRIGGER `vn`.`supplierAccount_afterUpdate`;
|
||||||
|
DROP TRIGGER `vn`.`supplierAccount_afterDelete`;
|
|
@ -0,0 +1,72 @@
|
||||||
|
CREATE TABLE `vn`.`wagonType` (
|
||||||
|
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(30) NOT NULL UNIQUE,
|
||||||
|
`divisible` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;
|
||||||
|
|
||||||
|
CREATE TABLE `vn`.`wagonTypeColor` (
|
||||||
|
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(30) NOT NULL UNIQUE,
|
||||||
|
`rgb` varchar(30) NOT NULL UNIQUE,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;
|
||||||
|
|
||||||
|
CREATE TABLE `vn`.`wagonTypeTray` (
|
||||||
|
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`typeFk` int(11) unsigned,
|
||||||
|
`height` int(11) unsigned NOT NULL,
|
||||||
|
`colorFk` int(11) unsigned,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY (`typeFk`,`height`),
|
||||||
|
CONSTRAINT `wagonTypeTray_type` FOREIGN KEY (`typeFk`) REFERENCES `wagonType` (`id`) ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `wagonTypeTray_color` FOREIGN KEY (`colorFk`) REFERENCES `wagonTypeColor` (`id`) ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;
|
||||||
|
|
||||||
|
CREATE TABLE `vn`.`wagonConfig` (
|
||||||
|
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`width` int(11) unsigned DEFAULT 1350,
|
||||||
|
`height` int(11) unsigned DEFAULT 1900,
|
||||||
|
`maxWagonHeight` int(11) unsigned DEFAULT 200,
|
||||||
|
`minHeightBetweenTrays` int(11) unsigned DEFAULT 50,
|
||||||
|
`maxTrays` int(11) unsigned DEFAULT 6,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;
|
||||||
|
|
||||||
|
CREATE TABLE `vn`.`collectionWagon` (
|
||||||
|
`collectionFk` int(11) NOT NULL,
|
||||||
|
`wagonFk` int(11) NOT NULL,
|
||||||
|
`position` int(11) unsigned,
|
||||||
|
PRIMARY KEY (`collectionFk`,`position`),
|
||||||
|
UNIQUE KEY `collectionWagon_unique` (`collectionFk`,`wagonFk`),
|
||||||
|
CONSTRAINT `collectionWagon_collection` FOREIGN KEY (`collectionFk`) REFERENCES `collection` (`id`) ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `collectionWagon_wagon` FOREIGN KEY (`wagonFk`) REFERENCES `wagon` (`id`) ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
|
||||||
|
|
||||||
|
CREATE TABLE `vn`.`collectionWagonTicket` (
|
||||||
|
`ticketFk` int(11) NOT NULL,
|
||||||
|
`wagonFk` int(11) NOT NULL,
|
||||||
|
`trayFk` int(11) unsigned NOT NULL,
|
||||||
|
`side` SET('L', 'R') NULL,
|
||||||
|
PRIMARY KEY (`ticketFk`),
|
||||||
|
CONSTRAINT `collectionWagonTicket_ticket` FOREIGN KEY (`ticketFk`) REFERENCES `ticket` (`id`) ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `collectionWagonTicket_wagon` FOREIGN KEY (`wagonFk`) REFERENCES `wagon` (`id`) ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `collectionWagonTicket_tray` FOREIGN KEY (`trayFk`) REFERENCES `wagonTypeTray` (`id`) ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`wagon` ADD `typeFk` int(11) unsigned NOT NULL;
|
||||||
|
ALTER TABLE `vn`.`wagon` ADD `label` int(11) unsigned NOT NULL;
|
||||||
|
ALTER TABLE `vn`.`wagon` ADD CONSTRAINT `wagon_type` FOREIGN KEY (`typeFk`) REFERENCES `wagonType` (`id`) ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES
|
||||||
|
('WagonType', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('WagonTypeColor', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('WagonTypeTray', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('WagonConfig', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('CollectionWagon', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('CollectionWagonTicket', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('Wagon', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('WagonType', 'createWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('WagonType', 'deleteWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'),
|
||||||
|
('WagonType', 'editWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi');
|
|
@ -2490,9 +2490,9 @@ REPLACE INTO `vn`.`invoiceIn`(`id`, `serialNumber`,`serial`, `supplierFk`, `issu
|
||||||
(9, 1009, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1242, 1, 442, 1),
|
(9, 1009, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1242, 1, 442, 1),
|
||||||
(10, 1010, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1243, 1, 442, 1);
|
(10, 1010, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1243, 1, 442, 1);
|
||||||
|
|
||||||
INSERT INTO `vn`.`invoiceInConfig` (`id`, `retentionRate`, `retentionName`, `sageWithholdingFk`)
|
INSERT INTO `vn`.`invoiceInConfig` (`id`, `retentionRate`, `retentionName`, `sageWithholdingFk`, `daysAgo`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, -2, '2% retention', 2);
|
(1, -2, '2% retention', 2, 45);
|
||||||
|
|
||||||
INSERT INTO `vn`.`invoiceInDueDay`(`invoiceInFk`, `dueDated`, `bankFk`, `amount`)
|
INSERT INTO `vn`.`invoiceInDueDay`(`invoiceInFk`, `dueDated`, `bankFk`, `amount`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2840,4 +2840,27 @@ INSERT INTO `vn`.`workerTimeControlMail` (`id`, `workerFk`, `year`, `week`, `sta
|
||||||
(3, 9, 2000, 51, 'CONFIRMED', util.VN_NOW(), 1, NULL),
|
(3, 9, 2000, 51, 'CONFIRMED', util.VN_NOW(), 1, NULL),
|
||||||
(4, 9, 2001, 1, 'SENDED', util.VN_NOW(), 1, NULL);
|
(4, 9, 2001, 1, 'SENDED', util.VN_NOW(), 1, NULL);
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`wagonConfig` (`id`, `width`, `height`, `maxWagonHeight`, `minHeightBetweenTrays`, `maxTrays`)
|
||||||
|
VALUES
|
||||||
|
(1, 1350, 1900, 200, 50, 6);
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`wagonTypeColor` (`id`, `name`, `rgb`)
|
||||||
|
VALUES
|
||||||
|
(1, 'white', '#ffffff'),
|
||||||
|
(2, 'red', '#ff0000'),
|
||||||
|
(3, 'green', '#00ff00'),
|
||||||
|
(4, 'blue', '#0000ff');
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`wagonType` (`id`, `name`, `divisible`)
|
||||||
|
VALUES
|
||||||
|
(1, 'Wagon Type #1', 1);
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`wagonTypeTray` (`id`, `typeFk`, `height`, `colorFk`)
|
||||||
|
VALUES
|
||||||
|
(1, 1, 100, 1),
|
||||||
|
(2, 1, 50, 2),
|
||||||
|
(3, 1, 0, 3);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1127,6 +1127,15 @@ export default {
|
||||||
saveButton: 'vn-invoice-in-tax vn-submit',
|
saveButton: 'vn-invoice-in-tax vn-submit',
|
||||||
|
|
||||||
},
|
},
|
||||||
|
invoiceInIndex: {
|
||||||
|
topbarSearchParams: 'vn-searchbar div.search-params > span',
|
||||||
|
},
|
||||||
|
invoiceInSerial: {
|
||||||
|
daysAgo: 'vn-invoice-in-serial-search-panel vn-input-number[ng-model="$ctrl.filter.daysAgo"]',
|
||||||
|
serial: 'vn-invoice-in-serial-search-panel vn-textfield[ng-model="$ctrl.filter.serial"]',
|
||||||
|
chip: 'vn-chip > vn-icon',
|
||||||
|
goToIndex: 'vn-invoice-in-serial vn-icon-button[icon="icon-invoice-in"]',
|
||||||
|
},
|
||||||
travelIndex: {
|
travelIndex: {
|
||||||
anySearchResult: 'vn-travel-index vn-tbody > a',
|
anySearchResult: 'vn-travel-index vn-tbody > a',
|
||||||
firstSearchResult: 'vn-travel-index vn-tbody > a:nth-child(1)',
|
firstSearchResult: 'vn-travel-index vn-tbody > a:nth-child(1)',
|
||||||
|
@ -1138,7 +1147,16 @@ export default {
|
||||||
landingDate: 'vn-travel-create vn-date-picker[ng-model="$ctrl.travel.landed"]',
|
landingDate: 'vn-travel-create vn-date-picker[ng-model="$ctrl.travel.landed"]',
|
||||||
warehouseOut: 'vn-travel-create vn-autocomplete[ng-model="$ctrl.travel.warehouseOutFk"]',
|
warehouseOut: 'vn-travel-create vn-autocomplete[ng-model="$ctrl.travel.warehouseOutFk"]',
|
||||||
warehouseIn: 'vn-travel-create vn-autocomplete[ng-model="$ctrl.travel.warehouseInFk"]',
|
warehouseIn: 'vn-travel-create vn-autocomplete[ng-model="$ctrl.travel.warehouseInFk"]',
|
||||||
save: 'vn-travel-create vn-submit > button'
|
save: 'vn-travel-create vn-submit > button',
|
||||||
|
generalSearchFilter: 'vn-travel-search-panel vn-textfield[ng-model="$ctrl.search"]',
|
||||||
|
agencyFilter: 'vn-travel-search-panel vn-autocomplete[ng-model="$ctrl.filter.agencyModeFk"]',
|
||||||
|
warehouseOutFilter: 'vn-travel-search-panel vn-autocomplete[ng-model="$ctrl.filter.warehouseOutFk"]',
|
||||||
|
warehouseInFilter: 'vn-travel-search-panel vn-autocomplete[ng-model="$ctrl.filter.warehouseInFk"]',
|
||||||
|
scopeDaysFilter: 'vn-travel-search-panel vn-input-number[ng-model="$ctrl.filter.scopeDays"]',
|
||||||
|
continentFilter: 'vn-travel-search-panel vn-autocomplete[ng-model="$ctrl.filter.continent"]',
|
||||||
|
totalEntriesFilter: 'vn-travel-search-panel vn-input-number[ng-model="$ctrl.totalEntries"]',
|
||||||
|
chip: 'vn-travel-search-panel vn-chip > vn-icon',
|
||||||
|
|
||||||
},
|
},
|
||||||
travelExtraCommunity: {
|
travelExtraCommunity: {
|
||||||
anySearchResult: 'vn-travel-extra-community > vn-card div > tbody > tr[ng-attr-id="{{::travel.id}}"]',
|
anySearchResult: 'vn-travel-extra-community > vn-card div > tbody > tr[ng-attr-id="{{::travel.id}}"]',
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import selectors from '../../helpers/selectors.js';
|
||||||
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
|
describe('InvoiceIn serial path', () => {
|
||||||
|
let browser;
|
||||||
|
let page;
|
||||||
|
let httpRequest;
|
||||||
|
|
||||||
|
beforeAll(async() => {
|
||||||
|
browser = await getBrowser();
|
||||||
|
page = browser.page;
|
||||||
|
await page.loginAndModule('administrative', 'invoiceIn');
|
||||||
|
await page.accessToSection('invoiceIn.serial');
|
||||||
|
page.on('request', req => {
|
||||||
|
if (req.url().includes(`InvoiceIns/getSerial`))
|
||||||
|
httpRequest = req.url();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async() => {
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check that passes the correct params to back', async() => {
|
||||||
|
await page.overwrite(selectors.invoiceInSerial.daysAgo, '30');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('daysAgo=30');
|
||||||
|
|
||||||
|
await page.overwrite(selectors.invoiceInSerial.serial, 'R');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('serial=R');
|
||||||
|
await page.click(selectors.invoiceInSerial.chip);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should go to index and check if the search-panel has the correct params', async() => {
|
||||||
|
await page.click(selectors.invoiceInSerial.goToIndex);
|
||||||
|
const params = await page.$$(selectors.invoiceInIndex.topbarSearchParams);
|
||||||
|
const serial = await params[0].getProperty('title');
|
||||||
|
const isBooked = await params[1].getProperty('title');
|
||||||
|
const from = await params[2].getProperty('title');
|
||||||
|
|
||||||
|
expect(await serial.jsonValue()).toContain('serial');
|
||||||
|
expect(await isBooked.jsonValue()).toContain('not isBooked');
|
||||||
|
expect(await from.jsonValue()).toContain('from');
|
||||||
|
});
|
||||||
|
});
|
|
@ -9,7 +9,8 @@ describe('Travel basic data path', () => {
|
||||||
browser = await getBrowser();
|
browser = await getBrowser();
|
||||||
page = browser.page;
|
page = browser.page;
|
||||||
await page.loginAndModule('buyer', 'travel');
|
await page.loginAndModule('buyer', 'travel');
|
||||||
await page.accessToSearchResult('3');
|
await page.write(selectors.travelIndex.generalSearchFilter, '3');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
await page.accessToSection('travel.card.basicData');
|
await page.accessToSection('travel.card.basicData');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,8 @@ describe('Travel descriptor path', () => {
|
||||||
browser = await getBrowser();
|
browser = await getBrowser();
|
||||||
page = browser.page;
|
page = browser.page;
|
||||||
await page.loginAndModule('buyer', 'travel');
|
await page.loginAndModule('buyer', 'travel');
|
||||||
await page.accessToSearchResult('1');
|
await page.write(selectors.travelIndex.generalSearchFilter, '1');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
await page.waitForState('travel.card.summary');
|
await page.waitForState('travel.card.summary');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -81,7 +82,8 @@ describe('Travel descriptor path', () => {
|
||||||
await page.waitToClick('.cancel');
|
await page.waitToClick('.cancel');
|
||||||
await page.waitToClick(selectors.globalItems.homeButton);
|
await page.waitToClick(selectors.globalItems.homeButton);
|
||||||
await page.selectModule('travel');
|
await page.selectModule('travel');
|
||||||
await page.accessToSearchResult('3');
|
await page.write(selectors.travelIndex.generalSearchFilter, '3');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
await page.waitForState('travel.card.summary');
|
await page.waitForState('travel.card.summary');
|
||||||
const state = await page.getState();
|
const state = await page.getState();
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,8 @@ describe('Travel thermograph path', () => {
|
||||||
browser = await getBrowser();
|
browser = await getBrowser();
|
||||||
page = browser.page;
|
page = browser.page;
|
||||||
await page.loginAndModule('buyer', 'travel');
|
await page.loginAndModule('buyer', 'travel');
|
||||||
await page.accessToSearchResult('3');
|
await page.write(selectors.travelIndex.generalSearchFilter, '3');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
await page.accessToSection('travel.card.thermograph.index');
|
await page.accessToSection('travel.card.thermograph.index');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import selectors from '../../helpers/selectors.js';
|
||||||
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
|
describe('Travel search panel path', () => {
|
||||||
|
let browser;
|
||||||
|
let page;
|
||||||
|
let httpRequest;
|
||||||
|
|
||||||
|
beforeAll(async() => {
|
||||||
|
browser = await getBrowser();
|
||||||
|
page = browser.page;
|
||||||
|
await page.loginAndModule('buyer', 'travel');
|
||||||
|
page.on('request', req => {
|
||||||
|
if (req.url().includes(`Travels/filter`))
|
||||||
|
httpRequest = req.url();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async() => {
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter using all the fields', async() => {
|
||||||
|
await page.click(selectors.travelIndex.chip);
|
||||||
|
await page.write(selectors.travelIndex.generalSearchFilter, 'travel');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('search=travel');
|
||||||
|
|
||||||
|
await page.click(selectors.travelIndex.chip);
|
||||||
|
await page.autocompleteSearch(selectors.travelIndex.agencyFilter, 'Entanglement');
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('agencyModeFk');
|
||||||
|
|
||||||
|
await page.click(selectors.travelIndex.chip);
|
||||||
|
await page.autocompleteSearch(selectors.travelIndex.warehouseOutFilter, 'Warehouse One');
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('warehouseOutFk');
|
||||||
|
|
||||||
|
await page.click(selectors.travelIndex.chip);
|
||||||
|
await page.autocompleteSearch(selectors.travelIndex.warehouseInFilter, 'Warehouse Two');
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('warehouseInFk');
|
||||||
|
|
||||||
|
await page.click(selectors.travelIndex.chip);
|
||||||
|
await page.overwrite(selectors.travelIndex.scopeDaysFilter, '15');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('scopeDays=15');
|
||||||
|
|
||||||
|
await page.click(selectors.travelIndex.chip);
|
||||||
|
await page.autocompleteSearch(selectors.travelIndex.continentFilter, 'Asia');
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('continent');
|
||||||
|
|
||||||
|
await page.click(selectors.travelIndex.chip);
|
||||||
|
await page.write(selectors.travelIndex.totalEntriesFilter, '1');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
|
expect(httpRequest).toContain('totalEntries=1');
|
||||||
|
});
|
||||||
|
});
|
|
@ -26,7 +26,7 @@ Value should have at most %s characters: El valor debe tener un máximo de %s ca
|
||||||
Enter a new search: Introduce una nueva búsqueda
|
Enter a new search: Introduce una nueva búsqueda
|
||||||
No results: Sin resultados
|
No results: Sin resultados
|
||||||
Ups! It seems there was an error: ¡Vaya! Parece que ha habido un error
|
Ups! It seems there was an error: ¡Vaya! Parece que ha habido un error
|
||||||
General search: Busqueda general
|
General search: Búsqueda general
|
||||||
January: Enero
|
January: Enero
|
||||||
February: Febrero
|
February: Febrero
|
||||||
March: Marzo
|
March: Marzo
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
$font-size: 11pt;
|
$font-size: 11pt;
|
||||||
$menu-width: 256px;
|
$menu-width: 256px;
|
||||||
|
$right-menu-width: 318px;
|
||||||
$topbar-height: 56px;
|
$topbar-height: 56px;
|
||||||
$mobile-width: 800px;
|
$mobile-width: 800px;
|
||||||
$float-spacing: 20px;
|
$float-spacing: 20px;
|
||||||
|
|
|
@ -88,13 +88,13 @@ vn-layout {
|
||||||
}
|
}
|
||||||
&.right-menu {
|
&.right-menu {
|
||||||
& > vn-topbar > .end {
|
& > vn-topbar > .end {
|
||||||
width: 80px + $menu-width;
|
width: 80px + $right-menu-width;
|
||||||
}
|
}
|
||||||
& > .main-view {
|
& > .main-view {
|
||||||
padding-right: $menu-width;
|
padding-right: $right-menu-width;
|
||||||
}
|
}
|
||||||
[fixed-bottom-right] {
|
[fixed-bottom-right] {
|
||||||
right: $menu-width;
|
right: $right-menu-width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& > .main-view {
|
& > .main-view {
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
const mysql = require('mysql');
|
const mysql = require('mysql');
|
||||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
|
||||||
const MySQL = require('loopback-connector-mysql').MySQL;
|
const MySQL = require('loopback-connector-mysql').MySQL;
|
||||||
const EnumFactory = require('loopback-connector-mysql').EnumFactory;
|
const EnumFactory = require('loopback-connector-mysql').EnumFactory;
|
||||||
const Transaction = require('loopback-connector').Transaction;
|
const { Transaction, SQLConnector, ParameterizedSQL } = require('loopback-connector');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const limitSet = new Set([
|
const limitSet = new Set([
|
||||||
|
@ -254,49 +253,49 @@ class VnMySQL extends MySQL {
|
||||||
}
|
}
|
||||||
|
|
||||||
create(model, data, opts, cb) {
|
create(model, data, opts, cb) {
|
||||||
const ctx = {data};
|
const ctx = { data };
|
||||||
this.invokeMethod('create',
|
this.invokeMethod('create',
|
||||||
arguments, model, ctx, opts, cb);
|
arguments, model, ctx, opts, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
createAll(model, data, opts, cb) {
|
createAll(model, data, opts, cb) {
|
||||||
const ctx = {data};
|
const ctx = { data };
|
||||||
this.invokeMethod('createAll',
|
this.invokeMethod('createAll',
|
||||||
arguments, model, ctx, opts, cb);
|
arguments, model, ctx, opts, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
save(model, data, opts, cb) {
|
save(model, data, opts, cb) {
|
||||||
const ctx = {data};
|
const ctx = { data };
|
||||||
this.invokeMethod('save',
|
this.invokeMethod('save',
|
||||||
arguments, model, ctx, opts, cb);
|
arguments, model, ctx, opts, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOrCreate(model, data, opts, cb) {
|
updateOrCreate(model, data, opts, cb) {
|
||||||
const ctx = {data};
|
const ctx = { data };
|
||||||
this.invokeMethod('updateOrCreate',
|
this.invokeMethod('updateOrCreate',
|
||||||
arguments, model, ctx, opts, cb);
|
arguments, model, ctx, opts, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceOrCreate(model, data, opts, cb) {
|
replaceOrCreate(model, data, opts, cb) {
|
||||||
const ctx = {data};
|
const ctx = { data };
|
||||||
this.invokeMethod('replaceOrCreate',
|
this.invokeMethod('replaceOrCreate',
|
||||||
arguments, model, ctx, opts, cb);
|
arguments, model, ctx, opts, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyAll(model, where, opts, cb) {
|
destroyAll(model, where, opts, cb) {
|
||||||
const ctx = {where};
|
const ctx = { where };
|
||||||
this.invokeMethod('destroyAll',
|
this.invokeMethod('destroyAll',
|
||||||
arguments, model, ctx, opts, cb);
|
arguments, model, ctx, opts, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
update(model, where, data, opts, cb) {
|
update(model, where, data, opts, cb) {
|
||||||
const ctx = {where, data};
|
const ctx = { where, data };
|
||||||
this.invokeMethod('update',
|
this.invokeMethod('update',
|
||||||
arguments, model, ctx, opts, cb);
|
arguments, model, ctx, opts, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceById(model, id, data, opts, cb) {
|
replaceById(model, id, data, opts, cb) {
|
||||||
const ctx = {id, data};
|
const ctx = { id, data };
|
||||||
this.invokeMethod('replaceById',
|
this.invokeMethod('replaceById',
|
||||||
arguments, model, ctx, opts, cb);
|
arguments, model, ctx, opts, cb);
|
||||||
}
|
}
|
||||||
|
@ -321,16 +320,16 @@ class VnMySQL extends MySQL {
|
||||||
let tx;
|
let tx;
|
||||||
if (!opts.transaction) {
|
if (!opts.transaction) {
|
||||||
tx = await Transaction.begin(this, {});
|
tx = await Transaction.begin(this, {});
|
||||||
opts = Object.assign({transaction: tx, httpCtx: opts.httpCtx}, opts);
|
opts = Object.assign({ transaction: tx, httpCtx: opts.httpCtx }, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch old values (update|delete) or login
|
// Fetch old values (update|delete) or login
|
||||||
let where, id, data, idName, limit, op, oldInstances, newInstances;
|
let where, id, data, idName, limit, op, oldInstances, newInstances;
|
||||||
const hasGrabUser = settings.log && settings.log.grabUser;
|
const hasGrabUser = settings.log && settings.log.grabUser;
|
||||||
if(hasGrabUser){
|
if (hasGrabUser) {
|
||||||
const userId = opts.httpCtx && opts.httpCtx.active.accessToken.userId;
|
const userId = opts.httpCtx && opts.httpCtx.active.accessToken.userId;
|
||||||
const user = await Model.app.models.Account.findById(userId, {fields: ['name']}, opts);
|
const user = await Model.app.models.Account.findById(userId, { fields: ['name'] }, opts);
|
||||||
await this.executeP(`CALL account.myUser_loginWithName(?)`, [user.name], opts);
|
await this.executeP(`CALL account.myUser_loginWithName(?)`, [user.name], opts);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -344,8 +343,8 @@ class VnMySQL extends MySQL {
|
||||||
op = opMap.get(method);
|
op = opMap.get(method);
|
||||||
|
|
||||||
if (!where) {
|
if (!where) {
|
||||||
if (id) where = {[idName]: id};
|
if (id) where = { [idName]: id };
|
||||||
else where = {[idName]: data[idName]};
|
else where = { [idName]: data[idName] };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch old values
|
// Fetch old values
|
||||||
|
@ -365,7 +364,7 @@ class VnMySQL extends MySQL {
|
||||||
super[method].apply(this, fnArgs);
|
super[method].apply(this, fnArgs);
|
||||||
});
|
});
|
||||||
|
|
||||||
if(hasGrabUser)
|
if (hasGrabUser)
|
||||||
await this.executeP(`CALL account.myUser_logout()`, null, opts);
|
await this.executeP(`CALL account.myUser_logout()`, null, opts);
|
||||||
else {
|
else {
|
||||||
// Fetch new values
|
// Fetch new values
|
||||||
|
@ -388,7 +387,7 @@ class VnMySQL extends MySQL {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newWhere = ids.length ? {[idName]: ids} : where;
|
const newWhere = ids.length ? { [idName]: ids } : where;
|
||||||
|
|
||||||
const stmt = this.buildSelectStmt(op, data, idName, model, newWhere, limit);
|
const stmt = this.buildSelectStmt(op, data, idName, model, newWhere, limit);
|
||||||
newInstances = await this.executeStmt(stmt, opts);
|
newInstances = await this.executeStmt(stmt, opts);
|
||||||
|
@ -431,7 +430,7 @@ class VnMySQL extends MySQL {
|
||||||
|
|
||||||
const stmt = new ParameterizedSQL(
|
const stmt = new ParameterizedSQL(
|
||||||
'SELECT ' +
|
'SELECT ' +
|
||||||
this.buildColumnNames(model, {fields}) +
|
this.buildColumnNames(model, { fields }) +
|
||||||
' FROM ' +
|
' FROM ' +
|
||||||
this.tableEscaped(model)
|
this.tableEscaped(model)
|
||||||
);
|
);
|
||||||
|
@ -537,13 +536,13 @@ exports.initialize = function initialize(dataSource, callback) {
|
||||||
modelBuilder.defineValueType.bind(modelBuilder) :
|
modelBuilder.defineValueType.bind(modelBuilder) :
|
||||||
modelBuilder.constructor.registerType.bind(modelBuilder.constructor);
|
modelBuilder.constructor.registerType.bind(modelBuilder.constructor);
|
||||||
|
|
||||||
defineType(function Point() {});
|
defineType(function Point() { });
|
||||||
|
|
||||||
dataSource.EnumFactory = EnumFactory;
|
dataSource.EnumFactory = EnumFactory;
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
if (dataSource.settings.lazyConnect) {
|
if (dataSource.settings.lazyConnect) {
|
||||||
process.nextTick(function() {
|
process.nextTick(function () {
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
} else
|
} else
|
||||||
|
@ -551,13 +550,13 @@ exports.initialize = function initialize(dataSource, callback) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MySQL.prototype.connect = function(callback) {
|
MySQL.prototype.connect = function (callback) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const options = generateOptions(this.settings);
|
const options = generateOptions(this.settings);
|
||||||
|
|
||||||
if (this.client) {
|
if (this.client) {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
process.nextTick(function() {
|
process.nextTick(function () {
|
||||||
callback(null, self.client);
|
callback(null, self.client);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -566,7 +565,7 @@ MySQL.prototype.connect = function(callback) {
|
||||||
|
|
||||||
function connectionHandler(options, callback) {
|
function connectionHandler(options, callback) {
|
||||||
const client = mysql.createPool(options);
|
const client = mysql.createPool(options);
|
||||||
client.getConnection(function(err, connection) {
|
client.getConnection(function (err, connection) {
|
||||||
const conn = connection;
|
const conn = connection;
|
||||||
if (!err) {
|
if (!err) {
|
||||||
if (self.debug)
|
if (self.debug)
|
||||||
|
@ -645,3 +644,31 @@ function generateOptions(settings) {
|
||||||
}
|
}
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SQLConnector.prototype.all = function find(model, filter, options, cb) {
|
||||||
|
const self = this;
|
||||||
|
// Order by id if no order is specified
|
||||||
|
filter = filter || {};
|
||||||
|
const stmt = this.buildSelect(model, filter, options);
|
||||||
|
this.execute(stmt.sql, stmt.params, options, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
return cb(err, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const objs = data.map(function (obj) {
|
||||||
|
return self.fromRow(model, obj);
|
||||||
|
});
|
||||||
|
if (filter && filter.include) {
|
||||||
|
self.getModelDefinition(model).model.include(
|
||||||
|
objs, filter.include, options, cb,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
cb(null, objs);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
cb(error, [])
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,65 @@
|
||||||
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
const buildFilter = require('vn-loopback/util/filter').buildFilter;
|
||||||
|
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('getSerial', {
|
||||||
|
description: 'Return invoiceIn serial',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'filter',
|
||||||
|
type: 'object'
|
||||||
|
}, {
|
||||||
|
arg: 'daysAgo',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
}, {
|
||||||
|
arg: 'serial',
|
||||||
|
type: 'string'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: '/getSerial',
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.getSerial = async(ctx, options) => {
|
||||||
|
const conn = Self.dataSource.connector;
|
||||||
|
const args = ctx.args;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const issued = Date.vnNew();
|
||||||
|
const where = buildFilter(args, (param, value) => {
|
||||||
|
switch (param) {
|
||||||
|
case 'daysAgo':
|
||||||
|
issued.setDate(issued.getDate() - value);
|
||||||
|
return {'i.issued': {gte: issued}};
|
||||||
|
case 'serial':
|
||||||
|
return {'i.serial': {like: `%${value}%`}};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
filter = mergeFilters(args.filter, {where});
|
||||||
|
|
||||||
|
const stmt = new ParameterizedSQL(
|
||||||
|
`SELECT i.serial, SUM(IF(i.isBooked, 0,1)) pending, COUNT(*) total
|
||||||
|
FROM vn.invoiceIn i`
|
||||||
|
);
|
||||||
|
|
||||||
|
stmt.merge(conn.makeWhere(filter.where));
|
||||||
|
stmt.merge(`GROUP BY i.serial`);
|
||||||
|
stmt.merge(conn.makeOrderBy(filter.order));
|
||||||
|
stmt.merge(conn.makeLimit(filter));
|
||||||
|
|
||||||
|
const result = await conn.executeStmt(stmt, myOptions);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('invoiceIn getSerial()', () => {
|
||||||
|
it('should check that returns without serial param', async() => {
|
||||||
|
const ctx = {args: {daysAgo: 45}};
|
||||||
|
const result = await models.InvoiceIn.getSerial(ctx);
|
||||||
|
|
||||||
|
expect(result.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check that returns with serial param', async() => {
|
||||||
|
const ctx = {args: {daysAgo: 45, serial: 'R'}};
|
||||||
|
const result = await models.InvoiceIn.getSerial(ctx);
|
||||||
|
|
||||||
|
expect(result.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check that returns with non exist serial param', async() => {
|
||||||
|
const ctx = {args: {daysAgo: 45, serial: 'Mock serial'}};
|
||||||
|
const result = await models.InvoiceIn.getSerial(ctx);
|
||||||
|
|
||||||
|
expect(result.length).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
|
@ -17,6 +17,9 @@
|
||||||
},
|
},
|
||||||
"retentionName": {
|
"retentionName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"daysAgo": {
|
||||||
|
"type": "number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -6,6 +6,7 @@ module.exports = Self => {
|
||||||
require('../methods/invoice-in/getTotals')(Self);
|
require('../methods/invoice-in/getTotals')(Self);
|
||||||
require('../methods/invoice-in/invoiceInPdf')(Self);
|
require('../methods/invoice-in/invoiceInPdf')(Self);
|
||||||
require('../methods/invoice-in/invoiceInEmail')(Self);
|
require('../methods/invoice-in/invoiceInEmail')(Self);
|
||||||
|
require('../methods/invoice-in/getSerial')(Self);
|
||||||
require('../methods/invoice-in/negativeBases')(Self);
|
require('../methods/invoice-in/negativeBases')(Self);
|
||||||
require('../methods/invoice-in/negativeBasesCsv')(Self);
|
require('../methods/invoice-in/negativeBasesCsv')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,4 +13,6 @@ import './dueDay';
|
||||||
import './intrastat';
|
import './intrastat';
|
||||||
import './create';
|
import './create';
|
||||||
import './log';
|
import './log';
|
||||||
|
import './serial';
|
||||||
|
import './serial-search-panel';
|
||||||
import './negative-bases';
|
import './negative-bases';
|
||||||
|
|
|
@ -7,6 +7,7 @@ Foreign value: Divisa
|
||||||
InvoiceIn: Facturas recibidas
|
InvoiceIn: Facturas recibidas
|
||||||
InvoiceIn cloned: Factura clonada
|
InvoiceIn cloned: Factura clonada
|
||||||
InvoiceIn deleted: Factura eliminada
|
InvoiceIn deleted: Factura eliminada
|
||||||
|
InvoiceIn Serial: Facturas por series
|
||||||
Invoice list: Listado de facturas recibidas
|
Invoice list: Listado de facturas recibidas
|
||||||
InvoiceIn booked: Factura contabilizada
|
InvoiceIn booked: Factura contabilizada
|
||||||
Net: Neto
|
Net: Neto
|
||||||
|
@ -22,4 +23,5 @@ Total stems: Total tallos
|
||||||
Show agricultural receipt as PDF: Ver recibo agrícola como PDF
|
Show agricultural receipt as PDF: Ver recibo agrícola como PDF
|
||||||
Send agricultural receipt as PDF: Enviar recibo agrícola como PDF
|
Send agricultural receipt as PDF: Enviar recibo agrícola como PDF
|
||||||
New InvoiceIn: Nueva Factura
|
New InvoiceIn: Nueva Factura
|
||||||
|
Days ago: Últimos días
|
||||||
Negative bases: Bases negativas
|
Negative bases: Bases negativas
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"menus": {
|
"menus": {
|
||||||
"main": [
|
"main": [
|
||||||
{ "state": "invoiceIn.index", "icon": "icon-invoice-in"},
|
{ "state": "invoiceIn.index", "icon": "icon-invoice-in"},
|
||||||
|
{ "state": "invoiceIn.serial", "icon": "icon-invoice-in"},
|
||||||
{ "state": "invoiceIn.negative-bases", "icon": "icon-ticket"}
|
{ "state": "invoiceIn.negative-bases", "icon": "icon-ticket"}
|
||||||
],
|
],
|
||||||
"card": [
|
"card": [
|
||||||
|
@ -61,6 +62,15 @@
|
||||||
"administrative"
|
"administrative"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"url": "/serial",
|
||||||
|
"state": "invoiceIn.serial",
|
||||||
|
"component": "vn-invoice-in-serial",
|
||||||
|
"description": "InvoiceIn Serial",
|
||||||
|
"acl": [
|
||||||
|
"administrative"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"url": "/:id",
|
"url": "/:id",
|
||||||
"state": "invoiceIn.card",
|
"state": "invoiceIn.card",
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<vn-side-menu side="right">
|
||||||
|
<vn-horizontal class="input">
|
||||||
|
<vn-input-number
|
||||||
|
label="Days ago"
|
||||||
|
ng-model="$ctrl.filter.daysAgo"
|
||||||
|
vn-focus
|
||||||
|
ng-keydown="$ctrl.onKeyPress($event)"
|
||||||
|
min="0">
|
||||||
|
</vn-input-number>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal class="input">
|
||||||
|
<vn-textfield
|
||||||
|
label="Serial"
|
||||||
|
ng-model="$ctrl.filter.serial"
|
||||||
|
ng-keydown="$ctrl.onKeyPress($event)">
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
|
<div class="chips">
|
||||||
|
<vn-chip
|
||||||
|
ng-if="$ctrl.filter.serial"
|
||||||
|
removable="true"
|
||||||
|
on-remove="$ctrl.removeItemFilter('serial')"
|
||||||
|
class="colored">
|
||||||
|
<span>{{$ctrl.$t('Serial')}}: {{$ctrl.filter.serial}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
</div>
|
||||||
|
</vn-side-menu>
|
|
@ -0,0 +1,44 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import SearchPanel from 'core/components/searchbar/search-panel';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class Controller extends SearchPanel {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
this.filter = {};
|
||||||
|
const filter = {
|
||||||
|
fields: ['daysAgo']
|
||||||
|
};
|
||||||
|
this.$http.get('InvoiceInConfigs', {filter}).then(res => {
|
||||||
|
if (res.data) {
|
||||||
|
this.invoiceInConfig = res.data[0];
|
||||||
|
this.addFilters();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
removeItemFilter(param) {
|
||||||
|
this.filter[param] = null;
|
||||||
|
this.addFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyPress($event) {
|
||||||
|
if ($event.key === 'Enter')
|
||||||
|
this.addFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
addFilters() {
|
||||||
|
if (!this.filter.daysAgo)
|
||||||
|
this.filter.daysAgo = this.invoiceInConfig.daysAgo;
|
||||||
|
|
||||||
|
return this.model.addFilter({}, this.filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.component('vnInvoiceInSerialSearchPanel', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
model: '<'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,43 @@
|
||||||
|
import './index.js';
|
||||||
|
|
||||||
|
describe('InvoiceIn', () => {
|
||||||
|
describe('Component serial-search-panel', () => {
|
||||||
|
let controller;
|
||||||
|
let $scope;
|
||||||
|
|
||||||
|
beforeEach(ngModule('invoiceIn'));
|
||||||
|
|
||||||
|
beforeEach(inject(($componentController, $rootScope) => {
|
||||||
|
$scope = $rootScope.$new();
|
||||||
|
const $element = angular.element('<vn-invoice-in-serial-search-panel></vn-invoice-in-serial-search-panel>');
|
||||||
|
controller = $componentController('vnInvoiceInSerialSearchPanel', {$element, $scope});
|
||||||
|
controller.model = {
|
||||||
|
addFilter: jest.fn(),
|
||||||
|
};
|
||||||
|
controller.invoiceInConfig = {
|
||||||
|
daysAgo: 45,
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('addFilters()', () => {
|
||||||
|
it('should add default daysAgo if it is not already set', () => {
|
||||||
|
controller.filter = {
|
||||||
|
serial: 'R',
|
||||||
|
};
|
||||||
|
controller.addFilters();
|
||||||
|
|
||||||
|
expect(controller.filter.daysAgo).toEqual(controller.invoiceInConfig.daysAgo);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add default daysAgo if it is already set', () => {
|
||||||
|
controller.filter = {
|
||||||
|
daysAgo: 1,
|
||||||
|
serial: 'R',
|
||||||
|
};
|
||||||
|
controller.addFilters();
|
||||||
|
|
||||||
|
expect(controller.filter.daysAgo).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
vn-invoice-in-serial-search-panel vn-side-menu div {
|
||||||
|
& > .input {
|
||||||
|
padding-left: $spacing-md;
|
||||||
|
padding-right: $spacing-md;
|
||||||
|
border-color: $color-spacer;
|
||||||
|
border-bottom: $border-thin;
|
||||||
|
}
|
||||||
|
& > .horizontal {
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-column-gap: $spacing-sm;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
& > .chips {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: $spacing-md;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 100%;
|
||||||
|
border-color: $color-spacer;
|
||||||
|
border-top: $border-thin;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<vn-crud-model
|
||||||
|
vn-id="model"
|
||||||
|
url="InvoiceIns/getSerial"
|
||||||
|
limit="20">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-portal slot="topbar">
|
||||||
|
</vn-portal>
|
||||||
|
<vn-invoice-in-serial-search-panel
|
||||||
|
model="model">
|
||||||
|
</vn-invoice-in-serial-search-panel>
|
||||||
|
<vn-data-viewer
|
||||||
|
model="model"
|
||||||
|
class="vn-w-lg">
|
||||||
|
<vn-card>
|
||||||
|
<vn-table model="model">
|
||||||
|
<vn-thead>
|
||||||
|
<vn-tr>
|
||||||
|
<vn-th field="serial">Serial</vn-th>
|
||||||
|
<vn-th field="pending">Pending</vn-th>
|
||||||
|
<vn-th field="total">Total</vn-th>
|
||||||
|
<vn-th></vn-th>
|
||||||
|
</vn-tr>
|
||||||
|
</vn-thead>
|
||||||
|
<vn-tbody>
|
||||||
|
<vn-tr ng-repeat="invoiceIn in model.data">
|
||||||
|
<vn-td>{{::invoiceIn.serial}}</vn-td>
|
||||||
|
<vn-td>{{::invoiceIn.pending}}</vn-td>
|
||||||
|
<vn-td>{{::invoiceIn.total}}</vn-td>
|
||||||
|
<vn-td shrink>
|
||||||
|
<vn-icon-button
|
||||||
|
vn-click-stop="$ctrl.goToIndex(model.userParams.daysAgo, invoiceIn.serial)"
|
||||||
|
vn-tooltip="Go to InvoiceIn"
|
||||||
|
icon="icon-invoice-in">
|
||||||
|
</vn-icon-button>
|
||||||
|
</vn-td>
|
||||||
|
</vn-tr>
|
||||||
|
</vn-tbody>
|
||||||
|
</vn-table>
|
||||||
|
</vn-card>
|
||||||
|
</vn-data-viewer>
|
|
@ -0,0 +1,22 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
|
||||||
|
export default class Controller extends Section {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
}
|
||||||
|
|
||||||
|
goToIndex(daysAgo, serial) {
|
||||||
|
const issued = Date.vnNew();
|
||||||
|
issued.setDate(issued.getDate() - daysAgo);
|
||||||
|
this.$state.go('invoiceIn.index',
|
||||||
|
{q: `{"serial": "${serial}", "isBooked": false, "from": ${issued.getTime()}}`});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.$inject = ['$element', '$scope'];
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnInvoiceInSerial', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
Serial: Serie
|
||||||
|
Pending: Pendientes
|
||||||
|
Go to InvoiceIn: Ir al listado de facturas recibidas
|
|
@ -40,11 +40,11 @@ Create: Crear
|
||||||
Client card: Ficha del cliente
|
Client card: Ficha del cliente
|
||||||
Shipped: F. envío
|
Shipped: F. envío
|
||||||
stems: Tallos
|
stems: Tallos
|
||||||
Weight/Piece: Peso/tallo
|
Weight/Piece: Peso (gramos)/tallo
|
||||||
Search items by id, name or barcode: Buscar articulos por identificador, nombre o codigo de barras
|
Search items by id, name or barcode: Buscar articulos por identificador, nombre o codigo de barras
|
||||||
SalesPerson: Comercial
|
SalesPerson: Comercial
|
||||||
Concept: Concepto
|
Concept: Concepto
|
||||||
Units/Box: Unidades/Caja
|
Units/Box: Unidades/caja
|
||||||
|
|
||||||
# Sections
|
# Sections
|
||||||
Items: Artículos
|
Items: Artículos
|
||||||
|
|
|
@ -367,20 +367,37 @@ module.exports = Self => {
|
||||||
return {'t.alertLevel': value};
|
return {'t.alertLevel': value};
|
||||||
case 'pending':
|
case 'pending':
|
||||||
if (value) {
|
if (value) {
|
||||||
return {and: [
|
return {'t.alertLevelCode': {inq: [
|
||||||
{'t.alertLevel': 0},
|
'FIXING',
|
||||||
{'t.alertLevelCode': {nin: [
|
'FREE',
|
||||||
'OK',
|
'NOT_READY',
|
||||||
'BOARDING',
|
'BLOCKED',
|
||||||
'PRINTED',
|
'EXPANDABLE',
|
||||||
'PRINTED_AUTO',
|
'CHAINED',
|
||||||
'PICKER_DESIGNED'
|
'WAITING_FOR_PAYMENT'
|
||||||
]}}
|
]}};
|
||||||
]};
|
|
||||||
} else {
|
} else {
|
||||||
return {and: [
|
return {'t.alertLevelCode': {inq: [
|
||||||
{'t.alertLevel': {gt: 0}}
|
'ON_PREPARATION',
|
||||||
]};
|
'ON_CHECKING',
|
||||||
|
'CHECKED',
|
||||||
|
'PACKING',
|
||||||
|
'PACKED',
|
||||||
|
'INVOICED',
|
||||||
|
'ON_DELIVERY',
|
||||||
|
'PREPARED',
|
||||||
|
'WAITING_FOR_PICKUP',
|
||||||
|
'DELIVERED',
|
||||||
|
'PRINTED_BACK',
|
||||||
|
'LAST_CALL',
|
||||||
|
'PREVIOUS_PREPARATION',
|
||||||
|
'ASSISTED_PREPARATION',
|
||||||
|
'BOARD',
|
||||||
|
'PRINTED STOWAWAY',
|
||||||
|
'OK STOWAWAY',
|
||||||
|
'HALF_PACKED',
|
||||||
|
'COOLER_PREPARATION'
|
||||||
|
]}};
|
||||||
}
|
}
|
||||||
case 'agencyModeFk':
|
case 'agencyModeFk':
|
||||||
case 'warehouseFk':
|
case 'warehouseFk':
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Status log: Hitorial de estados
|
Status log: Historial de estados
|
||||||
Expedition removed: Expedición eliminada
|
Expedition removed: Expedición eliminada
|
||||||
Move: Mover
|
Move: Mover
|
||||||
New ticket without route: Nuevo ticket sin ruta
|
New ticket without route: Nuevo ticket sin ruta
|
||||||
|
|
|
@ -28,8 +28,9 @@
|
||||||
</vn-button-menu>
|
</vn-button-menu>
|
||||||
<vn-button icon="keyboard_arrow_down"
|
<vn-button icon="keyboard_arrow_down"
|
||||||
label="More"
|
label="More"
|
||||||
ng-click="moreOptions.show($event)"
|
disabled="!$ctrl.hasSelectedSales()"
|
||||||
ng-show="$ctrl.hasSelectedSales()">
|
vn-tooltip="Select lines to see the options"
|
||||||
|
ng-click="moreOptions.show($event)">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
<vn-button
|
<vn-button
|
||||||
disabled="!$ctrl.hasSelectedSales() || !$ctrl.isEditable"
|
disabled="!$ctrl.hasSelectedSales() || !$ctrl.isEditable"
|
||||||
|
|
|
@ -40,3 +40,4 @@ Refund: Abono
|
||||||
Promotion mana: Maná promoción
|
Promotion mana: Maná promoción
|
||||||
Claim mana: Maná reclamación
|
Claim mana: Maná reclamación
|
||||||
History: Historial
|
History: Historial
|
||||||
|
Select lines to see the options: Seleccione lineas para ver las opciones
|
||||||
|
|
|
@ -6,6 +6,6 @@ Phy. KG: KG físico
|
||||||
Vol. KG: KG Vol.
|
Vol. KG: KG Vol.
|
||||||
Search by travel id or reference: Buscar por id de travel o referencia
|
Search by travel id or reference: Buscar por id de travel o referencia
|
||||||
Search by extra community travel: Buscar por envío extra comunitario
|
Search by extra community travel: Buscar por envío extra comunitario
|
||||||
Continent Out: Continente salida
|
Continent Out: Cont. salida
|
||||||
W. Shipped: F. envío
|
W. Shipped: F. envío
|
||||||
W. Landed: F. llegada
|
W. Landed: F. llegada
|
|
@ -2,6 +2,15 @@
|
||||||
<vn-auto-search
|
<vn-auto-search
|
||||||
model="model">
|
model="model">
|
||||||
</vn-auto-search>
|
</vn-auto-search>
|
||||||
|
<vn-travel-search-panel
|
||||||
|
model="model">
|
||||||
|
</vn-travel-search-panel>
|
||||||
|
<vn-crud-model
|
||||||
|
vn-id="model"
|
||||||
|
url="Travels/filter"
|
||||||
|
limit="20"
|
||||||
|
order="shipped DESC, landed DESC">
|
||||||
|
</vn-crud-model>
|
||||||
<vn-data-viewer
|
<vn-data-viewer
|
||||||
model="model"
|
model="model"
|
||||||
class="vn-mb-xl vn-w-xl">
|
class="vn-mb-xl vn-w-xl">
|
||||||
|
@ -9,15 +18,15 @@
|
||||||
<vn-table model="model">
|
<vn-table model="model">
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th field="id" number filter-enabled="false">Id</vn-th>
|
|
||||||
<vn-th field="ref">Reference</vn-th>
|
<vn-th field="ref">Reference</vn-th>
|
||||||
<vn-th field="agencyModeFk">Agency</vn-th>
|
<vn-th field="agencyModeFk">Agency</vn-th>
|
||||||
<vn-th field="warehouseOutFk">Warehouse Out</vn-th>
|
<vn-th field="warehouseOutFk">Warehouse Out</vn-th>
|
||||||
<vn-th field="shipped" center shrink-date>Shipped</vn-th>
|
<vn-th field="shipped" center shrink-date>Shipped</vn-th>
|
||||||
<vn-th field="isDelivered" center>Delivered</vn-th>
|
<vn-th shrink></vn-th>
|
||||||
<vn-th field="warehouseInFk">Warehouse In</vn-th>
|
<vn-th field="warehouseInFk">Warehouse In</vn-th>
|
||||||
<vn-th field="landed" center shrink-date>Landed</vn-th>
|
<vn-th field="landed" center shrink-date>Landed</vn-th>
|
||||||
<vn-th field="isReceived" center>Received</vn-th>
|
<vn-th shrink></vn-th>
|
||||||
|
<vn-th shrink field="totalEntries">Total entries</vn-th>
|
||||||
<vn-th shrink></vn-th>
|
<vn-th shrink></vn-th>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
</vn-thead>
|
</vn-thead>
|
||||||
|
@ -25,7 +34,6 @@
|
||||||
<a ng-repeat="travel in model.data"
|
<a ng-repeat="travel in model.data"
|
||||||
class="clickable vn-tr search-result"
|
class="clickable vn-tr search-result"
|
||||||
ui-sref="travel.card.summary({id: {{::travel.id}}})">
|
ui-sref="travel.card.summary({id: {{::travel.id}}})">
|
||||||
<vn-td number>{{::travel.id}}</vn-td>
|
|
||||||
<vn-td>{{::travel.ref}}</vn-td>
|
<vn-td>{{::travel.ref}}</vn-td>
|
||||||
<vn-td>{{::travel.agencyModeName}}</vn-td>
|
<vn-td>{{::travel.agencyModeName}}</vn-td>
|
||||||
<vn-td>{{::travel.warehouseOutName}}</vn-td>
|
<vn-td>{{::travel.warehouseOutName}}</vn-td>
|
||||||
|
@ -34,14 +42,27 @@
|
||||||
{{::travel.shipped | date:'dd/MM/yyyy'}}
|
{{::travel.shipped | date:'dd/MM/yyyy'}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td center><vn-check ng-model="travel.isDelivered" disabled="true"></vn-check></vn-td>
|
<vn-td shrink>
|
||||||
|
<vn-icon
|
||||||
|
icon="flight_takeoff"
|
||||||
|
translate-attr="{title: 'Delivered'}"
|
||||||
|
ng-class="{active: travel.isDelivered}">
|
||||||
|
</vn-icon>
|
||||||
|
</vn-td>
|
||||||
<vn-td expand>{{::travel.warehouseInName}}</vn-td>
|
<vn-td expand>{{::travel.warehouseInName}}</vn-td>
|
||||||
<vn-td center shrink-date>
|
<vn-td center shrink-date>
|
||||||
<span class="chip {{$ctrl.compareDate(travel.landed)}}">
|
<span class="chip {{$ctrl.compareDate(travel.landed)}}">
|
||||||
{{::travel.landed | date:'dd/MM/yyyy'}}
|
{{::travel.landed | date:'dd/MM/yyyy'}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td center><vn-check ng-model="travel.isReceived" disabled="true"></vn-check></vn-td>
|
<vn-td shrink>
|
||||||
|
<vn-icon
|
||||||
|
icon="flight_land"
|
||||||
|
translate-attr="{title: 'Received'}"
|
||||||
|
ng-class="{active: travel.isReceived}">
|
||||||
|
</vn-icon>
|
||||||
|
</vn-td>
|
||||||
|
<vn-td shrink>{{::travel.totalEntries}}</vn-td>
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<vn-horizontal class="buttons">
|
<vn-horizontal class="buttons">
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
|
@ -84,32 +105,3 @@
|
||||||
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>
|
||||||
<vn-contextmenu vn-id="contextmenu" targets="['vn-data-viewer']" model="model"
|
|
||||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
|
||||||
<slot-menu>
|
|
||||||
<vn-item translate
|
|
||||||
ng-if="contextmenu.isFilterAllowed()"
|
|
||||||
ng-click="contextmenu.filterBySelection()">
|
|
||||||
Filter by selection
|
|
||||||
</vn-item>
|
|
||||||
<vn-item translate
|
|
||||||
ng-if="contextmenu.isFilterAllowed()"
|
|
||||||
ng-click="contextmenu.excludeSelection()">
|
|
||||||
Exclude selection
|
|
||||||
</vn-item>
|
|
||||||
<vn-item translate
|
|
||||||
ng-if="contextmenu.isFilterAllowed()"
|
|
||||||
ng-click="contextmenu.removeFilter()">
|
|
||||||
Remove filter
|
|
||||||
</vn-item>
|
|
||||||
<vn-item translate
|
|
||||||
ng-click="contextmenu.removeAllFilters()" >
|
|
||||||
Remove all filters
|
|
||||||
</vn-item>
|
|
||||||
<vn-item translate
|
|
||||||
ng-if="contextmenu.isActionAllowed()"
|
|
||||||
ng-click="contextmenu.copyValue()">
|
|
||||||
Copy value
|
|
||||||
</vn-item>
|
|
||||||
</slot-menu>
|
|
||||||
</vn-contextmenu>
|
|
|
@ -1,5 +1,6 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
export default class Controller extends Section {
|
export default class Controller extends Section {
|
||||||
preview(travel) {
|
preview(travel) {
|
||||||
|
@ -30,37 +31,6 @@ export default class Controller extends Section {
|
||||||
if (timeDifference == 0) return 'warning';
|
if (timeDifference == 0) return 'warning';
|
||||||
if (timeDifference < 0) return 'success';
|
if (timeDifference < 0) return 'success';
|
||||||
}
|
}
|
||||||
|
|
||||||
exprBuilder(param, value) {
|
|
||||||
switch (param) {
|
|
||||||
case 'search':
|
|
||||||
return /^\d+$/.test(value)
|
|
||||||
? {'t.id': value}
|
|
||||||
: {'t.ref': {like: `%${value}%`}};
|
|
||||||
case 'ref':
|
|
||||||
return {'t.ref': {like: `%${value}%`}};
|
|
||||||
case 'shipped':
|
|
||||||
return {'t.shipped': {between: this.dateRange(value)}};
|
|
||||||
case 'landed':
|
|
||||||
return {'t.landed': {between: this.dateRange(value)}};
|
|
||||||
case 'id':
|
|
||||||
case 'agencyModeFk':
|
|
||||||
case 'warehouseOutFk':
|
|
||||||
case 'warehouseInFk':
|
|
||||||
case 'totalEntries':
|
|
||||||
param = `t.${param}`;
|
|
||||||
return {[param]: value};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dateRange(value) {
|
|
||||||
const minHour = new Date(value);
|
|
||||||
minHour.setHours(0, 0, 0, 0);
|
|
||||||
const maxHour = new Date(value);
|
|
||||||
maxHour.setHours(23, 59, 59, 59);
|
|
||||||
|
|
||||||
return [minHour, maxHour];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnTravelIndex', {
|
ngModule.vnComponent('vnTravelIndex', {
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
vn-travel-index {
|
||||||
|
vn-icon {
|
||||||
|
color: $color-font-secondary
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-icon.active {
|
||||||
|
color: $color-success
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
#Ordenar alfabeticamente
|
#Ordenar alfabeticamente
|
||||||
Reference: Referencia
|
Reference: Referencia
|
||||||
Warehouse Out: Almacén salida
|
Warehouse Out: Alm salida
|
||||||
Warehouse In: Almacén llegada
|
Warehouse In: Alm llegada
|
||||||
Shipped from: Salida desde
|
Shipped from: Salida desde
|
||||||
Shipped to: Salida hasta
|
Shipped to: Salida hasta
|
||||||
Landed from: Llegada desde
|
Landed from: Llegada desde
|
||||||
|
@ -10,8 +10,8 @@ Shipped: F. salida
|
||||||
Landed: F. llegada
|
Landed: F. llegada
|
||||||
Delivered: Enviado
|
Delivered: Enviado
|
||||||
Received: Recibido
|
Received: Recibido
|
||||||
Travel id: Id envío
|
Travel id: Id
|
||||||
Search travels by id: Buscar envíos por identificador
|
Search travels by id: Buscar envíos por identificador o referencia
|
||||||
New travel: Nuevo envío
|
New travel: Nuevo envío
|
||||||
travel: envío
|
travel: envío
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,4 @@
|
||||||
<vn-crud-model
|
|
||||||
vn-id="model"
|
|
||||||
url="Travels/filter"
|
|
||||||
limit="20"
|
|
||||||
order="shipped DESC, landed DESC">
|
|
||||||
</vn-crud-model>
|
|
||||||
<vn-portal slot="topbar">
|
<vn-portal slot="topbar">
|
||||||
<vn-searchbar
|
|
||||||
vn-focus
|
|
||||||
panel="vn-travel-search-panel"
|
|
||||||
info="Search travels by id"
|
|
||||||
model="model"
|
|
||||||
fetch-params="$ctrl.fetchParams($params)"
|
|
||||||
suggested-filter="$ctrl.filterParams">
|
|
||||||
</vn-searchbar>
|
|
||||||
</vn-portal>
|
</vn-portal>
|
||||||
<vn-portal slot="menu">
|
<vn-portal slot="menu">
|
||||||
<vn-left-menu></vn-left-menu>
|
<vn-left-menu></vn-left-menu>
|
||||||
|
|
|
@ -4,28 +4,6 @@ import ModuleMain from 'salix/components/module-main';
|
||||||
export default class Travel extends ModuleMain {
|
export default class Travel extends ModuleMain {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.filterParams = {
|
|
||||||
scopeDays: 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchParams($params) {
|
|
||||||
if (!Object.entries($params).length)
|
|
||||||
$params.scopeDays = 1;
|
|
||||||
|
|
||||||
if (typeof $params.scopeDays === 'number') {
|
|
||||||
const shippedFrom = Date.vnNew();
|
|
||||||
shippedFrom.setHours(0, 0, 0, 0);
|
|
||||||
|
|
||||||
const shippedTo = new Date(shippedFrom.getTime());
|
|
||||||
shippedTo.setDate(shippedTo.getDate() + $params.scopeDays);
|
|
||||||
shippedTo.setHours(23, 59, 59, 999);
|
|
||||||
|
|
||||||
Object.assign($params, {shippedFrom, shippedTo});
|
|
||||||
}
|
|
||||||
|
|
||||||
return $params;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
import './index.js';
|
|
||||||
|
|
||||||
describe('Travel Component vnTravel', () => {
|
|
||||||
let controller;
|
|
||||||
|
|
||||||
beforeEach(ngModule('travel'));
|
|
||||||
|
|
||||||
beforeEach(inject($componentController => {
|
|
||||||
let $element = angular.element(`<div></div>`);
|
|
||||||
controller = $componentController('vnTravel', {$element});
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('fetchParams()', () => {
|
|
||||||
it('should return a range of dates with passed scope days', () => {
|
|
||||||
let params = controller.fetchParams({
|
|
||||||
scopeDays: 2
|
|
||||||
});
|
|
||||||
const shippedFrom = Date.vnNew();
|
|
||||||
shippedFrom.setHours(0, 0, 0, 0);
|
|
||||||
const shippedTo = new Date(shippedFrom.getTime());
|
|
||||||
shippedTo.setDate(shippedTo.getDate() + params.scopeDays);
|
|
||||||
shippedTo.setHours(23, 59, 59, 999);
|
|
||||||
|
|
||||||
const expectedParams = {
|
|
||||||
shippedFrom,
|
|
||||||
scopeDays: params.scopeDays,
|
|
||||||
shippedTo
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(params).toEqual(expectedParams);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return default value for scope days', () => {
|
|
||||||
let params = controller.fetchParams({
|
|
||||||
scopeDays: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(params.scopeDays).toEqual(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return the given scope days', () => {
|
|
||||||
let params = controller.fetchParams({
|
|
||||||
scopeDays: 2
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(params.scopeDays).toEqual(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,109 +1,146 @@
|
||||||
<div class="search-panel">
|
<vn-side-menu side="right">
|
||||||
<form ng-submit="$ctrl.onSearch()" id="manifold-form">
|
<vn-horizontal class="input">
|
||||||
<vn-horizontal class="vn-px-lg vn-pt-lg">
|
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
|
||||||
label="General search"
|
label="General search"
|
||||||
ng-model="filter.search"
|
|
||||||
info="Search travels by id"
|
info="Search travels by id"
|
||||||
vn-focus>
|
ng-model="$ctrl.search"
|
||||||
|
ng-keydown="$ctrl.onKeyPress($event, 'search')">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal class="vn-px-lg">
|
<vn-horizontal class="input horizontal">
|
||||||
<vn-textfield
|
<vn-autocomplete
|
||||||
vn-one
|
vn-id="agency"
|
||||||
label="Reference"
|
|
||||||
ng-model="filter.ref">
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="Total entries"
|
|
||||||
ng-model="filter.totalEntries">
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal class="vn-px-lg">
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="Travel id"
|
|
||||||
ng-model="filter.id">
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-autocomplete vn-one
|
|
||||||
label="Agency"
|
label="Agency"
|
||||||
ng-model="filter.agencyModeFk"
|
ng-model="$ctrl.filter.agencyModeFk"
|
||||||
url="AgencyModes"
|
data="$ctrl.agencyModes"
|
||||||
show-field="name"
|
show-field="name"
|
||||||
value-field="id">
|
value-field="id"
|
||||||
|
on-change="$ctrl.applyFilters()">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<section class="vn-px-md">
|
<vn-horizontal class="input horizontal">
|
||||||
<vn-horizontal class="manifold-panel vn-pa-md">
|
<vn-autocomplete
|
||||||
<vn-date-picker
|
vn-id="warehouseOut"
|
||||||
vn-one
|
label="Warehouse Out"
|
||||||
label="Shipped from"
|
ng-model="$ctrl.filter.warehouseOutFk"
|
||||||
ng-model="filter.shippedFrom"
|
data="$ctrl.warehouses"
|
||||||
on-change="$ctrl.shippedFrom = value">
|
show-field="name"
|
||||||
</vn-date-picker>
|
value-field="id"
|
||||||
<vn-date-picker
|
on-change="$ctrl.applyFilters()">
|
||||||
vn-one
|
</vn-autocomplete>
|
||||||
label="Shipped to"
|
<vn-autocomplete
|
||||||
ng-model="filter.shippedTo"
|
vn-id="warehouseIn"
|
||||||
on-change="$ctrl.shippedTo = value">
|
label="Warehouse In"
|
||||||
</vn-date-picker>
|
ng-model="$ctrl.filter.warehouseInFk"
|
||||||
<vn-none class="or vn-px-md" translate>Or</vn-none>
|
data="$ctrl.warehouses"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id"
|
||||||
|
on-change="$ctrl.applyFilters()">
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal class="input horizontal">
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
vn-one
|
|
||||||
min="0"
|
min="0"
|
||||||
step="1"
|
step="1"
|
||||||
label="Days onward"
|
label="Days onward"
|
||||||
ng-model="filter.scopeDays"
|
ng-model="$ctrl.filter.scopeDays"
|
||||||
on-change="$ctrl.scopeDays = value"
|
on-change="$ctrl.applyFilters()"
|
||||||
display-controls="true">
|
display-controls="true"
|
||||||
|
info="Cannot choose a range of dates and days onward at the same time">
|
||||||
</vn-input-number>
|
</vn-input-number>
|
||||||
<vn-icon color-marginal
|
|
||||||
icon="info"
|
|
||||||
vn-tooltip="Cannot choose a range of dates and days onward at the same time">
|
|
||||||
</vn-icon>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</section>
|
<vn-horizontal class="input horizontal">
|
||||||
<vn-horizontal class="vn-px-lg">
|
|
||||||
<vn-date-picker
|
<vn-date-picker
|
||||||
vn-one
|
|
||||||
label="Landed from"
|
label="Landed from"
|
||||||
ng-model="filter.landedFrom">
|
ng-model="$ctrl.filter.landedFrom"
|
||||||
|
on-change="$ctrl.applyFilters()">
|
||||||
</vn-date-picker>
|
</vn-date-picker>
|
||||||
<vn-date-picker
|
<vn-date-picker
|
||||||
vn-one
|
|
||||||
label="Landed to"
|
label="Landed to"
|
||||||
ng-model="filter.landedTo">
|
ng-model="$ctrl.filter.landedTo"
|
||||||
|
on-change="$ctrl.applyFilters()">
|
||||||
</vn-date-picker>
|
</vn-date-picker>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal class="vn-px-lg">
|
<vn-horizontal class="input horizontal">
|
||||||
<vn-autocomplete vn-one
|
<vn-autocomplete
|
||||||
label="Warehouse Out"
|
vn-id="continent"
|
||||||
ng-model="filter.warehouseOutFk"
|
|
||||||
url="Warehouses"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id">
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-autocomplete vn-one
|
|
||||||
label="Warehouse In"
|
|
||||||
ng-model="filter.warehouseInFk"
|
|
||||||
url="Warehouses"
|
|
||||||
show-field="name"
|
|
||||||
value-field="id">
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal class="vn-px-lg">
|
|
||||||
<vn-autocomplete vn-one
|
|
||||||
label="Continent Out"
|
label="Continent Out"
|
||||||
ng-model="filter.continent"
|
ng-model="$ctrl.filter.continent"
|
||||||
url="Continents"
|
data="$ctrl.continents"
|
||||||
show-field="name"
|
show-field="name"
|
||||||
value-field="code">
|
value-field="code"
|
||||||
|
on-change="$ctrl.applyFilters()">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
|
<vn-input-number
|
||||||
|
min="0"
|
||||||
|
label="Total entries"
|
||||||
|
ng-model="$ctrl.totalEntries"
|
||||||
|
ng-keydown="$ctrl.onKeyPress($event, 'totalEntries')">
|
||||||
|
</vn-input-number>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
|
<div class="chips">
|
||||||
<vn-submit label="Search"></vn-submit>
|
<vn-chip
|
||||||
</vn-horizontal>
|
ng-if="$ctrl.filter.search"
|
||||||
</form>
|
removable="true"
|
||||||
</div>
|
on-remove="$ctrl.removeParamFilter('search')"
|
||||||
|
class="colored">
|
||||||
|
<span>Id/{{$ctrl.$t('Reference')}}: {{$ctrl.filter.search}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip
|
||||||
|
ng-if="agency.selection"
|
||||||
|
removable="true"
|
||||||
|
on-remove="$ctrl.removeParamFilter('agencyModeFk')"
|
||||||
|
class="colored">
|
||||||
|
<span>{{$ctrl.$t('Agency')}}: {{agency.selection.name}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip
|
||||||
|
ng-if="warehouseOut.selection"
|
||||||
|
removable="true"
|
||||||
|
on-remove="$ctrl.removeParamFilter('warehouseOutFk')"
|
||||||
|
class="colored">
|
||||||
|
<span>{{$ctrl.$t('Warehouse Out')}}: {{warehouseOut.selection.name}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip
|
||||||
|
ng-if="warehouseIn.selection"
|
||||||
|
removable="true"
|
||||||
|
on-remove="$ctrl.removeParamFilter('warehouseInFk')"
|
||||||
|
class="colored">
|
||||||
|
<span>{{$ctrl.$t('Warehouse In')}}: {{warehouseIn.selection.name}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip
|
||||||
|
ng-if="$ctrl.filter.scopeDays"
|
||||||
|
removable="true"
|
||||||
|
on-remove="$ctrl.removeParamFilter('scopeDays')"
|
||||||
|
class="colored">
|
||||||
|
<span>{{$ctrl.$t('Days onward')}}: {{$ctrl.filter.scopeDays}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip
|
||||||
|
ng-if="$ctrl.filter.landedFrom"
|
||||||
|
removable="true"
|
||||||
|
on-remove="$ctrl.removeParamFilter('landedFrom')"
|
||||||
|
class="colored">
|
||||||
|
<span>{{$ctrl.$t('Landed from')}}: {{$ctrl.filter.landedFrom | date:'dd/MM/yyyy'}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip
|
||||||
|
ng-if="$ctrl.filter.landedTo"
|
||||||
|
removable="true"
|
||||||
|
on-remove="$ctrl.removeParamFilter('landedTo')"
|
||||||
|
class="colored">
|
||||||
|
<span>{{$ctrl.$t('Landed to')}}: {{$ctrl.filter.landedTo | date:'dd/MM/yyyy'}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip
|
||||||
|
ng-if="continent.selection"
|
||||||
|
removable="true"
|
||||||
|
on-remove="$ctrl.removeParamFilter('continent')"
|
||||||
|
class="colored">
|
||||||
|
<span>{{$ctrl.$t('Continent Out')}}: {{continent.selection.name}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
<vn-chip
|
||||||
|
ng-if="$ctrl.filter.totalEntries"
|
||||||
|
removable="true"
|
||||||
|
on-remove="$ctrl.removeParamFilter('totalEntries')"
|
||||||
|
class="colored">
|
||||||
|
<span>{{$ctrl.$t('Total entries')}}: {{$ctrl.filter.totalEntries}}</span>
|
||||||
|
</vn-chip>
|
||||||
|
</div>
|
||||||
|
</vn-side-menu>
|
||||||
|
|
|
@ -1,43 +1,69 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import SearchPanel from 'core/components/searchbar/search-panel';
|
import SearchPanel from 'core/components/searchbar/search-panel';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
class Controller extends SearchPanel {
|
class Controller extends SearchPanel {
|
||||||
constructor($, $element) {
|
constructor($, $element) {
|
||||||
super($, $element);
|
super($, $element);
|
||||||
this.filter = this.$.filter;
|
this.initFilter();
|
||||||
|
this.fetchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
get shippedFrom() {
|
$onChanges() {
|
||||||
return this._shippedFrom;
|
if (this.model)
|
||||||
|
this.applyFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
set shippedFrom(value) {
|
fetchData() {
|
||||||
this._shippedFrom = value;
|
this.$http.get('AgencyModes').then(res => {
|
||||||
this.filter.scopeDays = null;
|
this.agencyModes = res.data;
|
||||||
|
});
|
||||||
|
this.$http.get('Warehouses').then(res => {
|
||||||
|
this.warehouses = res.data;
|
||||||
|
});
|
||||||
|
this.$http.get('Continents').then(res => {
|
||||||
|
this.continents = res.data;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get shippedTo() {
|
initFilter() {
|
||||||
return this._shippedTo;
|
this.filter = {};
|
||||||
|
if (this.$params.q) {
|
||||||
|
this.filter = JSON.parse(this.$params.q);
|
||||||
|
this.search = this.filter.search;
|
||||||
|
this.totalEntries = this.filter.totalEntries;
|
||||||
|
}
|
||||||
|
if (!this.filter.scopeDays) this.filter.scopeDays = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
set shippedTo(value) {
|
applyFilters(param) {
|
||||||
this._shippedTo = value;
|
this.model.applyFilter({}, this.filter)
|
||||||
this.filter.scopeDays = null;
|
.then(() => {
|
||||||
|
if (param && this.model._orgData.length === 1)
|
||||||
|
this.$state.go('travel.card.summary', {id: this.model._orgData[0].id});
|
||||||
|
else
|
||||||
|
this.$state.go(this.$state.current.name, {q: JSON.stringify(this.filter)}, {location: 'replace'});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get scopeDays() {
|
removeParamFilter(param) {
|
||||||
return this._scopeDays;
|
if (this[param]) delete this[param];
|
||||||
|
delete this.filter[param];
|
||||||
|
this.applyFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
set scopeDays(value) {
|
onKeyPress($event, param) {
|
||||||
this._scopeDays = value;
|
if ($event.key === 'Enter') {
|
||||||
|
this.filter[param] = this[param];
|
||||||
this.filter.shippedFrom = null;
|
this.applyFilters(param === 'search');
|
||||||
this.filter.shippedTo = null;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnTravelSearchPanel', {
|
ngModule.vnComponent('vnTravelSearchPanel', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Controller
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
model: '<'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,41 +8,31 @@ describe('Travel Component vnTravelSearchPanel', () => {
|
||||||
beforeEach(inject($componentController => {
|
beforeEach(inject($componentController => {
|
||||||
controller = $componentController('vnTravelSearchPanel', {$element: null});
|
controller = $componentController('vnTravelSearchPanel', {$element: null});
|
||||||
controller.$t = () => {};
|
controller.$t = () => {};
|
||||||
controller.filter = {};
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('shippedFrom() setter', () => {
|
describe('applyFilters()', () => {
|
||||||
it('should clear the scope days when setting the from property', () => {
|
it('should apply filters', async() => {
|
||||||
controller.filter.scopeDays = 1;
|
controller.filter = {foo: 'bar'};
|
||||||
|
controller.model = {
|
||||||
|
applyFilter: jest.fn().mockResolvedValue(),
|
||||||
|
_orgData: [{id: 1}]
|
||||||
|
};
|
||||||
|
controller.$state = {
|
||||||
|
current: {
|
||||||
|
name: 'foo'
|
||||||
|
},
|
||||||
|
go: jest.fn()
|
||||||
|
};
|
||||||
|
|
||||||
controller.shippedFrom = Date.vnNew();
|
await controller.applyFilters(true);
|
||||||
|
|
||||||
expect(controller.filter.scopeDays).toBeNull();
|
expect(controller.model.applyFilter).toHaveBeenCalledWith({}, controller.filter);
|
||||||
expect(controller.shippedFrom).toBeDefined();
|
expect(controller.$state.go).toHaveBeenCalledWith('travel.card.summary', {id: 1});
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('shippedTo() setter', () => {
|
await controller.applyFilters(false);
|
||||||
it('should clear the scope days when setting the to property', () => {
|
|
||||||
controller.filter.scopeDays = 1;
|
|
||||||
|
|
||||||
controller.shippedTo = Date.vnNew();
|
expect(controller.$state.go).toHaveBeenCalledWith(controller.$state.current.name,
|
||||||
|
{q: JSON.stringify(controller.filter)}, {location: 'replace'});
|
||||||
expect(controller.filter.scopeDays).toBeNull();
|
|
||||||
expect(controller.shippedTo).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('scopeDays() setter', () => {
|
|
||||||
it('should clear the date range when setting the scopeDays property', () => {
|
|
||||||
controller.filter.shippedFrom = Date.vnNew();
|
|
||||||
controller.filter.shippedTo = Date.vnNew();
|
|
||||||
|
|
||||||
controller.scopeDays = 1;
|
|
||||||
|
|
||||||
expect(controller.filter.shippedFrom).toBeNull();
|
|
||||||
expect(controller.filter.shippedTo).toBeNull();
|
|
||||||
expect(controller.scopeDays).toBeDefined();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
vn-travel-search-panel vn-side-menu {
|
||||||
|
.menu {
|
||||||
|
min-width: $right-menu-width;
|
||||||
|
}
|
||||||
|
& > div {
|
||||||
|
.input {
|
||||||
|
padding-left: $spacing-md;
|
||||||
|
padding-right: $spacing-md;
|
||||||
|
border-color: $color-spacer;
|
||||||
|
border-bottom: $border-thin;
|
||||||
|
}
|
||||||
|
.horizontal {
|
||||||
|
padding-left: $spacing-md;
|
||||||
|
padding-right: $spacing-md;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-column-gap: $spacing-sm;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.chips {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: $spacing-md;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 100%;
|
||||||
|
border-color: $color-spacer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.or {
|
||||||
|
align-self: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 26px;
|
||||||
|
color: $color-font-secondary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
Reference: Referencia
|
Reference: Referencia
|
||||||
Warehouse In: Almacén entrada
|
Warehouse In: Alm. entrada
|
||||||
Warehouse Out: Almacén salida
|
Warehouse Out: Alm. salida
|
||||||
Shipped: F. envío
|
Shipped: F. envío
|
||||||
Landed: F. entrega
|
Landed: F. entrega
|
||||||
Total entries: Entradas totales
|
Total entries: Ent. totales
|
||||||
Delivered: Enviada
|
Delivered: Enviada
|
||||||
Received: Recibida
|
Received: Recibida
|
||||||
Agency: Agencia
|
Agency: Agencia
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('createWagonType', {
|
||||||
|
description: 'Creates a new wagon type',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'name',
|
||||||
|
type: 'String',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'divisible',
|
||||||
|
type: 'boolean',
|
||||||
|
required: true
|
||||||
|
}, {
|
||||||
|
arg: 'trays',
|
||||||
|
type: 'any',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/createWagonType`,
|
||||||
|
verb: 'PATCH'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.createWagonType = async(ctx, options) => {
|
||||||
|
const args = ctx.args;
|
||||||
|
const models = Self.app.models;
|
||||||
|
const myOptions = {};
|
||||||
|
let tx;
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newWagonType = await models.WagonType.create({name: args.name, divisible: args.divisible}, myOptions);
|
||||||
|
args.trays.forEach(async tray => {
|
||||||
|
await models.WagonTypeTray.create({
|
||||||
|
typeFk: newWagonType.id,
|
||||||
|
height: tray.position,
|
||||||
|
colorFk: tray.color.id
|
||||||
|
}, myOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,43 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('deleteWagonType', {
|
||||||
|
description: 'Deletes a wagon type',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'Number',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/deleteWagonType`,
|
||||||
|
verb: 'DELETE'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deleteWagonType = async(ctx, options) => {
|
||||||
|
const args = ctx.args;
|
||||||
|
const models = Self.app.models;
|
||||||
|
const myOptions = {};
|
||||||
|
let tx;
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await models.Wagon.destroyAll({typeFk: args.id}, myOptions);
|
||||||
|
await models.WagonTypeTray.destroyAll({typeFk: args.id}, myOptions);
|
||||||
|
await models.WagonType.destroyAll({id: args.id}, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,64 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('editWagonType', {
|
||||||
|
description: 'Edits a new wagon type',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'String',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'name',
|
||||||
|
type: 'String',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'divisible',
|
||||||
|
type: 'boolean',
|
||||||
|
required: true
|
||||||
|
}, {
|
||||||
|
arg: 'trays',
|
||||||
|
type: 'any',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/editWagonType`,
|
||||||
|
verb: 'PATCH'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.editWagonType = async(ctx, options) => {
|
||||||
|
const args = ctx.args;
|
||||||
|
const models = Self.app.models;
|
||||||
|
const myOptions = {};
|
||||||
|
let tx;
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const wagonType = await models.WagonType.findById(args.id, null, myOptions);
|
||||||
|
wagonType.updateAttributes({name: args.name, divisible: args.divisible}, myOptions);
|
||||||
|
models.WagonTypeTray.destroyAll({typeFk: args.id}, myOptions);
|
||||||
|
args.trays.forEach(async tray => {
|
||||||
|
await models.WagonTypeTray.create({
|
||||||
|
typeFk: args.id,
|
||||||
|
height: tray.position,
|
||||||
|
colorFk: tray.color.id
|
||||||
|
}, myOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,63 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('WagonType crudWagonType()', () => {
|
||||||
|
const ctx = {
|
||||||
|
args: {
|
||||||
|
name: 'Mock wagon type',
|
||||||
|
divisible: true,
|
||||||
|
trays: [{position: 0, color: {id: 1}},
|
||||||
|
{position: 50, color: {id: 2}},
|
||||||
|
{position: 100, color: {id: 3}}]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
it(`should create, edit and delete a new wagon type and its trays`, async() => {
|
||||||
|
const tx = await models.WagonType.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
// create
|
||||||
|
await models.WagonType.createWagonType(ctx, options);
|
||||||
|
|
||||||
|
const newWagonType = await models.WagonType.findOne({where: {name: ctx.args.name}}, options);
|
||||||
|
const newWagonTrays = await models.WagonTypeTray.find({where: {typeFk: newWagonType.id}}, options);
|
||||||
|
|
||||||
|
expect(newWagonType).not.toEqual(null);
|
||||||
|
expect(newWagonType.name).toEqual(ctx.args.name);
|
||||||
|
expect(newWagonType.divisible).toEqual(ctx.args.divisible);
|
||||||
|
expect(newWagonTrays.length).toEqual(ctx.args.trays.length);
|
||||||
|
|
||||||
|
ctx.args = {
|
||||||
|
id: newWagonType.id,
|
||||||
|
name: 'Edited wagon type',
|
||||||
|
divisible: false,
|
||||||
|
trays: [{position: 0, color: {id: 1}}]
|
||||||
|
};
|
||||||
|
|
||||||
|
// edit
|
||||||
|
await models.WagonType.editWagonType(ctx, options);
|
||||||
|
|
||||||
|
const editedWagonType = await models.WagonType.findById(newWagonType.id, null, options);
|
||||||
|
const editedWagonTrays = await models.WagonTypeTray.find({where: {typeFk: newWagonType.id}}, options);
|
||||||
|
|
||||||
|
expect(editedWagonType.name).toEqual(ctx.args.name);
|
||||||
|
expect(editedWagonType.divisible).toEqual(ctx.args.divisible);
|
||||||
|
expect(editedWagonTrays.length).toEqual(ctx.args.trays.length);
|
||||||
|
|
||||||
|
// delete
|
||||||
|
await models.WagonType.deleteWagonType(ctx, options);
|
||||||
|
|
||||||
|
const deletedWagonType = await models.WagonType.findById(newWagonType.id, null, options);
|
||||||
|
const deletedWagonTrays = await models.WagonTypeTray.find({where: {typeFk: newWagonType.id}}, options);
|
||||||
|
|
||||||
|
expect(deletedWagonType).toEqual(null);
|
||||||
|
expect(deletedWagonTrays).toEqual([]);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"Wagon": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"WagonType": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"WagonTypeColor": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"WagonTypeTray": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"WagonConfig": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"CollectionWagon": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"CollectionWagonTicket": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"name": "CollectionWagonTicket",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "collectionWagonTicket"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"ticketFk": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"wagonFk": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"trayFk": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"side": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"ticket": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Ticket",
|
||||||
|
"foreignKey": "ticketFk"
|
||||||
|
},
|
||||||
|
"wagon": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Wagon",
|
||||||
|
"foreignKey": "wagonFk"
|
||||||
|
},
|
||||||
|
"tray": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "WagonTypeTray",
|
||||||
|
"foreignKey": "trayFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"name": "CollectionWagon",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "collectionWagon"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"collectionFk": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"wagonFk": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"collection": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Collection",
|
||||||
|
"foreignKey": "collectionFk"
|
||||||
|
},
|
||||||
|
"wagon": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Wagon",
|
||||||
|
"foreignKey": "wagonFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"name": "WagonConfig",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "wagonConfig"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"width": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"maxWagonHeight": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"minHeightBetweenTrays": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"maxTrays": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "WagonTypeColor",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "wagonTypeColor"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"rgb": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"name": "WagonTypeTray",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "wagonTypeTray"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"typeFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"colorFk": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"type": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "WagonType",
|
||||||
|
"foreignKey": "typeFk"
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "WagonTypeColor",
|
||||||
|
"foreignKey": "colorFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
require('../methods/wagonType/createWagonType')(Self);
|
||||||
|
require('../methods/wagonType/editWagonType')(Self);
|
||||||
|
require('../methods/wagonType/deleteWagonType')(Self);
|
||||||
|
};
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "WagonType",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "wagonType"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"divisible": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"name": "Wagon",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "wagon"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"volume": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"plate": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"typeFk": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"type": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "WagonType",
|
||||||
|
"foreignKey": "typeFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "salix-back",
|
"name": "salix-back",
|
||||||
"version": "23.12.01",
|
"version": "23.14.01",
|
||||||
"author": "Verdnatura Levante SL",
|
"author": "Verdnatura Levante SL",
|
||||||
"description": "Salix backend",
|
"description": "Salix backend",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
|
|
Loading…
Reference in New Issue