Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 7202-AddCustomAgentFkColumn

This commit is contained in:
Jon Elias 2024-10-01 12:43:47 +02:00
commit f4015117e3
65 changed files with 88 additions and 3515 deletions

View File

@ -1544,7 +1544,7 @@ INSERT INTO `bs`.`waste`(`buyerFk`, `year`, `week`, `itemFk`, `itemTypeFk`, `sal
('103', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 6, 1, '186', '0', '51', '53.12', '56.20', '56.20', '56.20'), ('103', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 6, 1, '186', '0', '51', '53.12', '56.20', '56.20', '56.20'),
('103', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 7, 1, '277', '0', '53.12', '56.20', '56.20', '56.20', '56.20'); ('103', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 7, 1, '277', '0', '53.12', '56.20', '56.20', '56.20', '56.20');
INSERT INTO `vn`.`buy`(`id`,`entryFk`,`itemFk`,`buyingValue`,`quantity`,`packagingFk`,`stickers`,`freightValue`,`packageValue`,`comissionValue`,`packing`,`grouping`,`groupingMode`,`location`,`price1`,`price2`,`price3`, `printedStickers`,`isChecked`,`isIgnored`,`weight`, `created`) INSERT INTO vn.buy(id,entryFk,itemFk,buyingValue,quantity,packagingFk,stickers,freightValue,packageValue,comissionValue,packing,grouping,groupingMode,location,price1,price2,price3,printedStickers,isChecked,isIgnored,weight,created)
VALUES VALUES
(1, 1, 1, 50, 5000, 4, 1, 1.500, 1.500, 0.000, 1, 1, 'packing', NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, util.VN_CURDATE() - INTERVAL 2 MONTH), (1, 1, 1, 50, 5000, 4, 1, 1.500, 1.500, 0.000, 1, 1, 'packing', NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, util.VN_CURDATE() - INTERVAL 2 MONTH),
(2, 2, 1, 50, 100, 4, 1, 1.500, 1.500, 0.000, 1, 1, 'packing', NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, util.VN_CURDATE() - INTERVAL 1 MONTH), (2, 2, 1, 50, 100, 4, 1, 1.500, 1.500, 0.000, 1, 1, 'packing', NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, util.VN_CURDATE() - INTERVAL 1 MONTH),
@ -1560,7 +1560,8 @@ INSERT INTO `vn`.`buy`(`id`,`entryFk`,`itemFk`,`buyingValue`,`quantity`,`packagi
(12, 6, 4, 1.25, 0, 3, 1, 2.500, 2.500, 0.000, 10, 10, 'grouping', NULL, 0.00, 1.75, 1.67, 0, 1, 0, 4, util.VN_CURDATE()), (12, 6, 4, 1.25, 0, 3, 1, 2.500, 2.500, 0.000, 10, 10, 'grouping', NULL, 0.00, 1.75, 1.67, 0, 1, 0, 4, util.VN_CURDATE()),
(13, 7, 1, 50, 0, 3, 1, 2.000, 2.000, 0.000, 1, 1, 'packing', NULL, 0.00, 99.6, 99.4, 0, 1, 0, 4, util.VN_CURDATE()), (13, 7, 1, 50, 0, 3, 1, 2.000, 2.000, 0.000, 1, 1, 'packing', NULL, 0.00, 99.6, 99.4, 0, 1, 0, 4, util.VN_CURDATE()),
(14, 7, 2, 5, 0, 3, 1, 2.000, 2.000, 0.000, 10, 10, 'grouping', NULL, 0.00, 7.30, 7.00, 0, 1, 0, 4, util.VN_CURDATE()), (14, 7, 2, 5, 0, 3, 1, 2.000, 2.000, 0.000, 10, 10, 'grouping', NULL, 0.00, 7.30, 7.00, 0, 1, 0, 4, util.VN_CURDATE()),
(15, 7, 4, 1.25, 0, 3, 1, 2.000, 2.000, 0.000, 10, 10, 'grouping', NULL, 0.00, 1.75, 1.67, 0, 1, 0, 4, util.VN_CURDATE()); (15, 7, 4, 1.25, 0, 3, 1, 2.000, 2.000, 0.000, 10, 10, 'grouping', NULL, 0.00, 1.75, 1.67, 0, 1, 0, 4, util.VN_CURDATE()),
(16, 99,1,50.0000, 5000, 4, 1, 1.500, 1.500, 0.000, 1, 1, 'packing', NULL, 0.00, 99.60, 99.40, 0, 1, 0, 1.00, '2024-07-30 08:13:51.000');
INSERT INTO `hedera`.`order`(`id`, `date_send`, `customer_id`, `delivery_method_id`, `agency_id`, `address_id`, `company_id`, `note`, `source_app`, `confirmed`,`total`, `date_make`, `first_row_stamp`, `confirm_date`) INSERT INTO `hedera`.`order`(`id`, `date_send`, `customer_id`, `delivery_method_id`, `agency_id`, `address_id`, `company_id`, `note`, `source_app`, `confirmed`,`total`, `date_make`, `first_row_stamp`, `confirm_date`)
VALUES VALUES
@ -2441,7 +2442,8 @@ INSERT INTO `vn`.`workerTimeControl`(`userFk`, `timed`, `manual`, `direction`, `
(1107, CONCAT(util.VN_CURDATE(), ' 10:20'), TRUE, 'middle', 1), (1107, CONCAT(util.VN_CURDATE(), ' 10:20'), TRUE, 'middle', 1),
(1107, CONCAT(util.VN_CURDATE(), ' 14:50'), TRUE, 'out', 1); (1107, CONCAT(util.VN_CURDATE(), ' 14:50'), TRUE, 'out', 1);
INSERT INTO `vn`.`dmsType`(`id`, `name`, `readRoleFk`, `writeRoleFk`, `code`) INSERT INTO `vn`.`dmsType`
(`id`, `name`, `readRoleFk`, `writeRoleFk`, `code`)
VALUES VALUES
(1, 'Facturas Recibidas', NULL, NULL, 'invoiceIn'), (1, 'Facturas Recibidas', NULL, NULL, 'invoiceIn'),
(2, 'Doc oficial', NULL, NULL, 'officialDoc'), (2, 'Doc oficial', NULL, NULL, 'officialDoc'),
@ -2464,7 +2466,8 @@ INSERT INTO `vn`.`dmsType`(`id`, `name`, `readRoleFk`, `writeRoleFk`, `code`)
(19, 'inmovilizado', NULL, NULL, 'fixedAssets'), (19, 'inmovilizado', NULL, NULL, 'fixedAssets'),
(20, 'Reclamación', 1, 1, 'claim'), (20, 'Reclamación', 1, 1, 'claim'),
(21, 'Entrada', 1, 1, 'entry'), (21, 'Entrada', 1, 1, 'entry'),
(22, 'Proveedor', 1, 1, 'supplier'); (22, 'Proveedor', 1, 1, 'supplier'),
(23, 'Termografos', 35, 35, 'thermograph');
INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `warehouseFk`, `companyFk`, `hardCopyNumber`, `hasFile`, `reference`, `description`, `created`) INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `warehouseFk`, `companyFk`, `hardCopyNumber`, `hasFile`, `reference`, `description`, `created`)
VALUES VALUES
@ -2472,7 +2475,7 @@ INSERT INTO `vn`.`dms`(`id`, `dmsTypeFk`, `file`, `contentType`, `workerFk`, `wa
(2, 5, '2.txt', 'text/plain', 5, 1, 442, 1, TRUE, 'Client:104', 'Client:104 dms for the client', util.VN_CURDATE()), (2, 5, '2.txt', 'text/plain', 5, 1, 442, 1, TRUE, 'Client:104', 'Client:104 dms for the client', util.VN_CURDATE()),
(3, 5, '3.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Client: 104', 'Client:104 readme', util.VN_CURDATE()), (3, 5, '3.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Client: 104', 'Client:104 readme', util.VN_CURDATE()),
(4, 3, '4.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Worker: 106', 'Worker:106 readme', util.VN_CURDATE()), (4, 3, '4.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'Worker: 106', 'Worker:106 readme', util.VN_CURDATE()),
(5, 5, '5.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'travel: 1', 'dmsForThermograph', util.VN_CURDATE()), (5, 23, '5.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'travel: 1', 'dmsForThermograph', util.VN_CURDATE()),
(6, 5, '6.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'NotExists', 'DoesNotExists', util.VN_CURDATE()), (6, 5, '6.txt', 'text/plain', 5, 1, 442, NULL, TRUE, 'NotExists', 'DoesNotExists', util.VN_CURDATE()),
(7, 20, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', util.VN_CURDATE()), (7, 20, '7.jpg', 'image/jpeg', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', util.VN_CURDATE()),
(8, 20, '8.mp4', 'video/mp4', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', util.VN_CURDATE()), (8, 20, '8.mp4', 'video/mp4', 9, 1, 442, NULL, FALSE, '1', 'TICKET ID DEL CLIENTE BRUCE WAYNE ID 1101', util.VN_CURDATE()),

View File

@ -52,13 +52,13 @@ BEGIN
FROM vn.sale s FROM vn.sale s
JOIN vn.ticket t ON t.id = s.ticketFk JOIN vn.ticket t ON t.id = s.ticketFk
LEFT JOIN vn.itemShelvingSale iss ON iss.saleFk = s.id LEFT JOIN vn.itemShelvingSale iss ON iss.saleFk = s.id
WHERE t.shipped BETWEEN CURDATE() AND CURDATE() + INTERVAL vDaysInForward DAY WHERE t.shipped >= CURDATE() + INTERVAL vDaysInForward DAY
AND iss.saleFk IS NULL AND iss.saleFk IS NULL
AND t.warehouseFk = vWarehouseFk AND t.warehouseFk = vWarehouseFk
GROUP BY s.itemFk GROUP BY s.itemFk
) )
SELECT i.id itemFk, SELECT i.id itemFk,
CAST(sd.quantity AS INT) advanceable, LEAST(CAST(sd.quantity AS INT), sk.stock) advanceable,
i.longName, i.longName,
i.subName, i.subName,
i.tag5, i.tag5,

View File

@ -4,5 +4,14 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`travelThermograph_befor
FOR EACH ROW FOR EACH ROW
BEGIN BEGIN
SET NEW.editorFk = account.myUser_getId(); SET NEW.editorFk = account.myUser_getId();
IF NEW.travelFk IS NULL AND
(SELECT COUNT(*) FROM travelThermograph
WHERE thermographFk = NEW.thermographFk
AND travelFk IS NULL
AND id <> NEW.id) > 0
THEN
CALL util.throw('Duplicate thermographFk without travelFk not allowed.');
END IF;
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -4,5 +4,14 @@ CREATE OR REPLACE DEFINER=`vn`@`localhost` TRIGGER `vn`.`travelThermograph_befor
FOR EACH ROW FOR EACH ROW
BEGIN BEGIN
SET NEW.editorFk = account.myUser_getId(); SET NEW.editorFk = account.myUser_getId();
IF NEW.travelFk IS NULL AND
(SELECT COUNT(*) FROM travelThermograph
WHERE thermographFk = NEW.thermographFk
AND travelFk IS NULL
AND id <> NEW.id) > 0
THEN
CALL util.throw('Duplicate thermographFk without travelFk not allowed.');
END IF;
END$$ END$$
DELIMITER ; DELIMITER ;

View File

@ -1,68 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('SmartTable SearchBar integration', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('salesPerson', 'item');
await page.waitToClick(selectors.globalItems.searchButton);
});
afterAll(async() => {
await browser.close();
});
it('should search by type in searchBar, reload page and have same results', async() => {
await page.waitToClick(selectors.itemsIndex.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.itemsIndex.advancedSearchItemType, 'Anthurium');
await page.waitToClick(selectors.itemsIndex.advancedSearchButton);
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 4);
await page.reload({
waitUntil: 'networkidle2'
});
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 4);
await page.write(selectors.itemsIndex.advancedSmartTableGrouping, '1');
await page.keyboard.press('Enter');
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 2);
await page.reload({
waitUntil: 'networkidle2'
});
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1);
});
it('should filter in section without smart-table and search in searchBar go to zone section', async() => {
await page.loginAndModule('salesPerson', 'zone');
await page.waitToClick(selectors.globalItems.searchButton);
await page.doSearch('A');
const firstCount = await page.countElement(selectors.zoneIndex.searchResult);
await page.doSearch('A');
const secondCount = await page.countElement(selectors.zoneIndex.searchResult);
expect(firstCount).toEqual(7);
expect(secondCount).toEqual(7);
});
it('should order orders by first id and order by last id, reload page and have same order', async() => {
await page.loginAndModule('developer', 'item');
await page.accessToSection('item.fixedPrice');
await page.keyboard.press('Enter');
await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '1');
await page.waitToClick(selectors.itemFixedPrice.orderColumnId);
await page.reload({
waitUntil: 'networkidle2'
});
await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '3');
});
});

View File

@ -1,104 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Zone basic data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('deliveryAssistant',
'zone'); // turns up the zone module name and route aint the same lol
await page.accessToSearchResult('10');
await page.accessToSection('zone.card.basicData');
});
afterAll(async() => {
await browser.close();
});
it('should reach the basic data section', async() => {
await page.waitForState('zone.card.basicData');
});
it('should edit de form and then save', async() => {
await page.clearInput(selectors.zoneBasicData.name);
await page.write(selectors.zoneBasicData.name, 'Brimstone teleportation');
await page.autocompleteSearch(selectors.zoneBasicData.agency, 'Quantum break device');
await page.clearInput(selectors.zoneBasicData.maxVolume);
await page.write(selectors.zoneBasicData.maxVolume, '10');
await page.clearInput(selectors.zoneBasicData.travelingDays);
await page.write(selectors.zoneBasicData.travelingDays, '1');
await page.clearInput(selectors.zoneBasicData.closing);
await page.pickTime(selectors.zoneBasicData.closing, '21:00');
await page.clearInput(selectors.zoneBasicData.price);
await page.write(selectors.zoneBasicData.price, '999');
await page.clearInput(selectors.zoneBasicData.bonus);
await page.write(selectors.zoneBasicData.bonus, '100');
await page.clearInput(selectors.zoneBasicData.inflation);
await page.write(selectors.zoneBasicData.inflation, '200');
await page.waitToClick(selectors.zoneBasicData.volumetric);
await page.waitToClick(selectors.zoneBasicData.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should now reload the section', async() => {
await page.reloadSection('zone.card.basicData');
});
it('should confirm the name was updated', async() => {
const result = await page.waitToGetProperty(selectors.zoneBasicData.name, 'value');
expect(result).toEqual('Brimstone teleportation');
});
it('should confirm the agency was updated', async() => {
const result = await page.waitToGetProperty(selectors.zoneBasicData.agency, 'value');
expect(result).toEqual('Quantum break device');
});
it('should confirm the max volume was updated', async() => {
const result = await page.waitToGetProperty(selectors.zoneBasicData.maxVolume, 'value');
expect(result).toEqual('10');
});
it('should confirm the traveling days were updated', async() => {
const result = await page.waitToGetProperty(selectors.zoneBasicData.travelingDays, 'value');
expect(result).toEqual('1');
});
it('should confirm the closing hour was updated', async() => {
const result = await page.waitToGetProperty(selectors.zoneBasicData.closing, 'value');
expect(result).toEqual('21:00');
});
it('should confirm the price was updated', async() => {
const result = await page.waitToGetProperty(selectors.zoneBasicData.price, 'value');
expect(result).toEqual('999');
});
it('should confirm the bonus was updated', async() => {
const result = await page.waitToGetProperty(selectors.zoneBasicData.bonus, 'value');
expect(result).toEqual('100');
});
it('should confirm the inflation was updated', async() => {
const result = await page.waitToGetProperty(selectors.zoneBasicData.inflation, 'value');
expect(result).toEqual('200');
});
it('should confirm the volumetric checkbox was checked', async() => {
await page.waitForClassPresent(selectors.zoneBasicData.volumetric, 'checked');
});
});

View File

@ -1,32 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Zone descriptor path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('deliveryAssistant', 'zone');
await page.accessToSearchResult('13');
});
afterAll(async() => {
await browser.close();
});
it('should eliminate the zone using the descriptor option', async() => {
await page.waitToClick(selectors.zoneDescriptor.menu);
await page.waitToClick(selectors.zoneDescriptor.deleteZone);
await page.respondToDialog('accept');
await page.waitForState('zone.index');
});
it('should search for the deleted zone to find no results', async() => {
await page.doSearch('13');
const count = await page.countElement(selectors.zoneIndex.searchResult);
expect(count).toEqual(0);
});
});

View File

@ -117,7 +117,8 @@ module.exports = Self => {
result: state, result: state,
maxTemperature, maxTemperature,
minTemperature, minTemperature,
temperatureFk temperatureFk,
warehouseFk: warehouseId,
}, myOptions); }, myOptions);
if (tx) await tx.commit(); if (tx) await tx.commit();

View File

@ -4,7 +4,7 @@ describe('Thermograph saveThermograph()', () => {
const ctx = beforeAll.getCtx(); const ctx = beforeAll.getCtx();
const travelFk = 1; const travelFk = 1;
const thermographId = '138350-0'; const thermographId = '138350-0';
const warehouseFk = '1'; const warehouseFk = 1;
const state = 'COMPLETED'; const state = 'COMPLETED';
const maxTemperature = 30; const maxTemperature = 30;
const minTemperature = 10; const minTemperature = 10;
@ -41,7 +41,7 @@ describe('Thermograph saveThermograph()', () => {
maxTemperature, maxTemperature,
minTemperature, minTemperature,
temperatureFk, temperatureFk,
null, warehouseFk,
null, null,
null, null,
null, null,

View File

@ -1,114 +0,0 @@
<mg-ajax path="Zones/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.zone"
form="form"
save="patch">
</vn-watcher>
<form
name="form"
ng-submit="$ctrl.onSubmit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
label="Name"
ng-model="$ctrl.zone.name"
vn-acl="deliveryAssistant"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
ng-model="$ctrl.zone.agencyModeFk"
url="AgencyModes"
show-field="name"
value-field="id"
label="Agency"
vn-acl="deliveryAssistant"
rule>
</vn-autocomplete>
<vn-input-number
vn-one
label="Max m³"
ng-model="$ctrl.zone.itemMaxSize"
min="0"
vn-acl="deliveryAssistant"
rule>
</vn-input-number>
<vn-input-number
vn-one
label="Maximum m³"
ng-model="$ctrl.zone.m3Max"
min="0"
vn-acl="deliveryAssistant"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
label="Traveling days"
ng-model="$ctrl.zone.travelingDays"
min="0"
step="1"
vn-acl="deliveryAssistant"
rule>
</vn-input-number>
<vn-input-time
label="Closing"
ng-model="$ctrl.zone.hour"
vn-acl="deliveryAssistant"
rule>
</vn-input-time>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
label="Price"
ng-model="$ctrl.zone.price"
min="0"
step="0.01"
vn-acl="deliveryAssistant"
rule>
</vn-input-number>
<vn-input-number
label="Bonus"
ng-model="$ctrl.zone.bonus"
step="0.01"
vn-acl="deliveryAssistant"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
vn-one
label="Inflation"
ng-model="$ctrl.zone.inflation"
min="0"
step="0.01"
vn-acl="deliveryAssistant"
rule>
</vn-input-number>
<vn-check
vn-one
label="Volumetric"
ng-model="$ctrl.zone.isVolumetric"
vn-acl="deliveryAssistant"
rule>
</vn-check>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
vn-acl="deliveryAssistant"
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() {
this.$.watcher.submit().then(() =>
this.card.reload()
);
}
}
ngModule.vnComponent('vnZoneBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
zone: '<'
},
require: {
card: '^vnZoneCard'
}
});

View File

@ -1,33 +0,0 @@
<vn-card>
<div class="header">
<vn-button
icon="navigate_before"
ng-click="$ctrl.step(-1)"
class="flat">
</vn-button>
<div>
<span translate>{{$ctrl.firstDay | date:'MMMM'}}</span>
<span>{{$ctrl.firstDay | date:'yyyy'}} -</span>
<span translate>{{$ctrl.lastDay | date:'MMMM'}}</span>
<span>{{$ctrl.lastDay | date:'yyyy'}}</span>
</div>
<vn-button
icon="navigate_next"
ng-click="$ctrl.step(1)"
class="flat">
</vn-button>
</div>
<div class="calendars vn-pa-md">
<vn-calendar
ng-repeat="date in $ctrl.months track by date.getTime()"
default-date="date"
display-controls="false"
hide-contiguous="true"
has-events="$ctrl.hasEvents($day)"
get-class="$ctrl.getClass($day)"
on-selection="$ctrl.onSelection($event, $days, $type, $weekday)"
class="vn-pa-md"
style="min-width: 250px; flex: 1;">
</vn-calendar>
</div>
</vn-card>

View File

@ -1,191 +0,0 @@
import ngModule from '../module';
import Component from 'core/lib/component';
import './style.scss';
class Controller extends Component {
constructor($element, $, vnWeekDays) {
super($element, $);
this.vnWeekDays = vnWeekDays;
this.nMonths = 4;
let date = Date.vnNew();
date.setDate(1);
date.setHours(0, 0, 0, 0);
this.date = date;
}
get date() {
return this._date;
}
set date(value) {
this._date = value;
let stamp = value.getTime();
let firstDay = new Date(stamp);
firstDay.setDate(1);
this.firstDay = firstDay;
let lastDay = new Date(stamp);
lastDay.setMonth(lastDay.getMonth() + this.nMonths);
lastDay.setDate(0);
this.lastDay = lastDay;
this.months = [];
for (let i = 0; i < this.nMonths; i++) {
let monthDate = new Date(stamp);
monthDate.setMonth(value.getMonth() + i);
this.months.push(monthDate);
}
this.refreshEvents();
}
step(direction) {
let date = new Date(this.date.getTime());
date.setMonth(date.getMonth() + (this.nMonths * direction));
this.date = date;
this.emit('step');
}
get data() {
return this._data;
}
set data(value) {
this._data = value;
value = value || {};
this.events = value.events;
function toStamp(date) {
return date && new Date(date).setHours(0, 0, 0, 0);
}
this.exclusions = {};
let exclusions = value.exclusions;
if (exclusions) {
for (let exclusion of exclusions) {
let stamp = toStamp(exclusion.dated);
if (!this.exclusions[stamp]) this.exclusions[stamp] = [];
this.exclusions[stamp].push(exclusion);
}
}
this.geoExclusions = {};
let geoExclusions = value.geoExclusions;
if (geoExclusions) {
for (let geoExclusion of geoExclusions) {
let stamp = toStamp(geoExclusion.dated);
if (!this.geoExclusions[stamp]) this.geoExclusions[stamp] = [];
this.geoExclusions[stamp].push(geoExclusion);
}
}
let events = value.events;
if (events) {
for (let event of events) {
event.dated = toStamp(event.dated);
event.ended = toStamp(event.ended);
event.started = toStamp(event.started);
event.wdays = this.vnWeekDays.fromSet(event.weekDays);
}
}
this.refreshEvents();
let calendars = this.element.querySelectorAll('vn-calendar');
for (let calendar of calendars)
calendar.$ctrl.repaint();
}
refreshEvents() {
this.days = {};
if (!this.data) return;
let day = new Date(this.firstDay.getTime());
while (day <= this.lastDay) {
let stamp = day.getTime();
let wday = day.getDay();
let dayEvents = [];
let exclusions = this.exclusions[stamp] || [];
if (this.events) {
for (let event of this.events) {
let match;
switch (event.type) {
case 'day':
match = event.dated == stamp;
break;
default:
match = event.wdays[wday]
&& (!event.started || stamp >= event.started)
&& (!event.ended || stamp <= event.ended);
break;
}
if (match && !exclusions.find(e => e.zoneFk == event.zoneFk))
dayEvents.push(event);
}
}
if (dayEvents.length)
this.days[stamp] = dayEvents;
day.setDate(day.getDate() + 1);
}
}
onSelection($event, $days, $type, $weekday) {
let $events = [];
let $exclusions = [];
let $geoExclusions = [];
for (let day of $days) {
let stamp = day.getTime();
$events = $events.concat(this.days[stamp] || []);
$exclusions = $exclusions.concat(this.exclusions[stamp] || []);
$geoExclusions = $geoExclusions.concat(this.geoExclusions[stamp] || []);
}
this.emit('selection', {
$event,
$days,
$type,
$weekday,
$events,
$exclusions,
$geoExclusions
});
}
hasEvents(day) {
let stamp = day.getTime();
return this.days[stamp] || this.exclusions[stamp] || this.geoExclusions[stamp];
}
getClass(day) {
let stamp = day.getTime();
if (this.geoExclusions[stamp])
return 'geoExcluded';
else if (this.exclusions[stamp])
return 'excluded';
else return '';
}
}
Controller.$inject = ['$element', '$scope', 'vnWeekDays'];
ngModule.vnComponent('vnZoneCalendar', {
template: require('./index.html'),
controller: Controller,
bindings: {
data: '<?'
}
});

View File

@ -1,172 +0,0 @@
import './index';
import crudModel from 'core/mocks/crud-model';
describe('component vnZoneCalendar', () => {
let $scope;
let controller;
beforeEach(ngModule('zone'));
beforeEach(inject(($componentController, $rootScope) => {
$scope = $rootScope.$new();
const $element = angular.element(`<vn-zone-calendar></vn-zone-calendar>`);
controller = $componentController('vnZoneCalendar', {$element, $scope});
controller.$.model = crudModel;
controller.zone = {id: 1};
controller.days = [];
controller.exclusions = [];
controller.geoExclusions = [];
}));
describe('date() setter', () => {
it('should set the month property and then call the refreshEvents() method', () => {
jest.spyOn(controller, 'refreshEvents').mockReturnThis();
controller.date = Date.vnNew();
expect(controller.refreshEvents).toHaveBeenCalledWith();
expect(controller.months.length).toEqual(4);
});
});
describe('step()', () => {
it('should set the date month to 4 months backwards', () => {
const now = Date.vnNew();
now.setDate(15);
now.setMonth(now.getMonth() - 4);
controller.step(-1);
const expectedMonth = now.getMonth();
const currentMonth = controller.date.getMonth();
expect(currentMonth).toEqual(expectedMonth);
});
it('should set the date month to 4 months forwards', () => {
const now = Date.vnNew();
now.setDate(15);
now.setMonth(now.getMonth() + 4);
controller.step(1);
const expectedMonth = now.getMonth();
const currentMonth = controller.date.getMonth();
expect(currentMonth).toEqual(expectedMonth);
});
});
describe('data() setter', () => {
it('should set the events, exclusions and geoExclusions and then call the refreshEvents() method', () => {
jest.spyOn(controller, 'refreshEvents').mockReturnThis();
controller.data = {
exclusions: [{
dated: Date.vnNew()
}],
events: [{
dated: Date.vnNew()
}],
geoExclusions: [{
dated: Date.vnNew()
}],
};
expect(controller.refreshEvents).toHaveBeenCalledWith();
expect(controller.events).toBeDefined();
expect(controller.events.length).toEqual(1);
expect(controller.exclusions).toBeDefined();
expect(controller.geoExclusions).toBeDefined();
expect(Object.keys(controller.exclusions).length).toEqual(1);
});
});
describe('refreshEvents()', () => {
it('should fill the days property with the events.', () => {
controller.data = [];
controller.firstDay = Date.vnNew();
const lastDay = Date.vnNew();
lastDay.setDate(lastDay.getDate() + 10);
controller.lastDay = lastDay;
const firstEventStamp = controller.firstDay.getTime();
const lastEventStamp = controller.lastDay.getTime();
controller.events = [{
type: 'day',
dated: firstEventStamp
},
{
type: 'day',
dated: lastEventStamp
}];
controller.refreshEvents();
const expectedDays = Object.keys(controller.days);
expect(expectedDays.length).toEqual(2);
});
});
describe('onSelection()', () => {
it('should call the emit() method', () => {
jest.spyOn(controller, 'emit');
const $event = {};
const $days = [Date.vnNew()];
const $type = 'day';
const $weekday = 1;
controller.onSelection($event, $days, $type, $weekday);
expect(controller.emit).toHaveBeenCalledWith('selection',
{
$days: $days,
$event: {},
$events: [],
$exclusions: [],
$type: 'day',
$weekday: 1,
$geoExclusions: [],
}
);
});
});
describe('hasEvents()', () => {
it('should return true for an existing event on a date', () => {
const dated = Date.vnNew();
controller.days[dated.getTime()] = true;
const result = controller.hasEvents(dated);
expect(result).toBeTruthy();
});
});
describe('getClass()', () => {
it('should return the className "excluded" for an excluded date', () => {
const dated = Date.vnNew();
controller.exclusions = [];
controller.exclusions[dated.getTime()] = true;
const result = controller.getClass(dated);
expect(result).toEqual('excluded');
});
it('should return the className "geoExcluded" for a date with geo excluded', () => {
const dated = Date.vnNew();
controller.geoExclusions = [];
controller.geoExclusions[dated.getTime()] = true;
const result = controller.getClass(dated);
expect(result).toEqual('geoExcluded');
});
});
});

View File

@ -1,43 +0,0 @@
@import "variables";
vn-zone-calendar {
display: block;
& > vn-card {
& > .header {
display: flex;
align-items: center;
justify-content: space-between;
background-color: $color-main;
color: white;
font-weight: bold;
height: 45px;
& > .vn-button {
color: inherit;
height: 100%;
}
}
& > .calendars {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
& > .vn-calendar {
max-width: 288px;
#days-container .day {
&.event .day-number {
background-color: $color-success;
}
&.excluded .day-number {
background-color: $color-alert;
}
&.geoExcluded .day-number {
background-color: $color-main;
}
}
}
}
}
}

View File

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

View File

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

View File

@ -1,26 +0,0 @@
import './index.js';
describe('Zone Component vnZoneCard', () => {
let controller;
let $httpBackend;
let data = {id: 1, name: 'fooName'};
beforeEach(ngModule('zone'));
beforeEach(inject(($componentController, _$httpBackend_, $stateParams) => {
$httpBackend = _$httpBackend_;
let $element = angular.element('<div></div>');
controller = $componentController('vnZoneCard', {$element});
$stateParams.id = data.id;
$httpBackend.whenRoute('GET', 'Zones/:id').respond(data);
}));
it('should request data and set it on the controller', () => {
controller.reload();
$httpBackend.flush();
expect(controller.zone).toEqual(data);
});
});

View File

@ -1,98 +0,0 @@
<vn-watcher
vn-id="watcher"
url="Zones"
data="$ctrl.zone"
insert-mode="true"
form="form">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="Warehouses"
data="warehouses"
order="name">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="AgencyModes/isActive"
data="activeAgencyModes"
order="name">
</vn-crud-model>
<form
name="form"
vn-http-submit="$ctrl.onSubmit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
label="Name"
ng-model="$ctrl.zone.name"
rule
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
label="Warehouse"
ng-model="$ctrl.zone.warehouseFk"
data="warehouses"
show-field="name"
value-field="id"
label="Warehouse"
rule>
</vn-autocomplete>
<vn-autocomplete
label="Agency"
ng-model="$ctrl.zone.agencyModeFk"
data="activeAgencyModes"
show-field="name"
value-field="id"
label="Agency"
rule>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
label="Traveling days"
ng-model="$ctrl.zone.travelingDays"
min="0"
step="1"
rule>
</vn-input-number>
<vn-input-time
label="Closing hour"
ng-model="$ctrl.zone.hour"
rule>
</vn-input-time>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
label="Price"
ng-model="$ctrl.zone.price"
min="0"
step="0.01"
rule>
</vn-input-number>
<vn-input-number
label="Bonus"
ng-model="$ctrl.zone.bonus"
min="0"
step="0.01"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-check ng-model="$ctrl.zone.isVolumetric" label="Volumetric"></vn-check>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="zone.index">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,24 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
$onInit() {
this.zone = {
travelingDays: 0,
price: 0.20,
bonus: 0.20,
hour: Date.vnNew()
};
}
onSubmit() {
return this.$.watcher.submit().then(res =>
this.$state.go('zone.card.location', {id: res.data.id})
);
}
}
ngModule.vnComponent('vnZoneCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,40 +0,0 @@
import './index';
import watcher from 'core/mocks/watcher';
describe('Zone Component vnZoneCreate', () => {
let $scope;
let $state;
let controller;
beforeEach(ngModule('zone'));
beforeEach(inject(($componentController, $rootScope, _$state_) => {
$scope = $rootScope.$new();
$state = _$state_;
$scope.watcher = watcher;
$scope.watcher.submit = () => {
return {
then: callback => {
callback({data: {id: 1234}});
}
};
};
const $element = angular.element('<vn-zone-create></vn-zone-create>');
controller = $componentController('vnZoneCreate', {$element, $scope});
}));
describe('onSubmit()', () => {
it(`should call submit() on the watcher then expect a callback`, () => {
jest.spyOn($state, 'go');
controller.zone = {
name: 'Zone One'
};
controller.onSubmit();
expect(controller.$state.go).toHaveBeenCalledWith('zone.card.location', Object({id: 1234}));
});
});
});

View File

@ -1,3 +0,0 @@
Traveling days: Días de viaje
Closing hour: Hora de cierre
Bonus: Bonificación

View File

@ -1,102 +0,0 @@
<div class="vn-w-md">
<vn-zone-calendar
data="data"
on-selection="$ctrl.onSelection($event, $events, $days)">
</vn-zone-calendar>
</div>
<vn-side-menu side="right">
<form ng-submit="$ctrl.fetchData()" class="vn-pa-md">
<vn-radio
label="Pick up"
val="pickUp"
ng-model="$ctrl.deliveryMethodFk"
on-change="$ctrl.agencyModeFk = null"
tabindex="-1">
</vn-radio>
<vn-radio
label="Delivery"
val="delivery"
ng-model="$ctrl.deliveryMethodFk"
on-change="$ctrl.agencyModeFk = null"
class="vn-mb-sm"
tabindex="-1">
</vn-radio>
<vn-autocomplete
vn-one
ng-if="$ctrl.deliveryMethodFk === 'delivery'"
vn-focus
label="Postcode"
ng-model="$ctrl.geoFk"
url="Postcodes/location"
fields="['code','townFk']"
order="code, townFk"
value-field="geoFk"
show-field="code">
<tpl-item>
<div>
{{code}} {{town.name}}
</div>
<div class="text-caption text-secondary">
{{town.province.name}}, {{town.province.country.name}}
</div>
</tpl-item>
</vn-autocomplete>
<vn-autocomplete
label="{{$ctrl.deliveryMethodFk == 'delivery' ? 'Agency' : 'Warehouse'}}"
ng-model="$ctrl.agencyModeFk"
url="AgencyModes/isActive"
where="$ctrl.agencyFilter"
vn-id="agencymode">
</vn-autocomplete>
<vn-submit label="Query" class="vn-mt-sm"></vn-submit>
</form>
</vn-side-menu>
<!-- Zone Popover -->
<vn-popover vn-id="zoneEvents">
<div class="zoneEvents">
<div class="header vn-pa-sm" translate>Zones</div>
<vn-data-viewer
data="::$ctrl.zoneClosing"
class="vn-w-md vn-mb-xl">
<vn-card>
<vn-table>
<vn-thead>
<vn-tr>
<vn-th field="id" number>Id</vn-th>
<vn-th field="name" expand>Name</vn-th>
<vn-th field="agencyModeFk">Agency</vn-th>
<vn-th field="hour" shrink>Closing</vn-th>
<vn-th field="price" number>Price</vn-th>
<vn-th shrink></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr
ng-repeat="zone in $ctrl.zoneClosing"
ui-sref="zone.card.summary({id: zone.id})"
class="clickable search-result">
<vn-td number>{{::zone.id}}</vn-td>
<vn-td expand>{{::zone.name}}</vn-td>
<vn-td>{{::zone.agencyModeName}}</vn-td>
<vn-td shrink>{{::zone.hour | date: 'HH:mm'}}</vn-td>
<vn-td number>{{::zone.price | currency: 'EUR':2}}</vn-td>
<vn-td shrink>
<vn-horizontal class="buttons">
<vn-icon-button
vn-click-stop="$ctrl.preview(zone)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-horizontal>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
</div>
</vn-popover>
<vn-popup vn-id="summary">
<vn-zone-summary zone="$ctrl.selectedZone"></vn-zone-summary>
</vn-popup>

View File

@ -1,95 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
$onInit() {
this.setParams();
}
$postLink() {
this.deliveryMethodFk = 'delivery';
}
setParams() {
const hasParams = this.$params.deliveryMethodFk || this.$params.geoFk || this.$params.agencyModeFk;
if (hasParams) {
if (this.$params.deliveryMethodFk)
this.deliveryMethodFk = this.$params.deliveryMethodFk;
if (this.$params.geoFk)
this.geoFk = this.$params.geoFk;
if (this.$params.agencyModeFk)
this.agencyModeFk = this.$params.agencyModeFk;
this.fetchData();
}
}
fetchData() {
const params = {
deliveryMethodFk: this.deliveryMethodFk,
geoFk: this.geoFk,
agencyModeFk: this.agencyModeFk
};
this.$.data = null;
this.$http.get(`Zones/getEvents`, {params})
.then(res => {
let data = res.data;
this.$.data = data;
if (!data.events.length)
this.vnApp.showMessage(this.$t('No service for the specified zone'));
this.$state.go(this.$state.current.name, params);
});
}
get deliveryMethodFk() {
return this._deliveryMethodFk;
}
set deliveryMethodFk(value) {
this._deliveryMethodFk = value;
let filter;
if (value === 'pickUp')
filter = {where: {code: 'PICKUP'}};
else
filter = {where: {code: {inq: ['DELIVERY', 'AGENCY']}}};
this.$http.get(`DeliveryMethods`, {filter}).then(res => {
const deliveryMethods = res.data.map(deliveryMethod => deliveryMethod.id);
this.agencyFilter = {deliveryMethodFk: {inq: deliveryMethods}};
});
}
onSelection($event, $events, $days) {
if (!$events.length) return;
const day = $days[0];
const zoneIds = [];
for (let event of $events)
zoneIds.push(event.zoneFk);
const params = {
zoneIds: zoneIds,
date: day
};
this.$http.post(`Zones/getZoneClosing`, params)
.then(res => this.zoneClosing = res.data)
.then(() => this.$.zoneEvents.show($event.target));
}
preview(zone) {
this.selectedZone = zone;
this.$.summary.show();
}
}
ngModule.vnComponent('vnZoneDeliveryDays', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,126 +0,0 @@
import './index.js';
import popover from 'core/mocks/popover';
import crudModel from 'core/mocks/crud-model';
describe('Zone Component vnZoneDeliveryDays', () => {
let $httpBackend;
let controller;
let $element;
beforeEach(ngModule('zone'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$element = angular.element('<vn-zone-delivery-days></vn-zone-delivery-days');
controller = $componentController('vnZoneDeliveryDays', {$element});
controller.$.zoneEvents = popover;
controller.$.params = {};
controller.$.zoneModel = crudModel;
}));
describe('deliveryMethodFk() setter', () => {
it('should set the deliveryMethodFk property as pickup and then perform a query that sets the filter', () => {
$httpBackend.expect('GET', 'DeliveryMethods').respond([{id: 999}]);
controller.deliveryMethodFk = 'pickUp';
$httpBackend.flush();
expect(controller.agencyFilter).toEqual({deliveryMethodFk: {inq: [999]}});
});
});
describe('setParams()', () => {
it('should do nothing when no params are received', () => {
controller.setParams();
expect(controller.deliveryMethodFk).toBeUndefined();
expect(controller.geoFk).toBeUndefined();
expect(controller.agencyModeFk).toBeUndefined();
});
it('should set the controller properties when the params are provided', () => {
controller.$params = {
deliveryMethodFk: 3,
geoFk: 2,
agencyModeFk: 1
};
controller.setParams();
expect(controller.deliveryMethodFk).toEqual(controller.$params.deliveryMethodFk);
expect(controller.geoFk).toEqual(controller.$params.geoFk);
expect(controller.agencyModeFk).toEqual(controller.$params.agencyModeFk);
});
});
describe('fetchData()', () => {
it('should make an HTTP GET query and then call the showMessage() method', () => {
jest.spyOn(controller.vnApp, 'showMessage');
jest.spyOn(controller.$state, 'go');
controller.agencyModeFk = 1;
controller.deliveryMethodFk = 2;
controller.geoFk = 3;
controller.$state.current.name = 'myState';
const expectedData = {events: []};
const url = 'Zones/getEvents?agencyModeFk=1&deliveryMethodFk=2&geoFk=3';
$httpBackend.when('GET', 'DeliveryMethods').respond([]);
$httpBackend.expect('GET', url).respond({events: []});
controller.fetchData();
$httpBackend.flush();
expect(controller.$.data).toEqual(expectedData);
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No service for the specified zone');
expect(controller.$state.go).toHaveBeenCalledWith(
controller.$state.current.name,
{
agencyModeFk: 1,
deliveryMethodFk: 2,
geoFk: 3
}
);
});
});
describe('onSelection()', () => {
it('should not call the show popover method if events array is empty', () => {
jest.spyOn(controller.$.zoneEvents, 'show');
const event = new Event('click');
const target = document.createElement('div');
target.dispatchEvent(event);
const events = [];
controller.onSelection(event, events);
expect(controller.$.zoneEvents.show).not.toHaveBeenCalled();
});
it('should call the show() method and call getZoneClosing() with the expected ids', () => {
jest.spyOn(controller.$.zoneEvents, 'show');
const event = new Event('click');
const target = document.createElement('div');
target.dispatchEvent(event);
const day = Date.vnNew();
const events = [
{zoneFk: 1},
{zoneFk: 2},
{zoneFk: 8}
];
const params = {
zoneIds: [1, 2, 8],
date: [day][0]
};
const response = [{id: 1, hour: ''}];
$httpBackend.when('POST', 'Zones/getZoneClosing', params).respond({response});
controller.onSelection(event, events, [day]);
$httpBackend.flush();
expect(controller.$.zoneEvents.show).toHaveBeenCalledWith(target);
expect(controller.zoneClosing.id).toEqual(response.id);
});
});
});

View File

@ -1,36 +0,0 @@
@import "variables";
vn-zone-delivery-days {
vn-zone-calendar {
display: flex;
justify-content: center;
flex-wrap: wrap;
& > vn-calendar {
min-width: 264px;
}
}
form {
display: flex;
flex-direction: column;
}
}
.zoneEvents {
width: 700px;
max-height: 450px;
vn-data-viewer {
margin-bottom: 0;
vn-pagination {
padding: 0
}
}
& > .header {
background-color: $color-main;
color: white;
font-weight: bold;
text-align: center
}
}

View File

@ -1,4 +0,0 @@
<slot-descriptor>
<vn-zone-descriptor>
</vn-zone-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('vnZoneDescriptorPopover', {
slotTemplate: require('./index.html'),
controller: Controller
});

View File

@ -1,57 +0,0 @@
<vn-descriptor-content
module="zone"
description="$ctrl.zone.name"
summary="$ctrl.$.summary">
<slot-menu>
<vn-item class="vn-item"
ng-click="$ctrl.onDelete()"
translate>
Delete
</vn-item>
<vn-item
ng-click="clone.show()"
name="cloneZone"
translate>
Clone
</vn-item>
</slot-menu>
<slot-body>
<div class="attributes">
<vn-label-value
label="Agency"
value="{{$ctrl.zone.agencyMode.name}}">
</vn-label-value>
<vn-label-value
label="Closing hour"
value="{{$ctrl.zone.hour | date: 'HH:mm'}}">
</vn-label-value>
<vn-label-value
label="Traveling days"
value="{{$ctrl.zone.travelingDays}}">
</vn-label-value>
<vn-label-value
label="Price"
value="{{$ctrl.zone.price | currency: 'EUR': 2}}">
</vn-label-value>
<vn-label-value
label="Bonus"
value="{{$ctrl.zone.bonus | currency: 'EUR': 2}}">
</vn-label-value>
</div>
</slot-body>
</vn-descriptor-content>
<vn-popup vn-id="summary">
<vn-zone-summary zone="$ctrl.zone"></vn-zone-summary>
</vn-popup>
<vn-confirm
vn-id="deleteZone"
on-accept="$ctrl.deleteZone()"
message="This zone will be removed">
</vn-confirm>
<vn-confirm
vn-id="clone"
on-accept="$ctrl.onCloneAccept()"
question="Do you want to clone this zone?"
message="All it's properties will be copied">
</vn-confirm>

View File

@ -1,65 +0,0 @@
import ngModule from '../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get zone() {
return this.entity;
}
set zone(value) {
this.entity = value;
}
loadData() {
const filter = {
include: [
{
relation: 'agencyMode',
scope: {
fields: ['name'],
}
}
]
};
return this.getData(`Zones/${this.id}`, {filter})
.then(res => this.entity = res.data);
}
onDelete() {
const $t = this.$translate.instant;
const today = Date.vnNew();
today.setHours(0, 0, 0, 0);
const filter = {where: {zoneFk: this.id, shipped: {gte: today}}};
this.$http.get(`Tickets`, {filter}).then(res => {
const ticketsAmount = res.data.length;
if (ticketsAmount) {
const params = {ticketsAmount};
const question = $t('This zone contains tickets', params, null, null, 'sanitizeParameters');
this.$.deleteZone.question = question;
this.$.deleteZone.show();
} else
this.deleteZone();
});
}
deleteZone() {
return this.$http.post(`Zones/${this.id}/deleteZone`).then(() => {
this.$state.go('zone.index');
this.vnApp.showSuccess(this.$t('Zone deleted'));
});
}
onCloneAccept() {
return this.$http.post(`Zones/${this.id}/clone`).
then(res => this.$state.go('zone.card.basicData', {id: res.data.id}));
}
}
ngModule.vnComponent('vnZoneDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
zone: '<'
}
});

View File

@ -1,74 +0,0 @@
import './index.js';
describe('Zone descriptor', () => {
let $httpBackend;
let controller;
let $element;
beforeEach(ngModule('zone'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$element = angular.element('<vn-zone-descriptor></vn-zone-descriptor');
controller = $componentController('vnZoneDescriptor', {$element});
controller.zone = {id: 1};
controller.id = 1;
controller.$.deleteZone = {
hide: () => {},
show: () => {}
};
}));
describe('onDelete()', () => {
it('should make an HTTP POST query and then call the deleteZone show() method', () => {
jest.spyOn(controller.$.deleteZone, 'show');
const expectedData = [{id: 16}];
$httpBackend.when('GET', 'Tickets').respond(expectedData);
controller.onDelete();
$httpBackend.flush();
expect(controller.$.deleteZone.show).toHaveBeenCalledWith();
});
it('should make an HTTP POST query and then call the deleteZone() method', () => {
jest.spyOn(controller, 'deleteZone').mockReturnThis();
const expectedData = [];
$httpBackend.when('GET', 'Tickets').respond(expectedData);
controller.onDelete();
$httpBackend.flush();
expect(controller.deleteZone).toHaveBeenCalledWith();
});
});
describe('deleteZone()', () => {
it('should make an HTTP POST query and then call the showMessage() method', () => {
jest.spyOn(controller.$state, 'go').mockReturnThis();
jest.spyOn(controller.vnApp, 'showSuccess');
const stateName = 'zone.index';
$httpBackend.when('POST', 'Zones/1/deleteZone').respond(200);
controller.deleteZone();
$httpBackend.flush();
expect(controller.$state.go).toHaveBeenCalledWith(stateName);
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Zone deleted');
});
});
describe('onCloneAccept()', () => {
it('should make an HTTP POST query and then call the state go() method', () => {
jest.spyOn(controller.$state, 'go').mockReturnThis();
const stateName = 'zone.card.basicData';
const expectedData = {id: 1};
$httpBackend.when('POST', 'Zones/1/clone').respond(expectedData);
controller.onCloneAccept();
$httpBackend.flush();
expect(controller.$state.go).toHaveBeenCalledWith(stateName, expectedData);
});
});
});

View File

@ -1,4 +0,0 @@
This zone contains tickets: Esta zona contiene {{ticketsAmount}} tickets por servir. ¿Seguro que quieres eliminar esta zona?
Do you want to clone this zone?: ¿Quieres clonar esta zona?
All it's properties will be copied: Todas sus propiedades serán copiadas
Zone deleted: Zona eliminada

View File

@ -1,277 +0,0 @@
<vn-zone-calendar
id="calendar"
vn-id="calendar"
data="data"
on-selection="$ctrl.onSelection($days, $type, $weekday, $events, $exclusions, $geoExclusions)"
on-step="$ctrl.refresh()"
class="vn-w-md">
</vn-zone-calendar>
<vn-side-menu side="right">
<div class="vn-pa-md">
<h6
class="text-secondary"
style="font-weight: normal;"
translate>
Edit mode
</h6>
<vn-vertical>
<vn-radio
label="Include"
val="include"
ng-model="$ctrl.editMode">
</vn-radio>
<vn-radio
label="Exclude"
val="exclude"
ng-model="$ctrl.editMode">
</vn-radio>
</vn-vertical>
</div>
<h6
class="text-secondary vn-px-md"
style="font-weight: normal;"
translate>
Events
</h6>
<vn-data-viewer
data="data.events"
is-loading="!data.events">
<div class="vn-list separated">
<a
ng-repeat="row in data.events"
translate-attr="{title: 'Edit'}"
ng-click="$ctrl.onEditClick(row, $event)"
class="vn-item">
<vn-item-section>
<div
ng-if="::row.type == 'day'"
class="vn-mb-sm">
{{::row.dated | date:'dd/MM/yy'}}
</div>
<div
ng-if="::row.type != 'day'"
class="vn-mb-sm ellipsize">
<span ng-if="row.weekDays">
{{::$ctrl.formatWdays(row.weekDays)}}
</span>
<span ng-if="row.type == 'range'">
({{::row.started | date:'dd/MM/yy'}} - {{::row.ended | date:'dd/MM/yy'}})
</span>
</div>
<vn-label-value
label="Closing"
value="{{::row.hour | date:'HH:mm'}}">
</vn-label-value>
<vn-label-value
label="Traveling days"
value="{{::row.travelingDays}}">
</vn-label-value>
<vn-label-value
label="Price"
value="{{::row.price | currency:'EUR':2}}">
</vn-label-value>
<vn-label-value
label="Bonus"
value="{{::row.bonus | currency:'EUR':2}}">
</vn-label-value>
<vn-label-value
label="Max m³"
value="{{::row.m3Max}}">
</vn-label-value>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
icon="delete"
translate-attr="{title: 'Delete'}"
ng-click="$ctrl.onDeleteClick(row.id, $event)">
</vn-icon-button>
</vn-item-section>
</a>
</div>
</vn-data-viewer>
</vn-side-menu>
<vn-float-button
ng-click="$ctrl.createInclusion('weekday')"
icon="add"
vn-tooltip="Add event"
vn-bind="+"
fixed-bottom-right>
</vn-float-button>
<vn-dialog
vn-id="includeDialog"
on-response="$ctrl.onIncludeResponse($response)"
message="{{$ctrl.isNew ? 'Add event' : 'Edit event'}}">
<tpl-body>
<vn-vertical>
<vn-vertical class="vn-pb-md">
<vn-radio
ng-model="$ctrl.selected.type"
label="One day"
val="day">
</vn-radio>
<vn-radio
ng-model="$ctrl.selected.type"
label="Indefinitely"
val="indefinitely">
</vn-radio>
<vn-radio
ng-model="$ctrl.selected.type"
label="Range of dates"
val="range">
</vn-radio>
</vn-vertical>
<vn-wday-picker
ng-if="$ctrl.selected.type != 'day'"
ng-model="$ctrl.selected.wdays"
class="vn-mt-sm vn-mb-md">
</vn-wday-picker>
<vn-date-picker
ng-if="$ctrl.selected.type == 'day'"
label="Day"
ng-model="$ctrl.selected.dated">
</vn-date-picker>
<vn-horizontal
ng-if="$ctrl.selected.type == 'range'">
<vn-date-picker
label="From"
ng-model="$ctrl.selected.started">
</vn-date-picker>
<vn-date-picker
label="To"
ng-model="$ctrl.selected.ended">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-input-time
label="Closing"
ng-model="$ctrl.selected.hour">
</vn-input-time>
<vn-input-number
label="Traveling days"
ng-model="$ctrl.selected.travelingDays"
min="0"
step="1">
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
label="Price"
ng-model="$ctrl.selected.price"
min="0"
step="0.01">
</vn-input-number>
<vn-input-number
label="Bonus"
ng-model="$ctrl.selected.bonus"
min="0"
step="0.01">
</vn-input-number>
</vn-horizontal>
<vn-input-number
label="Max m³"
ng-model="$ctrl.selected.m3Max"
min="0"
step="0.01">
</vn-input-number>
</vn-vertical>
</tpl-body>
<tpl-buttons>
<input
type="button"
response="cancel"
translate-attr="{value: 'Cancel'}">
</input>
<input
type="button"
ng-if="!$ctrl.isNew"
response="delete"
translate-attr="{value: 'Delete'}">
</input>
<button response="accept">
<span ng-if="$ctrl.isNew" translate>Add</span>
<span ng-if="!$ctrl.isNew" translate>Save</span>
</button>
</tpl-buttons>
</vn-dialog>
<vn-confirm
vn-id="confirm"
message="This item will be deleted"
question="Are you sure you want to continue?">
</vn-confirm>
<vn-dialog
vn-id="excludeDialog"
on-response="$ctrl.onExcludeResponse($response)"
message="{{$ctrl.isNew ? 'Add exclusion' : 'Edit exclusion'}}"
on-open="$ctrl.onSearch($params)"
on-close="$ctrl.resetExclusions()">
<tpl-body>
<vn-date-picker
label="Day"
ng-model="$ctrl.excludeSelected.dated">
</vn-date-picker>
<vn-vertical class="width">
<vn-vertical class="vn-pb-md">
<vn-radio
ng-model="$ctrl.excludeSelected.type"
label="All"
on-change="$ctrl.test()"
val="all">
</vn-radio>
<vn-radio
ng-model="$ctrl.excludeSelected.type"
label="Specific locations"
on-change="$ctrl.onSearch($params)"
val="specificLocations">
</vn-radio>
</vn-vertical>
<vn-crud-model
vn-id="model"
url="Zones/{{$ctrl.$params.id}}/getLeaves"
filter="$ctrl.filter">
</vn-crud-model>
<div ng-if="$ctrl.excludeSelected.type == 'specificLocations'">
<vn-textfield
label="Search"
ng-keydown="$ctrl.onKeyDown($event)"
ng-model="$ctrl.excludeSearch">
<prepend>
<vn-icon icon="search"></vn-icon>
</prepend>
</vn-textfield>
<div class="treeview">
<vn-treeview
vn-id="treeview"
root-label="Locations where it is not distributed"
fetch-func="$ctrl.onFetch($item)"
sort-func="$ctrl.onSort($a, $b)">
<vn-check
ng-model="item.checked"
ng-click="$event.preventDefault()"
on-change="$ctrl.onItemCheck(item.id, value)"
label="{{::item.name}}">
</vn-check>
</vn-treeview>
</div>
</div>
</vn-vertical>
</tpl-body>
<tpl-buttons>
<input
type="button"
response="cancel"
translate-attr="{value: 'Cancel'}"
tabindex="0">
</input>
<input
type="button"
ng-if="!$ctrl.isNew"
response="delete"
translate-attr="{value: 'Delete'}"
tabindex="0">
</input>
<button response="accept">
<span ng-if="$ctrl.isNew" translate>Add</span>
<span ng-if="!$ctrl.isNew" translate>Save</span>
</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,316 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $, vnWeekDays) {
super($element, $);
this.vnWeekDays = vnWeekDays;
this.editMode = 'include';
}
$onInit() {
this.refresh();
}
get path() {
return `Zones/${this.$params.id}/events`;
}
get exclusionsPath() {
return `Zones/${this.$params.id}/exclusions`;
}
get checked() {
const geos = this.$.model.data || [];
const checkedLines = [];
for (let geo of geos) {
if (geo.checked)
checkedLines.push(geo);
}
return checkedLines;
}
refresh() {
this.$.data = null;
this.$.$applyAsync(() => {
const params = {
zoneFk: this.$params.id,
started: this.$.calendar.firstDay,
ended: this.$.calendar.lastDay
};
this.$http.get(`Zones/getEventsFiltered`, {params}).then(res => {
const data = res.data;
this.$.data = data;
});
});
}
formatWdays(weekDays) {
if (!weekDays) return;
let abrWdays = weekDays
.split(',')
.map(wday => this.vnWeekDays.map[wday].localeAbr);
return abrWdays.length < 7
? abrWdays.join(', ')
: this.$t('Everyday');
}
onSelection(days, type, weekday, events, exclusions, exclusionGeos) {
if (this.editMode == 'include') {
if (events.length)
return this.editInclusion(events[0]);
return this.createInclusion(type, days, weekday);
} else if (this.editMode == 'exclude') {
if (exclusions.length || exclusionGeos.length)
return this.editExclusion(exclusions[0] || {}, exclusionGeos);
return this.createExclusion(days);
}
}
editExclusion(exclusion, exclusionGeos) {
this.isNew = false;
this.excludeSelected = angular.copy(exclusion);
this.excludeSelected.type = exclusionGeos.length ?
'specificLocations' : 'all';
this.exclusionGeos = new Set();
if (exclusionGeos.length) {
this.excludeSelected.id = exclusionGeos[0].zoneExclusionFk;
exclusionGeos.forEach(x => this.exclusionGeos.add(x.geoFk));
}
this.$.excludeDialog.show();
}
createExclusion(days) {
this.isNew = true;
this.excludeSelected = {
type: 'all',
dated: days[0]
};
this.exclusionGeos = new Set();
this.$.excludeDialog.show();
}
onEditClick(row, event) {
if (event.defaultPrevented) return;
this.editInclusion(row);
}
editInclusion(row) {
this.isNew = false;
this.selected = angular.copy(row);
this.selected.wdays = this.vnWeekDays.fromSet(row.weekDays);
this.$.includeDialog.show();
}
createInclusion(type, days, weekday) {
this.isNew = true;
if (type == 'weekday') {
let wdays = [];
if (weekday) wdays[weekday] = true;
this.selected = {
type: 'indefinitely',
wdays
};
} else {
this.selected = {
type: 'day',
dated: days[0]
};
}
this.$.includeDialog.show();
}
onIncludeResponse(response) {
switch (response) {
case 'accept': {
let selected = this.selected;
let type = selected.type;
selected.weekDays = this.vnWeekDays.toSet(selected.wdays);
if (type == 'day')
selected.weekDays = '';
else
selected.dated = null;
if (type != 'range') {
selected.started = null;
selected.ended = null;
}
let req;
if (this.isNew)
req = this.$http.post(this.path, selected);
else
req = this.$http.put(`${this.path}/${selected.id}`, selected);
return req.then(() => {
this.selected = null;
this.isNew = null;
this.refresh();
});
}
case 'delete':
return this.onDelete(this.selected.id)
.then(response => response == 'accept');
}
}
onExcludeResponse(response) {
const type = this.excludeSelected.type;
switch (response) {
case 'accept': {
if (type == 'all')
return this.exclusionCreate();
return this.exclusionGeoCreate();
}
case 'delete':
return this.exclusionDelete(this.excludeSelected);
}
}
onDeleteClick(id, event) {
if (event.defaultPrevented) return;
event.preventDefault();
this.onDelete(id);
}
onDelete(id) {
return this.$.confirm.show(
response => this.onDeleteResponse(response, id));
}
onDeleteResponse(response, id) {
if (response != 'accept' || !id) return;
return this.$http.delete(`${this.path}/${id}`)
.then(() => this.refresh());
}
exclusionCreate() {
const excludeSelected = this.excludeSelected;
const dated = excludeSelected.dated;
let req;
if (this.isNew)
req = this.$http.post(this.exclusionsPath, [{dated}]);
if (!this.isNew)
req = this.$http.put(`${this.exclusionsPath}/${excludeSelected.id}`, {dated});
return req.then(() => {
this.refresh();
});
}
exclusionGeoCreate() {
const excludeSelected = this.excludeSelected;
let req;
const geoIds = [];
this.exclusionGeos.forEach(id => geoIds.push(id));
if (this.isNew) {
const params = {
zoneFk: parseInt(this.$params.id),
date: excludeSelected.dated,
geoIds
};
req = this.$http.post(`Zones/exclusionGeo`, params);
} else {
const params = {
zoneExclusionFk: this.excludeSelected.id,
geoIds
};
req = this.$http.post(`Zones/updateExclusionGeo`, params);
}
return req.then(() => this.refresh());
}
exclusionDelete(exclusion) {
const path = `${this.exclusionsPath}/${exclusion.id}`;
return this.$http.delete(path)
.then(() => this.refresh());
}
set excludeSearch(value) {
this._excludeSearch = value;
if (!value) this.onSearch();
}
get excludeSearch() {
return this._excludeSearch;
}
onKeyDown(event) {
if (event.key == 'Enter') {
event.preventDefault();
this.onSearch();
}
}
onSearch() {
const params = {search: this._excludeSearch};
if (this.excludeSelected.type == 'specificLocations') {
this.$.model.applyFilter({}, params).then(() => {
const data = this.$.model.data;
this.getChecked(data);
this.$.treeview.data = data;
});
}
}
onFetch(item) {
const params = item ? {parentId: item.id} : null;
return this.$.model.applyFilter({}, params).then(() => {
const data = this.$.model.data;
this.getChecked(data);
return data;
});
}
onSort(a, b) {
if (b.selected !== a.selected) {
if (a.selected == null)
return 1;
if (b.selected == null)
return -1;
return b.selected - a.selected;
}
return a.name.localeCompare(b.name);
}
getChecked(data) {
for (let geo of data) {
geo.checked = this.exclusionGeos.has(geo.id);
if (geo.childs) this.getChecked(geo.childs);
}
}
onItemCheck(geoId, checked) {
if (checked)
this.exclusionGeos.add(geoId);
else
this.exclusionGeos.delete(geoId);
}
}
Controller.$inject = ['$element', '$scope', 'vnWeekDays'];
ngModule.vnComponent('vnZoneEvents', {
template: require('./index.html'),
controller: Controller,
bindings: {
zone: '<'
},
require: {
card: '^vnZoneCard'
}
});

View File

@ -1,340 +0,0 @@
import './index';
import crudModel from 'core/mocks/crud-model';
describe('component vnZoneEvents', () => {
let $scope;
let controller;
let $httpBackend;
beforeEach(ngModule('zone'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element(`<vn-zone-events></vn-zone-events>`);
controller = $componentController('vnZoneEvents', {$element, $scope});
controller.$params = {id: 1};
}));
describe('refresh()', () => {
it('should set the zone and then call both getSummary() and getWarehouses()', () => {
const date = '2021-10-01';
controller.$params.id = 999;
controller.$.calendar = {
firstDay: date,
lastDay: date
};
const params = {
zoneFk: controller.$params.id,
started: date,
ended: date
};
const query = `Zones/getEventsFiltered?ended=${date}&started=${date}&zoneFk=${params.zoneFk}`;
const response = {
events: 'myEvents',
exclusions: 'myExclusions',
geoExclusions: 'myGeoExclusions',
};
$httpBackend.whenGET(query).respond(response);
controller.refresh();
$httpBackend.flush();
const data = controller.$.data;
expect(data.events).toBeDefined();
expect(data.exclusions).toBeDefined();
});
});
describe('onSelection()', () => {
it('should call the editInclusion() method', () => {
jest.spyOn(controller, 'editInclusion').mockReturnThis();
const weekday = {};
const days = [];
const type = 'EventType';
const events = [{name: 'Event'}];
const exclusions = [];
const exclusionsGeo = [];
controller.editMode = 'include';
controller.onSelection(days, type, weekday, events, exclusions, exclusionsGeo);
expect(controller.editInclusion).toHaveBeenCalledWith({name: 'Event'});
});
it('should call the createInclusion() method', () => {
jest.spyOn(controller, 'createInclusion').mockReturnThis();
const weekday = {dated: Date.vnNew()};
const days = [weekday];
const type = 'EventType';
const events = [];
const exclusions = [];
const exclusionsGeo = [];
controller.editMode = 'include';
controller.onSelection(days, type, weekday, events, exclusions, exclusionsGeo);
expect(controller.createInclusion).toHaveBeenCalledWith(type, days, weekday);
});
it('should call the editExclusion() method with exclusions', () => {
jest.spyOn(controller, 'editExclusion').mockReturnThis();
const weekday = {};
const days = [];
const type = 'EventType';
const events = [];
const exclusions = [{name: 'Exclusion'}];
const exclusionsGeo = [];
controller.editMode = 'exclude';
controller.onSelection(days, type, weekday, events, exclusions, exclusionsGeo);
expect(controller.editExclusion).toHaveBeenCalled();
});
it('should call the editExclusion() method with exclusionsGeo', () => {
jest.spyOn(controller, 'editExclusion').mockReturnThis();
const weekday = {};
const days = [];
const type = 'EventType';
const events = [];
const exclusions = [];
const exclusionsGeo = [{name: 'GeoExclusion'}];
controller.editMode = 'exclude';
controller.onSelection(days, type, weekday, events, exclusions, exclusionsGeo);
expect(controller.editExclusion).toHaveBeenCalled();
});
it('should call the createExclusion() method', () => {
jest.spyOn(controller, 'createExclusion').mockReturnThis();
const weekday = {};
const days = [{dated: Date.vnNew()}];
const type = 'EventType';
const events = [];
const exclusions = [];
const exclusionsGeo = [];
controller.editMode = 'exclude';
controller.onSelection(days, type, weekday, events, exclusions, exclusionsGeo);
expect(controller.createExclusion).toHaveBeenCalledWith(days);
});
});
describe('editExclusion()', () => {
it('shoud set the excludeSelected.type = "specificLocations" and then call the excludeDialog show() method', () => {
controller.$.excludeDialog = {show: jest.fn()};
const exclusionGeos = [{id: 1}];
const exclusions = [];
controller.editExclusion(exclusions, exclusionGeos);
expect(controller.excludeSelected.type).toEqual('specificLocations');
expect(controller.$.excludeDialog.show).toHaveBeenCalledWith();
});
it('shoud set the excludeSelected.type = "all" and then call the excludeDialog show() method', () => {
controller.$.excludeDialog = {show: jest.fn()};
const exclusionGeos = [];
const exclusions = [{id: 1}];
controller.editExclusion(exclusions, exclusionGeos);
expect(controller.excludeSelected.type).toEqual('all');
expect(controller.$.excludeDialog.show).toHaveBeenCalledWith();
});
});
describe('createExclusion()', () => {
it('shoud set the excludeSelected property and then call the excludeDialog show() method', () => {
controller.$.excludeDialog = {show: jest.fn()};
const days = [Date.vnNew()];
controller.createExclusion(days);
expect(controller.excludeSelected).toBeDefined();
expect(controller.isNew).toBeTruthy();
expect(controller.$.excludeDialog.show).toHaveBeenCalledWith();
});
});
describe('createInclusion()', () => {
it('shoud set the selected property and then call the includeDialog show() method', () => {
controller.$.includeDialog = {show: jest.fn()};
const type = 'weekday';
const days = [Date.vnNew()];
const weekday = 1;
controller.createInclusion(type, days, weekday);
const selection = controller.selected;
const firstWeekday = selection.wdays[weekday];
expect(selection.type).toEqual('indefinitely');
expect(firstWeekday).toBeTruthy();
expect(controller.isNew).toBeTruthy();
expect(controller.$.includeDialog.show).toHaveBeenCalledWith();
});
it('shoud set the selected property with the first day and then call the includeDialog show() method', () => {
controller.$.includeDialog = {show: jest.fn()};
const type = 'nonListedType';
const days = [Date.vnNew()];
const weekday = 1;
controller.createInclusion(type, days, weekday);
const selection = controller.selected;
expect(selection.type).toEqual('day');
expect(selection.dated).toEqual(days[0]);
expect(controller.isNew).toBeTruthy();
expect(controller.$.includeDialog.show).toHaveBeenCalledWith();
});
});
describe('onIncludeResponse()', () => {
it('shoud call the onDelete() method', () => {
jest.spyOn(controller, 'onDelete').mockReturnValue(
new Promise(accept => accept())
);
controller.selected = {id: 1};
controller.onIncludeResponse('delete');
expect(controller.onDelete).toHaveBeenCalledWith(1);
});
it('shoud make an HTTP POST query to create a new one and then call the refresh() method', () => {
jest.spyOn(controller, 'refresh').mockReturnThis();
controller.selected = {id: 1};
controller.isNew = true;
$httpBackend.when('POST', `Zones/1/events`).respond(200);
controller.onIncludeResponse('accept');
$httpBackend.flush();
expect(controller.refresh).toHaveBeenCalledWith();
});
it('shoud make an HTTP PUT query and then call the refresh() method', () => {
jest.spyOn(controller, 'refresh').mockReturnThis();
controller.selected = {id: 1};
controller.isNew = false;
const eventId = 1;
$httpBackend.when('PUT', `Zones/1/events/${eventId}`).respond(200);
controller.onIncludeResponse('accept');
$httpBackend.flush();
expect(controller.refresh).toHaveBeenCalledWith();
});
});
describe('onExcludeResponse()', () => {
it('should call the exclusionCreate() method', () => {
jest.spyOn(controller, 'exclusionCreate').mockReturnThis();
controller.excludeSelected = {type: 'all'};
controller.onExcludeResponse('accept');
expect(controller.exclusionCreate).toHaveBeenCalledWith();
});
it('should call the exclusionGeoCreate() method', () => {
jest.spyOn(controller, 'exclusionGeoCreate').mockReturnThis();
controller.excludeSelected = {type: 'specificLocations'};
controller.onExcludeResponse('accept');
expect(controller.exclusionGeoCreate).toHaveBeenCalledWith();
});
it('should call the exclusionDelete() method', () => {
jest.spyOn(controller, 'exclusionDelete').mockReturnThis();
controller.excludeSelected = {id: 1, type: 'all'};
controller.onExcludeResponse('delete');
expect(controller.exclusionDelete).toHaveBeenCalledWith(controller.excludeSelected);
});
});
describe('onDeleteResponse()', () => {
it('shoud make an HTTP DELETE query and then call the refresh() method', () => {
jest.spyOn(controller, 'refresh').mockReturnThis();
const eventId = 1;
$httpBackend.expect('DELETE', `Zones/1/events/1`).respond({id: 1});
controller.onDeleteResponse('accept', eventId);
$httpBackend.flush();
expect(controller.refresh).toHaveBeenCalledWith();
});
});
describe('exclusionCreate()', () => {
it('shoud make an HTTP POST query and then call the refresh() method', () => {
jest.spyOn(controller, 'refresh').mockReturnThis();
controller.excludeSelected = {};
controller.isNew = true;
$httpBackend.expect('POST', `Zones/1/exclusions`).respond({id: 1});
controller.exclusionCreate();
$httpBackend.flush();
expect(controller.refresh).toHaveBeenCalledWith();
});
});
describe('exclusionDelete()', () => {
it('shoud make an HTTP DELETE query once and then call the refresh() method', () => {
jest.spyOn(controller, 'refresh').mockReturnThis();
const exclusions = {id: 1};
const firstExclusionId = 1;
$httpBackend.expectDELETE(`Zones/1/exclusions/${firstExclusionId}`).respond(200);
controller.exclusionDelete(exclusions);
$httpBackend.flush();
expect(controller.refresh).toHaveBeenCalledWith();
});
});
describe('onSearch()', () => {
it('should call the applyFilter() method and then set the data', () => {
jest.spyOn(controller, 'getChecked').mockReturnValue([1, 2, 3]);
controller.$.treeview = {};
controller.$.model = crudModel;
controller.excludeSelected = {type: 'specificLocations'};
controller._excludeSearch = 'es';
controller.onSearch();
const treeviewData = controller.$.treeview.data;
expect(treeviewData).toBeDefined();
expect(treeviewData.length).toEqual(3);
});
});
describe('onFetch()', () => {
it('should call the applyFilter() method and then return the model data', () => {
jest.spyOn(controller, 'getChecked').mockReturnValue([1, 2, 3]);
controller.$.model = crudModel;
const result = controller.onFetch();
expect(result.length).toEqual(3);
});
});
});

View File

@ -1,12 +0,0 @@
Edit mode: Modo de edición
Include: Incluir
Exclude: Excluir
Events: Eventos
Add event: Añadir evento
Edit event: Editar evento
All: Todo
Specific locations: Localizaciones concretas
Locations where it is not distributed: Localizaciones en las que no se reparte
You must select a location: Debes seleccionar una localización
Add exclusion: Añadir exclusión
Edit exclusion: Editar exclusión

View File

@ -1,11 +0,0 @@
@import "variables";
.width{
width: 600px
}
.treeview{
max-height: 300px;
overflow: auto;
}

View File

@ -1,19 +1,3 @@
export * from './module'; export * from './module';
import './main'; import './main';
import './index/';
import './delivery-days';
import './summary';
import './card';
import './descriptor';
import './descriptor-popover';
import './search-panel';
import './create';
import './basic-data';
import './warehouses';
import './events';
import './calendar';
import './location';
import './calendar';
import './upcoming-deliveries';
import './log';

View File

@ -1,68 +0,0 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-data-viewer
model="model"
class="vn-w-md vn-mb-xl">
<vn-card>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="id" number>Id</vn-th>
<vn-th field="name" expand>Name</vn-th>
<vn-th field="agencyModeFk">Agency</vn-th>
<vn-th field="hour" shrink>Closing</vn-th>
<vn-th field="price" number>Price</vn-th>
<vn-th shrink></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<a
ng-repeat="zone in model.data"
ui-sref="zone.card.summary({id: zone.id})"
class="clickable vn-tr search-result">
<vn-td number>{{::zone.id}}</vn-td>
<vn-td expand>{{::zone.name}}</vn-td>
<vn-td expand>{{::zone.agencyMode.name}}</vn-td>
<vn-td shrink>{{::zone.hour | date: 'HH:mm'}}</vn-td>
<vn-td number>{{::zone.price | currency: 'EUR':2}}</vn-td>
<vn-td shrink>
<vn-horizontal class="buttons">
<vn-icon-button
vn-click-stop="clone.show(zone)"
vn-tooltip="Clone"
icon="icon-clone"
vn-acl="deliveryAssistant"
vn-acl-action="remove">
</vn-icon-button>
<vn-icon-button
vn-click-stop="$ctrl.preview(zone)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-horizontal>
</vn-td>
</a>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-popup vn-id="summary">
<vn-zone-summary zone="$ctrl.selectedZone"></vn-zone-summary>
</vn-popup>
<vn-confirm
vn-id="clone"
on-accept="$ctrl.onCloneAccept($data)"
question="Do you want to clone this zone?"
message="All it's properties will be copied">
</vn-confirm>
<a ui-sref="zone.create"
vn-tooltip="New zone"
vn-bind="+"
fixed-bottom-right>
<vn-float-button
icon="add"
vn-acl="deliveryAssistant"
vn-acl-action="remove">
</vn-float-button>
</a>

View File

@ -1,21 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
preview(zone) {
this.selectedZone = zone;
this.$.summary.show();
}
onCloneAccept(zone) {
return this.$http.post(`Zones/${zone.id}/clone`)
.then(res => {
this.$state.go('zone.card.basicData', {id: res.data.id});
});
}
}
ngModule.vnComponent('vnZoneIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,2 +0,0 @@
Do you want to clone this zone?: ¿Seguro que quieres clonar esta zona?
All it's properties will be copied: Todas sus propiedades serán copiadas

View File

@ -1,28 +0,0 @@
<vn-crud-model
vn-id="model"
url="Zones/{{$ctrl.$params.id}}/getLeaves"
filter="$ctrl.filter">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
on-search="$ctrl.onSearch($params)"
auto-state="false">
</vn-searchbar>
</vn-portal>
<div class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-treeview
vn-id="treeview"
root-label="Locations"
fetch-func="$ctrl.onFetch($item)"
sort-func="$ctrl.onSort($a, $b)">
<vn-check acl-role="deliveryAssistant"
ng-model="item.selected"
on-change="$ctrl.onSelection(value, item)"
triple-state="true"
ng-click="$event.preventDefault()"
label="{{::item.name}}">
</vn-check>
</vn-treeview>
</vn-card>
</div>

View File

@ -1,56 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
onSearch(params) {
this.$.model.applyFilter({}, params).then(() => {
const data = this.$.model.data;
this.$.treeview.data = data;
});
}
onFetch(item) {
const params = item ? {parentId: item.id} : null;
return this.$.model.applyFilter({}, params)
.then(() => this.$.model.data);
}
onSort(a, b) {
if (b.selected !== a.selected) {
if (a.selected == null)
return 1;
if (b.selected == null)
return -1;
return b.selected - a.selected;
}
return a.name.localeCompare(b.name);
}
exprBuilder(param, value) {
switch (param) {
case 'search':
return {name: {like: `%${value}%`}};
}
}
onSelection(value, item) {
if (value == null)
value = undefined;
const params = {geoId: item.id, isIncluded: value};
const path = `zones/${this.zone.id}/toggleIsIncluded`;
this.$http.post(path, params);
}
}
ngModule.vnComponent('vnZoneLocation', {
template: require('./index.html'),
controller: Controller,
bindings: {
zone: '<'
},
require: {
card: '^vnZoneCard'
}
});

View File

@ -1,50 +0,0 @@
import './index';
import crudModel from 'core/mocks/crud-model';
describe('component vnZoneLocation', () => {
let $scope;
let controller;
let $httpBackend;
beforeEach(ngModule('zone'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element(`<vn-zone-location></vn-zone-location>`);
controller = $componentController('vnZoneLocation', {$element, $scope});
controller.$.model = crudModel;
controller.zone = {id: 1};
}));
describe('onSearch()', () => {
it('should call the applyFilter() method and then set the data', () => {
controller.$.treeview = {};
controller.onSearch({});
const treeviewData = controller.$.treeview.data;
expect(treeviewData).toBeDefined();
expect(treeviewData.length).toEqual(3);
});
});
describe('onFetch()', () => {
it('should call the applyFilter() method and then return the model data', () => {
const result = controller.onFetch();
expect(result.length).toEqual(3);
});
});
describe('onSelection()', () => {
it('should make an HTTP POST query', () => {
const item = {id: 123};
const expectedParams = {geoId: 123, isIncluded: true};
$httpBackend.expect('POST', `zones/1/toggleIsIncluded`, expectedParams).respond(200);
controller.onSelection(true, item);
$httpBackend.flush();
});
});
});

View File

@ -1,21 +0,0 @@
@import "variables";
vn-zone-location {
vn-treeview-child {
.content > .vn-check:not(.indeterminate):not(.checked) {
color: $color-alert;
& > .btn {
border-color: $color-alert;
}
}
.content > .vn-check.checked {
color: $color-notice;
& > .btn {
background-color: $color-notice;
border-color: $color-notice
}
}
}
}

View File

@ -1 +0,0 @@
<vn-log url="ZoneLogs" 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('vnZoneLog', {
template: require('./index.html'),
controller: Section,
});

View File

@ -1,19 +0,0 @@
<vn-crud-model
vn-id="model"
url="Zones"
filter="::$ctrl.filter"
limit="20">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
info="Search zone by id or name"
panel="vn-zone-search-panel"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
</vn-searchbar>
</vn-portal>
<vn-portal slot="menu">
<vn-left-menu></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -4,25 +4,10 @@ import ModuleMain from 'salix/components/module-main';
export default class Zone extends ModuleMain { export default class Zone extends ModuleMain {
constructor($element, $) { constructor($element, $) {
super($element, $); super($element, $);
this.filter = {
include: {
relation: 'agencyMode',
scope: {fields: ['name']}
}
};
}
exprBuilder(param, value) {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {id: value}
: {name: {like: `%${value}%`}};
case 'name':
return {[param]: {like: `%${value}%`}};
case 'agencyModeFk':
return {[param]: value};
} }
async $onInit() {
this.$state.go('home');
window.location.href = await this.vnApp.getUrl(`zone/`);
} }
} }

View File

@ -1,30 +0,0 @@
import './index.js';
describe('Zone Component vnZone', () => {
let controller;
beforeEach(ngModule('zone'));
beforeEach(inject($componentController => {
const $element = angular.element('<vn-zone></vn-zone>');
controller = $componentController('vnZone', {$element});
}));
describe('exprBuilder()', () => {
it('should return a formated object with the id in case of search', () => {
let param = 'search';
let value = 1;
let result = controller.exprBuilder(param, value);
expect(result).toEqual({id: 1});
});
it('should return a formated object with the agencyModeFk in case of agencyModeFk', () => {
let param = 'agencyModeFk';
let value = 'My Delivery';
let result = controller.exprBuilder(param, value);
expect(result).toEqual({agencyModeFk: 'My Delivery'});
});
});
});

View File

@ -7,15 +7,6 @@
"menus": { "menus": {
"main": [ "main": [
{"state": "zone.index", "icon": "icon-zone"}, {"state": "zone.index", "icon": "icon-zone"},
{"state": "zone.deliveryDays", "icon": "today"},
{"state": "zone.upcomingDeliveries", "icon": "today"}
],
"card": [
{"state": "zone.card.basicData", "icon": "settings"},
{"state": "zone.card.location", "icon": "my_location"},
{"state": "zone.card.warehouses", "icon": "home"},
{"state": "zone.card.log", "icon": "history"},
{"state": "zone.card.events", "icon": "today"}
] ]
}, },
"keybindings": [ "keybindings": [
@ -34,79 +25,6 @@
"state": "zone.index", "state": "zone.index",
"component": "vn-zone-index", "component": "vn-zone-index",
"description": "Zones" "description": "Zones"
},
{
"url": "/delivery-days?q&deliveryMethodFk&geoFk&agencyModeFk",
"state": "zone.deliveryDays",
"component": "vn-zone-delivery-days",
"description": "Delivery days"
},
{
"url": "/upcoming-deliveries",
"state": "zone.upcomingDeliveries",
"component": "vn-upcoming-deliveries",
"description": "Upcoming deliveries"
},
{
"url": "/create",
"state": "zone.create",
"component": "vn-zone-create",
"description": "New zone"
},
{
"url": "/:id",
"state": "zone.card",
"component": "vn-zone-card",
"abstract": true,
"description": "Detail"
},
{
"url": "/summary",
"state": "zone.card.summary",
"component": "vn-zone-summary",
"description": "Summary",
"params": {
"zone": "$ctrl.zone"
}
},
{
"url": "/basic-data",
"state": "zone.card.basicData",
"component": "vn-zone-basic-data",
"description": "Basic data",
"params": {
"zone": "$ctrl.zone"
}
},
{
"url": "/warehouses",
"state": "zone.card.warehouses",
"component": "vn-zone-warehouses",
"description": "Warehouses"
},
{
"url": "/events?q",
"state": "zone.card.events",
"component": "vn-zone-events",
"description": "Calendar",
"params": {
"zone": "$ctrl.zone"
}
},
{
"url": "/location?q",
"state": "zone.card.location",
"component": "vn-zone-location",
"description": "Locations",
"params": {
"zone": "$ctrl.zone"
}
},
{
"url" : "/log",
"state": "zone.card.log",
"component": "vn-zone-log",
"description": "Log"
} }
] ]
} }

View File

@ -1,34 +0,0 @@
<div class="search-panel">
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield
vn-one
label="General search"
ng-model="filter.search"
info="Search zone by id or name"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Name"
ng-model="filter.name">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
label="Agency"
ng-model="filter.agencyModeFk"
url="AgencyModes/isActive"
where="{deliveryMethodFk: {neq: null}}"
value-field="id"
show-field="name">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -1,7 +0,0 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
ngModule.vnComponent('vnZoneSearchPanel', {
template: require('./index.html'),
controller: SearchPanel
});

View File

@ -1,59 +0,0 @@
<vn-card class="summary">
<h5>
<a
ng-if="::$ctrl.summary.id"
vn-tooltip="Go to the zone"
ui-sref="zone.card.summary({id: {{::$ctrl.summary.id}}})"
name="goToSummary">
<vn-icon-button icon="launch"></vn-icon-button>
</a>
<span>#{{$ctrl.summary.id}} - {{$ctrl.summary.name}}</span>
</h5>
<vn-horizontal class="vn-pa-md">
<vn-one>
<vn-label-value label="Agency"
value="{{$ctrl.summary.agencyMode.name}}">
</vn-label-value>
<vn-label-value label="Price"
value="{{$ctrl.summary.price | currency: 'EUR': 2}}">
</vn-label-value>
<vn-label-value label="Bonus"
value="{{$ctrl.summary.bonus | currency: 'EUR': 2}}">
</vn-label-value>
</vn-one>
<vn-one>
<vn-label-value label="Closing hour"
value="{{$ctrl.summary.hour | date: 'HH:mm'}}">
</vn-label-value>
<vn-label-value label="Traveling days"
value="{{$ctrl.summary.travelingDays}}">
</vn-label-value>
<vn-vertical>
<vn-check label="Volumetric" disabled="true"
ng-model="$ctrl.summary.isVolumetric">
</vn-check>
</vn-vertical>
</vn-one>
</vn-horizontal>
<vn-horizontal class="vn-pa-md">
<vn-auto>
<h4>
<a ui-sref="zone.card.warehouses({id:$ctrl.zone.id})">
<span translate vn-tooltip="Go to">Warehouse</span>
</a>
</h4>
<vn-table model="model" auto-load="false">
<vn-thead>
<vn-tr>
<vn-th>Name</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="zoneWarehouse in $ctrl.zoneWarehouses">
<vn-td>{{zoneWarehouse.warehouse.name}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-auto>
</vn-horizontal>
</vn-card>

View File

@ -1,56 +0,0 @@
import ngModule from '../module';
import Summary from 'salix/components/summary';
class Controller extends Summary {
get zone() {
return this._zone;
}
set zone(value) {
this._zone = value;
if (!value) return;
this.getSummary();
this.getWarehouses();
}
getSummary() {
const params = {
filter: {
include: {
relation: 'agencyMode',
fields: ['name']
},
where: {
id: this.zone.id
}
}
};
this.$http.get(`Zones/findOne`, {params}).then(res => {
this.summary = res.data;
});
}
getWarehouses() {
const params = {
filter: {
include: {
relation: 'warehouse',
fields: ['name']
}
}
};
this.$http.get(`Zones/${this.zone.id}/warehouses`, {params}).then(res => {
this.zoneWarehouses = res.data;
});
}
}
ngModule.vnComponent('vnZoneSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
zone: '<'
}
});

View File

@ -1,76 +0,0 @@
import './index';
describe('component vnZoneSummary', () => {
let $scope;
let controller;
let $httpBackend;
let $httpParamSerializer;
beforeEach(ngModule('zone'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
$scope = $rootScope.$new();
const $element = angular.element(`<vn-zone-summary></vn-zone-summary>`);
controller = $componentController('vnZoneSummary', {$element, $scope});
}));
describe('zone setter', () => {
it('should set the zone and then call both getSummary() and getWarehouses()', () => {
jest.spyOn(controller, 'getSummary');
jest.spyOn(controller, 'getWarehouses');
controller.zone = {id: 1};
expect(controller.getSummary).toHaveBeenCalledWith();
expect(controller.getWarehouses).toHaveBeenCalledWith();
});
});
describe('getSummary()', () => {
it('should perform a get and then store data on the controller', () => {
controller._zone = {id: 1};
let params = {
filter: {
include: {
relation: 'agencyMode',
fields: ['name']
},
where: {
id: controller._zone.id
}
}
};
const serializedParams = $httpParamSerializer(params);
const query = `Zones/findOne?${serializedParams}`;
$httpBackend.expectGET(query).respond({id: 1});
controller.getSummary();
$httpBackend.flush();
expect(controller.summary).toBeDefined();
});
});
describe('getWarehouses()', () => {
it('should make an HTTP get query and then store data on the controller', () => {
controller._zone = {id: 1};
const params = {
filter: {
include: {
relation: 'warehouse',
fields: ['name']
}
}
};
const serializedParams = $httpParamSerializer(params);
const query = `Zones/1/warehouses?${serializedParams}`;
$httpBackend.expect('GET', query).respond([{id: 1}]);
controller.getWarehouses();
$httpBackend.flush();
expect(controller.zoneWarehouses.length).toEqual(1);
});
});
});

View File

@ -1,31 +0,0 @@
<vn-crud-model
vn-id="model"
url="Zones/getUpcomingDeliveries"
data="details"
auto-load="true">
</vn-crud-model>
<vn-data-viewer model="model">
<vn-card>
<section ng-repeat="detail in details" class="vn-pa-md">
<vn-horizontal class="header">
<h5>{{$ctrl.getWeekDay(detail.shipped)}} - {{detail.shipped | date: 'dd/MM/yyyy'}}</h5>
</vn-horizontal>
<vn-table>
<vn-thead>
<vn-tr>
<vn-th class="waste-family">Province</vn-th>
<vn-th number>Closing</vn-th>
<vn-th number>Id</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="zone in detail.lines">
<vn-td class="waste-family">{{::zone.name}}</vn-td>
<vn-td number>{{::zone.hour}}</vn-td>
<vn-td number>{{::zone.zoneFk}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</section>
</vn-card>
</vn-data-viewer>

View File

@ -1,23 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $, vnWeekDays) {
super($element, $);
this.days = vnWeekDays.days;
}
getWeekDay(jsonDate) {
const weekDay = new Date(jsonDate).getDay();
return this.days[weekDay].locale;
}
}
Controller.$inject = ['$element', '$scope', 'vnWeekDays'];
ngModule.vnComponent('vnUpcomingDeliveries', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,22 +0,0 @@
import './index';
describe('component vnUpcomingDeliveries', () => {
let $scope;
let controller;
beforeEach(ngModule('zone'));
beforeEach(inject(($componentController, $rootScope) => {
$scope = $rootScope.$new();
const $element = angular.element(`<vn-upcoming-deliveries></vn-upcoming-deliveries>`);
controller = $componentController('vnUpcomingDeliveries', {$element, $scope});
}));
describe('getWeekDay()', () => {
it('should retrieve a weekday for a json passed', () => {
let jsonDate = '1970-01-01T22:00:00.000Z';
expect(controller.getWeekDay(jsonDate)).toEqual('Thursday');
});
});
});

View File

@ -1,3 +0,0 @@
Family: Familia
Percentage: Porcentaje
Dwindle: Mermas

View File

@ -1,26 +0,0 @@
@import "variables";
vn-upcoming-deliveries {
.header {
margin-bottom: 16px;
text-transform: uppercase;
font-size: 1.25rem;
line-height: 1;
padding: 7px;
padding-bottom: 7px;
padding-bottom: 4px;
font-weight: lighter;
background-color: $color-main-light;
border-bottom: 1px solid $color-primary;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
background-color: $color-bg;
}
vn-table vn-th.waste-family,
vn-table vn-td.waste-family {
max-width: 64px;
width: 64px
}
}

View File

@ -1,57 +0,0 @@
<vn-data-viewer
data="data"
is-loading="!data"
class="vn-w-xs">
<vn-card>
<vn-table>
<vn-tbody>
<vn-tr ng-repeat="row in data | orderBy:'warehouse.name'">
<vn-td>{{::row.warehouse.name}}</vn-td>
<vn-td shrink>
<vn-icon-button
icon="delete"
translate-attr="{title: 'Delete'}"
ng-click="$ctrl.onDelete(row)">
</vn-icon-button>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-float-button
icon="add"
translate-attr="{title: 'Add'}"
vn-bind="+"
ng-click="$ctrl.onCreate()"
fixed-bottom-right>
</vn-float-button>
<vn-crud-model
auto-load="true"
url="Warehouses"
data="warehouses"
order="name">
</vn-crud-model>
<vn-dialog
vn-id="dialog"
on-accept="$ctrl.onSave()">
<tpl-body>
<vn-autocomplete
ng-model="$ctrl.selected.warehouseFk"
data="warehouses"
show-field="name"
value-field="id"
label="Warehouse">
</vn-autocomplete>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>
<vn-confirm
vn-id="confirm"
message="This item will be deleted"
question="Are you sure you want to continue?"
on-accept="$ctrl.delete()">
</vn-confirm>

View File

@ -1,56 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
$onInit() {
this.refresh();
}
get path() {
return `Zones/${this.$params.id}/warehouses`;
}
refresh() {
let filter = {include: 'warehouse'};
this.$http.get(this.path, {params: {filter}})
.then(res => this.$.data = res.data);
}
onCreate() {
this.selected = {};
this.$.dialog.show();
}
onSave() {
this.$http.post(this.path, this.selected)
.then(() => {
this.selected = null;
this.isNew = null;
this.$.dialog.hide();
this.refresh();
});
return false;
}
onDelete(row) {
this.$.confirm.show();
this.deleteRow = row;
}
delete() {
let row = this.deleteRow;
if (!row) return;
return this.$http.delete(`${this.path}/${row.id}`)
.then(() => {
let index = this.$.data.indexOf(row);
if (index !== -1) this.$.data.splice(index, 1);
this.deleteRow = null;
});
}
}
ngModule.vnComponent('vnZoneWarehouses', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,60 +0,0 @@
import './index.js';
describe('Zone warehouses', () => {
let $httpBackend;
let $httpParamSerializer;
let controller;
let $element;
beforeEach(ngModule('zone'));
beforeEach(inject(($componentController, _$httpBackend_, _$httpParamSerializer_) => {
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
$element = angular.element('<vn-zone-warehouses></vn-zone-warehouses');
controller = $componentController('vnZoneWarehouses', {$element});
controller.zone = {id: 1};
controller.$params = {id: 1};
controller.$.dialog = {hide: jest.fn()};
}));
describe('refresh()', () => {
it('should make an HTTP GET query and then set the data', () => {
const params = {filter: {include: 'warehouse'}};
const serializedParams = $httpParamSerializer(params);
const path = `Zones/1/warehouses?${serializedParams}`;
$httpBackend.expect('GET', path).respond([{id: 1, name: 'Warehouse one'}]);
controller.refresh();
$httpBackend.flush();
expect(controller.$.data).toBeDefined();
});
});
describe('onSave()', () => {
it('should make an HTTP POST query and then call the refresh() method', () => {
jest.spyOn(controller, 'refresh').mockReturnThis();
$httpBackend.expect('POST', `Zones/1/warehouses`).respond(200);
controller.onSave();
$httpBackend.flush();
expect(controller.selected).toBeNull();
expect(controller.isNew).toBeNull();
expect(controller.$.dialog.hide).toHaveBeenCalledWith();
expect(controller.refresh).toHaveBeenCalledWith();
});
});
describe('delete()', () => {
it('should make an HTTP DELETE query and then set deleteRow property to null value', () => {
controller.deleteRow = {id: 1};
controller.$.data = [{id: 1}];
$httpBackend.expect('DELETE', `Zones/1/warehouses/1`).respond(200);
controller.delete();
$httpBackend.flush();
expect(controller.deleteRow).toBeNull();
});
});
});