8069-Overstocking #3051

Merged
pako merged 7 commits from 8069-Overstocking into dev 2024-10-02 12:34:14 +00:00
72 changed files with 72 additions and 3604 deletions
Showing only changes of commit abb39ec0e7 - Show all commits

View File

@ -3190,7 +3190,7 @@ UPDATE vn.department
SET workerFk = null;
INSERT INTO vn.packaging
VALUES('--', 2745600.00, 100.00, 120.00, 220.00, 0.00, 1, '2001-01-01 00:00:00.000', NULL, NULL, NULL, 0.00, 16, 0.00, 0, NULL, 0.00, NULL, NULL, 0, NULL, 0, 0,0);
VALUES('--', 2745600.00, 100.00, 120.00, 220.00, 0.00, 1, '2001-01-01 00:00:00.000', NULL, NULL, NULL, 0.00, 16, 0.00, 0, NULL, 0.00, NULL, NULL, 0, NULL, 0, 0,0,1);
INSERT IGNORE INTO vn.intrastat

View File

@ -0,0 +1,25 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`vn`@`localhost` PROCEDURE `vn`.`ticketRefund_upsert`(
vRefundTicketFk INT,
vOriginalTicketFk INT
)
READS SQL DATA
BEGIN
/**
* Common code for ticketRefund triggers
*
* @param vRefundTicketFk
* @param vOriginalTicketFk
*/
DECLARE vIsDeleted BOOL;
SELECT COUNT(*) INTO vIsDeleted
FROM ticket
WHERE id IN (vRefundTicketFk, vOriginalTicketFk)
AND isDeleted;
IF vIsDeleted THEN
CALL util.throw('The refund ticket cannot be deleted tickets');
END IF;
END$$
DELIMITER ;

View File

@ -3,6 +3,8 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`ticketRefund_beforeInse
BEFORE INSERT ON `ticketRefund`
FOR EACH ROW
BEGIN
CALL ticketRefund_upsert(NEW.refundTicketFk, NEW.originalTicketFk);
SET NEW.editorFk = account.myUser_getId();
END$$
DELIMITER ;

View File

@ -3,6 +3,8 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`ticketRefund_beforeUpda
BEFORE UPDATE ON `ticketRefund`
FOR EACH ROW
BEGIN
CALL ticketRefund_upsert(NEW.refundTicketFk, NEW.originalTicketFk);
SET NEW.editorFk = account.myUser_getId();
END$$
DELIMITER ;

View File

@ -17,5 +17,6 @@ AS SELECT `p`.`id` AS `Id_Cubo`,
`p`.`upload` AS `Suben`,
`p`.`base` AS `Base`,
`p`.`isBox` AS `box`,
`p`.`returnCost` AS `costeRetorno`
`p`.`returnCost` AS `costeRetorno`,
`p`.`isActive` AS `isActive`
FROM `vn`.`packaging` `p`

View File

@ -0,0 +1,6 @@
-- Place your SQL code here
ALTER TABLE vn.priceDelta ADD IF NOT EXISTS zoneGeoFk int(11) NULL;
ALTER TABLE vn.priceDelta ADD CONSTRAINT priceDelta_zoneGeo_FK FOREIGN KEY IF NOT EXISTS (zoneGeoFk)
REFERENCES vn.zoneGeo (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,6 @@
-- Place your SQL code here
ALTER TABLE vn.priceDelta ADD IF NOT EXISTS zoneGeoFk int(11) NULL;
ALTER TABLE vn.priceDelta ADD CONSTRAINT priceDelta_zoneGeo_FK FOREIGN KEY IF NOT EXISTS (zoneGeoFk)
REFERENCES vn.zoneGeo (`id`) ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,24 @@
ALTER TABLE `vn`.`packaging`
ADD COLUMN IF NOT EXISTS `isActive` TINYINT(1) DEFAULT 1;
UPDATE vn.packaging
SET isActive = FALSE
WHERE id IN('06x04x06','07x04x03','1000','100SM','1031','104','105','1060','10x04x06','10x04x07','1100','118','119','1200','129','1300',
'134','146','147','148','158','159','17x01x02','17X01X03','17x01x04','17x01x05','18X01X04','198','199',
'20P','20x01x03','246','273','278','279','280','290','359','37247','382','40P','453','463','464','465','466',
'467','469','471','473','494','508','509','511','512','514','515','516','518','519-50B','575','598-3x6','604','605','606',
'607','609','647','67515','676','680','682','685','687','688','691','692','693','694','695','730','751','7808','790','7910',
'7920','79450','7950','7952','7960','7976','7982','7986','7988',
'7993','8000','8046','8049','8053','8057','8058','8065','8076','8085','8086','8088',
'8091','8095','8096','8097','8101','8106','8108','8110','8112','8124','8134','8140','8141','8143','8145','8149','8150',
'8170','8174','8192','8200','8210','8249','8270','8275','8288','8300','8350','8375','8399','8400','8420','845','847','8480','8500',
'855','858','8600','862','869','871','872','8720','878','879','880','8800','882','885','910','911','912','914','916','917','918','919',
'920','921','922','923','924','925','926','927','930','9300','932','934','935','936','938','942','948','9600','980','984','9920',
'B20x16','B43x13','Bande Rota','bb3','Bcesta','BcestaOVAL','BcestaRED','Bcirios','BciriosG','BjarronBLN','BjarronNGR',
'Btazon','Bvelas','cactus200','Caja040','CajaTGLF','CC Alza Pl','CC_falso',
'EB-RSMINA','EMB 1_4','EMB 2_5','espuma','FB-BENCH','granel','Grenex','guzma1200','guzma1400','guzma330','guzma400','guzma650','guzma900','HB-ALEX',
'HB-APOSENT','HB-MAGIC','HB-NATUF','HB-RSMINA','HB-TES-RSR','HB068','HB117','HB2-CIRCA','JB-AROMA','jumboX3','kalan330','kalan400',
'kalan577','kalan900','L12','L120','L14','L2-120','L200','L3-120','L4-120','L44','L6','L6-180','L8','L8-200','MB-BENCH','MBOLA','mc_11',
'mc_13','Msp','NO VALIDO','NO-002','PANIC','PBLG','PISOCC/3','PISOCC/4','PISOCC/5','PISOCC/6',
'procona','QB-CARDENA','QB-PANDERO','QB-TES-RSR','QB7-TOSCA','QB9-TOSCA','RB-BENCH','SemiEuroPa','spolette','t_flori11','T26x23',
'T26x25','T27x24','T27x30','T28x26','T30x24','T33x30','THA50','ti_13','Tumbado','UB-BENCH')

View File

@ -1,42 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Travel create path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'travel');
});
afterAll(async() => {
await browser.close();
});
it('should create a new travel and check it was created with the correct data', async() => {
const date = Date.vnNew();
date.setDate(15);
date.setUTCHours(0, 0, 0, 0);
await page.waitToClick(selectors.travelIndex.newTravelButton);
await page.waitForState('travel.create');
const values = {
reference: 'Testing reference',
agencyMode: 'inhouse pickup',
shipped: date,
landed: date,
warehouseOut: 'Warehouse One',
warehouseIn: 'Warehouse Five'
};
const message = await page.sendForm('vn-travel-create form', values);
await page.waitForState('travel.card.basicData');
const formValues = await page.fetchForm('vn-travel-basic-data form', Object.keys(values));
expect(message.isSuccess).toBeTrue();
expect(formValues).toEqual(values);
});
});

View File

@ -1,97 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Travel basic data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'travel');
await page.write(selectors.travelIndex.generalSearchFilter, '3');
await page.keyboard.press('Enter');
await page.accessToSection('travel.card.basicData');
});
afterAll(async() => {
await browser.close();
});
it('should reach the thermograph section', async() => {
await page.waitForState('travel.card.basicData');
});
it('should set a wrong delivery date then receive an error on submit', async() => {
await page.loginAndModule('buyer', 'travel');
await page.write(selectors.travelIndex.generalSearchFilter, '4');
await page.keyboard.press('Enter');
await page.accessToSection('travel.card.basicData');
await page.waitForState('travel.card.basicData');
const lastMonth = Date.vnNew();
lastMonth.setMonth(lastMonth.getMonth() - 2);
await page.pickDate(selectors.travelBasicData.deliveryDate, lastMonth);
await page.waitToClick(selectors.travelBasicData.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Landing cannot be lesser than shipment');
});
it('should undo the changes', async() => {
await page.clearInput(selectors.travelBasicData.reference);
await page.write(selectors.travelBasicData.reference, 'totally pointless ref');
await page.waitToClick(selectors.travelBasicData.undoChanges);
const result = await page.waitToGetProperty(selectors.travelBasicData.reference, 'value');
expect(result).toEqual('fourth travel');
});
it('should now edit the whole form then save', async() => {
await page.clearInput(selectors.travelBasicData.reference);
await page.write(selectors.travelBasicData.reference, 'new reference!');
await page.autocompleteSearch(selectors.travelBasicData.agency, 'Entanglement');
await page.autocompleteSearch(selectors.travelBasicData.outputWarehouse, 'Warehouse Three');
await page.autocompleteSearch(selectors.travelBasicData.inputWarehouse, 'Warehouse Four');
await page.waitToClick(selectors.travelBasicData.delivered);
await page.waitToClick(selectors.travelBasicData.received);
await page.waitToClick(selectors.travelBasicData.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reload the section and check the reference was saved', async() => {
await page.reloadSection('travel.card.basicData');
const result = await page.waitToGetProperty(selectors.travelBasicData.reference, 'value');
expect(result).toEqual('new reference!');
});
it('should check the agency was saved', async() => {
const result = await page.waitToGetProperty(selectors.travelBasicData.agency, 'value');
expect(result).toEqual('Entanglement');
});
it('should check the output warehouse date was saved', async() => {
const result = await page.waitToGetProperty(selectors.travelBasicData.outputWarehouse, 'value');
expect(result).toEqual('Warehouse Three');
});
it('should check the input warehouse date was saved', async() => {
const result = await page.waitToGetProperty(selectors.travelBasicData.inputWarehouse, 'value');
expect(result).toEqual('Warehouse Four');
});
it(`should check the delivered checkbox was saved even tho it doesn't make sense`, async() => {
await page.waitForClassPresent(selectors.travelBasicData.delivered, 'checked');
});
it(`should check the received checkbox was saved even tho it doesn't make sense`, async() => {
await page.waitForClassPresent(selectors.travelBasicData.received, 'checked');
});
});

View File

@ -1,36 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Travel descriptor path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'travel');
await page.write(selectors.travelIndex.generalSearchFilter, '3');
await page.keyboard.press('Enter');
await page.waitForState('travel.card.summary');
});
afterAll(async() => {
await browser.close();
});
it('should click the descriptor button to navigate to the travel index showing all travels with current agency', async() => {
await page.waitToClick(selectors.travelDescriptor.filterByAgencyButton);
await page.waitForState('travel.index');
const result = await page.countElement(selectors.travelIndex.anySearchResult);
expect(result).toBeGreaterThanOrEqual(1);
});
it('should navigate to the first search result', async() => {
await page.waitToClick(selectors.travelIndex.firstSearchResult);
await page.waitForState('travel.card.summary');
const state = await page.getState();
expect(state).toBe('travel.card.summary');
});
});

View File

@ -1,42 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Travel extra community path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'travel');
await page.accessToSection('travel.extraCommunity');
});
afterAll(async() => {
await browser.close();
});
it('should edit the travel reference and the locked kilograms', async() => {
await page.waitToClick(selectors.travelExtraCommunity.removeContinentFilter);
await page.waitForSpinnerLoad();
await page.writeOnEditableTD(selectors.travelExtraCommunity.firstTravelReference, 'edited reference');
await page.waitForSpinnerLoad();
await page.writeOnEditableTD(selectors.travelExtraCommunity.firstTravelLockedKg, '1500');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reload the index and confirm the reference and locked kg were edited', async() => {
await page.accessToSection('travel.index');
await page.accessToSection('travel.extraCommunity');
await page.waitToClick(selectors.travelExtraCommunity.removeContinentFilter);
await page.waitForTextInElement(selectors.travelExtraCommunity.firstTravelReference, 'edited reference');
const reference = await page.getProperty(selectors.travelExtraCommunity.firstTravelReference, 'innerText');
const lockedKg = await page.getProperty(selectors.travelExtraCommunity.firstTravelLockedKg, 'innerText');
expect(reference).toContain('edited reference');
expect(lockedKg).toContain(1500);
});
});

View File

@ -1,62 +0,0 @@
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');
});
});

View File

@ -1,92 +0,0 @@
<mg-ajax path="Travels/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.travel"
form="form"
save="patch">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="Warehouses"
data="warehouses"
order="name">
</vn-crud-model>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
vn-one
label="Reference"
ng-model="$ctrl.travel.ref"
vn-name="reference">
</vn-textfield>
<vn-autocomplete
vn-one
ng-model="$ctrl.travel.agencyModeFk"
url="AgencyModes"
show-field="name"
value-field="id"
label="Agency"
vn-name="agencyMode">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
vn-one
label="Shipped"
ng-model="$ctrl.travel.shipped"
vn-name="shipped">
</vn-date-picker>
<vn-date-picker
vn-one
label="Landed"
ng-model="$ctrl.travel.landed"
vn-name="landed">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
ng-model="$ctrl.travel.warehouseOutFk"
vn-name="warehouseOut"
data="warehouses"
show-field="name"
value-field="id"
label="Warehouse Out">
</vn-autocomplete>
<vn-autocomplete
vn-one
ng-model="$ctrl.travel.warehouseInFk"
vn-name="warehouseIn"
data="warehouses"
show-field="name"
value-field="id"
label="Warehouse In">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-check
vn-one
label="Delivered"
ng-model="$ctrl.travel.isDelivered">
</vn-check>
<vn-check
vn-one
label="Received"
ng-model="$ctrl.travel.isReceived">
</vn-check>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,21 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
onSubmit() {
return this.$.watcher.submit().then(() =>
this.card.reload()
);
}
}
ngModule.vnComponent('vnTravelBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
travel: '<'
},
require: {
card: '^vnTravelCard'
}
});

View File

@ -1,28 +0,0 @@
import './index.js';
describe('Travel Component vnTravelBasicData', () => {
let controller;
beforeEach(angular.mock.module('travel', $translateProvider => {
$translateProvider.translations('en', {});
}));
beforeEach(inject($componentController => {
const $element = angular.element('<vn-travel-basic-data></vn-travel-basic-data>');
controller = $componentController('vnTravelBasicData', {$element});
controller.card = {reload: () => {}};
controller.$.watcher = {submit: () => {}};
}));
describe('onSubmit()', () => {
it('should call the card reload method after the watcher submits', done => {
jest.spyOn(controller.card, 'reload');
jest.spyOn(controller.$.watcher, 'submit').mockReturnValue(Promise.resolve());
controller.onSubmit().then(() => {
expect(controller.card.reload).toHaveBeenCalledWith();
done();
}).catch(done.fail);
});
});
});

View File

@ -1 +0,0 @@
Undo changes: Deshacer cambios

View File

@ -1,5 +0,0 @@
<vn-portal slot="menu">
<vn-travel-descriptor travel="$ctrl.travel"></vn-travel-descriptor>
<vn-left-menu source="card"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,31 +0,0 @@
import ngModule from '../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
let filter = {
include: [
{
relation: 'warehouseIn',
scope: {
fields: ['id', 'name']
}
}, {
relation: 'warehouseOut',
scope: {
fields: ['id', 'name']
}
}
]
};
this.$http.get(`Travels/${this.$params.id}`, {filter})
.then(response => this.travel = response.data);
}
}
ngModule.vnComponent('vnTravelCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,61 +0,0 @@
<vn-watcher
vn-id="watcher"
url="Travels"
data="$ctrl.travel"
insert-mode="true"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
label="Reference"
ng-model="$ctrl.travel.ref"
vn-name="reference">
</vn-textfield>
<vn-autocomplete
label="Agency"
ng-model="$ctrl.travel.agencyModeFk"
vn-name="agencyMode"
url="AgencyModes">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
on-change="$ctrl.onShippedChange(value)"
label="Shipped"
ng-model="$ctrl.travel.shipped"
vn-name="shipped">
</vn-date-picker>
<vn-date-picker
label="Landed"
ng-model="$ctrl.travel.landed"
vn-name="landed">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
label="Warehouse Out"
ng-model="$ctrl.travel.warehouseOutFk"
vn-name="warehouseOut"
url="Warehouses">
</vn-autocomplete>
<vn-autocomplete
label="Warehouse In"
ng-model="$ctrl.travel.warehouseInFk"
vn-name="warehouseIn"
url="Warehouses">
</vn-autocomplete>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="travel.index">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,48 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
$onChanges() {
if (this.$params && this.$params.q)
this.travel = JSON.parse(this.$params.q);
}
onShippedChange(value) {
let hasFilledProperties;
let hasAgencyMode;
if (this.travel) {
hasAgencyMode = Boolean(this.travel.agencyModeFk);
hasFilledProperties = this.travel.landed || this.travel.warehouseInFk || this.travel.warehouseOutFk;
}
if (!hasAgencyMode || hasFilledProperties)
return;
const query = `travels/getAverageDays`;
const params = {
agencyModeFk: this.travel.agencyModeFk
};
this.$http.get(query, {params}).then(res => {
if (!res.data)
return;
const landed = new Date(value);
const futureDate = landed.getDate() + res.data.dayDuration;
landed.setDate(futureDate);
this.travel.landed = landed;
this.travel.warehouseInFk = res.data.warehouseInFk;
this.travel.warehouseOutFk = res.data.warehouseOutFk;
});
}
onSubmit() {
return this.$.watcher.submit().then(
res => this.$state.go('travel.card.basicData', {id: res.data.id})
);
}
}
ngModule.vnComponent('vnTravelCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,88 +0,0 @@
import './index';
import watcher from 'core/mocks/watcher';
describe('Travel Component vnTravelCreate', () => {
let $scope;
let $state;
let controller;
let $httpBackend;
beforeEach(ngModule('travel'));
beforeEach(inject(($componentController, $rootScope, _$state_, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
$state = _$state_;
$scope.watcher = watcher;
const $element = angular.element('<vn-travel-create></vn-travel-create>');
controller = $componentController('vnTravelCreate', {$element, $scope});
}));
describe('onSubmit()', () => {
it(`should call submit() on the watcher then expect a callback`, () => {
jest.spyOn($state, 'go');
controller.onSubmit();
expect(controller.$state.go).toHaveBeenCalledWith('travel.card.basicData', {id: 1234});
});
});
describe('$onChanges()', () => {
it('should update the travel data when $params.q is defined', () => {
controller.$params = {q: '{"ref": 1,"agencyModeFk": 1}'};
const params = {q: '{"ref": 1, "agencyModeFk": 1}'};
const json = JSON.parse(params.q);
controller.$onChanges();
expect(controller.travel).toEqual(json);
});
});
describe('onShippedChange()', () => {
it(`should do nothing if there's no agencyModeFk in the travel.`, () => {
controller.travel = {};
controller.onShippedChange();
expect(controller.travel.landed).toBeUndefined();
expect(controller.travel.warehouseInFk).toBeUndefined();
expect(controller.travel.warehouseOutFk).toBeUndefined();
});
it(`should do nothing if there's no response data.`, () => {
controller.travel = {agencyModeFk: 4};
const tomorrow = Date.vnNew();
const query = `travels/getAverageDays?agencyModeFk=${controller.travel.agencyModeFk}`;
$httpBackend.expectGET(query).respond(undefined);
controller.onShippedChange(tomorrow);
$httpBackend.flush();
expect(controller.travel.warehouseInFk).toBeUndefined();
expect(controller.travel.warehouseOutFk).toBeUndefined();
expect(controller.travel.dayDuration).toBeUndefined();
});
it(`should fill the fields when it's selected a date and agency.`, () => {
controller.travel = {agencyModeFk: 1};
const tomorrow = Date.vnNew();
tomorrow.setDate(tomorrow.getDate() + 9);
const expectedResponse = {
id: 8,
dayDuration: 9,
warehouseInFk: 5,
warehouseOutFk: 1
};
const query = `travels/getAverageDays?agencyModeFk=${controller.travel.agencyModeFk}`;
$httpBackend.expectGET(query).respond(expectedResponse);
controller.onShippedChange(tomorrow);
$httpBackend.flush();
expect(controller.travel.warehouseInFk).toEqual(expectedResponse.warehouseInFk);
expect(controller.travel.warehouseOutFk).toEqual(expectedResponse.warehouseOutFk);
});
});
});

View File

@ -1,59 +0,0 @@
<vn-icon-button
icon="more_vert"
vn-popover="menu">
</vn-icon-button>
<vn-menu vn-id="menu">
<vn-list>
<vn-item
id="clone"
ng-click="clone.show()"
ng-show="::$ctrl.isBuyer"
translate>
Clone travel
</vn-item>
<vn-item
id="cloneWithEntries"
ng-click="cloneWithEntries.show()"
ng-show="::$ctrl.isBuyer"
translate>
Clone travel and his entries
</vn-item>
<vn-item
id="delete"
ng-click="delete.show()"
ng-show="$ctrl.isBuyer && !$ctrl.entries.length"
translate>
Delete travel
</vn-item>
<a class="vn-item"
name="addEntry"
ng-click="$ctrl.redirectToCreateEntry()"
translate>
Add entry
</a>
</vn-list>
</vn-menu>
<!-- Clone travel popup -->
<vn-confirm
vn-id="clone"
on-accept="$ctrl.onCloneAccept()"
question="Do you want to clone this travel?"
message="All it's properties will be copied">
</vn-confirm>
<!-- Delete travel popup -->
<vn-confirm
vn-id="delete"
on-accept="$ctrl.onDeleteAccept()"
question="Do you want to delete this travel?"
message="The travel will be deleted">
</vn-confirm>
<!-- Clone travel popup -->
<vn-confirm
vn-id="cloneWithEntries"
on-accept="$ctrl.onCloneWithEntriesAccept()"
question="Do you want to clone this travel and all containing entries?"
message="All it's properties will be copied">
</vn-confirm>

View File

@ -1,95 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $) {
super($element, $);
}
get travelId() {
return this._travelId;
}
set travelId(value) {
this._travelId = value;
if (value) this.loadData();
}
loadData() {
const filter = {
fields: [
'id',
'ref',
'shipped',
'landed',
'totalEntries',
'agencyModeFk',
'warehouseInFk',
'warehouseOutFk',
'cargoSupplierFk'
],
include: [
{
relation: 'warehouseIn',
scope: {
fields: ['name']
}
}, {
relation: 'warehouseOut',
scope: {
fields: ['name']
}
}
]
};
this.$http.get(`Travels/${this.travelId}`, {filter})
.then(res => this.travel = res.data);
this.$http.get(`Travels/${this.travelId}/getEntries`)
.then(res => this.entries = res.data);
}
get isBuyer() {
return this.aclService.hasAny(['buyer']);
}
onDeleteAccept() {
this.$http.delete(`Travels/${this.travelId}`)
.then(() => this.$state.go('travel.index'))
.then(() => this.vnApp.showSuccess(this.$t('Travel deleted')));
}
onCloneAccept() {
const params = JSON.stringify({
ref: this.travel.ref,
agencyModeFk: this.travel.agencyModeFk,
shipped: this.travel.shipped,
landed: this.travel.landed,
warehouseInFk: this.travel.warehouseInFk,
warehouseOutFk: this.travel.warehouseOutFk
});
this.$state.go('travel.create', {q: params});
}
async redirectToCreateEntry() {
this.$state.go('home');
window.location.href = await this.vnApp.getUrl(`entry/create?travelFk=${this.travelId}`);
}
onCloneWithEntriesAccept() {
this.$http.post(`Travels/${this.travelId}/cloneWithEntries`)
.then(res => this.$state.go('travel.card.basicData', {id: res.data}));
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnTravelDescriptorMenu', {
template: require('./index.html'),
controller: Controller,
bindings: {
travelId: '<',
}
});

View File

@ -1,71 +0,0 @@
import './index.js';
describe('Travel Component vnTravelDescriptorMenu', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('travel'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-travel-descriptor-menu></vn-travel-descriptor-menu>');
controller = $componentController('vnTravelDescriptorMenu', {$element});
controller._travelId = 5;
}));
describe('onCloneAccept()', () => {
it('should call state.go with the travel data', () => {
jest.spyOn(controller.$state, 'go').mockReturnValue('ok');
controller.travel = {
ref: 'the ref',
agencyModeFk: 'the agency',
shipped: 'the shipped date',
landed: 'the landing date',
warehouseInFk: 'the receiver warehouse',
warehouseOutFk: 'the sender warehouse'
};
controller.onCloneAccept();
const params = JSON.stringify({
ref: controller.travel.ref,
agencyModeFk: controller.travel.agencyModeFk,
shipped: controller.travel.shipped,
landed: controller.travel.landed,
warehouseInFk: controller.travel.warehouseInFk,
warehouseOutFk: controller.travel.warehouseOutFk
});
expect(controller.$state.go).toHaveBeenCalledWith('travel.create', {'q': params});
});
});
describe('onDeleteAccept()', () => {
it('should perform a delete query', () => {
jest.spyOn(controller.$state, 'go').mockReturnValue('ok');
controller.travelId = 1;
$httpBackend.when('GET', `Travels/${controller.travelId}`).respond(200);
$httpBackend.when('GET', `Travels/${controller.travelId}/getEntries`).respond(200);
$httpBackend.expect('DELETE', `Travels/${controller.travelId}`).respond(200);
controller.onDeleteAccept();
$httpBackend.flush();
expect(controller.$state.go).toHaveBeenCalledWith('travel.index');
});
});
describe('onCloneWithEntriesAccept()', () => {
it('should make an HTTP query and then call to the $state.go method with the returned id', () => {
jest.spyOn(controller.$state, 'go').mockReturnValue('ok');
$httpBackend.expect('POST', `Travels/${controller.travelId}/cloneWithEntries`).respond(200, 9);
controller.onCloneWithEntriesAccept();
$httpBackend.flush();
expect(controller.$state.go).toHaveBeenCalledWith('travel.card.basicData', {
id: jasmine.any(Number)
});
});
});
});

View File

@ -1,8 +0,0 @@
Clone travel: Clonar envío
Add entry: Añadir entrada
Clone travel and his entries: Clonar travel y sus entradas
Do you want to clone this travel and all containing entries?: ¿Quieres clonar este travel y todas las entradas que contiene?
Delete travel: Eliminar envío
The travel will be deleted: El envío será eliminado
Do you want to delete this travel?: ¿Quieres eliminar este envío?
Travel deleted: Envío eliminado

View File

@ -1,24 +0,0 @@
@import "./effects";
@import "variables";
vn-travel-descriptor-menu {
& > vn-icon-button[icon="more_vert"] {
display: flex;
min-width: 45px;
height: 45px;
box-sizing: border-box;
align-items: center;
justify-content: center;
}
& > vn-icon-button[icon="more_vert"] {
@extend %clickable;
color: inherit;
& > vn-icon {
padding: 10px;
}
vn-icon {
font-size: 1.75rem;
}
}
}

View File

@ -1,4 +0,0 @@
<slot-descriptor>
<vn-travel-descriptor>
</vn-travel-descriptor>
</slot-descriptor>

View File

@ -1,9 +0,0 @@
import ngModule from '../module';
import DescriptorPopover from 'salix/components/descriptor-popover';
class Controller extends DescriptorPopover {}
ngModule.vnComponent('vnTravelDescriptorPopover', {
slotTemplate: require('./index.html'),
controller: Controller
});

View File

@ -1,48 +0,0 @@
<vn-descriptor-content
module="travel"
description="$ctrl.travel.ref"
summary="$ctrl.$.summary">
<slot-dot-menu>
<vn-travel-descriptor-menu travel-id="$ctrl.travel.id"/>
</slot-dot-menu>
<slot-body>
<div class="attributes">
<vn-label-value
label="Wh. In"
value="{{$ctrl.travel.warehouseIn.name}}">
</vn-label-value>
<vn-label-value
label="Wh. Out"
value="{{$ctrl.travel.warehouseOut.name}}">
</vn-label-value>
<vn-label-value
label="Shipped"
value="{{$ctrl.travel.shipped | date: 'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value
label="Landed"
value="{{$ctrl.travel.landed | date: 'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value
label="Total entries"
value="{{$ctrl.travel.totalEntries}}">
</vn-label-value>
</div>
<div class="quicklinks">
<div ng-transclude="btnOne">
<vn-quick-link
tooltip="All travels with current agency"
state="['travel.index', {q: $ctrl.travelFilter}]"
icon="local_airport">
</vn-quick-link>
</div>
<div ng-transclude="btnTwo">
</div>
<div ng-transclude="btnThree">
</div>
</div>
</slot-body>
</vn-descriptor-content>
<vn-popup vn-id="summary">
<vn-travel-summary travel="$ctrl.travel"></vn-travel-summary>
</vn-popup>

View File

@ -1,63 +0,0 @@
import ngModule from '../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get travel() {
return this.entity;
}
set travel(value) {
this.entity = value;
}
get travelFilter() {
let travelFilter;
const travel = this.travel;
if (travel && travel.agencyModeFk) {
travelFilter = this.travel && JSON.stringify({
agencyModeFk: this.travel.agencyModeFk
});
}
return travelFilter;
}
loadData() {
const filter = {
fields: [
'id',
'ref',
'shipped',
'landed',
'totalEntries',
'warehouseInFk',
'warehouseOutFk',
'cargoSupplierFk'
],
include: [
{
relation: 'warehouseIn',
scope: {
fields: ['name']
}
}, {
relation: 'warehouseOut',
scope: {
fields: ['name']
}
}
]
};
return this.getData(`Travels/${this.id}`, {filter})
.then(res => this.entity = res.data);
}
}
ngModule.vnComponent('vnTravelDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
travel: '<'
}
});

View File

@ -1,26 +0,0 @@
import './index.js';
describe('vnTravelDescriptor', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('travel'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnTravelDescriptor', {$element: null});
}));
describe('loadData()', () => {
it(`should perform a get query to store the worker data into the controller`, () => {
const id = 1;
const response = 'foo';
$httpBackend.expectRoute('GET', `Travels/${id}`).respond(response);
controller.id = id;
$httpBackend.flush();
expect(controller.travel).toEqual(response);
});
});
});

View File

@ -1,6 +0,0 @@
Reference: Referencia
Wh. In: Alm. entrada
Wh. Out: Alm. salida
Shipped: F. envío
Landed: F. entrega
Total entries: Entradas totales

View File

@ -1,92 +0,0 @@
<div class="search-panel">
<vn-crud-model
auto-load="true"
url="Warehouses"
data="warehouses">
</vn-crud-model>
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield
vn-one
label="General search"
ng-model="filter.search"
info="Search travels by id"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
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>
<vn-textfield
vn-one
label="Travel id"
ng-model="filter.id">
</vn-textfield>
<vn-autocomplete vn-one
label="Agency"
ng-model="filter.agencyModeFk"
url="AgencyModes"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
vn-one
label="Shipped from"
ng-model="$ctrl.shippedFrom">
</vn-date-picker>
<vn-date-picker
vn-one
label="Landed to"
ng-model="$ctrl.landedTo">
</vn-date-picker>
</vn-horizontal>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Warehouse Out"
ng-model="filter.warehouseOutFk"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one
label="Warehouse In"
ng-model="filter.warehouseInFk"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Freighter"
ng-model="filter.cargoSupplierFk"
url="Suppliers"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one
label="Continent Out"
ng-model="filter.continent"
url="Continents"
show-field="name"
value-field="code">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -1,31 +0,0 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
class Controller extends SearchPanel {
constructor($, $element) {
super($, $element);
this.filter = this.$.filter;
}
get shippedFrom() {
return this.filter.shippedFrom;
}
set shippedFrom(value) {
this.filter.shippedFrom = value;
}
get landedTo() {
return this.filter.landedTo;
}
set landedTo(value) {
this.filter.landedTo = value;
}
}
ngModule.vnComponent('vnExtraCommunitySearchPanel', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,189 +0,0 @@
<vn-crud-model
vn-id="model"
url="Travels/extraCommunityFilter"
user-params="::$ctrl.defaultFilter"
data="travels"
order="landed ASC, shipped ASC, travelFk, loadPriority, agencyModeFk, supplierName, evaNotes"
limit="20"
auto-load="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
panel="vn-extra-community-search-panel"
info="Search by travel id or reference"
placeholder="Search by extra community travel"
suggested-filter="$ctrl.defaultFilter"
filter="$ctrl.defaultFilter"
auto-state="false"
model="model">
</vn-searchbar>
</vn-portal>
<vn-card class="travel-list scrollable">
<smart-table
model="model"
options="$ctrl.smartTableOptions">
<slot-actions>
<section>
<vn-tool-bar class="vn-mb-md">
<vn-button
disabled="!travels.length"
icon="picture_as_pdf"
ng-click="$ctrl.showReport()"
vn-tooltip="Open as PDF">
</vn-button>
</vn-tool-bar>
</section>
</slot-actions>
<slot-table>
<table>
<thead>
<tr>
<th field="id" shrink>
<span translate>Id</span>
</th>
<th field="cargoSupplierFk">
<span translate>Supplier</span>
</th>
<th field="agencyModeFk">
<span translate>Agency</span>
</th>
<th field="invoiceAmount">
<span translate>Amount</span>
</th>
<th field="ref">
<span translate>Reference</span>
</th>
<th field="stickers" number>
<span translate>Packages</span>
</th>
<th field="kg" number>
<span translate>Bl. KG</span>
</th>
<th field="loadedKg" number>
<span translate>Phy. KG</span>
</th>
<th field="volumeKg" number>
<span translate>Vol. KG</span>
</th>
<th
field="warehouseOutFk"
translate-attr="{title: 'Warehouse Out'}">
<span translate>Wh. Out</span>
</th>
<th field="shipped">
<span translate>W. Shipped</span>
</th>
<th
field="warehouseInFk"
translate-attr="{title: 'Warehouse In'}">
<span translate>Wh. In</span>
</th>
<th field="landed">
<span translate>W. Landed</span>
</th>
</tr>
</thead>
<tbody
ng-repeat="travel in travels"
class="vn-mb-md"
vn-droppable="$ctrl.onDrop($event)"
ng-attr-id="{{::travel.id}}"
vn-stop-click>
<tr
class="header"
vn-anchor="::{
state: 'travel.card.basicData',
params: {id: travel.id}
}">
<td vn-click-stop>
<span
class="link"
ng-click="travelDescriptor.show($event, travel.id)">
{{::travel.id}}
</span>
</td>
<td class="multi-line" vn-click-stop>
<span
class="link"
ng-click="supplierDescriptor.show($event, travel.cargoSupplierFk)">
{{::travel.cargoSupplierNickname}}
</span>
</td>
<td></td>
<td>{{::travel.agencyModeName}}</td>
<td vn-click-stop>
<vn-td-editable name="reference" expand>
<text>{{travel.ref}}</text>
<field>
<vn-textfield class="dense" vn-focus
ng-model="travel.ref"
on-change="$ctrl.save(travel.id, {ref: value})">
</vn-textfield>
</field>
</vn-td-editable>
</td>
<td number>{{::travel.stickers}}</td>
<td vn-click-stop>
<vn-td-editable name="lockedKg" expand style="text-align: right">
<text number>{{travel.kg}}</text>
<field>
<vn-input-number class="dense" vn-focus
ng-model="travel.kg"
on-change="$ctrl.save(travel.id, {kg: value})"
min="0">
</vn-input-number>
</field>
</vn-td-editable>
</td>
<td number>{{::travel.loadedKg}}</td>
<td number>{{::travel.volumeKg}}</td>
<td expand>{{::travel.warehouseOutName}}</td>
<td expand>{{::travel.shipped | date: 'dd/MM/yyyy'}}</td>
<td expand>{{::travel.warehouseInName}}</td>
<td expand>{{::travel.landed | date: 'dd/MM/yyyy'}}</td>
</tr>
<tr
ng-repeat="entry in travel.entries"
draggable
ng-attr-id="{{::entry.id}}"
ng-click="$event.preventDefault()">
<td>
<span class="link"
ng-click="entryDescriptor.show($event, entry.id)">
{{::entry.id}}
</span>
</td>
<td class="multi-line">
<span
class="link"
ng-click="supplierDescriptor.show($event, entry.supplierFk)">
{{::entry.supplierName}}
</span>
</td>
<td number>{{::entry.invoiceAmount | currency: 'EUR': 2}}</td>
<td></td>
<td class="td-editable">{{::entry.reference}}</td>
<td number>{{::entry.stickers}}</td>
<td number></td>
<td number>{{::entry.loadedkg}}</td>
<td number>{{::entry.volumeKg}}</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<vn-travel-descriptor-popover
vn-id="travelDescriptor">
</vn-travel-descriptor-popover>
<vn-entry-descriptor-popover
vn-id="entryDescriptor">
</vn-entry-descriptor-popover>
<vn-supplier-descriptor-popover
vn-id="supplierDescriptor">
</vn-supplier-descriptor-popover>

View File

@ -1,162 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $, vnReport) {
super($element, $);
this.vnReport = vnReport;
const draggable = this.element.querySelector('.travel-list');
draggable.addEventListener('dragstart',
event => this.dragStart(event));
draggable.addEventListener('dragend',
event => this.dragEnd(event));
draggable.addEventListener('dragover',
event => this.dragOver(event));
draggable.addEventListener('dragenter',
event => this.dragEnter(event));
draggable.addEventListener('dragleave',
event => this.dragLeave(event));
this.draggableElement = 'tr[draggable]';
this.droppableElement = 'tbody[vn-droppable]';
const twoDays = 2;
const shippedFrom = Date.vnNew();
shippedFrom.setDate(shippedFrom.getDate() - twoDays);
shippedFrom.setHours(0, 0, 0, 0);
const sevenDays = 7;
const landedTo = Date.vnNew();
landedTo.setDate(landedTo.getDate() + sevenDays);
landedTo.setHours(23, 59, 59, 59);
this.defaultFilter = {
shippedFrom: shippedFrom,
landedTo: landedTo,
continent: 'AM'
};
this.smartTableOptions = {};
}
onDragInterval() {
if (this.dragClientY > 0 && this.dragClientY < 75)
this.$window.scrollTo(0, this.$window.scrollY - 10);
const maxHeight = window.screen.availHeight - (window.outerHeight - window.innerHeight);
if (this.dragClientY > maxHeight - 75 && this.dragClientY < maxHeight)
this.$window.scrollTo(0, this.$window.scrollY + 10);
}
findDraggable($event) {
const target = $event.target;
const draggable = target.closest(this.draggableElement);
return draggable;
}
findDroppable($event) {
const target = $event.target;
const droppable = target.closest(this.droppableElement);
return droppable;
}
dragStart($event) {
const draggable = this.findDraggable($event);
draggable.classList.add('dragging');
const id = parseInt(draggable.id);
this.entryId = id;
this.entry = draggable;
this.interval = setInterval(() => this.onDragInterval(), 50);
}
dragEnd($event) {
const draggable = this.findDraggable($event);
draggable.classList.remove('dragging');
this.entryId = null;
this.entry = null;
clearInterval(this.interval);
}
onDrop($event) {
const model = this.$.model;
const droppable = this.findDroppable($event);
const travelId = parseInt(droppable.id);
const currentDroppable = this.entry.closest(this.droppableElement);
if (currentDroppable == droppable) return;
if (this.entryId && travelId) {
const path = `Entries/${this.entryId}`;
this.$http.patch(path, {travelFk: travelId})
.then(() => model.refresh())
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
}
}
undrop() {
if (!this.dropping) return;
this.dropping.classList.remove('dropping');
this.dropping = null;
}
dragOver($event) {
this.dragClientY = $event.clientY;
$event.preventDefault();
}
dragEnter($event) {
let element = this.findDroppable($event);
if (element) this.dropCount++;
if (element != this.dropping) {
this.undrop();
if (element) element.classList.add('dropping');
this.dropping = element;
}
}
dragLeave($event) {
let element = this.findDroppable($event);
if (element) {
this.dropCount--;
if (this.dropCount == 0) this.undrop();
}
}
save(id, data) {
const endpoint = `Travels/${id}`;
this.$http.patch(endpoint, data)
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
}
get reportParams() {
const userParams = this.$.model.userParams;
const currentFilter = this.$.model.currentFilter;
return Object.assign({
authorization: this.vnToken.tokenMultimedia,
filter: currentFilter
}, userParams);
}
showReport() {
this.vnReport.show(`Travels/extra-community-pdf`, this.reportParams);
}
}
Controller.$inject = ['$element', '$scope', 'vnReport'];
ngModule.vnComponent('vnTravelExtraCommunity', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,128 +0,0 @@
import './index.js';
describe('Travel Component vnTravelExtraCommunity', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('travel'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-travel-extra-community><div class="travel-list"></div></vn-travel-extra-community>');
controller = $componentController('vnTravelExtraCommunity', {$element});
controller.$.model = {};
controller.$.model.refresh = jest.fn();
}));
describe('findDraggable()', () => {
it('should find the draggable element', () => {
const draggable = document.createElement('tr');
draggable.setAttribute('draggable', true);
const $event = new Event('dragstart');
const target = document.createElement('div');
draggable.appendChild(target);
target.dispatchEvent($event);
const result = controller.findDraggable($event);
expect(result).toEqual(draggable);
});
});
describe('findDroppable()', () => {
it('should find the droppable element', () => {
const droppable = document.createElement('tbody');
droppable.setAttribute('vn-droppable', true);
const $event = new Event('drop');
const target = document.createElement('div');
droppable.appendChild(target);
target.dispatchEvent($event);
const result = controller.findDroppable($event);
expect(result).toEqual(droppable);
});
});
describe('dragStart()', () => {
it(`should add the class "dragging" to the draggable element
and then set the entryId controller property`, () => {
const draggable = document.createElement('tr');
draggable.setAttribute('draggable', true);
draggable.setAttribute('id', 3);
jest.spyOn(controller, 'findDraggable').mockReturnValue(draggable);
const $event = new Event('dragStart');
controller.dragStart($event);
const firstClass = draggable.classList[0];
expect(firstClass).toEqual('dragging');
expect(controller.entryId).toEqual(3);
expect(controller.entry).toEqual(draggable);
});
});
describe('dragEnd()', () => {
it(`should remove the class "dragging" from the draggable element
and then set the entryId controller property to null`, () => {
const draggable = document.createElement('tr');
draggable.setAttribute('draggable', true);
draggable.setAttribute('id', 3);
draggable.classList.add('dragging');
jest.spyOn(controller, 'findDraggable').mockReturnValue(draggable);
const $event = new Event('dragStart');
controller.dragEnd($event);
const classList = draggable.classList;
expect(classList.length).toEqual(0);
expect(controller.entryId).toBeNull();
expect(controller.entry).toBeNull();
});
});
describe('onDrop()', () => {
it('should make an HTTP patch query', () => {
const droppable = document.createElement('tbody');
droppable.setAttribute('vn-droppable', true);
droppable.setAttribute('id', 1);
jest.spyOn(controller, 'findDroppable').mockReturnValue(droppable);
const oldDroppable = document.createElement('tbody');
oldDroppable.setAttribute('vn-droppable', true);
const entry = document.createElement('div');
oldDroppable.appendChild(entry);
controller.entryId = 3;
controller.entry = entry;
const $event = new Event('drop');
const expectedData = {travelFk: 1};
$httpBackend.expect('PATCH', `Entries/3`, expectedData).respond(200);
controller.onDrop($event);
$httpBackend.flush();
});
});
describe('save()', () => {
it('should make an HTTP query', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
const travelId = 1;
const data = {ref: 'New reference'};
const expectedData = {ref: 'New reference'};
$httpBackend.expect('PATCH', `Travels/${travelId}`, expectedData).respond(200);
controller.save(travelId, data);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
});
});
});

View File

@ -1,11 +0,0 @@
Family: Familia
Extra community: Extra comunitarios
Freighter: Transitario
Bl. KG: KG Bloq.
Phy. KG: KG físico
Vol. KG: KG Vol.
Search by travel id or reference: Buscar por id de travel o referencia
Search by extra community travel: Buscar por envío extra comunitario
Continent Out: Cont. salida
W. Shipped: F. envío
W. Landed: F. llegada

View File

@ -1,67 +0,0 @@
@import "variables";
vn-travel-extra-community {
.header {
margin-bottom: 16px;
line-height: 1;
padding: 7px;
padding-bottom: 7px;
padding-bottom: 4px;
font-weight: lighter;
background-color: $color-bg;
color: white;
border-bottom: 1px solid #f7931e;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
.multi-line{
padding-top: 15px;
padding-bottom: 15px;
}
}
table[vn-droppable] {
border-radius: 0;
}
tr[draggable] {
transition: all .5s;
cursor: move;
overflow: auto;
outline: 0;
height: 65px;
pointer-events: fill;
user-select: all;
}
tr[draggable] *::selection {
background-color: transparent;
}
tr[draggable]:hover {
background-color: $color-hover-cd;
}
tr[draggable].dragging {
background-color: $color-primary-light;
color: $color-font-light;
font-weight: bold;
}
.multi-line{
max-width: 200px;
word-wrap: normal;
white-space: normal;
}
vn-td-editable text {
background-color: transparent;
padding: 0;
border: 0;
border-bottom: 1px dashed $color-active;
border-radius: 0;
color: $color-active
}
}

View File

@ -1,18 +1,3 @@
export * from './module';
import './main';
import './index/';
import './search-panel';
import './descriptor';
import './card';
import './summary';
import './basic-data';
import './log';
import './create';
import './thermograph/index/';
import './thermograph/create/';
import './thermograph/edit/';
import './descriptor-popover';
import './descriptor-menu';
import './extra-community';
import './extra-community-search-panel';

View File

@ -1,107 +0,0 @@
<vn-auto-search
model="model">
</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
model="model"
class="vn-mb-xl vn-w-xl">
<vn-card>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="ref">Reference</vn-th>
<vn-th field="agencyModeFk">Agency</vn-th>
<vn-th field="warehouseOutFk">Warehouse Out</vn-th>
<vn-th field="shipped" center shrink-date>Shipped</vn-th>
<vn-th shrink></vn-th>
<vn-th field="warehouseInFk">Warehouse In</vn-th>
<vn-th field="landed" center shrink-date>Landed</vn-th>
<vn-th shrink></vn-th>
<vn-th shrink field="totalEntries">Total entries</vn-th>
<vn-th shrink></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<a ng-repeat="travel in model.data"
class="clickable vn-tr search-result"
ui-sref="travel.card.summary({id: {{::travel.id}}})">
<vn-td>{{::travel.ref}}</vn-td>
<vn-td>{{::travel.agencyModeName}}</vn-td>
<vn-td>{{::travel.warehouseOutName}}</vn-td>
<vn-td center shrink-date>
<span class="chip {{$ctrl.compareDate(travel.shipped)}}">
{{::travel.shipped | date:'dd/MM/yyyy'}}
</span>
</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 center shrink-date>
<span class="chip {{$ctrl.compareDate(travel.landed)}}">
{{::travel.landed | date:'dd/MM/yyyy'}}
</span>
</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-horizontal class="buttons">
<vn-icon-button
vn-click-stop="clone.show(travel)"
vn-tooltip="Clone"
icon="icon-clone">
</vn-icon-button>
<vn-icon-button
vn-anchor="::{state: 'entry.create', params: {travelFk: travel.id}}"
vn-tooltip="Add entry"
icon="icon-ticket">
</vn-icon-button>
<vn-icon-button
vn-click-stop="$ctrl.preview(travel)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-horizontal>
</vn-td>
</a>
</vn-tbody>
</vn-table>
</vn-data-viewer>
</vn-card>
<vn-popup vn-id="summary">
<vn-travel-summary
travel="$ctrl.travelSelected">
</vn-travel-summary>
</vn-popup>
<a ui-sref="travel.create"
vn-tooltip="New travel"
vn-bind="+"
vn-acl="buyer"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>
<vn-confirm
vn-id="clone"
on-accept="$ctrl.onCloneAccept($data)"
question="Do you want to clone this travel?"
message="All it's properties will be copied">
</vn-confirm>

View File

@ -1,39 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
export default class Controller extends Section {
preview(travel) {
this.travelSelected = travel;
this.$.summary.show();
}
onCloneAccept(travel) {
const params = JSON.stringify({
ref: travel.ref,
agencyModeFk: travel.agencyModeFk,
shipped: travel.shipped,
landed: travel.landed,
warehouseInFk: travel.warehouseInFk,
warehouseOutFk: travel.warehouseOutFk
});
this.$state.go('travel.create', {q: params});
}
compareDate(date) {
let today = Date.vnNew();
today.setHours(0, 0, 0, 0);
date = new Date(date);
date.setHours(0, 0, 0, 0);
const timeDifference = today - date;
if (timeDifference == 0) return 'warning';
if (timeDifference < 0) return 'success';
}
}
ngModule.vnComponent('vnTravelIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,78 +0,0 @@
import './index.js';
describe('Travel Component vnTravelIndex', () => {
let controller;
let travel = {
id: 1,
warehouseInFk: 1,
totalEntries: 3,
isDelivered: false
};
beforeEach(ngModule('travel'));
beforeEach(inject($componentController => {
const $element = angular.element('<vn-travel-index></vn-travel-index>');
controller = $componentController('vnTravelIndex', {$element});
controller.$.summary = {show: jasmine.createSpy('show')};
}));
describe('preview()', () => {
it('should show the dialog summary', () => {
let event = new MouseEvent('click', {
bubbles: true,
cancelable: true
});
controller.preview(event, travel);
expect(controller.$.summary.show).toHaveBeenCalledWith();
});
});
describe('onCloneAccept()', () => {
it('should call go() then update travelSelected in the controller', () => {
jest.spyOn(controller.$state, 'go');
const travel = {
ref: 1,
agencyModeFk: 1
};
const travelParams = {
ref: travel.ref,
agencyModeFk: travel.agencyModeFk
};
const queryParams = JSON.stringify(travelParams);
controller.onCloneAccept(travel);
expect(controller.$state.go).toHaveBeenCalledWith('travel.create', {q: queryParams});
});
});
describe('compareDate()', () => {
it('should return warning if the date passed to compareDate() is todays', () => {
const today = Date.vnNew();
const result = controller.compareDate(today);
expect(result).toEqual('warning');
});
it('should return success if the date passed to compareDate() is in the future', () => {
const tomorrow = Date.vnNew();
tomorrow.setDate(tomorrow.getDate() + 1);
const result = controller.compareDate(tomorrow);
expect(result).toEqual('success');
});
it('should return undefined if the date passed to compareDate() is in the past', () => {
const yesterday = Date.vnNew();
yesterday.setDate(yesterday.getDate() - 1);
const result = controller.compareDate(yesterday);
expect(result).toBeUndefined();
});
});
});

View File

@ -1,3 +0,0 @@
Do you want to clone this travel?: ¿Desea clonar este envio?
All it's properties will be copied: Todas sus propiedades serán copiadas
Clone: Clonar

View File

@ -1,11 +0,0 @@
@import "variables";
vn-travel-index {
vn-icon {
color: $color-font-secondary
}
vn-icon.active {
color: $color-success
}
}

View File

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

View File

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

View File

@ -1,6 +0,0 @@
<vn-portal slot="topbar">
</vn-portal>
<vn-portal slot="menu">
<vn-left-menu></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -5,6 +5,10 @@ export default class Travel extends ModuleMain {
constructor() {
super();
}
async $onInit() {
this.$state.go('home');
window.location.href = await this.vnApp.getUrl(`travel/`);
}
}
ngModule.vnComponent('vnTravel', {

View File

@ -27,80 +27,6 @@
"state": "travel.index",
"component": "vn-travel-index",
"description": "Travels"
}, {
"url": "/:id",
"state": "travel.card",
"abstract": true,
"component": "vn-travel-card"
}, {
"url": "/summary",
"state": "travel.card.summary",
"component": "vn-travel-summary",
"description": "Summary",
"params": {
"travel": "$ctrl.travel"
}
}, {
"url": "/basic-data",
"state": "travel.card.basicData",
"component": "vn-travel-basic-data",
"description": "Basic data",
"acl": ["buyer","logistic"],
"params": {
"travel": "$ctrl.travel"
}
}, {
"url" : "/log",
"state": "travel.card.log",
"component": "vn-travel-log",
"description": "Log"
}, {
"url": "/create?q",
"state": "travel.create",
"component": "vn-travel-create",
"description": "New travel"
}, {
"url": "/thermograph",
"state": "travel.card.thermograph",
"abstract": true,
"component": "ui-view"
}, {
"url" : "/index",
"state": "travel.card.thermograph.index",
"component": "vn-travel-thermograph-index",
"description": "Thermographs",
"params": {
"travel": "$ctrl.travel"
},
"acl": ["buyer"]
}, {
"url" : "/create",
"state": "travel.card.thermograph.create",
"component": "vn-travel-thermograph-create",
"description": "Add thermograph",
"params": {
"travel": "$ctrl.travel"
},
"acl": ["buyer"]
}, {
"url" : "/:thermographId/edit",
"state": "travel.card.thermograph.edit",
"component": "vn-travel-thermograph-edit",
"description": "Edit thermograph",
"params": {
"travel": "$ctrl.travel"
},
"acl": ["buyer"]
},
{
"url": "/extra-community?q",
"state": "travel.extraCommunity",
"component": "vn-travel-extra-community",
"description": "Extra community",
"acl": ["buyer"],
"params": {
"travel": "$ctrl.travel"
}
}
]
}

View File

@ -1,146 +0,0 @@
<vn-side-menu side="right">
<vn-horizontal class="input">
<vn-textfield
label="General search"
info="Search travels by id"
ng-model="$ctrl.search"
ng-keydown="$ctrl.onKeyPress($event, 'search')">
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="input horizontal">
<vn-autocomplete
vn-id="agency"
label="Agency"
ng-model="$ctrl.filter.agencyModeFk"
data="$ctrl.agencyModes"
show-field="name"
value-field="id"
on-change="$ctrl.applyFilters()">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="input horizontal">
<vn-autocomplete
vn-id="warehouseOut"
label="Warehouse Out"
ng-model="$ctrl.filter.warehouseOutFk"
data="$ctrl.warehouses"
show-field="name"
value-field="id"
on-change="$ctrl.applyFilters()">
</vn-autocomplete>
<vn-autocomplete
vn-id="warehouseIn"
label="Warehouse In"
ng-model="$ctrl.filter.warehouseInFk"
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
min="0"
step="1"
label="Days onward"
ng-model="$ctrl.filter.scopeDays"
on-change="$ctrl.applyFilters()"
display-controls="true"
info="Cannot choose a range of dates and days onward at the same time">
</vn-input-number>
</vn-horizontal>
<vn-horizontal class="input horizontal">
<vn-date-picker
label="Landed from"
ng-model="$ctrl.filter.landedFrom"
on-change="$ctrl.applyFilters()">
</vn-date-picker>
<vn-date-picker
label="Landed to"
ng-model="$ctrl.filter.landedTo"
on-change="$ctrl.applyFilters()">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal class="input horizontal">
<vn-autocomplete
vn-id="continent"
label="Continent Out"
ng-model="$ctrl.filter.continent"
data="$ctrl.continents"
show-field="name"
value-field="code"
on-change="$ctrl.applyFilters()">
</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>
<div class="chips">
<vn-chip
ng-if="$ctrl.filter.search"
removable="true"
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>

View File

@ -1,72 +0,0 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
import './style.scss';
class Controller extends SearchPanel {
constructor($, $element) {
super($, $element);
this.initFilter();
this.fetchData();
}
$onChanges() {
if (this.model)
this.applyFilters();
}
fetchData() {
this.$http.get('AgencyModes').then(res => {
this.agencyModes = res.data;
});
this.$http.get('Warehouses').then(res => {
this.warehouses = res.data;
});
this.$http.get('Continents').then(res => {
this.continents = res.data;
});
}
initFilter() {
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;
}
applyFilters(param) {
if (this.filter?.search)
delete this.filter.scopeDays;
this.model.applyFilter({}, this.filter)
.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'});
});
}
removeParamFilter(param) {
if (this[param]) delete this[param];
delete this.filter[param];
this.applyFilters();
}
onKeyPress($event, param) {
if ($event.key === 'Enter') {
this.filter[param] = this[param];
this.applyFilters(param === 'search');
}
}
}
ngModule.vnComponent('vnTravelSearchPanel', {
template: require('./index.html'),
controller: Controller,
bindings: {
model: '<'
}
});

View File

@ -1,38 +0,0 @@
import './index';
describe('Travel Component vnTravelSearchPanel', () => {
let controller;
beforeEach(ngModule('travel'));
beforeEach(inject($componentController => {
controller = $componentController('vnTravelSearchPanel', {$element: null});
controller.$t = () => {};
}));
describe('applyFilters()', () => {
it('should apply filters', async() => {
controller.filter = {foo: 'bar'};
controller.model = {
applyFilter: jest.fn().mockResolvedValue(),
_orgData: [{id: 1}]
};
controller.$state = {
current: {
name: 'foo'
},
go: jest.fn()
};
await controller.applyFilters(true);
expect(controller.model.applyFilter).toHaveBeenCalledWith({}, controller.filter);
expect(controller.$state.go).toHaveBeenCalledWith('travel.card.summary', {id: 1});
await controller.applyFilters(false);
expect(controller.$state.go).toHaveBeenCalledWith(controller.$state.current.name,
{q: JSON.stringify(controller.filter)}, {location: 'replace'});
});
});
});

View File

@ -1,7 +0,0 @@
Ticket id: Id ticket
Client id: Id cliente
Nickname: Alias
From: Desde
To: Hasta
Agency: Agencia
Warehouse: Almacén

View File

@ -1,37 +0,0 @@
@import "variables";
vn-travel-search-panel vn-side-menu {
.menu {
min-width: $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;
}
}
}

View File

@ -1,167 +0,0 @@
<vn-card class="summary">
<h5>
<a
ng-if="::$ctrl.travelData.id"
vn-tooltip="Go to the travel"
ui-sref="travel.card.summary({id: {{::$ctrl.travelData.id}}})"
name="goToSummary">
<vn-icon-button icon="launch"></vn-icon-button>
</a>
<span>{{$ctrl.travelData.id}} - {{$ctrl.travelData.ref}}</span>
<vn-travel-descriptor-menu travel-id="$ctrl.travel.id"/>
</h5>
<vn-horizontal>
<vn-one>
<vn-label-value
label="Shipped"
value="{{$ctrl.travelData.shipped | date: 'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value
label="Warehouse Out"
value="{{$ctrl.travelData.warehouseOut.name}}">
</vn-label-value>
<vn-check
label="Delivered"
ng-model="$ctrl.travelData.isDelivered"
disabled="true">
</vn-check>
</vn-one>
<vn-one>
<vn-label-value
label="Landed"
value="{{$ctrl.travelData.landed | date: 'dd/MM/yyyy'}}">
</vn-label-value>
<vn-label-value
label="Warehouse In"
value="{{$ctrl.travelData.warehouseIn.name}}">
</vn-label-value>
<vn-check
label="Received"
ng-model="$ctrl.travelData.isReceived"
disabled="true">
</vn-check>
</vn-one>
<vn-one>
<vn-label-value
label="Agency"
value="{{$ctrl.travelData.agency.name}}">
</vn-label-value>
<vn-label-value
label="Reference"
value="{{$ctrl.travelData.ref}}">
</vn-label-value>
<vn-label-value
label="m³"
value="{{$ctrl.travelData.m3}}">
</vn-label-value>
<vn-label-value
label="Total entries"
value="{{$ctrl.travelData.totalEntries}}">
</vn-label-value>
</vn-one>
<vn-auto>
<h4 translate>Entries</h4>
<vn-table>
<vn-thead>
<vn-tr>
<vn-th shrink>Confirmed</vn-th>
<vn-th shrink>Entry Id</vn-th>
<vn-th shrink>Supplier</vn-th>
<vn-th shrink>Reference</vn-th>
<vn-th shrink title="Half box">HB</vn-th>
<vn-th shrink>Freight</vn-th>
<vn-th shrink>Package</vn-th>
<vn-th shrink>CC</vn-th>
<vn-th shrink>Pallet</vn-th>
<vn-th shrink></vn-th>
<vn-th shrink></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="entry in $ctrl.entries">
<vn-td shrink>
<vn-check
ng-model="entry.isConfirmed"
disabled="true">
</vn-check>
</vn-td>
<vn-td shrink>
<span class="link"
vn-click-stop="entryDescriptor.show($event, entry.id)">
{{entry.id}}
</span>
</vn-td>
<vn-td expand>{{entry.supplierName}}</vn-td>
<vn-td shrink>{{entry.reference}}</vn-td>
<vn-td shrink>{{entry.hb}}</vn-td>
<vn-td shrink>{{entry.freightValue | currency: 'EUR': 2}}</vn-td>
<vn-td shrink>{{entry.packageValue | currency: 'EUR': 2}}</vn-td>
<vn-td shrink>{{entry.cc}}</vn-td>
<vn-td shrink>{{entry.pallet}}</vn-td>
<vn-td shrink>{{entry.m3}}</vn-td>
<vn-td shrink>
<vn-icon
ng-if="entry.observation.length"
vn-tooltip="{{entry.observation}}"
icon="insert_drive_file"
class="bright">
</vn-icon>
</vn-td>
</vn-tr>
</vn-tbody>
<vn-tfoot>
<vn-tr>
<vn-td></vn-td>
<vn-td></vn-td>
<vn-td></vn-td>
<vn-td></vn-td>
<vn-td shrink><strong>{{$ctrl.total('hb')}}</strong></vn-td>
<vn-td shrink><strong>{{$ctrl.total('freightValue') | currency: 'EUR': 2}}</strong></vn-td>
<vn-td shrink><strong>{{$ctrl.total('packageValue') | currency: 'EUR': 2}}</strong></vn-td>
<vn-td shrink><strong>{{$ctrl.total('cc') | number:2}}</strong></vn-td>
<vn-td shrink><strong>{{$ctrl.total('pallet') | number:2}}</strong></vn-td>
<vn-td shrink><strong>{{$ctrl.total('m3') | number:2}}</strong></vn-td>
<vn-td></vn-td>
</vn-tr>
</vn-tfoot>
</vn-table>
</vn-auto>
<vn-auto ng-if="$ctrl.travelThermographs.length != 0">
<h4 ng-show="$ctrl.isBuyer">
<a
ui-sref="travel.card.thermograph.index({id:$ctrl.travelData.id})"
target="_self">
<span translate vn-tooltip="Go to">Thermograph</span>
</a>
</h4>
<h4
translate
ng-show="!$ctrl.isBuyer">
Thermograph
</h4>
<vn-table>
<vn-thead>
<vn-tr>
<vn-th>Code</vn-th>
<vn-th>Temperature</vn-th>
<vn-th expand>State</vn-th>
<vn-th>Destination</vn-th>
<vn-th expand>Created</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="thermograph in $ctrl.travelThermographs">
<vn-td>{{thermograph.thermographFk}} </vn-td>
<vn-td>{{thermograph.temperatureFk}}</vn-td>
<vn-td>{{thermograph.result}}</vn-td>
<vn-td>{{thermograph.warehouse.name}}</vn-td>
<vn-td expand>{{thermograph.created | date: 'dd/MM/yyyy'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-auto>
</vn-horizontal>
</vn-card>
<vn-entry-descriptor-popover
vn-id="entryDescriptor">
</vn-entry-descriptor-popover>

View File

@ -1,71 +0,0 @@
import ngModule from '../module';
import Summary from 'salix/components/summary';
import './style.scss';
class Controller extends Summary {
$onInit() {
this.entries = [];
}
get travel() {
return this._travel;
}
set travel(value) {
this._travel = value;
if (value && value.id) {
this.getTravel();
this.getEntries();
this.getThermographs();
}
}
getTravel() {
return this.$http.get(`Travels/${this.travel.id}/getTravel`)
.then(res => this.travelData = res.data);
}
getEntries() {
return this.$http.get(`Travels/${this.travel.id}/getEntries`)
.then(res => this.entries = res.data);
}
getThermographs() {
const filter = {
include: {
relation: 'warehouse',
scope: {
fields: ['id', 'name']
}
},
where: {
travelFk: this.travel.id
}
};
return this.$http.get(`TravelThermographs`, {filter})
.then(res => this.travelThermographs = res.data);
}
total(field) {
let total = 0;
for (let entry of this.entries)
total += entry[field];
return total;
}
get isBuyer() {
return this.aclService.hasAny(['buyer']);
}
}
ngModule.vnComponent('vnTravelSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
travel: '<'
}
});

View File

@ -1,86 +0,0 @@
import './index';
describe('component vnTravelSummary', () => {
let controller;
let $httpBackend;
let $scope;
let $httpParamSerializer;
beforeEach(angular.mock.module('travel', $translateProvider => {
$translateProvider.translations('en', {});
}));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
$scope = $rootScope.$new();
const $element = angular.element(`<vn-travel-summary></vn-travel-summary>`);
controller = $componentController('vnTravelSummary', {$element, $scope});
}));
describe('travel setter/getter', () => {
it('should return the travel and then call both getTravel() and getEntries()', () => {
jest.spyOn(controller, 'getTravel');
jest.spyOn(controller, 'getEntries');
jest.spyOn(controller, 'getThermographs');
controller.travel = {id: 99};
expect(controller._travel.id).toEqual(99);
expect(controller.getTravel).toHaveBeenCalledWith();
expect(controller.getEntries).toHaveBeenCalledWith();
expect(controller.getThermographs).toHaveBeenCalledWith();
});
});
describe('getTravel()', () => {
it('should perform a get and then store data on the controller', () => {
controller._travel = {id: 999};
const query = `Travels/${controller._travel.id}/getTravel`;
$httpBackend.expectGET(query).respond('I am the travelData');
controller.getTravel();
$httpBackend.flush();
expect(controller.travelData).toEqual('I am the travelData');
});
});
describe('getEntries()', () => {
it('should call the getEntries method to get the entries data', () => {
controller._travel = {id: 999};
const query = `Travels/${controller._travel.id}/getEntries`;
$httpBackend.expectGET(query).respond('I am the entries');
controller.getEntries();
$httpBackend.flush();
expect(controller.entries).toEqual('I am the entries');
});
});
describe('getThermographs()', () => {
it('should call the getThermographs method to get the thermographs', () => {
controller._travel = {id: 2};
$httpBackend.expectGET(`TravelThermographs`).respond('I am the thermographs');
controller.getThermographs();
$httpBackend.flush();
expect(controller.travelThermographs).toEqual('I am the thermographs');
});
});
describe('total()', () => {
it('should calculate the total amount of a given property for every row', () => {
controller.entries = [
{id: 1, freightValue: 1, packageValue: 2, cc: 0.01},
{id: 2, freightValue: 1, packageValue: 2, cc: 0.01},
{id: 3, freightValue: 1, packageValue: 2, cc: 0.01}
];
expect(controller.total('freightValue')).toEqual(3);
expect(controller.total('packageValue')).toEqual(6);
expect(controller.total('cc')).toEqual(0.03);
});
});
});

View File

@ -1,18 +0,0 @@
Reference: Referencia
Warehouse In: Alm. entrada
Warehouse Out: Alm. salida
Shipped: F. envío
Landed: F. entrega
Total entries: Ent. totales
Delivered: Enviada
Received: Recibida
Agency: Agencia
Entries: Entradas
Confirmed: Confirmada
Entry Id: Id entrada
Supplier: Proveedor
Pallet: Pallet
Freight: Porte
Package: Embalaje
Half box: Media caja
Go to the travel: Ir al envío

View File

@ -1,6 +0,0 @@
@import "variables";
vn-travel-summary .summary {
max-width: $width-lg;
}

View File

@ -1,177 +0,0 @@
<vn-watcher
vn-id="watcher"
data="$ctrl.dms">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="DmsTypes"
data="dmsTypes"
order="name">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Companies"
data="companies"
order="code">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Warehouses"
data="warehouses"
order="name">
</vn-crud-model>
<form
name="form"
ng-submit="$ctrl.onSubmit()"
class="vn-ma-md"
enctype="multipart/form-data">
<div class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-autocomplete vn-one
label="Thermograph"
ng-model="$ctrl.dms.thermographId"
url="TravelThermographs"
where="{travelFk: null}"
show-field="thermographFk"
value-field="thermographFk">
<tpl-item>
{{thermographFk}}
</tpl-item>
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New thermograph"
ng-click="$ctrl.onAddThermographClick($event)"
vn-acl="buyer"
vn-acl-action="remove">
</vn-icon-button>
</append>
</vn-autocomplete>
<vn-textfield vn-one
label="State"
ng-model="$ctrl.dms.state"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one
label="Reference"
ng-model="$ctrl.dms.reference"
rule>
</vn-textfield>
<vn-autocomplete vn-one
label="Type"
ng-model="$ctrl.dms.dmsTypeId"
data="dmsTypes"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Company"
ng-model="$ctrl.dms.companyId"
data="companies"
show-field="code"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one
label="Warehouse"
ng-model="$ctrl.dms.warehouseId"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-input-file
vn-one
label="File"
ng-model="$ctrl.dms.files"
accept="{{$ctrl.allowedContentTypes}}"
multiple="true">
<append>
<vn-icon vn-none
color-marginal
title="{{$ctrl.contentTypesInfo}}"
icon="info">
</vn-icon>
</append>
</vn-input-file>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Upload">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="travel.card.thermograph.index">
</vn-button>
</vn-button-bar>
</div>
</form>
<!-- Create thermograph dialog -->
<vn-crud-model
vn-id="modelsModel"
url="Thermographs/getThermographModels"
data="thermographModels">
</vn-crud-model>
<vn-crud-model
vn-id="temperaturesModel"
url="Temperatures"
data="Temperatures"
auto-load="true">
</vn-crud-model>
<vn-dialog class="edit"
vn-id="newThermographDialog"
on-accept="$ctrl.onNewThermographAccept()"
message="New thermograph">
<tpl-body>
<vn-horizontal>
<vn-textfield
vn-one
required="true"
label="Identifier"
ng-model="$ctrl.newThermograph.thermographId"
vn-focus>
</vn-textfield>
<vn-autocomplete
vn-one
required="true"
label="Model"
ng-model="$ctrl.newThermograph.model"
data="thermographModels"
show-field="value"
value-field="value">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
required="true"
label="Warehouse"
ng-model="$ctrl.newThermograph.warehouseId"
url="Warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete
vn-one
required="true"
label="Temperature"
ng-model="$ctrl.newThermograph.temperatureFk"
data='Temperatures'
show-field="name"
value-field="code">
</vn-autocomplete>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Create</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,122 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import UserError from 'core/lib/user-error';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.dms = {files: [], state: 'Ok'};
}
get travel() {
return this._travel;
}
set travel(value) {
this._travel = value;
if (value) {
this.setDefaultParams();
this.getAllowedContentTypes();
}
}
getAllowedContentTypes() {
this.$http.get('DmsContainers/allowedContentTypes').then(res => {
const contentTypes = res.data.join(', ');
this.allowedContentTypes = contentTypes;
});
}
get contentTypesInfo() {
return this.$t('ContentTypesInfo', {
allowedContentTypes: this.allowedContentTypes
});
}
setDefaultParams() {
const params = {filter: {
where: {code: 'miscellaneous'}
}};
this.$http.get('DmsTypes/findOne', {params}).then(res => {
const dmsTypeId = res.data && res.data.id;
const companyId = this.vnConfig.companyFk;
const warehouseId = this.vnConfig.warehouseFk;
const defaultParams = {
reference: this.travel.id,
warehouseId: warehouseId,
companyId: companyId,
dmsTypeId: dmsTypeId,
description: this.$t('TravelFileDescription', {
travelId: this.travel.id
}).toUpperCase()
};
this.dms = Object.assign(this.dms, defaultParams);
});
}
onAddThermographClick(event) {
const defaultTemperature = 'cool';
const defaultModel = 'DISPOSABLE';
event.preventDefault();
this.newThermograph = {
thermographId: this.thermographId,
warehouseId: this.warehouseId,
temperatureFk: defaultTemperature,
model: defaultModel
};
this.$.modelsModel.refresh();
this.$.newThermographDialog.show();
}
onNewThermographAccept() {
const hasMissingField =
!this.newThermograph.thermographId ||
!this.newThermograph.warehouseId ||
!this.newThermograph.temperatureFk ||
!this.newThermograph.model;
if (hasMissingField)
throw new UserError(`Some fields are invalid`);
return this.$http.post(`Thermographs/createThermograph`, this.newThermograph)
.then(res => this.dms.thermographId = res.data.id);
}
onSubmit() {
const query = `Travels/${this.travel.id}/saveThermograph`;
const options = {
method: 'POST',
url: query,
params: this.dms,
headers: {
'Content-Type': undefined
},
transformRequest: files => {
const formData = new FormData();
for (let i = 0; i < files.length; i++)
formData.append(files[i].name, files[i]);
return formData;
},
data: this.dms.files
};
this.$http(options).then(res => {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.$.watcher.updateOriginalData();
this.$state.go('travel.card.thermograph.index');
});
}
}
ngModule.vnComponent('vnTravelThermographCreate', {
template: require('./index.html'),
controller: Controller,
bindings: {
travel: '<'
}
});

View File

@ -1,108 +0,0 @@
import './index';
describe('Ticket', () => {
describe('Component vnTravelThermographCreate', () => {
let controller;
let $httpBackend;
let $httpParamSerializer;
const travelId = 3;
const dmsTypeId = 5;
beforeEach(ngModule('travel'));
beforeEach(inject(($componentController, _$httpBackend_, _$httpParamSerializer_) => {
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
const $element = angular.element('<vn-travel-thermograph-create></vn-travel-thermograph-create>');
controller = $componentController('vnTravelThermographCreate', {$element});
controller._travel = {
id: travelId
};
}));
describe('travel() setter', () => {
it('should set the travel data and then call setDefaultParams() and getAllowedContentTypes()', () => {
jest.spyOn(controller, 'setDefaultParams');
jest.spyOn(controller, 'getAllowedContentTypes');
controller.travel = {
id: travelId
};
expect(controller.travel).toBeDefined();
expect(controller.setDefaultParams).toHaveBeenCalledWith();
expect(controller.getAllowedContentTypes).toHaveBeenCalledWith();
});
});
describe('setDefaultParams()', () => {
it('should perform a GET query and define the dms property on controller', () => {
const params = {filter: {
where: {code: 'miscellaneous'}
}};
let serializedParams = $httpParamSerializer(params);
$httpBackend.expect('GET', `DmsTypes/findOne?${serializedParams}`).respond({id: dmsTypeId, code: 'miscellaneous'});
controller.setDefaultParams();
$httpBackend.flush();
expect(controller.dms).toBeDefined();
expect(controller.dms.reference).toEqual(travelId);
expect(controller.dms.dmsTypeId).toEqual(dmsTypeId);
});
});
describe('getAllowedContentTypes()', () => {
it('should make an HTTP GET request to get the allowed content types', () => {
const expectedResponse = ['application/pdf', 'image/png', 'image/jpg'];
$httpBackend.expect('GET', `DmsContainers/allowedContentTypes`).respond(expectedResponse);
controller.getAllowedContentTypes();
$httpBackend.flush();
expect(controller.allowedContentTypes).toBeDefined();
expect(controller.allowedContentTypes).toEqual('application/pdf, image/png, image/jpg');
});
});
describe('onAddThermographClick()', () => {
it('should call the show() function of the create thermograph dialog', () => {
controller.$.newThermographDialog = {show: jest.fn()};
controller.$.modelsModel = {refresh: jest.fn()};
controller.$.temperaturesModel = {refresh: jest.fn()};
const event = new Event('click');
jest.spyOn(event, 'preventDefault');
controller.onAddThermographClick(event);
expect(event.preventDefault).toHaveBeenCalledTimes(1);
expect(controller.$.newThermographDialog.show).toHaveBeenCalledTimes(1);
});
});
describe('onNewThermographAccept()', () => {
it('should set the created thermograph data on to the controller for the autocomplete to use it', () => {
const id = 'the created id';
const warehouseId = 1;
const temperatureId = 'cool';
const model = 'my model';
controller.newThermograph = {
thermographId: id,
warehouseId: warehouseId,
temperatureFk: temperatureId,
model: model
};
const response = {
id: id,
warehouseId: warehouseId,
temperatureFk: temperatureId,
model: model
};
$httpBackend.when('POST', `Thermographs/createThermograph`).respond(response);
controller.onNewThermographAccept();
$httpBackend.flush();
expect(controller.dms.thermographId).toEqual(response.id);
});
});
});
});

View File

@ -1,87 +0,0 @@
<vn-watcher
vn-id="watcher"
data="$ctrl.dms">
</vn-watcher>
<form
name="form"
ng-submit="$ctrl.onSubmit()"
class="vn-ma-md"
enctype="multipart/form-data">
<div class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-autocomplete vn-one
label="Thermograph"
ng-model="$ctrl.thermograph.thermographId"
url="TravelThermographs"
show-field="thermographFk"
value-field="thermographFk"
disabled="true">
</vn-autocomplete>
<vn-textfield vn-one
label="State"
ng-model="$ctrl.thermograph.state"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one
label="Reference"
ng-model="$ctrl.thermograph.reference"
rule>
</vn-textfield>
<vn-autocomplete vn-one
label="Type"
ng-model="$ctrl.thermograph.dmsTypeId"
url="DmsTypes"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Company"
ng-model="$ctrl.thermograph.companyId"
url="Companies"
show-field="code"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one
label="Warehouse"
ng-model="$ctrl.thermograph.warehouseId"
url="Warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textarea vn-one vn-focus
label="Description"
ng-model="$ctrl.thermograph.description"
rule>
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<vn-input-file
vn-one
label="File"
ng-model="$ctrl.thermograph.files"
on-change="$ctrl.onFileChange($files)"
accept="{{$ctrl.allowedContentTypes}}"
multiple="true">
<append>
<vn-icon vn-none
color-marginal
title="{{$ctrl.contentTypesInfo}}"
icon="info">
</vn-icon>
</append>
</vn-input-file>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
<vn-button ui-sref="travel.card.thermograph.index" label="Cancel"></vn-button>
</vn-button-bar>
</div>
</form>

View File

@ -1,98 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
get travel() {
return this._travel;
}
set travel(value) {
this._travel = value;
if (value) {
this.setDefaultParams();
this.getAllowedContentTypes();
}
}
getAllowedContentTypes() {
this.$http.get('DmsContainers/allowedContentTypes').then(res => {
const contentTypes = res.data.join(', ');
this.allowedContentTypes = contentTypes;
});
}
get contentTypesInfo() {
return this.$t('ContentTypesInfo', {
allowedContentTypes: this.allowedContentTypes
});
}
setDefaultParams() {
const filterObj = {include: {relation: 'dms'}};
const filter = encodeURIComponent(JSON.stringify(filterObj));
const path = `TravelThermographs/${this.$params.thermographId}?filter=${filter}`;
this.$http.get(path).then(res => {
const thermograph = res.data;
this.thermograph = {
thermographId: thermograph.thermographFk,
state: thermograph.result,
reference: thermograph.dms.reference,
warehouseId: thermograph.dms.warehouseFk,
companyId: thermograph.dms.companyFk,
dmsTypeId: thermograph.dms.dmsTypeFk,
description: thermograph.dms.description,
hasFile: thermograph.dms.hasFile,
hasFileAttached: false,
files: []
};
});
}
onSubmit() {
const query = `travels/${this.$params.id}/saveThermograph`;
const options = {
method: 'POST',
url: query,
params: this.thermograph,
headers: {
'Content-Type': undefined
},
transformRequest: files => {
const formData = new FormData();
for (const element of files)
formData.append(element.name, element);
return formData;
},
data: this.thermograph.files
};
this.$http(options).then(res => {
if (res) {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.$.watcher.updateOriginalData();
this.$state.go('travel.card.thermograph.index');
}
});
}
onFileChange(files) {
let hasFileAttached = false;
if (files.length > 0)
hasFileAttached = true;
this.$.$applyAsync(() => {
this.thermograph.hasFileAttached = hasFileAttached;
});
}
}
ngModule.vnComponent('vnTravelThermographEdit', {
template: require('./index.html'),
controller: Controller,
bindings: {
travel: '<'
}
});

View File

@ -1,120 +0,0 @@
import './index';
import watcher from 'core/mocks/watcher.js';
describe('Worker', () => {
describe('Component vnTravelThermographEdit', () => {
let controller;
let $scope;
let $httpBackend;
let $httpParamSerializer;
beforeEach(ngModule('travel'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
const $element = angular.element(`<vn-travel-thermograph-edit></vn-travel-thermograph-edit`);
controller = $componentController('vnTravelThermographEdit', {$element, $scope});
controller._travel = {id: 3};
controller.$params = {id: 3, thermographId: 6};
controller.$.watcher = watcher;
}));
describe('travel() setter', () => {
it('should set the travel data and then call setDefaultParams() and getAllowedContentTypes()', () => {
jest.spyOn(controller, 'setDefaultParams');
jest.spyOn(controller, 'getAllowedContentTypes');
controller._travel = undefined;
controller.travel = {
id: 3
};
expect(controller.setDefaultParams).toHaveBeenCalledWith();
expect(controller.travel).toBeDefined();
expect(controller.getAllowedContentTypes).toHaveBeenCalledWith();
});
});
describe('setDefaultParams()', () => {
it('should perform a GET query and define the dms property on controller', () => {
const thermographId = 6;
const expectedResponse = {
thermographFk: 6,
result: 'Ok',
dms: {
reference: '123456-01',
warehouseFk: 1,
companyFk: 442,
dmsTypeFk: 3,
description: 'Test'
}
};
const filterObj = {include: {relation: 'dms'}};
const filter = encodeURIComponent(JSON.stringify(filterObj));
const query = `TravelThermographs/${thermographId}?filter=${filter}`;
$httpBackend.expect('GET', query).respond(expectedResponse);
controller.setDefaultParams();
$httpBackend.flush();
expect(controller.thermograph).toBeDefined();
expect(controller.thermograph.reference).toEqual('123456-01');
expect(controller.thermograph.dmsTypeId).toEqual(3);
expect(controller.thermograph.state).toEqual('Ok');
});
});
describe('onFileChange()', () => {
it('should set dms hasFileAttached property to true if has any files', () => {
const files = [{id: 1, name: 'MyFile'}];
controller.thermograph = {hasFileAttached: false};
controller.onFileChange(files);
$scope.$apply();
expect(controller.thermograph.hasFileAttached).toBeTruthy();
});
});
describe('getAllowedContentTypes()', () => {
it('should make an HTTP GET request to get the allowed content types', () => {
const expectedResponse = ['image/png', 'image/jpg'];
$httpBackend.expect('GET', `DmsContainers/allowedContentTypes`).respond(expectedResponse);
controller.getAllowedContentTypes();
$httpBackend.flush();
expect(controller.allowedContentTypes).toBeDefined();
expect(controller.allowedContentTypes).toEqual('image/png, image/jpg');
});
});
describe('contentTypesInfo()', () => {
it('should return a description with a list of allowed content types', () => {
controller.allowedContentTypes = ['image/png', 'image/jpg'];
const expectedTypes = controller.allowedContentTypes.join(', ');
const expectedResult = `Allowed content types: ${expectedTypes}`;
jest.spyOn(controller.$translate, 'instant').mockReturnValue(expectedResult);
const result = controller.contentTypesInfo;
expect(result).toEqual(expectedResult);
});
});
describe('onSubmit()', () => {
it('should make an HTTP POST request to save the form data', () => {
jest.spyOn(controller.$.watcher, 'updateOriginalData');
const files = [{id: 1, name: 'MyFile'}];
controller.thermograph = {files};
const serializedParams = $httpParamSerializer(controller.thermograph);
const query = `travels/${controller.$params.id}/saveThermograph?${serializedParams}`;
$httpBackend.expect('POST', query).respond({});
controller.onSubmit();
$httpBackend.flush();
});
});
});
});

View File

@ -1,7 +0,0 @@
vn-ticket-request {
.vn-textfield {
margin: 0!important;
max-width: 100px;
}
}

View File

@ -1,70 +0,0 @@
<vn-crud-model
vn-id="model"
url="TravelThermographs"
data="$ctrl.travelThermographs"
order="created"
link="{travelFk: $ctrl.$params.id}"
filter="$ctrl.filter"
auto-load="true">
</vn-crud-model>
<vn-data-viewer model="model">
<form name="form">
<vn-card class="vn-mt-md">
<vn-table model="model" auto-load="false">
<vn-thead>
<vn-tr>
<vn-th>Code</vn-th>
<vn-th>Temperature</vn-th>
<vn-th expand>State</vn-th>
<vn-th>Destination</vn-th>
<vn-th expand>Created</vn-th>
<vn-th shrink></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="thermograph in $ctrl.travelThermographs">
<vn-td>{{::thermograph.thermographFk}} </vn-td>
<vn-td>{{::thermograph.temperatureFk}} </vn-td>
<vn-td expand>{{::thermograph.result}}</vn-td>
<vn-td>{{::thermograph.warehouse.name}}</vn-td>
<vn-td expand>{{::thermograph.created | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td shrink>
<vn-icon-button title="{{'Download file' | translate}}"
icon="cloud_download"
ng-click="$ctrl.downloadFile(thermograph.dmsFk)">
</vn-icon-button>
</vn-td>
<vn-td shrink>
<vn-icon-button ui-sref="travel.card.thermograph.edit({thermographId: {{::thermograph.id}}})"
icon="edit"
title="{{'Edit file' | translate}}">
</vn-icon-button>
</vn-td>
<vn-td shrink>
<vn-icon-button
icon="delete"
ng-click="$ctrl.showDeleteConfirm($index)"
title="{{'Remove thermograph' | translate}}"
tabindex="-1">
</vn-icon-button>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</form>
</vn-data-viewer>
<vn-confirm
vn-id="confirm"
question="Are you sure you want to remove the thermograph?"
on-accept="$ctrl.deleteThermograph()">
</vn-confirm>
<a
ui-sref="travel.card.thermograph.create"
vn-tooltip="Add thermograph"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -1,51 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $, vnFile) {
super($element, $);
this.vnFile = vnFile;
this.filter = {
include:
{relation: 'warehouse',
scope: {
fields: ['id', 'name']
}
}
};
}
showDeleteConfirm(index) {
this.thermographIndex = index;
this.$.confirm.show();
}
deleteThermograph() {
const data = this.travelThermographs;
const thermographId = data[this.thermographIndex].id;
const query = `Travels/deleteThermograph?id=${thermographId}`;
this.$http.delete(query).then(() => {
this.vnApp.showSuccess(this.$t('Thermograph deleted'));
this.$.model.remove(this.thermographIndex);
this.thermographIndex = null;
});
}
downloadFile(dmsId) {
this.vnFile.download(`api/dms/${dmsId}/downloadFile`);
}
}
Controller.$inject = ['$element', '$scope', 'vnFile'];
ngModule.vnComponent('vnTravelThermographIndex', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnTravelCard'
},
bindings: {
travel: '<'
}
});

View File

@ -1,6 +0,0 @@
@import "variables";
vn-route-tickets form{
margin: 0 auto;
max-width: $width-lg;
}

View File

@ -1,20 +0,0 @@
Code: Código
Temperature: Temperatura
State: Estado
Destination: Destino
Created: Creado
Remove thermograph: Eliminar termógrafo
Upload file: Subir fichero
Edit file: Editar fichero
Upload: Subir
File: Fichero
TravelFileDescription: Travel id {{travelId}}
ContentTypesInfo: 'Tipos de archivo permitidos: {{allowedContentTypes}}'
Are you sure you want to continue?: ¿Seguro que quieres continuar?
Add thermograph: Añadir termógrafo
Edit thermograph: Editar termógrafo
Thermograph deleted: Termógrafo eliminado
Thermograph: Termógrafo
New thermograph: Nuevo termógrafo
Are you sure you want to remove the thermograph?: ¿Seguro que quieres quitar el termógrafo?
Identifier: Identificador