8144-devToTest_2448 #3216

Merged
alexm merged 256 commits from 8144-devToTest_2448 into test 2024-11-19 07:36:04 +00:00
160 changed files with 171 additions and 8943 deletions
Showing only changes of commit d1c528c8fd - Show all commits

View File

@ -39,6 +39,9 @@ module.exports = Self => {
const xmlString = response.data;
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlString, 'text/xml');
await Self.rawSql('CALL util.debugAdd(?,?);', ['cancelShipment', xmlDoc]);
const result = xmlDoc.getElementsByTagName('Mensaje')[0].textContent;
return result.toLowerCase().includes('se ha cancelado correctamente');
};

View File

@ -13,6 +13,9 @@
},
"url": {
"type": "string"
},
"defaultChannel": {
"type": "number"
}
},
"acls": [

View File

@ -4023,9 +4023,8 @@ INSERT INTO srt.buffer (id, x, y, `size`, `length`, stateFk, typeFk, isActive, c
INSERT IGNORE INTO vn.saySimpleCountry (countryFk, channel)
VALUES (19, 1169),
(8, 1183),
(NULL, 1320);
VALUES (19, '1169'),
(8, '1183');
INSERT IGNORE INTO vn.saySimpleConfig (url)
VALUES ('saysimle-url-mock');
INSERT IGNORE INTO vn.saySimpleConfig (url, defaultChannel)
VALUES ('saysimle-url-mock', 1320);

View File

@ -7,8 +7,7 @@ AS SELECT `u`.`id` AS `id`,
`u`.`email` AS `email`,
`u`.`nickname` AS `nickname`,
`u`.`lang` AS `lang`,
`u`.`role` AS `role`,
`u`.`recoverPass` AS `recoverPass`
`u`.`role` AS `role`
FROM `account`.`user` `u`
WHERE `u`.`name` = `myUser_getName`()
WITH CASCADED CHECK OPTION

View File

@ -59,7 +59,7 @@ proc: BEGIN
INSERT INTO stockBought(workerFk, bought, dated)
SELECT tb.workerFk,
ROUND(GREATEST(tb.bought - IFNULL(ts.sold, 0), 0), 1),
ROUND(GREATEST(tb.bought - IFNULL(ts.sold, 0), 0), 2),
vDated
FROM tStockBought tb
LEFT JOIN tStockSold ts ON ts.workerFk = tb.workerFk;

View File

@ -1,3 +1,2 @@
UPDATE vn.sale
SET originalQuantity = quantity
WHERE originalQuantity IS NULL
-- Debido a que tardaba mucho en la subida a master, se ha creado una nueva versión para que el proceso no se vea afectado y se ejecute por la noche.
-- Se crea de nuevo en la versión 11344-grayBamboo

View File

@ -0,0 +1,9 @@
INSERT INTO salix.ACL (model, property, accessType, principalId)
VALUES ('Collection', 'create', 'WRITE', 'productionBoss'),
('Collection', 'upsert', 'WRITE', 'productionBoss'),
('Collection', 'replaceById', 'WRITE', 'productionBoss'),
('Collection', 'updateAll', 'WRITE', 'productionBoss'),
('Collection', 'updateAttributes', 'WRITE', 'productionBoss'),
('Collection', 'deleteById', 'WRITE', 'productionBoss'),
('Collection', 'destroyAll', 'WRITE', 'productionBoss'),
('Collection', 'destroyById', 'WRITE', 'productionBoss');

View File

@ -0,0 +1,3 @@
UPDATE vn.sale
SET originalQuantity = quantity
WHERE originalQuantity IS NULL

View File

@ -0,0 +1,2 @@
ALTER TABLE account.user CHANGE recoverPass recoverPass__ tinyint(3) unsigned NOT NULL DEFAULT 1 COMMENT '@deprecated 2024-11-13';
ALTER TABLE account.user CHANGE sync sync__ tinyint(4) NOT NULL DEFAULT 0 COMMENT '@deprecated 2024-11-13';

View File

@ -0,0 +1,9 @@
DROP TABLE IF EXISTS vn.saySimpleConfig;
CREATE TABLE vn.saySimpleConfig(
id INT AUTO_INCREMENT PRIMARY KEY,
url VARCHAR(255) NOT NULL,
defaultChannel VARCHAR(128)
);
ALTER TABLE vn.saySimpleCountry MODIFY COLUMN channel VARCHAR(128);

View File

@ -0,0 +1,2 @@
ALTER TABLE vn.clientObservation MODIFY COLUMN observationTypeFk tinyint(3) unsigned DEFAULT 4 NOT NULL;
UPDATE vn.clientObservation SET observationTypeFk=4 WHERE observationTypeFk=0;

View File

@ -1,133 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Item summary path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'item');
});
afterAll(async() => {
await browser.close();
});
it('should search for an item', async() => {
await page.doSearch('Ranged weapon');
const resultsCount = await page.countElement(selectors.itemsIndex.searchResult);
await page.waitForTextInElement(selectors.itemsIndex.firstSearchResult, 'Ranged weapon');
await page.waitToClick(selectors.itemsIndex.firstResultPreviewButton);
const isVisible = await page.isVisible(selectors.itemSummary.basicData);
expect(resultsCount).toBe(4);
expect(isVisible).toBeTruthy();
});
it(`should check the item summary preview shows fields from basic data`, async() => {
await page.waitForTextInElement(selectors.itemSummary.basicData, 'Ranged weapon longbow 200cm');
const result = await page.waitToGetProperty(selectors.itemSummary.basicData, 'innerText');
expect(result).toContain('Ranged weapon longbow 200cm');
});
it(`should check the item summary preview shows fields from tags`, async() => {
await page.waitForTextInElement(selectors.itemSummary.tags, 'Brown');
const result = await page.waitToGetProperty(selectors.itemSummary.tags, 'innerText');
expect(result).toContain('Brown');
});
it(`should check the item summary preview shows fields from botanical`, async() => {
await page.waitForTextInElement(selectors.itemSummary.botanical, 'Abelia');
const result = await page.waitToGetProperty(selectors.itemSummary.botanical, 'innerText');
expect(result).toContain('Abelia');
});
it(`should check the item summary preview shows fields from barcode`, async() => {
await page.waitForTextInElement(selectors.itemSummary.barcode, '1');
const result = await page.waitToGetProperty(selectors.itemSummary.barcode, 'innerText');
expect(result).toContain('1');
});
it(`should close the summary popup`, async() => {
await page.closePopup();
await page.waitForSelector(selectors.itemSummary.basicData, {hidden: true});
});
it('should search for other item', async() => {
await page.doSearch('Melee Reinforced');
const resultsCount = await page.countElement(selectors.itemsIndex.searchResult);
await page.waitToClick(selectors.itemsIndex.firstResultPreviewButton);
await page.waitForSelector(selectors.itemSummary.basicData, {visible: true});
expect(resultsCount).toBe(3);
});
it(`should now check the item summary preview shows fields from basic data`, async() => {
await page.waitForTextInElement(selectors.itemSummary.basicData, 'Melee Reinforced weapon combat fist 15cm');
const result = await page.waitToGetProperty(selectors.itemSummary.basicData, 'innerText');
expect(result).toContain('Melee Reinforced weapon combat fist 15cm');
});
it(`should now check the item summary preview shows fields from tags`, async() => {
await page.waitForTextInElement(selectors.itemSummary.tags, 'Silver');
const result = await page.waitToGetProperty(selectors.itemSummary.tags, 'innerText');
expect(result).toContain('Silver');
});
it(`should now check the item summary preview shows fields from botanical`, async() => {
await page.waitForTextInElement(selectors.itemSummary.botanical, '-');
const result = await page.waitToGetProperty(selectors.itemSummary.botanical, 'innerText');
expect(result).toContain('-');
});
it(`should now close the summary popup`, async() => {
await page.closePopup();
await page.waitForSelector(selectors.itemSummary.basicData, {hidden: true});
});
it(`should navigate to one of the items detailed section`, async() => {
await page.accessToSearchResult('Melee weapon combat fist 15cm');
await page.waitForState('item.card.summary');
});
it(`should check the descritor edit button is not visible for employee`, async() => {
const visibleButton = await page.isVisible(selectors.itemDescriptor.editButton);
expect(visibleButton).toBeFalsy();
});
it(`should check the item summary shows fields from basic data section`, async() => {
await page.waitForTextInElement(selectors.itemSummary.basicData, 'Melee weapon combat fist 15cm');
const result = await page.waitToGetProperty(selectors.itemSummary.basicData, 'innerText');
expect(result).toContain('Melee weapon combat fist 15cm');
});
it(`should check the item summary shows fields from tags section`, async() => {
const result = await page.waitToGetProperty(selectors.itemSummary.tags, 'innerText');
expect(result).toContain('Silver');
});
it(`should check the item summary shows fields from botanical section`, async() => {
const result = await page.waitToGetProperty(selectors.itemSummary.botanical, 'innerText');
expect(result).toContain('procera');
});
it(`should check the item summary shows fields from barcodes section`, async() => {
const result = await page.waitToGetProperty(selectors.itemSummary.barcode, 'innerText');
expect(result).toContain('4');
});
});

View File

@ -1,64 +0,0 @@
import getBrowser from '../../helpers/puppeteer';
const $ = {
form: 'vn-item-basic-data form',
intrastatForm: '.vn-dialog.shown form',
newIntrastatButton: 'vn-item-basic-data vn-icon-button[vn-tooltip="New intrastat"] > button'
};
describe('Item Edit basic data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'item');
await page.accessToSearchResult('Melee weapon combat fist 15cm');
});
beforeEach(async() => {
await page.accessToSection('item.card.basicData');
});
afterAll(async() => {
await browser.close();
});
it(`should edit the item basic data and confirm the item data was edited`, async() => {
const values = {
type: 'Anthurium',
intrastat: 'Coral y materiales similares',
relevancy: 1,
generic: 'Pallet',
isActive: false,
priceInKg: true,
isFragile: true,
packingOut: 5
};
const message = await page.sendForm($.form, values);
await page.reloadSection('item.card.basicData');
const formValues = await page.fetchForm($.form, Object.keys(values));
expect(message.isSuccess).toBeTrue();
expect(formValues).toEqual(values);
});
it(`should create a new intrastat and save it`, async() => {
await page.click($.newIntrastatButton);
await page.waitForSelector($.intrastatForm);
await page.fillForm($.intrastatForm, {
id: '588420239',
description: 'Tropical Flowers'
});
await page.respondToDialog('accept');
const message = await page.sendForm($.form);
await page.reloadSection('item.card.basicData');
const formValues = await page.fetchForm($.form, ['intrastat']);
expect(message.isSuccess).toBeTrue();
expect(formValues).toEqual({intrastat: 'Tropical Flowers'});
});
});

View File

@ -1,48 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Item edit tax path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'item');
await page.accessToSearchResult('Ranged weapon longbow 200cm');
await page.accessToSection('item.card.tax');
});
afterAll(async() => {
await browser.close();
});
it(`should add the item tax to all countries`, async() => {
await page.autocompleteSearch(selectors.itemTax.firstClass, 'General VAT');
await page.autocompleteSearch(selectors.itemTax.secondClass, 'General VAT');
await page.waitToClick(selectors.itemTax.submitTaxButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should confirm the first item tax class was edited`, async() => {
await page.reloadSection('item.card.tax');
const firstVatType = await page.waitToGetProperty(selectors.itemTax.firstClass, 'value');
expect(firstVatType).toEqual('General VAT');
});
it(`should confirm the second item tax class was edited`, async() => {
const secondVatType = await page
.waitToGetProperty(selectors.itemTax.secondClass, 'value');
expect(secondVatType).toEqual('General VAT');
});
it(`should edit the first class without saving the form`, async() => {
await page.autocompleteSearch(selectors.itemTax.firstClass, 'Reduced VAT');
const firstVatType = await page.waitToGetProperty(selectors.itemTax.firstClass, 'value');
expect(firstVatType).toEqual('Reduced VAT');
});
});

View File

@ -1,79 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Item create tags path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'item');
await page.accessToSearchResult('Ranged weapon longbow 200cm');
await page.accessToSection('item.card.tags');
});
afterAll(async() => {
await browser.close();
});
it('should create a new tag and delete a former one', async() => {
await page.waitToClick(selectors.itemTags.fourthRemoveTagButton);
await page.waitToClick(selectors.itemTags.addItemTagButton);
await page.autocompleteSearch(selectors.itemTags.seventhTag, 'Ancho de la base');
await page.write(selectors.itemTags.seventhValue, '50');
await page.clearInput(selectors.itemTags.seventhRelevancy);
await page.write(selectors.itemTags.seventhRelevancy, '4');
await page.waitToClick(selectors.itemTags.submitItemTagsButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the fourth row data is the expected one', async() => {
await page.reloadSection('item.card.tags');
await page.waitForSelector('vn-item-tags');
let result = await page.waitToGetProperty(selectors.itemTags.fourthTag, 'value');
expect(result).toEqual('Ancho de la base');
result = await page
.waitToGetProperty(selectors.itemTags.fourthValue, 'value');
expect(result).toEqual('50');
result = await page
.waitToGetProperty(selectors.itemTags.fourthRelevancy, 'value');
expect(result).toEqual('4');
});
it('should confirm the fifth row data is the expected one', async() => {
let tag = await page
.waitToGetProperty(selectors.itemTags.fifthTag, 'value');
let value = await page
.waitToGetProperty(selectors.itemTags.fifthValue, 'value');
let relevancy = await page
.waitToGetProperty(selectors.itemTags.fifthRelevancy, 'value');
expect(tag).toEqual('Color');
expect(value).toEqual('Brown');
expect(relevancy).toEqual('5');
});
it('should confirm the sixth row data is the expected one', async() => {
let tag = await page
.waitToGetProperty(selectors.itemTags.sixthTag, 'value');
let value = await page
.waitToGetProperty(selectors.itemTags.sixthValue, 'value');
let relevancy = await page
.waitToGetProperty(selectors.itemTags.sixthRelevancy, 'value');
expect(tag).toEqual('Categoria');
expect(value).toEqual('+1 precission');
expect(relevancy).toEqual('6');
});
});

View File

@ -1,66 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Item Create botanical path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'item');
await page.accessToSearchResult('Ranged weapon pistol 9mm');
await page.accessToSection('item.card.botanical');
});
afterAll(async() => {
await browser.close();
});
it(`should create a new botanical for the item`, async() => {
await page.autocompleteSearch(selectors.itemBotanical.genus, 'Abelia');
await page.autocompleteSearch(selectors.itemBotanical.species, 'dealbata');
await page.waitToClick(selectors.itemBotanical.submitBotanicalButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should confirm the Genus for the item was created`, async() => {
await page.waitForTextInField(selectors.itemBotanical.genus, 'Abelia');
const result = await page
.waitToGetProperty(selectors.itemBotanical.genus, 'value');
expect(result).toEqual('Abelia');
});
it(`should confirm the Species for the item was created`, async() => {
const result = await page
.waitToGetProperty(selectors.itemBotanical.species, 'value');
expect(result).toEqual('dealbata');
});
it(`should edit botanical for the item`, async() => {
await page.autocompleteSearch(selectors.itemBotanical.genus, 'Abies');
await page.autocompleteSearch(selectors.itemBotanical.species, 'decurrens');
await page.waitToClick(selectors.itemBotanical.submitBotanicalButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should confirm the Genus for the item was edited`, async() => {
await page.waitForTextInField(selectors.itemBotanical.genus, 'Abies');
const result = await page
.waitToGetProperty(selectors.itemBotanical.genus, 'value');
expect(result).toEqual('Abies');
});
it(`should confirm the Species for the item was edited`, async() => {
const result = await page
.waitToGetProperty(selectors.itemBotanical.species, 'value');
expect(result).toEqual('decurrens');
});
});

View File

@ -1,37 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Item Create barcodes path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'item');
await page.accessToSearchResult('Ranged weapon longbow 200cm');
await page.accessToSection('item.card.itemBarcode');
});
afterAll(async() => {
await browser.close();
});
it(`should click create a new code and delete a former one`, async() => {
await page.waitToClick(selectors.itemBarcodes.firstCodeRemoveButton);
await page.waitToClick(selectors.itemBarcodes.addBarcodeButton);
await page.write(selectors.itemBarcodes.thirdCode, '5');
await page.waitToClick(selectors.itemBarcodes.submitBarcodesButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should confirm the barcode 5 is created and it is now the third barcode as the first was deleted`, async() => {
await page.reloadSection('item.card.itemBarcode');
await page.waitForTextInField(selectors.itemBarcodes.thirdCode, '5');
const result = await page
.waitToGetProperty(selectors.itemBarcodes.thirdCode, 'value');
expect(result).toEqual('5');
});
});

View File

@ -1,65 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
const $ = {
form: 'vn-item-create form'
};
describe('Item Create', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'item');
});
afterAll(async() => {
await browser.close();
});
it('should access to the create item view by clicking the create floating button', async() => {
await page.waitToClick(selectors.itemsIndex.createItemButton);
await page.waitForState('item.create');
});
it('should return to the item index by clickig the cancel button', async() => {
await page.waitToClick(selectors.itemCreateView.cancelButton);
await page.waitForState('item.index');
});
it('should now access to the create item view by clicking the create floating button', async() => {
await page.waitToClick(selectors.itemsIndex.createItemButton);
await page.waitForState('item.create');
});
it('should throw an error when insert an invalid priority', async() => {
const values = {
name: 'Infinity Gauntlet',
type: 'Crisantemo',
intrastat: 'Coral y materiales similares',
origin: 'Holand',
priority: null
};
const message = await page.sendForm($.form, values);
expect(message.text).toContain('Valid priorities');
});
it('should create the Infinity Gauntlet item', async() => {
const values = {
name: 'Infinity Gauntlet',
type: 'Crisantemo',
intrastat: 'Coral y materiales similares',
origin: 'Holand',
priority: '2'
};
await page.fillForm($.form, values);
const formValues = await page.fetchForm($.form, Object.keys(values));
const message = await page.sendForm($.form);
expect(message.isSuccess).toBeTrue();
expect(formValues).toEqual(values);
});
});

View File

@ -1,141 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Item regularize path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'item');
});
afterAll(async() => {
await browser.close();
});
it('should edit the user local warehouse', async() => {
await page.waitForSpinnerLoad();
await page.waitToClick(selectors.globalItems.userMenuButton);
await page.autocompleteSearch(selectors.globalItems.userLocalWarehouse, 'Warehouse Four');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should check the local settings were saved', async() => {
const userLocalWarehouse = await page
.waitToGetProperty(selectors.globalItems.userLocalWarehouse, 'value');
await page.closePopup();
expect(userLocalWarehouse).toContain('Warehouse Four');
});
it('should search for a specific item', async() => {
await page.accessToSearchResult('Ranged weapon pistol 9mm');
await page.waitForState('item.card.summary');
});
it('should open the regularize dialog and check the warehouse matches the local user settings', async() => {
await page.waitToClick(selectors.itemDescriptor.moreMenu);
await page.waitToClick(selectors.itemDescriptor.moreMenuRegularizeButton);
const result = await page.waitToGetProperty(selectors.itemDescriptor.regularizeWarehouse, 'value');
expect(result).toEqual('Warehouse Four');
});
it('should regularize the item', async() => {
await page.write(selectors.itemDescriptor.regularizeQuantity, '100');
await page.autocompleteSearch(selectors.itemDescriptor.regularizeWarehouse, 'Warehouse One');
await page.waitToClick(selectors.itemDescriptor.regularizeSaveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should click on the Tickets button of the top bar menu', async() => {
await page.waitToClick(selectors.globalItems.applicationsMenuButton);
await page.waitForSelector(selectors.globalItems.applicationsMenuVisible);
await Promise.all([
page.waitForNavigation({waitUntil: ['load', 'networkidle0', 'domcontentloaded']}),
page.waitToClick(selectors.globalItems.ticketsButton)
]);
await page.waitForState('ticket.index');
});
it('should clear the user local settings now', async() => {
await page.waitToClick(selectors.globalItems.userMenuButton);
await page.waitForContentLoaded();
await page.clearInput(selectors.globalItems.userConfigFirstAutocomplete);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should search for the ticket with alias missing', async() => {
await page.keyboard.press('Escape');
await page.accessToSearchResult('missing');
await page.waitForState('ticket.card.summary');
});
it(`should check the ticket sale quantity is showing a negative value`, async() => {
await page.waitForTextInElement(selectors.ticketSummary.firstSaleQuantity, '-100');
const result = await page
.waitToGetProperty(selectors.ticketSummary.firstSaleQuantity, 'innerText');
expect(result).toContain('-100');
});
it(`should check the ticket sale discount is 100%`, async() => {
const result = await page
.waitToGetProperty(selectors.ticketSummary.firstSaleDiscount, 'innerText');
expect(result).toContain('100 %');
});
it('should now click on the Items button of the top bar menu', async() => {
await page.waitToClick(selectors.globalItems.applicationsMenuButton);
await page.waitForSelector(selectors.globalItems.applicationsMenuVisible);
await page.waitToClick(selectors.globalItems.itemsButton);
await page.waitForState('item.index');
});
it('should search for the item once again', async() => {
await page.accessToSearchResult('Ranged weapon pistol 9mm');
await page.waitForState('item.card.summary');
});
it('should regularize the item once more', async() => {
await page.waitToClick(selectors.itemDescriptor.moreMenu);
await page.waitToClick(selectors.itemDescriptor.moreMenuRegularizeButton);
await page.write(selectors.itemDescriptor.regularizeQuantity, '100');
await page.autocompleteSearch(selectors.itemDescriptor.regularizeWarehouse, 'Warehouse One');
await page.waitToClick(selectors.itemDescriptor.regularizeSaveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should again click on the Tickets button of the top bar menu', async() => {
await page.waitToClick(selectors.globalItems.applicationsMenuButton);
await page.waitForSelector(selectors.globalItems.applicationsMenuVisible);
await Promise.all([
page.waitForNavigation({waitUntil: ['load', 'networkidle0', 'domcontentloaded']}),
page.waitToClick(selectors.globalItems.ticketsButton)
]);
await page.waitForState('ticket.index');
});
it('should search for the ticket missing once again', async() => {
await page.accessToSearchResult('Missing');
await page.waitForState('ticket.card.summary');
});
it(`should check the ticket contains now two sales`, async() => {
await page.waitForTextInElement(selectors.ticketSummary.firstSaleQuantity, '-100');
const result = await page.countElement(selectors.ticketSummary.sale);
expect(result).toEqual(2);
});
});

View File

@ -1,84 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Item index path', () => {
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 click on the fields to show button to open the list of columns to show', async() => {
await page.waitToClick(selectors.itemsIndex.shownColumns);
const visible = await page.isVisible(selectors.itemsIndex.shownColumnsList);
expect(visible).toBeTruthy();
});
it('should unmark all checkboxes except the first and the last ones', async() => {
await page.waitToClick(selectors.itemsIndex.idCheckbox);
await page.waitToClick(selectors.itemsIndex.stemsCheckbox);
await page.waitToClick(selectors.itemsIndex.sizeCheckbox);
await page.waitToClick(selectors.itemsIndex.typeCheckbox);
await page.waitToClick(selectors.itemsIndex.categoryCheckbox);
await page.waitToClick(selectors.itemsIndex.intrastadCheckbox);
await page.waitToClick(selectors.itemsIndex.originCheckbox);
await page.waitToClick(selectors.itemsIndex.buyerCheckbox);
await page.waitToClick(selectors.itemsIndex.weightByPieceCheckbox);
await page.waitToClick(selectors.itemsIndex.saveFieldsButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should navigate forth and back to see the images column is still visible', async() => {
await page.closePopup();
await page.waitToClick(selectors.itemsIndex.firstSearchResult);
await page.waitToClick(selectors.itemDescriptor.goBackToModuleIndexButton);
await page.waitToClick(selectors.globalItems.searchButton);
await page.waitForSelector(selectors.itemsIndex.searchResult);
await page.waitImgLoad(selectors.itemsIndex.firstItemImage);
const imageVisible = await page.isVisible(selectors.itemsIndex.firstItemImageTd);
expect(imageVisible).toBeTruthy();
});
it('should check the ids column is not visible', async() => {
await page.waitForSelector(selectors.itemsIndex.firstItemId, {hidden: true});
});
it('should mark all unchecked boxes to leave the index as it was', async() => {
await page.waitToClick(selectors.itemsIndex.shownColumns);
await page.waitToClick(selectors.itemsIndex.idCheckbox);
await page.waitToClick(selectors.itemsIndex.stemsCheckbox);
await page.waitToClick(selectors.itemsIndex.sizeCheckbox);
await page.waitToClick(selectors.itemsIndex.typeCheckbox);
await page.waitToClick(selectors.itemsIndex.categoryCheckbox);
await page.waitToClick(selectors.itemsIndex.intrastadCheckbox);
await page.waitToClick(selectors.itemsIndex.originCheckbox);
await page.waitToClick(selectors.itemsIndex.buyerCheckbox);
await page.waitToClick(selectors.itemsIndex.weightByPieceCheckbox);
await page.waitToClick(selectors.itemsIndex.saveFieldsButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should now navigate forth and back to see the ids column is now visible', async() => {
await page.closePopup();
await page.waitToClick(selectors.itemsIndex.firstSearchResult);
await page.waitToClick(selectors.itemDescriptor.goBackToModuleIndexButton);
await page.waitToClick(selectors.globalItems.searchButton);
await page.waitForSelector(selectors.itemsIndex.searchResult);
const idVisible = await page.isVisible(selectors.itemsIndex.firstItemId);
expect(idVisible).toBeTruthy();
});
});

View File

@ -1,45 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Item log path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('developer', 'item');
});
afterAll(async() => {
await browser.close();
});
it(`should search for the Knowledge artifact to confirm it isn't created yet`, async() => {
await page.doSearch('Knowledge artifact');
const nResults = await page.countElement(selectors.itemsIndex.searchResult);
expect(nResults).toEqual(1);
});
it('should access to the create item view by clicking the create floating button', async() => {
await page.waitToClick(selectors.itemsIndex.createItemButton);
await page.waitForState('item.create');
});
it('should create the Knowledge artifact item', async() => {
await page.write(selectors.itemCreateView.temporalName, 'Knowledge artifact');
await page.autocompleteSearch(selectors.itemCreateView.type, 'Crisantemo');
await page.autocompleteSearch(selectors.itemCreateView.intrastat, 'Coral y materiales similares');
await page.autocompleteSearch(selectors.itemCreateView.origin, 'Holand');
await page.waitToClick(selectors.itemCreateView.createButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should return to the items index by clicking the return to items button', async() => {
await page.waitToClick(selectors.itemBasicData.goToItemIndexButton);
await page.waitForSelector(selectors.itemsIndex.createItemButton);
await page.waitForState('item.index');
});
});

View File

@ -1,41 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Item descriptor path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'item');
await page.accessToSearchResult('1');
await page.accessToSection('item.card.basicData');
});
afterAll(async() => {
await browser.close();
});
it('should set the item to inactive', async() => {
await page.waitToClick(selectors.itemBasicData.isActiveCheckbox);
await page.waitToClick(selectors.itemBasicData.submitBasicDataButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reload the section and check the inactive icon is visible', async() => {
await page.reloadSection('item.card.basicData');
const visibleIcon = await page.isVisible(selectors.itemDescriptor.inactiveIcon);
expect(visibleIcon).toBeTruthy();
});
it('should set the item back to active', async() => {
await page.waitToClick(selectors.itemBasicData.isActiveCheckbox);
await page.waitToClick(selectors.itemBasicData.submitBasicDataButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -1,45 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Item request path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'item');
await page.accessToSection('item.request');
});
afterAll(async() => {
await browser.close();
});
it('should reach the item request section', async() => {
await page.waitForState('item.request');
});
it('should fill the id and quantity then check the concept was updated', async() => {
await page.writeOnEditableTD(selectors.itemRequest.firstRequestItemID, '4');
await page.writeOnEditableTD(selectors.itemRequest.firstRequestQuantity, '10');
await page.waitForTextInElement(selectors.itemRequest.firstRequestConcept, 'Melee weapon heavy shield 100cm');
let filledConcept = await page.waitToGetProperty(selectors.itemRequest.firstRequestConcept, 'innerText');
expect(filledConcept).toContain('Melee weapon heavy shield 100cm');
});
it('should check the status of the request should now be accepted', async() => {
let status = await page.waitToGetProperty(selectors.itemRequest.firstRequestStatus, 'innerText');
expect(status).toContain('Accepted');
});
it('should now click on the second declain request icon then type the reason', async() => {
await page.waitToClick(selectors.itemRequest.secondRequestDecline);
await page.write(selectors.itemRequest.declineReason, 'Not quite as expected');
await page.respondToDialog('accept');
let status = await page.waitToGetProperty(selectors.itemRequest.secondRequestStatus, 'innerText');
expect(status).toContain('Denied');
});
});

View File

@ -1,97 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
const $ = selectors.itemFixedPrice;
describe('Item fixed prices path', () => {
let browser;
let page;
let httpRequest;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'item');
await page.accessToSection('item.fixedPrice');
page.on('request', req => {
if (req.url().includes(`FixedPrices/filter`))
httpRequest = req.url();
});
});
afterAll(async() => {
await browser.close();
});
it('should filter using all the fields', async() => {
await page.write($.generalSearchFilter, 'item');
await page.keyboard.press('Enter');
expect(httpRequest).toContain('search=item');
await page.click($.chip);
await page.click($.reignFilter);
expect(httpRequest).toContain('categoryFk');
await page.autocompleteSearch($.typeFilter, 'Alstroemeria');
expect(httpRequest).toContain('typeFk');
await page.click($.chip);
await page.autocompleteSearch($.buyerFilter, 'buyerNick');
expect(httpRequest).toContain('buyerFk');
await page.click($.chip);
await page.autocompleteSearch($.warehouseFilter, 'Algemesi');
expect(httpRequest).toContain('warehouseFk');
await page.click($.chip);
await page.click($.mineFilter);
expect(httpRequest).toContain('mine=true');
await page.click($.chip);
await page.click($.hasMinPriceFilter);
expect(httpRequest).toContain('hasMinPrice=true');
await page.click($.chip);
await page.click($.addTag);
await page.autocompleteSearch($.tagFilter, 'Color');
await page.autocompleteSearch($.tagValueFilter, 'Brown');
expect(httpRequest).toContain('tags');
await page.click($.chip);
});
it('should click on the add new fixed price button', async() => {
await page.waitToClick($.add);
await page.waitForSelector($.fourthFixedPrice);
});
it('should fill the fixed price data', async() => {
const now = Date.vnNew();
await page.autocompleteSearch($.fourthWarehouse, 'Warehouse one');
await page.writeOnEditableTD($.fourthGroupingPrice, '1');
await page.writeOnEditableTD($.fourthPackingPrice, '1');
await page.write($.fourthMinPrice, '1');
await page.pickDate($.fourthStarted, now);
await page.pickDate($.fourthEnded, now);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reload the section and check the created price has the expected ID', async() => {
await page.goto(`http://localhost:5000/#!/item/fixed-price`);
await page.autocompleteSearch($.warehouseFilter, 'Warehouse one');
await page.click($.chip);
const result = await page.waitToGetProperty($.fourthItemID, 'value');
expect(result).toContain('13');
});
});

View File

@ -1,99 +0,0 @@
import selectors from '../../../helpers/selectors.js';
import getBrowser from '../../../helpers/puppeteer';
describe('Ticket List sale path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSearchResult('13');
await page.accessToSection('ticket.card.sale');
});
afterAll(async() => {
await browser.close();
});
it('should confirm the first ticket sale contains the colour tag', async() => {
const value = await page
.waitToGetProperty(selectors.ticketSales.firstSaleColour, 'innerText');
expect(value).toContain('Black');
});
it('should confirm the first sale contains the price', async() => {
const value = await page
.waitToGetProperty(selectors.ticketSales.firstSalePrice, 'innerText');
expect(value).toContain('1.72');
});
it('should confirm the first sale contains the discount', async() => {
const value = await page
.waitToGetProperty(selectors.ticketSales.firstSaleDiscount, 'innerText');
expect(value).toContain('0.00%');
});
it('should confirm the first sale contains the total import', async() => {
const value = await page
.waitToGetProperty(selectors.ticketSales.firstSaleImport, 'innerText');
expect(value).toContain('34.40');
});
it('should add an empty item to the sale list', async() => {
await page.waitToClick(selectors.ticketSales.newItemButton);
const sales = await page
.countElement(selectors.ticketSales.saleLine);
expect(sales).toEqual(2);
});
it('should select a valid item to be added as the second item in the sales list', async() => {
let searchValue = 'Melee weapon heavy shield 100cm';
await page.autocompleteSearch(selectors.ticketSales.secondSaleIdAutocomplete, searchValue);
await page.waitToClick(selectors.ticketSales.secondSaleQuantityCell);
await page.type(selectors.ticketSales.secondSaleQuantity, '8');
await page.keyboard.press('Enter');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should update the description of the new sale', async() => {
await page.click(selectors.ticketSales.secondSaleConceptCell);
await page.write(selectors.ticketSales.secondSaleConceptInput, 'Aegis of Valor');
await page.keyboard.press('Enter');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should add a third empty item to the sale list', async() => {
await page.waitToClick(selectors.ticketSales.newItemButton);
await page.waitForNumberOfElements(selectors.ticketSales.saleLine, 3);
const sales = await page.countElement(selectors.ticketSales.saleLine);
expect(sales).toEqual(3);
});
it('should select the 2nd and 3th item and delete both', async() => {
await page.waitToClick(selectors.ticketSales.secondSaleCheckbox);
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
await page.waitToClick(selectors.ticketSales.deleteSaleButton);
await page.waitToClick(selectors.globalItems.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should verify there's only 1 single line remaining`, async() => {
const sales = await page.countElement(selectors.ticketSales.saleLine);
expect(sales).toEqual(1);
});
});

View File

@ -1,415 +0,0 @@
import selectors from '../../../helpers/selectors.js';
import getBrowser from '../../../helpers/puppeteer';
describe('Ticket Edit sale path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('salesPerson', 'ticket');
await page.accessToSearchResult('16');
await page.accessToSection('ticket.card.sale');
});
afterAll(async() => {
await browser.close();
});
it(`should click on the first sale claim icon to navigate over there`, async() => {
await page.waitToClick(selectors.ticketSales.firstSaleClaimIcon);
await page.waitForNavigation();
await page.goBack();
await page.goBack();
});
it('should navigate to the tickets index', async() => {
await page.waitToClick(selectors.globalItems.applicationsMenuButton);
await page.waitForSelector(selectors.globalItems.applicationsMenuVisible);
await page.waitToClick(selectors.globalItems.ticketsButton);
await page.waitForState('ticket.index');
});
it(`should search for a ticket and then navigate to it's sales`, async() => {
await page.accessToSearchResult('16');
await page.accessToSection('ticket.card.sale');
});
it(`should set the ticket as libre`, async() => {
const searchValue = 'libre';
await page.waitToClick(selectors.ticketSales.stateMenuButton);
await page.write(selectors.ticketSales.moreMenuState, searchValue);
try {
await page.waitForFunction(searchValue => {
const element = document.querySelector('li.active');
if (element)
return element.innerText.toLowerCase().includes(searchValue.toLowerCase());
}, {}, searchValue);
} catch (error) {
const builtSelector = await page.selectorFormater(selectors.ticketSales.moreMenuState);
const inputValue = await page.evaluate(() => {
return document.querySelector('.vn-drop-down.shown vn-textfield input').value;
});
throw new Error(`${builtSelector} value is ${inputValue}! ${error}`);
}
await page.waitForState('ticket.card.sale');
await page.keyboard.press('Enter');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should check it's state is libre now`, async() => {
await page.waitForTextInElement(selectors.ticketDescriptor.stateLabelValue, 'Libre');
const result = await page.waitToGetProperty(selectors.ticketDescriptor.stateLabelValue, 'innerText');
expect(result).toEqual('State Libre');
});
it(`should set the ticket as OK`, async() => {
await page.waitToClick(selectors.ticketSales.setOk);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should check it's state is OK now`, async() => {
await page.waitForTextInElement(selectors.ticketDescriptor.stateLabelValue, 'OK');
const result = await page.waitToGetProperty(selectors.ticketDescriptor.stateLabelValue, 'innerText');
expect(result).toEqual('State OK');
});
it(`should check the zoomed image isn't present`, async() => {
const result = await page.countElement(selectors.ticketSales.firstSaleZoomedImage);
expect(result).toEqual(0);
});
it(`should click on the thumbnail image of the 1st sale and see the zoomed image`, async() => {
await page.waitToClick(selectors.ticketSales.firstSaleThumbnailImage);
const result = await page.countElement(selectors.ticketSales.firstSaleZoomedImage);
expect(result).toEqual(1);
});
it(`should click on the zoomed image to close it`, async() => {
await page.waitToClick(selectors.ticketSales.firstSaleZoomedImage);
const result = await page.countElement(selectors.ticketSales.firstSaleZoomedImage);
expect(result).toEqual(0);
});
it(`should click on the first sale ID making now the item descriptor visible`, async() => {
await page.waitToClick(selectors.ticketSales.firstSaleId);
await page.waitImgLoad(selectors.ticketSales.firstSaleDescriptorImage);
const visible = await page.isVisible(selectors.ticketSales.saleDescriptorPopover);
expect(visible).toBeTruthy();
});
it(`should click on the descriptor image of the 1st sale and see the zoomed image`, async() => {
await page.waitToClick('vn-item-descriptor img');
const result = await page.countElement(selectors.ticketSales.firstSaleZoomedImage);
expect(result).toEqual(1);
});
it(`should now click on the zoomed image to close it`, async() => {
await page.waitToClick(selectors.ticketSales.firstSaleZoomedImage);
const result = await page.countElement(selectors.ticketSales.firstSaleZoomedImage);
expect(result).toEqual(0);
});
it(`should click on the summary icon of the item-descriptor to access to the item summary`, async() => {
await page.waitToClick(selectors.ticketSales.saleDescriptorPopoverSummaryButton);
await page.waitForState('item.card.summary');
});
it('should return to ticket sales section', async() => {
await page.waitToClick(selectors.globalItems.applicationsMenuButton);
await page.waitForSelector(selectors.globalItems.applicationsMenuVisible);
await page.waitToClick(selectors.globalItems.ticketsButton);
await page.accessToSearchResult('16');
await page.accessToSection('ticket.card.sale');
});
it('should remove 1 from the first sale quantity', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleQuantityCell);
await page.waitForSelector(selectors.ticketSales.firstSaleQuantity);
await page.type(selectors.ticketSales.firstSaleQuantity, '9\u000d');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should update the price', async() => {
await page.waitToClick(selectors.ticketSales.firstSalePrice);
await page.waitForSelector(selectors.ticketSales.firstSalePriceInput);
await page.type(selectors.ticketSales.firstSalePriceInput, '5\u000d');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the price have been updated', async() => {
const result = await page.waitToGetProperty(selectors.ticketSales.firstSalePrice, 'innerText');
expect(result).toContain('5.00');
});
it('should confirm the total price for that item have been updated', async() => {
const result = await page.waitToGetProperty(selectors.ticketSales.firstSaleImport, 'innerText');
expect(result).toContain('45.00');
});
it('should update the discount', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleDiscount);
await page.waitForSelector(selectors.ticketSales.firstSaleDiscountInput);
await page.type(selectors.ticketSales.firstSaleDiscountInput, '50');
await page.waitToClick(selectors.ticketSales.saveSaleDiscountButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the discount have been updated', async() => {
await page.waitForTextInElement(selectors.ticketSales.firstSaleDiscount, '50.00%');
const result = await page.waitToGetProperty(selectors.ticketSales.firstSaleDiscount, 'innerText');
expect(result).toContain('50.00%');
});
it('should confirm the total import for that item have been updated', async() => {
await page.waitForTextInElement(selectors.ticketSales.firstSaleImport, '22.50');
const result = await page.waitToGetProperty(selectors.ticketSales.firstSaleImport, 'innerText');
expect(result).toContain('22.50');
});
it('should recalculate price of sales', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
await page.waitToClick(selectors.ticketSales.secondSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuRecalculatePrice);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should log in as salesAssistant and navigate to ticket sales', async() => {
await page.loginAndModule('salesAssistant', 'ticket');
await page.accessToSearchResult('15');
await page.accessToSection('ticket.card.sale');
});
it('should select the first sale and create a refund with warehouse', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuRefund);
await page.waitToClick(selectors.ticketSales.refundWithWarehouse);
await page.waitForSnackbar();
await page.waitForState('ticket.card.sale');
});
it('should select the first sale and create a refund without warehouse', async() => {
await page.accessToSearchResult('18');
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuRefund);
await page.waitToClick(selectors.ticketSales.refundWithoutWarehouse);
await page.waitForSnackbar();
await page.waitForState('ticket.card.sale');
});
it('should show error trying to delete a ticket with a refund', async() => {
await page.loginAndModule('salesPerson', 'ticket');
await page.accessToSearchResult('8');
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
await page.waitToClick(selectors.globalItems.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Tickets with associated refunds can\'t be deleted');
await page.waitToClick(selectors.globalItems.cancelButton);
});
it('should select the third sale and create a claim of it', async() => {
await page.accessToSearchResult('16');
await page.accessToSection('ticket.card.sale');
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
await page.waitToClick(selectors.globalItems.acceptButton);
await page.waitForNavigation();
});
it('should search for a ticket then access to the sales section', async() => {
await page.goBack();
await page.goBack();
await page.loginAndModule('salesPerson', 'ticket');
await page.accessToSearchResult('16');
await page.accessToSection('ticket.card.sale');
});
it('should select the third sale and delete it', async() => {
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
await page.waitToClick(selectors.ticketSales.deleteSaleButton);
await page.waitToClick(selectors.globalItems.acceptButton);
await page.waitForSpinnerLoad();
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should confirm the third sale was deleted`, async() => {
const result = await page.countElement(selectors.ticketSales.saleLine);
expect(result).toEqual(3);
});
it('should select the second sale and transfer it to a valid ticket', async() => {
const targetTicketId = '12';
await page.waitToClick(selectors.ticketSales.secondSaleCheckbox);
await page.waitToClick(selectors.ticketSales.transferSaleButton);
await page.waitToClick(selectors.ticketSales.transferQuantityCell);
await page.type(selectors.ticketSales.transferQuantityInput, '10\u000d');
await page.type(selectors.ticketSales.moveToTicketInput, targetTicketId);
await page.waitToClick(selectors.ticketSales.moveToTicketButton);
await page.expectURL(`ticket/${targetTicketId}/sale`);
});
it('should confirm the transfered line is the correct one', async() => {
await page.waitForSelector(selectors.ticketSales.secondSaleText);
const result = await page.waitToGetProperty(selectors.ticketSales.secondSaleText, 'innerText');
expect(result).toContain(`Melee weapon heavy shield`);
});
it('should confirm the transfered quantity is the correct one', async() => {
const result = await page.waitToGetProperty(selectors.ticketSales.firstSaleQuantityCell, 'innerText');
expect(result).toContain('20');
});
it('should go back to the original ticket sales section', async() => {
await page.waitToClick(selectors.ticketDescriptor.goBackToModuleIndexButton);
await page.accessToSearchResult('16');
await page.accessToSection('ticket.card.sale');
});
it(`should confirm the original ticket has still three lines`, async() => {
await page.waitForSelector(selectors.ticketSales.saleLine);
const result = await page.countElement(selectors.ticketSales.saleLine);
expect(result).toEqual(3);
});
it(`should confirm the second sale quantity is now half of it's original value after the transfer`, async() => {
const result = await page.waitToGetProperty(selectors.ticketSales.secondSaleQuantityCell, 'innerText');
expect(result).toContain('10');
});
it('should go back to the receiver ticket sales section', async() => {
await page.waitToClick(selectors.ticketDescriptor.goBackToModuleIndexButton);
await page.accessToSearchResult('12');
await page.accessToSection('ticket.card.sale');
});
it('should transfer the sale back to the original ticket', async() => {
const targetTicketId = '16';
await page.waitToClick(selectors.ticketSales.secondSaleCheckbox);
await page.waitToClick(selectors.ticketSales.transferSaleButton);
await page.type(selectors.ticketSales.moveToTicketInput, targetTicketId);
await page.waitToClick(selectors.ticketSales.moveToTicketButton);
await page.expectURL(`ticket/${targetTicketId}/sale`);
});
it('should confirm the original ticket received the line', async() => {
const expectedLines = 4;
await page.waitForNumberOfElements(selectors.ticketSales.saleLine, expectedLines);
const result = await page.countElement(selectors.ticketSales.saleLine);
expect(result).toEqual(expectedLines);
});
it(`should throw an error when attempting to create a ticket for an inactive client`, async() => {
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
await page.waitToClick(selectors.ticketSales.transferSaleButton);
await page.waitToClick(selectors.ticketSales.moveToNewTicketButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`You can't create a ticket for an inactive client`);
await page.closePopup();
});
it('should go now to the ticket sales section of an active, not frozen client', async() => {
await page.waitToClick(selectors.ticketDescriptor.goBackToModuleIndexButton);
await page.accessToSearchResult('13');
await page.accessToSection('ticket.card.sale');
});
it(`should select all sales, tranfer them to a new ticket and delete the sender ticket as it would've been left empty`, async() => {
const senderTicketId = '13';
await page.waitToClick(selectors.ticketSales.selectAllSalesCheckbox);
await page.waitToClick(selectors.ticketSales.transferSaleButton);
await page.waitToClick(selectors.ticketSales.moveToNewTicketButton);
await page.evaluate((selector, ticketId) => {
return document.querySelector(selector).innerText.toLowerCase().indexOf(`#${ticketId}`) == -1;
}, selectors.ticketDescriptor.id, senderTicketId);
await page.waitForState('ticket.card.sale');
});
it('should confirm the new ticket received the line', async() => {
const expectedLines = 1;
const result = await page.countElement(selectors.ticketSales.saleLine);
expect(result).toEqual(expectedLines);
});
it('should check the first sale reserved icon isnt visible', async() => {
const result = await page.isVisible(selectors.ticketSales.firstSaleReservedIcon);
expect(result).toBeFalsy();
});
it('should mark the first sale as reserved', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuReserve);
await page.closePopup();
await page.waitForClassNotPresent(selectors.ticketSales.firstSaleReservedIcon, 'ng-hide');
const result = await page.isVisible(selectors.ticketSales.firstSaleReservedIcon);
expect(result).toBeTruthy();
});
it('should unmark the first sale as reserved', async() => {
await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuUnmarkReseved);
await page.waitForClassPresent(selectors.ticketSales.firstSaleReservedIcon, 'ng-hide');
const result = await page.isVisible(selectors.ticketSales.firstSaleReservedIcon);
expect(result).toBeFalsy();
});
it('should log in as Production role and go to a target ticket summary', async() => {
await page.loginAndModule('production', 'ticket');
await page.accessToSearchResult('13');
await page.waitForState('ticket.card.summary');
});
it(`should check the ticket is deleted`, async() => {
await page.waitForSelector(selectors.ticketDescriptor.isDeletedIcon);
});
});

View File

@ -1,50 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket Create notes path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSearchResult('5');
await page.accessToSection('ticket.card.observation');
});
afterAll(async() => {
await browser.close();
});
it('should create a new note', async() => {
await page.waitToClick(selectors.ticketNotes.addNoteButton);
await page.autocompleteSearch(selectors.ticketNotes.firstNoteType, 'ItemPicker');
await page.write(selectors.ticketNotes.firstDescription, 'description');
await page.waitToClick(selectors.ticketNotes.submitNotesButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the note is the expected one', async() => {
await page.reloadSection('ticket.card.observation');
const result = await page
.waitToGetProperty(selectors.ticketNotes.firstNoteType, 'value');
expect(result).toEqual('ItemPicker');
const firstDescription = await page
.waitToGetProperty(selectors.ticketNotes.firstDescription, 'value');
expect(firstDescription).toEqual('description');
});
it('should delete the note', async() => {
await page.waitToClick(selectors.ticketNotes.firstNoteRemoveButton);
await page.waitToClick(selectors.ticketNotes.submitNotesButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -1,32 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket expeditions and log path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('production', 'ticket');
await page.accessToSearchResult('1');
await page.accessToSection('ticket.card.expedition');
});
afterAll(async() => {
await browser.close();
});
it(`should delete a former expedition and confirm the remaining expedition are the expected ones`, async() => {
await page.waitToClick(selectors.ticketExpedition.thirdSaleCheckbox);
await page.waitToClick(selectors.ticketExpedition.deleteExpeditionButton);
await page.waitToClick(selectors.globalItems.acceptButton);
await page.reloadSection('ticket.card.expedition');
await page.waitForSelector(selectors.ticketExpedition.expeditionRow, {});
const result = await page
.countElement(selectors.ticketExpedition.expeditionRow);
expect(result).toEqual(6);
});
});

View File

@ -1,78 +0,0 @@
import getBrowser from '../../helpers/puppeteer';
const $ = {
firstPackage: 'vn-autocomplete[label="Package"]',
firstQuantity: 'vn-ticket-package vn-horizontal:nth-child(1) vn-input-number[ng-model="package.quantity"]',
firstRemovePackageButton: 'vn-icon-button[vn-tooltip="Remove package"]',
addPackageButton: 'vn-icon-button[vn-tooltip="Add package"]',
savePackagesButton: `button[type=submit]`
};
describe('Ticket Create packages path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSearchResult('1');
await page.accessToSection('ticket.card.package');
});
afterAll(async() => {
await browser.close();
});
it(`should attempt create a new package but receive an error if package is blank`, async() => {
await page.waitToClick($.firstRemovePackageButton);
await page.waitToClick($.addPackageButton);
await page.write($.firstQuantity, '99');
await page.waitToClick($.savePackagesButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Package cannot be blank');
});
it(`should delete the first package and receive and error to save a new one with blank quantity`, async() => {
await page.clearInput($.firstQuantity);
await page.autocompleteSearch($.firstPackage, 'Container medical box 100cm');
await page.waitToClick($.savePackagesButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Some fields are invalid');
});
it(`should confirm the quantity input isn't invalid yet`, async() => {
const result = await page
.evaluate(selector => {
return document.querySelector(`${selector} input`).checkValidity();
}, $.firstQuantity);
expect(result).toBeTruthy();
});
it(`should create a new package with correct data`, async() => {
await page.clearInput($.firstQuantity);
await page.write($.firstQuantity, '-99');
await page.waitToClick($.savePackagesButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should confirm the first select is the expected one`, async() => {
await page.reloadSection('ticket.card.package');
await page.waitForTextInField($.firstPackage, 'Container medical box 100cm');
const result = await page.waitToGetProperty($.firstPackage, 'value');
expect(result).toEqual('Container medical box 100cm');
});
it(`should confirm quantity is just a number and the string part was ignored by the imput number`, async() => {
await page.waitForTextInField($.firstQuantity, '-99');
const result = await page.waitToGetProperty($.firstQuantity, 'value');
expect(result).toEqual('-99');
});
});

View File

@ -1,72 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket Create new tracking state path', () => {
let browser;
let page;
afterAll(async() => {
await browser.close();
});
describe('as production', () => {
it('should log into the ticket 1 tracking', async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('production', 'ticket');
await page.accessToSearchResult('1');
await page.accessToSection('ticket.card.tracking.index');
});
it('should access to the create state view by clicking the create floating button', async() => {
await page.waitToClick(selectors.ticketTracking.createStateButton);
await page.waitForSelector(selectors.createStateView.state, {visible: true});
await page.waitForState('ticket.card.tracking.edit');
});
it(`should create a new state`, async() => {
await page.autocompleteSearch(selectors.createStateView.state, 'OK');
await page.waitToClick(selectors.createStateView.saveStateButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});
describe('as salesPerson', () => {
it('should now log into the ticket 1 tracking', async() => {
await page.loginAndModule('salesPerson', 'ticket');
await page.accessToSearchResult('1');
await page.accessToSection('ticket.card.tracking.index');
});
it('should now access to the create state view by clicking the create floating button', async() => {
await page.waitForSelector('.vn-popup', {hidden: true});
await page.waitToClick(selectors.ticketTracking.createStateButton);
await page.waitForState('ticket.card.tracking.edit');
});
it(`should attemp to create an state for which salesPerson doesn't have permissions`, async() => {
await page.autocompleteSearch(selectors.createStateView.state, 'Encajado');
await page.waitToClick(selectors.createStateView.saveStateButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`You don't have enough privileges`);
});
it(`should make sure the worker gets autocomplete uppon selecting the assigned state`, async() => {
await page.autocompleteSearch(selectors.createStateView.state, 'asignado');
const result = await page
.waitToGetProperty(selectors.createStateView.worker, 'value');
expect(result).toEqual('salesperson');
});
it(`should succesfully create a valid state`, async() => {
await page.waitToClick(selectors.createStateView.saveStateButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});
});

View File

@ -1,143 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket Edit basic data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSearchResult('11');
await page.accessToSection('ticket.card.basicData.stepOne');
});
afterAll(async() => {
await browser.close();
});
it(`should confirm the zone autocomplete is disabled unless your role is productionBoss`, async() => {
await page.waitForSelector(selectors.ticketBasicData.zone, {});
const disabled = await page.evaluate(selector => {
return document.querySelector(selector).disabled;
}, `${selectors.ticketBasicData.zone} input`);
expect(disabled).toBeTruthy();
});
it(`should now log as productionBoss to perform the rest of the tests`, async() => {
await page.loginAndModule('productionBoss', 'ticket');
await page.accessToSearchResult('11');
await page.accessToSection('ticket.card.basicData.stepOne');
});
it(`should confirm the zone autocomplete is enabled for the role productionBoss`, async() => {
await page.waitForSpinnerLoad();
await page.waitForSelector(selectors.ticketBasicData.zone);
const disabled = await page.evaluate(selector => {
return document.querySelector(selector).disabled;
}, `${selectors.ticketBasicData.zone} input`);
expect(disabled).toBeFalsy();
});
it(`should check the zone is for Gotham247`, async() => {
let zone = await page
.waitToGetProperty(selectors.ticketBasicData.zone, 'value');
expect(zone).toContain('Zone 247 A');
});
it(`should edit the ticket agency then check there are no zones for it`, async() => {
await page.autocompleteSearch(selectors.ticketBasicData.agency, 'Super-Man delivery');
let emptyZone = await page
.expectPropertyValue(selectors.ticketBasicData.zone, 'value', '');
expect(emptyZone).toBeTruthy();
});
it(`should edit the ticket zone then check the agency is for the new zone`, async() => {
await page.clearInput(selectors.ticketBasicData.agency);
await page.autocompleteSearch(selectors.ticketBasicData.zone, 'Zone expensive A');
let zone = await page
.waitToGetProperty(selectors.ticketBasicData.agency, 'value');
expect(zone).toContain('Gotham247Expensive');
});
it(`should click next`, async() => {
await page.waitToClick(selectors.ticketBasicData.nextStepButton);
await page.waitForState('ticket.card.basicData.stepTwo');
});
it(`should have a price diference`, async() => {
const result = await page
.waitToGetProperty(selectors.ticketBasicData.stepTwoTotalPriceDif, 'innerText');
expect(result).toContain('-€228.25');
});
it(`should select a new reason for the changes made then click on finalize`, async() => {
await page.waitToClick(selectors.ticketBasicData.chargesReason);
await page.waitToClick(selectors.ticketBasicData.finalizeButton);
await page.waitForState('ticket.card.summary');
});
it(`should not find ticket`, async() => {
await page.doSearch('29');
const count = await page.countElement(selectors.ticketsIndex.searchResult);
expect(count).toEqual(0);
});
it(`should split ticket without negatives`, async() => {
const newAgency = 'Gotham247';
const newDate = Date.vnNew();
newDate.setDate(newDate.getDate() - 1);
await page.accessToSearchResult('14');
await page.accessToSection('ticket.card.basicData.stepOne');
await page.autocompleteSearch(selectors.ticketBasicData.agency, newAgency);
await page.pickDate(selectors.ticketBasicData.shipped, newDate);
await page.waitToClick(selectors.ticketBasicData.nextStepButton);
await page.waitToClick(selectors.ticketBasicData.finalizeButton);
await page.waitForState('ticket.card.summary');
const newTicketAgency = await page
.waitToGetProperty(selectors.ticketDescriptor.descriptorDeliveryAgency, 'innerText');
const newTicketDate = await page
.waitToGetProperty(selectors.ticketDescriptor.descriptorDeliveryDate, 'innerText');
expect(newAgency).toEqual(newTicketAgency);
expect(newTicketDate).toContain(newDate.getDate());
});
it(`should new ticket have sale of old ticket`, async() => {
await page.accessToSection('ticket.card.sale');
await page.waitForState('ticket.card.sale');
const item = await page.waitToGetProperty(selectors.ticketSales.firstSaleId, 'innerText');
expect(item).toEqual('4');
});
it(`should old ticket have old date and agency`, async() => {
const oldDate = Date.vnNew();
const oldAgency = 'Super-Man delivery';
await page.accessToSearchResult('14');
const oldTicketAgency = await page
.waitToGetProperty(selectors.ticketDescriptor.descriptorDeliveryAgency, 'innerText');
const oldTicketDate = await page
.waitToGetProperty(selectors.ticketDescriptor.descriptorDeliveryDate, 'innerText');
expect(oldTicketAgency).toEqual(oldAgency);
expect(oldTicketDate).toContain(oldDate.getDate());
});
});

View File

@ -1,30 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket List components path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSearchResult('1');
await page.accessToSection('ticket.card.components');
});
afterAll(async() => {
await browser.close();
});
it('should confirm the total base is correct', async() => {
const name = 'Base €';
const minLength = name.length;
await page.waitPropertyLength(selectors.ticketComponents.base, 'innerText', minLength);
const base = await page.waitToGetProperty(selectors.ticketComponents.base, 'innerText');
expect(base).toContain('Base');
expect(base.length).toBeGreaterThan(minLength);
});
});

View File

@ -1,123 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket descriptor path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyerBoss', 'ticket');
await page.accessToSection('ticket.weekly.index');
});
afterAll(async() => {
await browser.close();
});
it('should count the amount of tickets in the turns section', async() => {
const result = await page.countElement(selectors.ticketsIndex.weeklyTicket);
expect(result).toEqual(6);
});
it('should go back to the ticket index then search and access a ticket summary', async() => {
await page.accessToSection('ticket.index');
await page.accessToSearchResult('33');
});
it('should add the ticket to thursday turn using the descriptor more menu', async() => {
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuAddToTurn);
await page.waitToClick(selectors.ticketDescriptor.thursdayButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Current ticket deleted and added to shift');
});
it('should again click on the Tickets button of the top bar menu', async() => {
await page.waitToClick(selectors.globalItems.applicationsMenuButton);
await page.waitForSelector(selectors.globalItems.applicationsMenuVisible);
await page.waitToClick(selectors.globalItems.ticketsButton);
await page.waitForState('ticket.index');
});
it('should confirm the ticket 33 was added to thursday', async() => {
await page.accessToSection('ticket.weekly.index');
const result = await page.waitToGetProperty(selectors.ticketsIndex.thirdWeeklyTicket, 'value');
expect(result).toEqual('Thursday');
});
it('should click on the Tickets button of the top bar menu once more', async() => {
await page.waitToClick(selectors.globalItems.applicationsMenuButton);
await page.waitForSelector(selectors.globalItems.applicationsMenuVisible);
await page.waitToClick(selectors.globalItems.ticketsButton);
await page.waitForState('ticket.index');
});
it('should now search for the ticket 33', async() => {
await page.accessToSearchResult('33');
await page.waitForState('ticket.card.summary');
});
it('should add the ticket to saturday turn using the descriptor more menu', async() => {
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuAddToTurn);
await page.waitToClick(selectors.ticketDescriptor.saturdayButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Current ticket deleted and added to shift');
});
it('should click on the Tickets button of the top bar menu once again', async() => {
await page.waitToClick(selectors.globalItems.applicationsMenuButton);
await page.waitForSelector(selectors.globalItems.applicationsMenuVisible);
await page.waitToClick(selectors.globalItems.ticketsButton);
await page.waitForState('ticket.index');
});
it('should confirm the ticket 33 was added on saturday', async() => {
await page.accessToSection('ticket.weekly.index');
await page.waitForTimeout(5000);
const result = await page.waitToGetProperty(selectors.ticketsIndex.thirdWeeklyTicket, 'value');
expect(result).toEqual('Saturday');
});
it('should now search for the weekly ticket 33', async() => {
await page.doSearch('33');
const nResults = await page.countElement(selectors.ticketsIndex.searchWeeklyResult);
expect(nResults).toEqual(2);
});
it('should delete the weekly ticket 33', async() => {
await page.waitToClick(selectors.ticketsIndex.firstWeeklyTicketDeleteIcon);
await page.waitToClick(selectors.ticketsIndex.acceptDeleteTurn);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the sixth weekly ticket was deleted', async() => {
await page.doSearch();
const nResults = await page.countElement(selectors.ticketsIndex.searchWeeklyResult);
expect(nResults).toEqual(6);
});
it('should update the agency then remove it afterwards', async() => {
await page.autocompleteSearch(selectors.ticketsIndex.firstWeeklyTicketAgency, 'Gotham247');
let message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
await page.clearInput(selectors.ticketsIndex.firstWeeklyTicketAgency);
message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -1,77 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket purchase request path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('salesPerson', 'ticket');
await page.accessToSearchResult('1');
await page.accessToSection('ticket.card.request.index');
});
afterAll(async() => {
await browser.close();
});
it('should add a new request', async() => {
await page.waitToClick(selectors.ticketRequests.addRequestButton);
await page.write(selectors.ticketRequests.descriptionInput, 'New stuff');
await page.write(selectors.ticketRequests.quantity, '9');
await page.autocompleteSearch(selectors.ticketRequests.atender, 'buyerNick');
await page.write(selectors.ticketRequests.price, '999');
await page.waitToClick(selectors.ticketRequests.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should have been redirected to the request index', async() => {
await page.waitForState('ticket.card.request.index');
});
it(`should edit the third request quantity as it's state is still new`, async() => {
await page.write(selectors.ticketRequests.thirdRequestQuantity, '9');
await page.keyboard.press('Enter');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should check the new request was added', async() => {
await page.reloadSection('ticket.card.request.index');
const result = await page.waitToGetProperty(selectors.ticketRequests.thirdRequestQuantity, 'value');
expect(result).toEqual('99');
});
it(`should check the first request can't be edited as its state is different to new`, async() => {
await page.waitForClassPresent(selectors.ticketRequests.firstRequestQuantity, 'disabled');
const result = await page.isDisabled(selectors.ticketRequests.firstRequestQuantity);
expect(result).toBe(true);
});
it(`should check the second request can't be edited as its state is different to new`, async() => {
await page.waitForClassPresent(selectors.ticketRequests.secondRequestQuantity, 'disabled');
const result = await page.isDisabled(selectors.ticketRequests.secondRequestQuantity);
expect(result).toBe(true);
});
it('should delete the added request', async() => {
await page.waitToClick(selectors.ticketRequests.thirdRemoveRequestButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should check the request was deleted', async() => {
await page.reloadSection('ticket.card.request.index');
await page.waitForSelector(selectors.ticketRequests.addRequestButton);
await page.waitForSelector(selectors.ticketRequests.thirdDescription, {hidden: true});
});
});

View File

@ -1,148 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket descriptor path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('salesperson', 'ticket');
});
afterAll(async() => {
await browser.close();
});
describe('Delete ticket', () => {
it('should search for an specific ticket', async() => {
await page.accessToSearchResult('18');
await page.waitForState('ticket.card.summary');
});
it(`should update the shipped hour using the descriptor menu`, async() => {
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuChangeShippedHour);
await page.pickTime(selectors.ticketDescriptor.changeShippedHour, '08:15');
await page.waitToClick(selectors.ticketDescriptor.acceptChangeHourButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Shipped hour updated');
});
it(`should confirm the ticket descriptor shows the correct shipping hour`, async() => {
await page.waitForState('ticket.card.summary');
const result = await page
.waitToGetProperty(selectors.ticketDescriptor.descriptorDeliveryDate, 'innerText');
expect(result).toContain('08:15');
});
it('should delete the ticket using the descriptor menu', async() => {
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
await page.waitToClick(selectors.ticketDescriptor.acceptDialog);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Ticket deleted. You can undo this action within the first hour');
});
it('should have been relocated to the ticket index', async() => {
await page.waitForState('ticket.index');
});
it(`should search for the deleted ticket and check the deletedTicket icon and it's date`, async() => {
await page.write(selectors.ticketsIndex.topbarSearch, '18');
await page.waitToClick(selectors.globalItems.searchButton);
await page.waitForState('ticket.card.summary');
await page.isVisible(selectors.ticketDescriptor.isDeletedIcon);
const result = await page.waitToGetProperty(selectors.ticketsIndex.searchResultDate, 'innerText');
expect(result).toContain(2000);
});
});
describe('Restore ticket', () => {
it('should restore the ticket using the descriptor menu', async() => {
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuRestoreTicket);
await page.waitToClick(selectors.ticketDescriptor.acceptDialog);
await page.waitForState('ticket.card.summary');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});
describe('Make invoice', () => {
it('should login as administrative role then search for a ticket', async() => {
const invoiceableTicketId = '14';
await page.loginAndModule('administrative', 'ticket');
await page.accessToSearchResult(invoiceableTicketId);
await page.waitForState('ticket.card.summary');
});
it(`should make sure the ticket doesn't have an invoiceOutFk yet`, async() => {
const result = await page
.waitToGetProperty(selectors.ticketSummary.invoiceOutRef, 'innerText');
expect(result).toEqual('-');
});
it('should invoice the ticket using the descriptor menu', async() => {
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitForContentLoaded();
await page.waitToClick(selectors.ticketDescriptor.moreMenuMakeInvoice);
await page.waitToClick(selectors.ticketDescriptor.acceptInvoiceOutButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Ticket invoiced');
});
it(`should make sure the ticket summary have an invoiceOutFk`, async() => {
await page.waitForTextInElement(selectors.ticketSummary.invoiceOutRef, 'T4444445');
const result = await page.waitToGetProperty(selectors.ticketSummary.invoiceOutRef, 'innerText');
expect(result).toEqual('T4444445');
});
it(`should regenerate the invoice using the descriptor menu`, async() => {
const expectedMessage = 'The invoice PDF document has been regenerated';
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitForContentLoaded();
await page.waitToClick(selectors.ticketDescriptor.moreMenuRegenerateInvoice);
await page.respondToDialog('accept');
const message = await page.waitForSnackbar();
expect(message.text).toContain(expectedMessage);
});
});
describe('SMS', () => {
it('should send the payment SMS using the descriptor menu', async() => {
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuSMSOptions);
await page.waitToClick(selectors.ticketDescriptor.moreMenuPaymentSMS);
await page.waitForSelector(selectors.ticketDescriptor.SMStext);
await page.waitPropertyLength(selectors.ticketDescriptor.SMStext, 'value', 128);
await page.waitToClick(selectors.ticketDescriptor.sendSMSbutton);
const message = await page.waitForSnackbar();
expect(message).toBeDefined();
});
it('should send the import SMS using the descriptor menu', async() => {
await page.waitToClick(selectors.ticketDescriptor.moreMenuSMSOptions);
await page.waitToClick(selectors.ticketDescriptor.moreMenuSendImportSms);
await page.waitForSelector(selectors.ticketDescriptor.SMStext);
await page.waitPropertyLength(selectors.ticketDescriptor.SMStext, 'value', 144);
await page.waitToClick(selectors.ticketDescriptor.sendSMSbutton);
const message = await page.waitForSnackbar();
expect(message).toBeDefined();
});
});
});

View File

@ -1,127 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket services path', () => {
let browser;
let page;
const invoicedTicketId = '1';
afterAll(async() => {
await browser.close();
});
describe('as employee', () => {
it('should log in as employee, search for an invoice and get to services', async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSearchResult(invoicedTicketId);
await page.accessToSection('ticket.card.service');
});
it('should find the add descripton button disabled for this user role', async() => {
await page.waitForClassPresent(selectors.ticketService.firstAddServiceTypeButton, 'disabled');
await page.waitToClick(selectors.ticketService.addServiceButton);
await page.waitForSelector(selectors.ticketService.firstAddServiceTypeButton);
const disabled = await page.isDisabled(selectors.ticketService.firstAddServiceTypeButton);
expect(disabled).toBe(true);
});
it('should receive an error if you attempt to save a service without access rights', async() => {
await page.clearInput(selectors.ticketService.firstPrice);
await page.write(selectors.ticketService.firstPrice, '999');
await page.waitToClick(selectors.ticketService.saveServiceButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`The current ticket can't be modified`);
});
});
describe('as administrative', () => {
let editableTicketId = '16';
it('should navigate to the services of a target ticket', async() => {
await page.loginAndModule('administrative', 'ticket');
await page.accessToSearchResult(editableTicketId);
await page.accessToSection('ticket.card.service');
});
it('should click on the add button to prepare the form to create a new service', async() => {
await page.waitToClick(selectors.ticketService.addServiceButton);
const result = await page
.isVisible(selectors.ticketService.firstServiceType);
expect(result).toBeTruthy();
});
it('should receive an error if you attempt to save it with empty fields', async() => {
await page.waitToClick(selectors.ticketService.saveServiceButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`can't be blank`);
});
it('should click on the add new service type to open the dialog', async() => {
await page.waitToClick(selectors.ticketService.firstAddServiceTypeButton);
await page.waitForSelector('.vn-dialog.shown');
const result = await page.isVisible(selectors.ticketService.newServiceTypeName);
expect(result).toBeTruthy();
});
it('should receive an error if service type is empty on submit', async() => {
await page.waitToClick(selectors.ticketService.saveServiceTypeButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`Name can't be empty`);
});
it('should create a new service type then add price then create the service', async() => {
await page.write(selectors.ticketService.newServiceTypeName, 'Documentos');
await page.waitToClick(selectors.ticketService.saveServiceTypeButton);
await page.write(selectors.ticketService.firstPrice, '999');
await page.waitToClick(selectors.ticketService.saveServiceButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the service description was created correctly', async() => {
await page.reloadSection('ticket.card.service');
const result = await page
.waitToGetProperty(selectors.ticketService.firstServiceType, 'value');
expect(result).toEqual('Documentos');
});
it('should confirm the service quantity was created correctly', async() => {
const result = await page
.waitToGetProperty(selectors.ticketService.firstQuantity, 'value');
expect(result).toEqual('1');
});
it('should confirm the service price was created correctly', async() => {
const result = await page
.waitToGetProperty(selectors.ticketService.firstPrice, 'value');
expect(result).toEqual('999');
});
it('should delete the service', async() => {
await page.waitToClick(selectors.ticketService.fistDeleteServiceButton);
await page.waitForNumberOfElements(selectors.ticketService.serviceLine, 0);
await page.waitToClick(selectors.ticketService.saveServiceButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should confirm the service was removed`, async() => {
await page.reloadSection('ticket.card.service');
const nResults = await page.countElement(selectors.ticketService.serviceLine);
expect(nResults).toEqual(0);
});
});
});

View File

@ -1,69 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket create path', () => {
let browser;
let page;
let nextMonth = Date.vnNew();
nextMonth.setMonth(nextMonth.getMonth() + 1);
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('salesPerson', 'ticket');
});
afterAll(async() => {
await browser.close();
});
it('should open the new ticket form', async() => {
await page.waitToClick(selectors.ticketsIndex.newTicketButton);
await page.waitForState('ticket.create');
});
it('should succeed to create a ticket', async() => {
await page.autocompleteSearch(selectors.createTicketView.client, 'Clark Kent');
await page.pickDate(selectors.createTicketView.deliveryDate, nextMonth);
await page.autocompleteSearch(selectors.createTicketView.warehouse, 'Warehouse Two');
await page.autocompleteSearch(selectors.createTicketView.agency, 'Gotham247');
await page.waitToClick(selectors.createTicketView.createButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should check the url is now the summary of the ticket', async() => {
await page.waitForState('ticket.card.summary');
});
it('should again open the new ticket form', async() => {
await page.waitToClick(selectors.globalItems.returnToModuleIndexButton);
await page.waitToClick(selectors.ticketsIndex.newTicketButton);
await page.waitForState('ticket.create');
});
it('should succeed to create another ticket for the same client', async() => {
await page.autocompleteSearch(selectors.createTicketView.client, 'Clark Kent');
await page.pickDate(selectors.createTicketView.deliveryDate, nextMonth);
await page.autocompleteSearch(selectors.createTicketView.warehouse, 'Warehouse One');
await page.autocompleteSearch(selectors.createTicketView.agency, 'Gotham247');
await page.waitToClick(selectors.createTicketView.createButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should check the url is now the summary of the created ticket', async() => {
await page.waitForState('ticket.card.summary');
});
it('should delete the current ticket', async() => {
await page.waitToClick(selectors.ticketDescriptor.moreMenu);
await page.waitToClick(selectors.ticketDescriptor.moreMenuDeleteTicket);
await page.waitToClick(selectors.ticketDescriptor.acceptDialog);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Ticket deleted. You can undo this action within the first hour');
});
});

View File

@ -1,37 +0,0 @@
import getBrowser from '../../helpers/puppeteer';
const $ = {
form: 'vn-ticket-create-card',
moreMenu: 'vn-client-descriptor vn-icon-button[icon=more_vert]',
simpleTicketButton: '.vn-menu [name="simpleTicket"]'
};
describe('Ticket create from client path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'client');
await page.accessToSearchResult('Petter Parker');
});
afterAll(async() => {
await browser.close();
});
it('should create simple ticket and check if the client details are the expected ones', async() => {
await page.waitToClick($.moreMenu);
await page.waitToClick($.simpleTicketButton);
await page.waitForState('ticket.create');
const values = {
client: 'Petter Parker',
address: 'Petter Parker'
};
const formValues = await page.fetchForm($.form, Object.keys(values));
expect(formValues).toEqual(values);
});
});

View File

@ -1,108 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket Summary path', () => {
let browser;
let page;
const ticketId = '20';
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
});
afterAll(async() => {
await browser.close();
});
it('should navigate to the target ticket summary section', async() => {
await page.loginAndModule('employee', 'ticket');
await page.accessToSearchResult(ticketId);
await page.waitForState('ticket.card.summary');
});
it(`should display details from the ticket and it's client on the top of the header`, async() => {
await page.waitForTextInElement(selectors.ticketSummary.header, 'Bruce Banner');
const result = await page.waitToGetProperty(selectors.ticketSummary.header, 'innerText');
expect(result).toContain(`Ticket #${ticketId}`);
expect(result).toContain('Bruce Banner (1109)');
expect(result).toContain('Somewhere in Thailand');
});
it('should display ticket details', async() => {
let result = await page
.waitToGetProperty(selectors.ticketSummary.state, 'innerText');
expect(result).toContain('Arreglar');
});
it('should display delivery details', async() => {
let result = await page
.waitToGetProperty(selectors.ticketSummary.route, 'innerText');
expect(result).toContain('3');
});
it('should display the ticket total', async() => {
let result = await page
.waitToGetProperty(selectors.ticketSummary.total, 'innerText');
expect(result).toContain('€155.54');
});
it('should display the ticket line(s)', async() => {
let result = await page
.waitToGetProperty(selectors.ticketSummary.firstSaleItemId, 'innerText');
expect(result).toContain('2');
});
it(`should click on the first sale ID to make the item descriptor visible`, async() => {
await page.waitToClick(selectors.ticketSummary.firstSaleItemId);
await page.waitImgLoad(selectors.ticketSummary.firstSaleDescriptorImage);
const visible = await page.isVisible(selectors.ticketSummary.itemDescriptorPopover);
expect(visible).toBeTruthy();
});
it(`should check the url for the item diary link of the descriptor is for the right item id`, async() => {
await page.waitForSelector(selectors.ticketSummary.itemDescriptorPopoverItemDiaryButton, {visible: true});
});
it('should log in as production then navigate to the summary of the same ticket', async() => {
await page.loginAndModule('production', 'ticket');
await page.accessToSearchResult(ticketId);
await page.waitForState('ticket.card.summary');
});
it('should set the ticket state to OK using the top right button', async() => {
const searchValue = 'OK';
await page.waitToClick(selectors.ticketSummary.stateButton);
await page.write(selectors.ticketSummary.stateAutocomplete, searchValue);
try {
await page.waitForFunction(text => {
const element = document.querySelector('li.active');
if (element)
return element.innerText.toLowerCase().includes(text.toLowerCase());
}, {}, searchValue);
} catch (error) {
const state = await page.evaluate(() => {
const stateSelector = 'vn-ticket-summary vn-label-value:nth-child(1) > section > span';
return document.querySelector(stateSelector).value;
});
throw new Error(`${stateSelector} innerText is ${state}! ${error}`);
}
await page.keyboard.press('Enter');
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the ticket state was updated', async() => {
await page.waitForSpinnerLoad();
const result = await page.waitToGetProperty(selectors.ticketSummary.state, 'innerText');
expect(result).toContain('OK');
});
});

View File

@ -1,34 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket log path', () => {
let browser;
let page;
const ticketId = '5';
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
});
afterAll(async() => {
await browser.close();
});
it('should navigate to the target ticket notes section', async() => {
await page.loginAndModule('employee', 'ticket');
await page.accessToSearchResult(ticketId);
await page.accessToSection('ticket.card.observation');
await page.waitForState('ticket.card.observation');
});
it('should create a new note for the test', async() => {
await page.waitToClick(selectors.ticketNotes.addNoteButton);
await page.autocompleteSearch(selectors.ticketNotes.firstNoteType, 'ItemPicker');
await page.write(selectors.ticketNotes.firstDescription, 'description');
await page.waitToClick(selectors.ticketNotes.submitNotesButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -1,70 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
const $ = {
newPayment: '.vn-dialog.shown',
anyBalanceLine: 'vn-client-balance-index vn-tbody > vn-tr',
firstLineReference: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td-editable'
};
describe('Ticket index payout path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('administrative', 'ticket');
await page.waitForState('ticket.index');
});
afterAll(async() => {
await browser.close();
});
it('should check the second ticket from a client and 1 of another', async() => {
await page.waitToClick(selectors.globalItems.searchButton);
await page.waitToClick(selectors.ticketsIndex.thirdTicketCheckbox);
await page.waitToClick(selectors.ticketsIndex.fifthTicketCheckbox);
await page.waitToClick(selectors.ticketsIndex.payoutButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('You cannot make a payment on account from multiple clients');
});
it('should search for tickets of the same client then open the payout form', async() => {
await page.waitToClick(selectors.ticketsIndex.openAdvancedSearchButton);
await page.write(selectors.ticketsIndex.advancedSearchClient, '1101');
await page.keyboard.press('Enter');
await page.waitForNumberOfElements(selectors.ticketsIndex.anySearchResult, 10);
await page.waitToClick(selectors.ticketsIndex.firstTicketCheckbox);
await page.waitToClick(selectors.ticketsIndex.secondTicketCheckbox);
await page.waitToClick(selectors.ticketsIndex.payoutButton);
await page.waitForSelector(selectors.ticketsIndex.payoutCompany);
});
it('should fill the company and bank to perform a payout and check a new balance line was entered', async() => {
await page.fillForm($.newPayment, {
company: 'VNL',
bank: 'cash',
amountPaid: 100,
description: 'Payment',
viewReceipt: false
});
await page.respondToDialog('accept');
const message = await page.waitForSnackbar();
await page.waitToClick(selectors.globalItems.homeButton);
await page.selectModule('client');
await page.accessToSearchResult('1101');
await page.accessToSection('client.card.balance.index');
await page.waitForSelector($.anyBalanceLine);
const count = await page.countElement($.anyBalanceLine);
const reference = await page.innerText($.firstLineReference);
expect(message.isSuccess).toBeTrue();
expect(count).toEqual(4);
expect(reference).toContain('Payment');
});
});

View File

@ -1,49 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket DMS path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSearchResult('1');
await page.accessToSection('ticket.card.dms.index');
});
afterAll(async() => {
await browser.close();
});
it('should import a document', async() => {
await page.waitToClick(selectors.ticketDms.import);
await page.autocompleteSearch(selectors.ticketDms.document, '1');
await page.waitToClick(selectors.ticketDms.saveImport);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should check there's a listed document now`, async() => {
const result = await page.countElement(selectors.ticketDms.anyDocument);
expect(result).toEqual(1);
});
it('should attempt to import an existing document on this ticket', async() => {
await page.waitToClick(selectors.ticketDms.import);
await page.autocompleteSearch(selectors.ticketDms.document, '1');
await page.waitToClick(selectors.ticketDms.saveImport);
const message = await page.waitForSnackbar();
expect(message.text).toContain('This document already exists on this ticket');
});
it(`should check there's still one document`, async() => {
const result = await page.countElement(selectors.ticketDms.anyDocument);
expect(result).toEqual(1);
});
});

View File

@ -1,50 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket expeditions', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('production', 'ticket');
await page.accessToSearchResult('1');
await page.accessToSection('ticket.card.expedition');
});
afterAll(async() => {
await browser.close();
});
it(`should move one expedition to new ticket withoute route`, async() => {
await page.waitToClick(selectors.ticketExpedition.thirdSaleCheckbox);
await page.waitToClick(selectors.ticketExpedition.moveExpeditionButton);
await page.waitToClick(selectors.ticketExpedition.moreMenuWithoutRoute);
await page.waitToClick(selectors.ticketExpedition.saveButton);
await page.waitForState('ticket.card.summary');
await page.accessToSection('ticket.card.expedition');
await page.waitForSelector(selectors.ticketExpedition.expeditionRow, {});
const result = await page
.countElement(selectors.ticketExpedition.expeditionRow);
expect(result).toEqual(2);
});
it(`should move one expedition to new ticket with route`, async() => {
await page.waitToClick(selectors.ticketExpedition.firstSaleCheckbox);
await page.waitToClick(selectors.ticketExpedition.moveExpeditionButton);
await page.waitToClick(selectors.ticketExpedition.moreMenuWithRoute);
await page.write(selectors.ticketExpedition.newRouteId, '1');
await page.waitToClick(selectors.ticketExpedition.saveButton);
await page.waitForState('ticket.card.summary');
await page.accessToSection('ticket.card.expedition');
await page.waitForSelector(selectors.ticketExpedition.expeditionRow, {});
const result = await page
.countElement(selectors.ticketExpedition.expeditionRow);
expect(result).toEqual(2);
});
});

View File

@ -1,99 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket Future path', () => {
let browser;
let page;
let httpRequest;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSection('ticket.future');
page.on('request', req => {
if (req.url().includes(`Tickets/getTicketsFuture`))
httpRequest = req.url();
});
});
afterAll(async() => {
await browser.close();
});
it('should search with required data, check three last tickets and move to the future', async() => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.warehouseFk);
await page.waitToClick(selectors.ticketFuture.submit);
let message = await page.waitForSnackbar();
expect(message.text).toContain('warehouseFk is a required argument');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.futureScopeDays);
await page.waitToClick(selectors.ticketFuture.submit);
message = await page.waitForSnackbar();
expect(message.text).toContain('futureScopeDays is a required argument');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.originScopeDays);
await page.waitToClick(selectors.ticketFuture.submit);
message = await page.waitForSnackbar();
expect(message.text).toContain('originScopeDays is a required argument');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toBeDefined();
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketFuture.ipt, 'H');
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('ipt=H');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.ipt);
await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'H');
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('futureIpt=H');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.futureIpt);
await page.autocompleteSearch(selectors.ticketFuture.state, 'Free');
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('state=0');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.state);
await page.autocompleteSearch(selectors.ticketFuture.futureState, 'Free');
await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('futureState=0');
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.clearInput(selectors.ticketFuture.state);
await page.clearInput(selectors.ticketFuture.futureState);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.searchResult, 5);
await page.waitToClick(selectors.ticketFuture.multiCheck);
await page.waitToClick(selectors.ticketFuture.firstCheck);
await page.waitToClick(selectors.ticketFuture.moveButton);
await page.waitToClick(selectors.globalItems.acceptButton);
message = await page.waitForSnackbar();
expect(message.text).toContain('Tickets moved successfully!');
});
});

View File

@ -1,79 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Ticket Advance path', () => {
let browser;
let page;
let httpRequest;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.accessToSection('ticket.advance');
page.on('request', req => {
if (req.url().includes(`Tickets/getTicketsAdvance`))
httpRequest = req.url();
});
});
afterAll(async() => {
await browser.close();
});
it('should search with the required data, check the first ticket and move to the present', async() => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.warehouseFk);
await page.waitToClick(selectors.ticketAdvance.submit);
let message = await page.waitForSnackbar();
expect(message.text).toContain('warehouseFk is a required argument');
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.dateToAdvance);
await page.waitToClick(selectors.ticketAdvance.submit);
message = await page.waitForSnackbar();
expect(message.text).toContain('dateToAdvance is a required argument');
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.dateFuture);
await page.waitToClick(selectors.ticketAdvance.submit);
message = await page.waitForSnackbar();
expect(message.text).toContain('dateFuture is a required argument');
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.submit);
expect(httpRequest).toBeDefined();
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'H');
await page.waitToClick(selectors.ticketAdvance.submit);
expect(httpRequest).toContain('futureIpt=H');
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.futureIpt);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'H');
await page.waitToClick(selectors.ticketAdvance.submit);
expect(httpRequest).toContain('ipt=H');
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.ipt);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitToClick(selectors.ticketAdvance.firstCheck);
await page.waitToClick(selectors.ticketAdvance.moveButton);
await page.waitToClick(selectors.ticketAdvance.acceptButton);
message = await page.waitForSnackbar();
expect(message.text).toContain('Tickets moved successfully!');
});
});

View File

@ -241,9 +241,8 @@
"The height must be greater than 50cm": "The height must be greater than 50cm",
"The maximum height of the wagon is 200cm": "The maximum height of the wagon is 200cm",
"The quantity claimed cannot be greater than the quantity of the line": "The quantity claimed cannot be greater than the quantity of the line",
"There are tickets for this area, delete them first": "There are tickets for this area, delete them first",
"ticketLostExpedition": "The ticket [{{ticketId}}]({{{ticketUrl}}}) has the following lost expedition:{{ expeditionId }}",
"null": "null",
"Invalid or expired verification code": "Invalid or expired verification code",
"There are tickets for this area, delete them first": "There are tickets for this area, delete them first",
"ticketLostExpedition": "The ticket [{{ticketId}}]({{{ticketUrl}}}) has the following lost expedition:{{ expeditionId }}",
"Payment method is required": "Payment method is required"
}
}

View File

@ -15,7 +15,5 @@ columns:
image: image
hasGrant: has grant
userFk: user
recoverPass: recover password
role: role
sync: pending sync
lastPassChange: password changed

View File

@ -15,7 +15,5 @@ columns:
image: imagen
hasGrant: puede delegar
userFk: usuario
recoverPass: recuperar contraseña
role: rol
sync: Pendiente de sincronizar
lastPassChange: contraseña modificada

View File

@ -55,7 +55,7 @@
"relation": "user",
"scope": {
"fields": [
"nickname"
"name"
]
}
}

View File

@ -6,6 +6,9 @@ module.exports = Self => {
arg: 'workerFk',
type: 'number',
description: 'The id for a buyer',
}, {
arg: 'filter',
type: 'object',
},
{
arg: 'dated',
@ -23,7 +26,7 @@ module.exports = Self => {
}
});
Self.getStockBought = async(workerFk, dated = Date.vnNew()) => {
Self.getStockBought = async(workerFk, filter, dated = Date.vnNew()) => {
const models = Self.app.models;
const today = Date.vnNew();
dated.setHours(0, 0, 0, 0);
@ -31,7 +34,7 @@ module.exports = Self => {
await models.StockBought.rawSql(`CALL vn.stockBought_calculate(?)`, [dated]);
const filter = {
const defaultFilter = {
where: {dated},
include: [
{
@ -53,6 +56,6 @@ module.exports = Self => {
if (workerFk) filter.where.workerFk = workerFk;
return models.StockBought.find(filter);
return models.StockBought.find({...filter, ...defaultFilter});
};
};

View File

@ -1,3 +1,4 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethod('getStockBoughtDetail', {
description: 'Returns the detail of stock bought for a given date and a worker',
@ -12,6 +13,9 @@ module.exports = Self => {
type: 'string',
description: 'The date to filter',
required: true,
}, {
arg: 'filter',
type: 'object',
}
],
returns: {
@ -24,11 +28,10 @@ module.exports = Self => {
}
});
Self.getStockBoughtDetail = async(workerFk, dated) => {
const models = Self.app.models;
Self.getStockBoughtDetail = async(workerFk, dated, filter, options) => {
const conn = Self.dataSource.connector;
const myOptions = {};
let tx;
let result;
if (typeof options == 'object')
Object.assign(myOptions, options);
@ -39,8 +42,10 @@ module.exports = Self => {
}
try {
await models.StockBought.rawSql(`CALL vn.item_calculateStock(?)`, [dated], myOptions);
result = await Self.rawSql(
const stmts = [];
stmts.push(new ParameterizedSQL(`CALL vn.item_calculateStock(?)`, [dated]));
const query = new ParameterizedSQL(
`SELECT b.entryFk entryFk,
i.id itemFk,
i.name itemName,
@ -61,11 +66,17 @@ module.exports = Self => {
JOIN volumeConfig vc
WHERE ic.display
AND w.id = ?`,
[workerFk], myOptions
[workerFk]
);
await Self.rawSql(`DROP TEMPORARY TABLE tmp.item, tmp.buyUltimate;`, [], myOptions);
stmts.push(query.merge(conn.makeSuffix(filter)));
stmts.push(new ParameterizedSQL(`DROP TEMPORARY TABLE tmp.item, tmp.buyUltimate`));
const sql = ParameterizedSQL.join(stmts, ';');
const result = await conn.executeStmt(sql, myOptions);
if (tx) await tx.commit();
return result;
return result[1];
} catch (e) {
await tx.rollback();
throw e;

View File

@ -1,57 +0,0 @@
<vn-crud-model
vn-id="model"
url="ItemBarcodes"
fields="['id', 'itemFk', 'code']"
link="{itemFk: $ctrl.$params.id}"
data="barcodes"
auto-load="true">
</vn-crud-model>
<vn-watcher
vn-id="watcher"
data="barcodes"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal ng-repeat="barcode in barcodes track by $index">
<vn-textfield
vn-three
label="Code"
ng-model="barcode.code"
vn-acl="buyer, replenisher"
vn-focus>
</vn-textfield>
<vn-none>
<vn-icon-button
vn-acl="buyer,replenisher"
pointer
vn-tooltip="Remove barcode"
icon="delete"
ng-click="model.remove($index)">
</vn-icon-button>
</vn-none>
</vn-horizontal>
<vn-one>
<vn-icon-button
vn-acl="buyer, replenisher"
vn-tooltip="Add barcode"
vn-bind="+"
icon="add_circle"
ng-click="model.insert()">
</vn-icon-button>
</vn-one>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<!-- # #2680 Undo changes button bugs -->
<!-- <vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button> -->
</vn-button-bar>
</form>

View File

@ -1,17 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onSubmit() {
this.$.watcher.check();
this.$.model.save().then(() => {
this.$.watcher.notifySaved();
this.$.watcher.updateOriginalData();
});
}
}
ngModule.vnComponent('vnItemBarcode', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,318 +0,0 @@
<mg-ajax
path="Items/{{patch.params.id}}"
options="vnPatch"
override="{filter: {include: [{relation: 'itemType'}, {relation: 'origin'}, {relation: 'ink'}, {relation: 'producer'}, {relation: 'expense'}]}}">
</mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.item"
form="form"
save="patch">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="Origins"
data="originsData"
order="name"
vn-id="origin-model">
</vn-crud-model>
<form name="form" ng-submit="watcher.submit()" ng-cloak class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-autocomplete
url="ItemTypes"
label="Type"
show-field="name"
value-field="id"
ng-model="$ctrl.item.typeFk"
vn-name="type"
initial-data="$ctrl.item.itemType"
fields="['categoryFk']"
include="'category'">
<tpl-item>
<div>{{::name}}</div>
<div class="text-caption text-secondary">
{{::category.name}}
</div>
</tpl-item>
</vn-autocomplete>
<vn-textfield
label="Reference"
ng-model="$ctrl.item.comment"
vn-name="comment"
rule>
</vn-textfield>
<vn-input-number
min="0"
label="Relevancy"
ng-model="$ctrl.item.relevancy"
vn-name="relevancy"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
min="0"
label="stems"
ng-model="$ctrl.item.stems"
vn-name="stems"
rule>
</vn-input-number>
<vn-input-number
min="0"
label="Multiplier"
ng-model="$ctrl.item.stemMultiplier"
vn-name="stemMultiplier">
</vn-input-number>
<vn-autocomplete
label="Generic"
url="Items/withName"
ng-model="$ctrl.item.genericFk"
vn-name="generic"
show-field="name"
value-field="id"
search-function="$ctrl.itemSearchFunc($search)"
order="id DESC"
tabindex="1">
<tpl-item>
<div>{{::name}}</div>
<div class="text-caption text-secondary">
#{{::id}}
</div>
</tpl-item>
<append>
<vn-icon-button
icon="filter_alt"
vn-click-stop="$ctrl.showFilterDialog($ctrl.item)"
vn-tooltip="Filter...">
</vn-icon-button>
</append>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
url="Intrastats"
label="Intrastat"
show-field="description"
value-field="id"
ng-model="$ctrl.item.intrastatFk"
vn-name="intrastat"
search-function="{or: [{id: {like: $search +'%'}}, {description: {like: '%'+ $search +'%'}}]}"
initial-data="$ctrl.item.intrastat">
<tpl-item>
<div>{{::description}}</div>
<div class="text-caption text-secondary">
#{{::id}}
</div>
</tpl-item>
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New intrastat"
ng-click="$ctrl.showIntrastat($event)">
</vn-icon-button>
</append>
</vn-autocomplete>
<vn-autocomplete
url="Expenses"
label="Expense"
ng-model="$ctrl.item.expenseFk"
vn-name="expense"
initial-data="$ctrl.item.expense">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
min="0"
label="Weight/Piece"
ng-model="$ctrl.item.weightByPiece"
vn-name="weightByPiece"
rule>
</vn-input-number>
<vn-input-number
min="0"
label="Units/Box"
ng-model="$ctrl.item.packingOut"
vn-name="packingOut"
rule>
</vn-input-number>
<vn-input-number
min="0"
label="Recycled Plastic"
ng-model="$ctrl.item.recycledPlastic"
vn-name="recycledPlastic"
rule>
</vn-input-number>
<vn-input-number
min="0"
label="Non recycled plastic"
ng-model="$ctrl.item.nonRecycledPlastic"
vn-name="nonRecycledPlastic"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-check
label="Active"
ng-model="$ctrl.item.isActive"
vn-name="isActive">
</vn-check>
<vn-check
label="Price in kg"
ng-model="$ctrl.item.hasKgPrice"
vn-name="priceInKg">
</vn-check>
<vn-check
label="Fragile"
ng-model="$ctrl.item.isFragile"
vn-name="isFragile"
info="Is shown at website, app that this item cannot travel (wreath, palms, ...)">
</vn-check>
<vn-check
label="Do photo"
ng-model="$ctrl.item.isPhotoRequested"
vn-name="isPhotoRequested"
info="This item does need a photo">
</vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
label="Description"
ng-model="$ctrl.item.description"
vn-name="description"
rule>
</vn-textarea>
</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>
<!-- Create custom agent dialog -->
<vn-dialog class="edit"
vn-id="intrastat"
on-accept="$ctrl.onIntrastatAccept()"
message="New intrastat">
<tpl-body>
<vn-horizontal>
<vn-input-number
vn-focus
label="Identifier"
ng-model="$ctrl.newIntrastat.intrastatId"
vn-name="id"
required="true">
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
label="Description"
ng-model="$ctrl.newIntrastat.description"
vn-name="description"
required="true">
</vn-textfield>
</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>
<!-- Filter item dialog -->
<vn-dialog
vn-id="filterDialog"
message="Filter item">
<tpl-body class="itemFilter">
<vn-horizontal>
<vn-textfield
label="Name"
ng-model="$ctrl.itemFilterParams.name"
vn-focus>
</vn-textfield>
<vn-textfield
label="Size"
ng-model="$ctrl.itemFilterParams.size">
</vn-textfield>
<vn-autocomplete
label="Producer"
ng-model="$ctrl.itemFilterParams.producerFk"
url="Producers"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete
label="Type"
ng-model="$ctrl.itemFilterParams.typeFk"
url="ItemTypes"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete
label="Color"
ng-model="$ctrl.itemFilterParams.inkFk"
url="Inks"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-mb-md">
<vn-button vn-none
label="Search"
ng-click="$ctrl.filter()">
</vn-button>
</vn-horizontal>
<vn-crud-model
vn-id="itemsModel"
url="Items/withName"
filter="$ctrl.itemFilter"
data="items"
limit="10">
</vn-crud-model>
<vn-data-viewer
model="itemsModel"
class="vn-w-lg">
<vn-table class="scrollable">
<vn-thead>
<vn-tr>
<vn-th shrink>ID</vn-th>
<vn-th expand>Item</vn-th>
<vn-th number>Size</vn-th>
<vn-th expand>Producer</vn-th>
<vn-th>Color</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<a ng-repeat="item in items"
class="clickable vn-tr search-result"
ng-click="$ctrl.selectItem(item.id)">
<vn-td shrink>
<span
vn-click-stop="itemDescriptor.show($event, item.id)"
class="link">
{{::item.id}}
</span>
</vn-td>
<vn-td expand>{{::item.name}}</vn-td>
<vn-td number>{{::item.size}}</vn-td>
<vn-td expand>{{::item.producer.name}}</vn-td>
<vn-td>{{::item.ink.name}}</vn-td>
</a>
</vn-tbody>
</vn-table>
</vn-data-viewer>
<vn-item-descriptor-popover
vn-id="item-descriptor"
warehouse-fk="$ctrl.vnConfig.warehouseFk">
</vn-item-descriptor-popover>
</tpl-body>
</vn-dialog>

View File

@ -1,87 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
showIntrastat(event) {
if (event.defaultPrevented) return;
event.preventDefault();
this.newIntrastat = {
taxClassFk: this.item.taxClassFk
};
this.$.intrastat.show();
}
onIntrastatAccept() {
const query = `Items/${this.$params.id}/createIntrastat`;
return this.$http.patch(query, this.newIntrastat)
.then(res => this.item.intrastatFk = res.data.id);
}
itemSearchFunc($search) {
return /^\d+$/.test($search)
? {id: $search}
: {name: {like: '%' + $search + '%'}};
}
showFilterDialog(item) {
this.activeItem = item;
this.itemFilterParams = {};
this.itemFilter = {
include: [
{
relation: 'producer',
scope: {
fields: ['name']
}
},
{
relation: 'ink',
scope: {
fields: ['name']
}
}
]
};
this.$.filterDialog.show();
}
selectItem(id) {
this.activeItem['genericFk'] = id;
this.$.filterDialog.hide();
}
filter() {
const filter = this.itemFilter;
const params = this.itemFilterParams;
const where = {};
for (let key in params) {
const value = params[key];
if (!value) continue;
switch (key) {
case 'name':
where[key] = {like: `%${value}%`};
break;
case 'producerFk':
case 'typeFk':
case 'size':
case 'inkFk':
where[key] = value;
}
}
filter.where = where;
this.$.itemsModel.applyFilter(filter);
}
}
ngModule.vnComponent('vnItemBasicData', {
template: require('./index.html'),
bindings: {
item: '<'
},
controller: Controller
});

View File

@ -1,32 +0,0 @@
import './index.js';
describe('vnItemBasicData', () => {
describe('Component vnItemBasicData', () => {
let $httpBackend;
let $scope;
let controller;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element('<vn-item-basic-data></vn-item-basic-data>');
controller = $componentController('vnItemBasicData', {$element, $scope});
controller.$.watcher = {};
controller.$params.id = 1;
controller.item = {id: 1, name: 'Rainbow Coral'};
}));
describe('onIntrastatAccept()', () => {
it('should pass the data to the watcher', () => {
const newIntrastatId = 20;
$httpBackend.expect('PATCH', 'Items/1/createIntrastat').respond({id: 20});
controller.onIntrastatAccept();
$httpBackend.flush();
expect(controller.item.intrastatFk).toEqual(newIntrastatId);
});
});
});
});

View File

@ -1,19 +0,0 @@
Reference: Referencia
Full name calculates based on tags 1-3. Is not recommended to change it manually: >-
El nombre completo se calcula
basado en los tags 1-3.
No se recomienda cambiarlo manualmente
Is active: Activo
Expense: Gasto
Price in kg: Precio en kg
New intrastat: Nuevo intrastat
Identifier: Identificador
Fragile: Frágil
Is shown at website, app that this item cannot travel (wreath, palms, ...): Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...)
Multiplier: Multiplicador
Generic: Genérico
This item does need a photo: Este artículo necesita una foto
Do photo: Hacer foto
Recycled Plastic: Plástico reciclado
Non recycled plastic: Plástico no reciclado
Minimum sales quantity: Cantidad mínima de venta

View File

@ -1,102 +0,0 @@
<vn-watcher
vn-id="watcher"
data="$ctrl.botanical"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" ng-cloak class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-autocomplete
label="Genus"
ng-model="$ctrl.botanical.genusFk"
url="Genera"
show-field="name"
value-field="id">
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New genus"
ng-click="$ctrl.showGenus($event)"
vn-acl="logisticBoss"
vn-acl-action="remove">
</vn-icon-button>
</append>
</vn-autocomplete>
<vn-autocomplete
label="Species"
ng-model="$ctrl.botanical.specieFk"
url="Species"
show-field="name"
value-field="id">
<tpl-item>
<div>{{name}}</div>
<div class="text-caption text-secondary">
{{genus.name}}
</div>
</tpl-item>
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New species"
ng-click="$ctrl.showSpecies($event)"
vn-acl="logisticBoss"
vn-acl-action="remove">
</vn-icon-button>
</append>
</vn-autocomplete>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<!-- # #2680 Undo changes button bugs -->
<!-- <vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button> -->
</vn-button-bar>
</form>
<!-- Create new genus dialog -->
<vn-dialog class="edit"
vn-id="genus"
on-response="$ctrl.onGenusResponse($response)"
on-accept="$ctrl.onGenusAccept()"
message="New genus">
<tpl-body>
<vn-horizontal>
<vn-textfield vn-one vn-focus
label="Latin genus name"
ng-model="$ctrl.data.name"
required="true">
</vn-textfield>
</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>
<!-- Create new species dialog -->
<vn-dialog class="edit"
vn-id="species"
on-response="$ctrl.onSpeciesResponse($response)"
on-accept="$ctrl.onSpeciesAccept()"
message="New species">
<tpl-body>
<vn-horizontal>
<vn-textfield vn-one vn-focus
label="Latin species name"
ng-model="$ctrl.data.name"
required="true">
</vn-textfield>
</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,99 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
get item() {
return this._item;
}
set item(value) {
this._item = value;
if (value)
this.getBotanicalData();
}
showGenus(event) {
if (event.defaultPrevented) return;
event.preventDefault();
this.$.genus.show();
}
showSpecies(event) {
if (event.defaultPrevented) return;
event.preventDefault();
this.$.species.show();
}
onGenusAccept() {
try {
if (!this.data.name)
throw new Error(`The name of the genus can't be empty`);
this.$http.post(`genera`, this.data).then(res => {
this.vnApp.showMessage(this.$t('The genus has been created'));
this.emit('response', {$response: res.data});
this.onGenusResponse(res.data);
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
return false;
}
return true;
}
onSpeciesAccept() {
try {
if (!this.data.name)
throw new Error(`The name of the species can't be empty`);
this.$http.post(`species`, this.data).then(res => {
this.vnApp.showMessage(this.$t('The species has been created'));
this.emit('response', {$response: res.data});
this.onSpeciesResponse(res.data);
});
} catch (e) {
this.vnApp.showError(this.$t(e.message));
return false;
}
return true;
}
getBotanicalData() {
const filter = {
where: {itemFk: this.item.id}
};
const filterParams = encodeURIComponent(JSON.stringify(filter));
this.$http.get(`ItemBotanicals?filter=${filterParams}`).then(res => {
if (res.data[0])
this.botanical = res.data[0];
else
this.botanical = {itemFk: this.item.id};
});
}
onSubmit() {
this.$.watcher.check();
this.$http.patch(`ItemBotanicals`, this.botanical).then(() => {
this.$.watcher.notifySaved();
this.$.watcher.updateOriginalData();
});
}
onGenusResponse(response) {
this.botanical.genusFk = response.id;
}
onSpeciesResponse(response) {
this.botanical.specieFk = response.id;
}
}
ngModule.vnComponent('vnItemBotanical', {
template: require('./index.html'),
bindings: {
item: '<'
},
controller: Controller
});

View File

@ -1,179 +0,0 @@
import './index.js';
describe('vnItemBotanical', () => {
describe('Component vnItemBotanical', () => {
let $httpBackend;
let $scope;
let controller;
let vnApp;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _vnApp_) => {
$httpBackend = _$httpBackend_;
vnApp = _vnApp_;
jest.spyOn(vnApp, 'showError');
$scope = $rootScope.$new();
const $element = angular.element('<vn-item-botanical></vn-item-botanical>');
controller = $componentController('vnItemBotanical', {$element, $scope});
controller.item = {id: 5};
controller.$params = {itemFk: 5};
controller.$ = {
watcher: {
check: () => {},
notifySaved: () => {},
updateOriginalData: () => {}},
genus: {
show: () => {}
},
species: {
show: () => {}
}};
}));
beforeEach(() => {
const response = {data: 'MyResult'};
$httpBackend.whenRoute('GET', 'ItemBotanicals').respond(response);
});
describe('showGenus()', () => {
it('should do nothing in genus field if it default is prevented', () => {
const event = {
defaultPrevented: true,
preventDefault: () => {}
};
jest.spyOn(event, 'preventDefault');
jest.spyOn(controller.$.genus, 'show');
controller.showGenus(event);
expect(event.preventDefault).not.toHaveBeenCalledWith();
expect(controller.$.genus.show).not.toHaveBeenCalledWith();
});
it('should call show function in genus field when the default is not prevented', () => {
const event = {
defaultPrevented: false,
preventDefault: () => {}
};
jest.spyOn(event, 'preventDefault');
jest.spyOn(controller.$.genus, 'show');
controller.showGenus(event);
expect(event.preventDefault).toHaveBeenCalledWith();
expect(controller.$.genus.show).toHaveBeenCalledWith();
});
});
describe('showSpecies()', () => {
it('should do nothing in species field if it default is prevented', () => {
const event = {
defaultPrevented: true,
preventDefault: () => {}
};
jest.spyOn(event, 'preventDefault');
jest.spyOn(controller.$.species, 'show');
controller.showSpecies(event);
expect(event.preventDefault).not.toHaveBeenCalledWith();
expect(controller.$.species.show).not.toHaveBeenCalledWith();
});
it('should call show function in species field when the default is not prevented', () => {
const event = {
defaultPrevented: false,
preventDefault: () => {}
};
jest.spyOn(event, 'preventDefault');
jest.spyOn(controller.$.species, 'show');
controller.showSpecies(event);
expect(event.preventDefault).toHaveBeenCalledWith();
expect(controller.$.species.show).toHaveBeenCalledWith();
});
});
describe('onGenusAccept()', () => {
it('should throw an error if the item botanical has no genus name', () => {
jest.spyOn(controller.vnApp, 'showMessage');
controller.data = {};
controller.onGenusAccept();
expect(controller.vnApp.showError).toHaveBeenCalledWith(`The name of the genus can't be empty`);
});
it('should add the new genus', () => {
controller.data = {
id: 4,
name: 'Anilius'
};
$httpBackend.expectPOST('genera', controller.data).respond(200, controller.data);
controller.onGenusAccept();
$httpBackend.flush();
controller.onGenusResponse(controller.data);
expect(controller.botanical.genusFk).toEqual(controller.data.id);
});
});
describe('onSpeciesAccept()', () => {
it('should throw an error if the item botanical has no species name', () => {
jest.spyOn(controller.vnApp, 'showMessage');
controller.data = {};
controller.onSpeciesAccept();
expect(controller.vnApp.showError).toHaveBeenCalledWith(`The name of the species can't be empty`);
});
it('should add the new species', () => {
controller.data = {
id: 2,
name: 'Spasiva'
};
$httpBackend.expectPOST('species', controller.data).respond(200, controller.data);
controller.onSpeciesAccept();
$httpBackend.flush();
controller.onSpeciesResponse(controller.data);
});
});
describe('onSubmit()', () => {
it('should make HTTP POST request to save the genus and species data', () => {
jest.spyOn(controller.$.watcher, 'updateOriginalData');
jest.spyOn(controller.$.watcher, 'check');
jest.spyOn(controller.$.watcher, 'notifySaved');
$httpBackend.expectPATCH('ItemBotanicals').respond();
controller.onSubmit();
$httpBackend.flush();
expect(controller.$.watcher.updateOriginalData).toHaveBeenCalledWith();
expect(controller.$.watcher.check).toHaveBeenCalledWith();
expect(controller.$.watcher.notifySaved).toHaveBeenCalledWith();
});
});
describe('getBotanicalData()', () => {
it('should get the species and genus data references of the item', () => {
controller.getBotanicalData();
$httpBackend.flush();
expect(controller.botanical).toEqual(controller.$params);
});
});
});
});

View File

@ -1,5 +0,0 @@
The genus has been created: El genus ha sido creado
The species has been created: La especie ha sido creada
Latin species name: Nombre de la especie en latín
Latin genus name: Nombre del genus en latín
Species: Especie

View File

@ -1,8 +0,0 @@
<vn-portal slot="menu">
<vn-item-descriptor
warehouse-fk="$ctrl.warehouseFk"
item="$ctrl.item"
card-reload="$ctrl.reload()"></vn-item-descriptor>
<vn-left-menu source="card"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,33 +0,0 @@
import ngModule from '../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
this.$http.get(`Items/${this.$params.id}/getCard`)
.then(res => this.item = res.data);
this.$http.get('ItemConfigs/findOne')
.then(res => {
if (this.$state.getCurrentPath()[4].state.name === 'item.card.diary') return;
this.warehouseFk = res.data.warehouseFk;
this.getWarehouseName(res.data.warehouseFk);
});
}
getWarehouseName(warehouseFk) {
const filter = {
where: {id: warehouseFk}
};
this.$http.get('Warehouses/findOne', {filter})
.then(res => {
this.warehouseText = this.$t('WarehouseFk', {
warehouseName: res.data.name
});
});
}
}
ngModule.vnComponent('vnItemCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,32 +0,0 @@
import './index.js';
describe('Item', () => {
describe('Component vnItemCard', () => {
let controller;
let $httpBackend;
let $state;
let data = {id: 1, name: 'fooName'};
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, _$httpBackend_, $stateParams, _$state_) => {
$httpBackend = _$httpBackend_;
$state = _$state_;
$state.getCurrentPath = () => [null, null, null, null, {state: {name: 'item.card.diary'}}];
let $element = angular.element('<div></div>');
controller = $componentController('vnItemCard', {$element});
$stateParams.id = data.id;
$httpBackend.whenRoute('GET', 'Items/:id/getCard').respond(data);
}));
it('should request data and set it on the controller', () => {
$httpBackend.expect('GET', `ItemConfigs/findOne`).respond({});
controller.reload();
$httpBackend.flush();
expect(controller.item).toEqual(data);
});
});
});

View File

@ -1,95 +0,0 @@
<vn-watcher
vn-id="watcher"
url="Items/new"
data="$ctrl.item"
insert-mode="true"
form="form">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="Origins"
data="originsData"
order="name"
vn-id="origin-model">
</vn-crud-model>
<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.item.provisionalName"
vn-name="name"
vn-focus>
</vn-textfield>
<vn-autocomplete
ng-model="$ctrl.item.tag"
url="Tags"
show-field="name"
value-field="id"
label="Tag"
vn-name="tag">
</vn-autocomplete>
<vn-autocomplete
data="$ctrl.validPriorities"
label="Priority"
ng-model="$ctrl.item.priority"
show-field="priority"
value-field="priority"
vn-name="priority">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
label="Type"
ng-model="$ctrl.item.typeFk"
vn-name="type"
url="ItemTypes"
fields="['code', 'categoryFk']"
search-function="{or: [{code: {like: $search +'%'}}, {name: {like: '%'+ $search +'%'}}]}"
include="'category'">
<tpl-item>
<div style="display: flex;">
<div style="width: 3em; padding-right: 1em;">{{::code}}</div>
<div>{{::name}}</div>
</div>
<div class="text-caption text-secondary">
{{category.name}}
</div>
</tpl-item>
</vn-autocomplete>
<vn-autocomplete
label="Intrastat"
ng-model="$ctrl.item.intrastatFk"
vn-name="intrastat"
url="Intrastats"
show-field="description"
search-function="{or: [{id: {like: $search +'%'}}, {description: {like: '%'+ $search +'%'}}]}">
<tpl-item>
<div>{{::description}}</div>
<div class="text-caption text-secondary">
#{{::id}}
</div>
</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
data="originsData"
label="Origin"
ng-model="$ctrl.item.originFk"
vn-name="origin">
</vn-autocomplete>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="item.index">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,40 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.fetchDefaultPriorityTag();
}
fetchDefaultPriorityTag() {
this.validPriorities = [];
const filter = {fields: ['defaultPriority', 'defaultTag', 'validPriorities'], limit: 1};
this.$http.get(`ItemConfigs`, {filter})
.then(res => {
if (res.data) {
const dataRow = res.data[0];
dataRow.validPriorities.forEach(priority => {
this.validPriorities.push({priority});
});
this.item = {
priority: dataRow.defaultPriority,
tag: dataRow.defaultTag
};
}
});
}
onSubmit() {
this.$.watcher.submit().then(
json => this.$state.go('item.card.basicData', {id: json.data.id})
);
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnItemCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,37 +0,0 @@
import './index.js';
describe('Item', () => {
describe('Component vnItemCreate', () => {
let $scope;
let $state;
let controller;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, $rootScope, _$state_) => {
$scope = $rootScope.$new();
$state = _$state_;
$scope.watcher = {
submit: () => {
return {
then: callback => {
callback({data: {id: 1}});
}
};
}
};
const $element = angular.element('<vn-item-create></vn-item-create>');
controller = $componentController('vnItemCreate', {$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('item.card.basicData', {id: 1});
});
});
});
});

View File

@ -1 +0,0 @@
Temporal name: Nombre temporal

View File

@ -1,2 +0,0 @@
<vn-card>
</vn-card>

View File

@ -1,21 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
}
async $onInit() {
this.$state.go('item.card.summary', {id: this.$params.id});
window.location.href = await this.vnApp.getUrl(`item/${this.$params.id}/diary`);
}
}
ngModule.vnComponent('vnItemDiary', {
template: require('./index.html'),
controller: Controller,
bindings: {
item: '<'
}
});

View File

@ -1,40 +0,0 @@
<vn-horizontal>
<vn-auto>
<section
class="inline-tag ellipsize"
ng-class="::{empty: !$ctrl.item.tag5}"
title="{{::$ctrl.item.tag5}}: {{::$ctrl.item.value5}}">
{{::$ctrl.item.value5}}
</section>
<section
class="inline-tag ellipsize"
ng-class="::{empty: !$ctrl.item.tag6}"
title="{{::$ctrl.item.tag6}}: {{::$ctrl.item.value6}}">
{{::$ctrl.item.value6}}
</section>
<section
class="inline-tag ellipsize"
ng-class="::{empty: !$ctrl.item.tag7}"
title="{{::$ctrl.item.tag7}}: {{::$ctrl.item.value7}}">
{{::$ctrl.item.value7}}
</section>
<section
class="inline-tag ellipsize"
ng-class="::{empty: !$ctrl.item.tag8}"
title="{{::$ctrl.item.tag8}}: {{::$ctrl.item.value8}}">
{{::$ctrl.item.value8}}
</section>
<section
class="inline-tag ellipsize"
ng-class="::{empty: !$ctrl.item.tag9}"
title="{{::$ctrl.item.tag9}}: {{::$ctrl.item.value9}}">
{{::$ctrl.item.value9}}
</section>
<section
class="inline-tag ellipsize"
ng-class="::{empty: !$ctrl.item.tag10}"
title="{{::$ctrl.item.tag10}}: {{::$ctrl.item.value10}}">
{{::$ctrl.item.value10}}
</section>
</vn-auto>
</vn-horizontal>

View File

@ -1,12 +0,0 @@
import ngModule from '../module';
import Component from 'core/lib/component';
import './style.scss';
ngModule.vnComponent('vnFetchedTags', {
template: require('./index.html'),
controller: Component,
bindings: {
maxLength: '<',
item: '<',
}
});

View File

@ -1,61 +0,0 @@
@import "variables";
[vn-fetched-tags] {
min-width: 155px;
& [wide] {
width: 430px;
}
& div {
display: flex;
flex-wrap: wrap;
align-items: center;
& vn-one {
min-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
font-size: 1rem;
}
& vn-one h3 {
color: $color-font-secondary;
text-transform: uppercase;
line-height: initial;
font-size: 0.75rem;
margin-bottom: 0px;
}
}
}
vn-fetched-tags {
& > vn-horizontal {
align-items: center;
max-width: 210px;
& > vn-auto {
flex-wrap: wrap;
& > .inline-tag {
margin: 1px;
}
}
& > vn-auto {
display: flex;
& > .inline-tag {
color: $color-font-secondary;
text-align: center;
font-size: .8rem;
height: 13px;
padding: 1px;
width: 64px;
min-width: 64px;
max-width: 64px;
flex: 1;
border: 1px solid $color-font-secondary;
&.empty {
border: 1px solid darken($color-font-secondary, 30%);
}
}
}
}
}

View File

@ -1,214 +0,0 @@
<vn-crud-model url="Tags" fields="['id','name','isFree']" data="$ctrl.tags" auto-load="true" />
<vn-crud-model url="ItemCategories" data="categories" auto-load="true" />
<vn-side-menu side="right">
<vn-horizontal class="input">
<vn-textfield
label="General search"
ng-model="$ctrl.filter.search"
info="Search items by id, name or barcode"
vn-focus
ng-keydown="$ctrl.onKeyPress($event)">
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="item-category">
<vn-autocomplete
vn-id="category"
data="categories"
ng-model="$ctrl.filter.categoryFk"
show-field="name"
value-field="id"
label="Category">
</vn-autocomplete>
<vn-one ng-repeat="category in categories">
<vn-icon
ng-class="{'active': $ctrl.filter.categoryFk == category.id}"
icon="{{::category.icon}}"
vn-tooltip="{{::category.name}}"
ng-click="$ctrl.changeCategory(category.id)">
</vn-icon>
</vn-one>
</vn-horizontal>
<vn-vertical class="input">
<vn-autocomplete
vn-id="type"
disabled="!$ctrl.filter.categoryFk"
url="ItemTypes"
label="Type"
where="{categoryFk: $ctrl.filter.categoryFk}"
show-field="name"
value-field="id"
ng-model="$ctrl.filter.typeFk"
fields="['categoryFk']"
include="'category'"
on-change="$ctrl.addFilters()">
<tpl-item>
<div>{{name}}</div>
<div class="text-caption text-secondary">
{{category.name}}
</div>
</tpl-item>
</vn-autocomplete>
</vn-vertical>
<vn-horizontal class="input horizontal">
<vn-autocomplete
vn-id="buyer"
disabled="false"
ng-model="$ctrl.filter.buyerFk"
url="TicketRequests/getItemTypeWorker"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"
label="Buyer"
on-change="$ctrl.addFilters()">
</vn-autocomplete>
<vn-autocomplete
vn-id="warehouse"
label="Warehouse"
ng-model="$ctrl.filter.warehouseFk"
url="Warehouses"
show-field="name"
value-field="id"
on-change="$ctrl.addFilters()">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="input horizontal">
<vn-date-picker
label="From"
ng-model="$ctrl.filter.started"
on-change="$ctrl.addFilters()">
</vn-date-picker>
<vn-date-picker
label="To"
ng-model="$ctrl.filter.ended"
on-change="$ctrl.addFilters()">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal class="input horizontal">
<vn-check
label="For me"
ng-model="$ctrl.filter.mine"
triple-state="true"
ng-click="$ctrl.addFilters()">
</vn-check>
<vn-check
label="Minimum price"
ng-model="$ctrl.filter.hasMinPrice"
triple-state="true"
ng-click="$ctrl.addFilters()">
</vn-check>
</vn-horizontal>
<vn-horizontal class="tags">
<vn-one class="text-subtitle1" translate> Tags </vn-one>
<vn-icon-button
vn-none
vn-tooltip="Add tag"
icon="add_circle"
ng-click="$ctrl.filter.tags.push({})">
</vn-icon-button>
</vn-horizontal>
<vn-horizontal class="tags horizontal" ng-repeat="itemTag in $ctrl.filter.tags">
<vn-autocomplete
vn-id="tag"
data="$ctrl.tags"
ng-model="itemTag.tagFk"
show-field="name"
label="Tag"
on-change="itemTag.value = null">
</vn-autocomplete>
<vn-textfield
ng-show="tag.selection.isFree || tag.selection.isFree == undefined"
label="Value"
ng-model="itemTag.value"
ng-keydown="$ctrl.onKeyPress($event)">
</vn-textfield>
<vn-autocomplete
ng-show="tag.selection.isFree === false"
url="{{'Tags/' + itemTag.tagFk + '/filterValue'}}"
search-function="{value: $search}"
label="Value"
ng-model="itemTag.value"
show-field="value"
value-field="value"
on-change="$ctrl.addFilters()">
</vn-autocomplete>
<vn-icon-button
vn-none
vn-tooltip="Remove tag"
icon="delete"
ng-click="$ctrl.removeTag(itemTag)">
</vn-icon-button>
</vn-horizontal>
<div class="chips">
<vn-chip
ng-if="$ctrl.filter.search"
removable="true"
on-remove="$ctrl.removeItemFilter('search')"
class="colored">
<span>Id/{{$ctrl.$t('Name')}}: {{$ctrl.filter.search}}</span>
</vn-chip>
<vn-chip
ng-if="category.selection"
removable="true"
on-remove="$ctrl.removeItemFilter('categoryFk')"
class="colored">
<span>{{$ctrl.$t('Category')}}: {{category.selection.name}}</span>
</vn-chip>
<vn-chip
ng-if="type.selection"
removable="true"
on-remove="$ctrl.removeItemFilter('typeFk')"
class="colored">
<span>{{$ctrl.$t('Type')}}: {{type.selection.name}}</span>
</vn-chip>
<vn-chip
ng-if="buyer.selection"
removable="true"
on-remove="$ctrl.removeItemFilter('buyerFk')"
class="colored">
<span>{{$ctrl.$t('Buyer')}}: {{buyer.selection.nickname}}</span>
</vn-chip>
<vn-chip
ng-if="warehouse.selection"
removable="true"
on-remove="$ctrl.removeItemFilter('warehouseFk')"
class="colored">
<span>{{$ctrl.$t('Warehouse')}}: {{warehouse.selection.name}}</span>
</vn-chip>
<vn-chip
ng-if="$ctrl.filter.started"
removable="true"
on-remove="$ctrl.removeItemFilter('started')"
class="colored">
<span>{{$ctrl.$t('Started')}}: {{$ctrl.filter.started | date:'dd/MM/yyyy'}}</span>
</vn-chip>
<vn-chip
ng-if="$ctrl.filter.ended"
removable="true"
on-remove="$ctrl.removeItemFilter('ended')"
class="colored">
<span>{{$ctrl.$t('Ended')}}: {{$ctrl.filter.ended | date:'dd/MM/yyyy'}}</span>
</vn-chip>
<vn-chip
ng-if="$ctrl.filter.mine != null"
removable="true"
on-remove="$ctrl.removeItemFilter('mine')"
class="colored">
<span>{{$ctrl.$t('For me')}}: {{$ctrl.filter.mine ? '✓' : '✗'}}</span>
</vn-chip>
<vn-chip
ng-if="$ctrl.filter.hasMinPrice != null"
removable="true"
on-remove="$ctrl.removeItemFilter('hasMinPrice')"
class="colored">
<span>{{$ctrl.$t('Minimum price')}}: {{$ctrl.filter.hasMinPrice ? '✓' : '✗'}}</span>
</vn-chip>
<vn-chip
ng-repeat="chipTag in $ctrl.filter.tags"
removable="true"
on-remove="$ctrl.removeTag(chipTag)"
class="colored"
ng-if="chipTag.value">
<span>{{$ctrl.showTagInfo(chipTag)}}</span>
</vn-chip>
</div>
</vn-side-menu>

View File

@ -1,60 +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, $);
}
$onInit() {
this.filter = {
tags: []
};
}
changeCategory(id) {
if (this.filter.categoryFk != id) {
this.filter.categoryFk = id;
this.addFilters();
}
}
removeItemFilter(param) {
this.filter[param] = null;
if (param == 'categoryFk') this.filter['typeFk'] = null;
this.addFilters();
}
removeTag(tag) {
const index = this.filter.tags.indexOf(tag);
if (index > -1) this.filter.tags.splice(index, 1);
this.addFilters();
}
onKeyPress($event) {
if ($event.key === 'Enter')
this.addFilters();
}
addFilters() {
for (let i = 0; i < this.filter.tags.length; i++) {
if (!this.filter.tags[i].value)
this.filter.tags.splice(i, 1);
}
return this.model.addFilter({}, this.filter);
}
showTagInfo(itemTag) {
if (!itemTag.tagFk) return itemTag.value;
return `${this.tags.find(tag => tag.id == itemTag.tagFk).name}: ${itemTag.value}`;
}
}
ngModule.vnComponent('vnFixedPriceSearchPanel', {
template: require('./index.html'),
controller: Controller,
bindings: {
model: '<'
}
});

View File

@ -1,56 +0,0 @@
import './index.js';
describe('Item', () => {
describe('Component vnFixedPriceSearchPanel', () => {
let $element;
let controller;
beforeEach(ngModule('item'));
beforeEach(angular.mock.inject($componentController => {
$element = angular.element(`<vn-fixed-price-search-panel></vn-fixed-price-search-panel>`);
controller = $componentController('vnFixedPriceSearchPanel', {$element});
controller.model = {addFilter: () => {}};
}));
describe('removeItemFilter()', () => {
it(`should remove param from filter`, () => {
controller.filter = {tags: [], categoryFk: 1, typeFk: 1};
const expectFilter = {tags: [], categoryFk: null, typeFk: null};
controller.removeItemFilter('categoryFk');
expect(controller.filter).toEqual(expectFilter);
});
});
describe('removeTag()', () => {
it(`should remove tag from filter`, () => {
const tag = {tagFk: 1, value: 'Value'};
controller.filter = {tags: [tag]};
const expectFilter = {tags: []};
controller.removeTag(tag);
expect(controller.filter).toEqual(expectFilter);
});
});
describe('showTagInfo()', () => {
it(`should show tag value`, () => {
const tag = {value: 'Value'};
const result = controller.showTagInfo(tag);
expect(result).toEqual('Value');
});
it(`should show tag name and value`, () => {
const tag = {tagFk: 1, value: 'Value'};
controller.tags = [{id: 1, name: 'tagName'}];
const result = controller.showTagInfo(tag);
expect(result).toEqual('tagName: Value');
});
});
});
});

View File

@ -1,4 +0,0 @@
Started: Inicio
Ended: Fin
Minimum price: Precio mínimo
Item ID: ID Artículo

View File

@ -1,71 +0,0 @@
@import "variables";
vn-fixed-price-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;
}
.tags {
padding: $spacing-md;
padding-bottom: 0%;
padding-top: 0%;
align-items: center;
}
.chips {
display: flex;
flex-wrap: wrap;
padding: $spacing-md;
overflow: hidden;
max-width: 100%;
border-color: $color-spacer;
border-top: $border-thin;
}
.item-category {
padding: $spacing-sm;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: wrap;
vn-autocomplete[vn-id="category"] {
display: none;
}
& > vn-one {
padding: $spacing-sm;
min-width: 33.33%;
text-align: center;
box-sizing: border-box;
& > vn-icon {
padding: $spacing-sm;
background-color: $color-font-secondary;
border-radius: 50%;
cursor: pointer;
&.active {
background-color: $color-main;
color: #fff;
}
& > i:before {
font-size: 2.6rem;
width: 16px;
height: 16px;
}
}
}
}
}
}

View File

@ -1,284 +0,0 @@
<vn-crud-model
vn-id="model"
url="FixedPrices/filter"
user-params="::$ctrl.filterParams"
limit="20"
data="prices"
order="itemFk"
auto-load="false">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Warehouses"
data="warehouses"
order="name">
</vn-crud-model>
<vn-portal slot="topbar">
</vn-portal>
<vn-fixed-price-search-panel
model="model">
</vn-fixed-price-search-panel>
<div class="vn-w-xl">
<vn-card>
<smart-table
model="model"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-table>
<table>
<thead>
<tr>
<th shrink>
<vn-multi-check
model="model"
checked="$ctrl.checkAll"
check-field="checked"
check-dummy-enabled="true"
checked-dummy-count="$ctrl.checkedDummyCount">
</vn-multi-check>
</th>
<th field="itemFk">
<span translate>Item ID</span>
</th>
<th field="name">
<span translate>Description</span>
</th>
<th
field="rate2">
<span translate>Grouping price</span>
</th>
<th
field="rate3">
<span translate>Packing price</span>
</th>
<th field="minPrice">
<span translate>Min price</span>
</th>
<th field="started">
<span translate>Started</span>
</th>
<th field="ended">
<span translate>Ended</span>
</th>
<th field="warehouseFk">
<span translate>Warehouse</span>
</th>
<th shrink></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="price in prices">
<td>
<vn-check
ng-model="price.checked"
on-change="$ctrl.saveChecked(price.id)"
vn-click-stop>
</vn-check>
</td>
<td shrink-field>
<vn-autocomplete
vn-id="itemFk"
class="dense"
url="Items/withName"
ng-model="price.itemFk"
fields="['name']"
show-field="id"
value-field="id"
search-function="$ctrl.itemSearchFunc($search)"
ng-change="$ctrl.upsertPrice(price, true)"
order="id DESC"
tabindex="1">
<tpl-item>
<div>{{id}}</div>
<div class="text-caption text-secondary">
{{name}}
</div>
</tpl-item>
</vn-autocomplete>
</td>
<td vn-fetched-tags>
<div>
<span
vn-one
ng-if="price.itemFk"
ng-click="itemDescriptor.show($event, price.itemFk)"
class="link">
{{itemFk.selection.name}}
</span>
<vn-one ng-if="price.subName">
<h3 title="{{price.subName}}">{{price.subName}}</h3>
</vn-one>
</div>
<vn-fetched-tags
max-length="6"
item="price"
tabindex="-1">
</vn-fetched-tags>
</td>
<td shrink-field>
<vn-td-editable number>
<text>
<strong>{{price.rate2 | currency: 'EUR':2}}</strong>
</text>
<field>
<vn-input-number
class="dense"
vn-focus
ng-model="price.rate2"
on-change="$ctrl.upsertPrice(price)"
step="0.01">
</vn-input-number>
</field>
</vn-td-editable>
</td>
<td shrink-field>
<vn-td-editable number>
<text>
<strong>{{price.rate3 | currency: 'EUR':2}}</strong>
</text>
<field>
<vn-input-number
class="dense"
vn-focus
ng-model="price.rate3"
on-change="$ctrl.upsertPrice(price);"
step="0.01"s>
</vn-input-number>
</field>
</vn-td-editable>
</td>
<td shrink-field-expand class="minPrice">
<vn-check
vn-one
ng-model="price.hasMinPrice"
on-change="$ctrl.upsertPrice(price)">
</vn-check>
<vn-input-number
ng-class="{inactive: !price.hasMinPrice}"
ng-model="price.minPrice"
on-change="$ctrl.upsertPrice(price)"
step="0.01">
</vn-input-number>
</td>
<td shrink-date>
<vn-chip class="chip {{$ctrl.isBigger(price.started)}} transparent">
<vn-date-picker
vn-one
ng-model="price.started"
on-change="$ctrl.upsertPrice(price)">
</vn-date-picker>
</vn-chip>
</td>
<td shrink-date>
<vn-chip class="chip {{$ctrl.isLower(price.ended)}} transparent">
<vn-date-picker
vn-one
ng-model="price.ended"
on-change="$ctrl.upsertPrice(price)">
</vn-date-picker>
</vn-chip>
</td>
<td expand>
<vn-autocomplete
vn-one
ng-model="price.warehouseFk"
data="warehouses"
on-change="$ctrl.upsertPrice(price)"
tabindex="2">
</vn-autocomplete>
</td>
<td shrink>
<vn-icon-button
icon="delete"
vn-tooltip="Delete"
ng-click="deleteFixedPrice.show({$index})">
</vn-icon-button>
</td>
</tr>
</tbody>
</table>
<div class="vn-pa-md">
<vn-icon-button
vn-tooltip="Add fixed price"
icon="add_circle"
vn-bind="+"
ng-click="$ctrl.add()">
</vn-icon-button>
</div>
</slot-table>
</smart-table>
</vn-card>
</div>
<div fixed-bottom-right>
<vn-vertical style="align-items: center;">
<vn-button class="round sm vn-mb-sm"
icon="edit"
ng-show="$ctrl.totalChecked > 0"
ng-click="edit.show($event)"
vn-tooltip="Edit fixed price(s)"
tooltip-position="left">
</vn-button>
</vn-vertical>
</div>
<vn-dialog class="edit"
vn-id="edit"
on-accept="$ctrl.onEditAccept()"
on-close="$ctrl.editedColumn = null">
<tpl-body style="width: 400px;">
<span translate>Edit</span>
<span class="countLines">
{{::$ctrl.totalChecked}}
</span>
<span translate>buy(s)</span>
<vn-horizontal>
<vn-autocomplete
vn-one
ng-model="$ctrl.editedColumn.field"
data="$ctrl.columns"
value-field="field"
show-field="displayName"
label="Field to edit">
</vn-autocomplete>
<vn-input-number
vn-one
ng-if="$ctrl.editedColumn.field == 'rate2' || $ctrl.editedColumn.field == 'rate3' || $ctrl.editedColumn.field == 'minPrice'"
label="Value"
ng-model="$ctrl.editedColumn.newValue">
</vn-input-number>
<vn-check
vn-one
ng-if="$ctrl.editedColumn.field == 'hasMinPrice'"
ng-model="$ctrl.editedColumn.newValue">
</vn-check>
<vn-date-picker
vn-one
ng-if="$ctrl.editedColumn.field == 'started' || $ctrl.editedColumn.field == 'ended'"
label="Date"
ng-model="$ctrl.editedColumn.newValue">
</vn-date-picker>
<vn-autocomplete
vn-one
ng-if="$ctrl.editedColumn.field == 'warehouseFk'"
label="Warehouse"
ng-model="$ctrl.editedColumn.newValue"
data="warehouses">
</vn-autocomplete>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>
<vn-item-descriptor-popover
vn-id="item-descriptor"
warehouse-fk="$ctrl.vnConfig.warehouseFk">
</vn-item-descriptor-popover>
<vn-confirm
vn-id="deleteFixedPrice"
on-accept="$ctrl.removePrice($data.$index)"
question="Are you sure you want to continue?"
message="This row will be removed">
</vn-confirm>

View File

@ -1,254 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.editedColumn;
this.checkAll = false;
this.checkedFixedPrices = [];
this.smartTableOptions = {
activeButtons: {
search: true
},
columns: [
{
field: 'warehouseFk',
autocomplete: {
url: 'Warehouses',
showField: 'name',
valueField: 'id',
}
},
{
field: 'started',
searchable: false
},
{
field: 'ended',
searchable: false
}
]
};
this.filterParams = {
warehouseFk: this.vnConfig.warehouseFk
};
}
getFilterParams() {
return {
warehouseFk: this.vnConfig.warehouseFk
};
}
get columns() {
if (this._columns) return this._columns;
this._columns = [
{field: 'rate2', displayName: this.$t('Grouping price')},
{field: 'rate3', displayName: this.$t('Packing price')},
{field: 'hasMinPrice', displayName: this.$t('Has min price')},
{field: 'minPrice', displayName: this.$t('Min price')},
{field: 'started', displayName: this.$t('Started')},
{field: 'ended', displayName: this.$t('Ended')},
{field: 'warehouseFk', displayName: this.$t('Warehouse')}
];
return this._columns;
}
get checked() {
const fixedPrices = this.$.model.data || [];
const checkedBuys = [];
for (let fixedPrice of fixedPrices) {
if (fixedPrice.checked)
checkedBuys.push(fixedPrice);
}
return checkedBuys;
}
uncheck() {
this.checkAll = false;
this.checkedFixedPrices = [];
}
get totalChecked() {
if (this.checkedDummyCount)
return this.checkedDummyCount;
return this.checked.length;
}
saveChecked(fixedPriceId) {
const index = this.checkedFixedPrices.indexOf(fixedPriceId);
if (index !== -1)
return this.checkedFixedPrices.splice(index, 1);
return this.checkedFixedPrices.push(fixedPriceId);
}
reCheck() {
if (!this.$.model.data) return;
if (!this.checkedFixedPrices.length) return;
this.$.model.data.forEach(fixedPrice => {
if (this.checkedFixedPrices.includes(fixedPrice.id))
fixedPrice.checked = true;
});
}
onEditAccept() {
const rowsToEdit = [];
for (let row of this.checked)
rowsToEdit.push({id: row.id, itemFk: row.itemFk});
const data = {
field: this.editedColumn.field,
newValue: this.editedColumn.newValue,
lines: rowsToEdit
};
if (this.checkedDummyCount && this.checkedDummyCount > 0) {
const params = {};
if (this.$.model.userParams) {
const userParams = this.$.model.userParams;
for (let param in userParams) {
let newParam = this.exprBuilder(param, userParams[param]);
if (!newParam)
newParam = {[param]: userParams[param]};
Object.assign(params, newParam);
}
}
if (this.$.model.userFilter)
Object.assign(params, this.$.model.userFilter.where);
data.filter = params;
}
return this.$http.post('FixedPrices/editFixedPrice', data)
.then(() => {
this.uncheck();
this.$.model.refresh();
});
}
isBigger(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';
}
isLower(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';
}
add() {
if (!this.$.model.data || this.$.model.data.length == 0) {
this.$.model.data = [];
this.$.model.proxiedData = [];
const today = Date.vnNew();
const millisecsInDay = 86400000;
const daysInWeek = 7;
const nextWeek = new Date(today.getTime() + daysInWeek * millisecsInDay);
this.$.model.insert({
started: today,
ended: nextWeek
});
return;
}
const lastIndex = this.$.model.data.length - 1;
const lastItem = this.$.model.data[lastIndex];
this.$.model.insert({
itemFk: lastItem.itemFk,
name: lastItem.name,
subName: lastItem.subName,
value5: lastItem.value5,
value6: lastItem.value6,
value7: lastItem.value7,
value8: lastItem.value8,
value9: lastItem.value9,
value10: lastItem.value10,
warehouseFk: lastItem.warehouseFk,
rate2: lastItem.rate2,
rate3: lastItem.rate3,
hasMinPrice: lastItem.hasMinPrice,
minPrice: lastItem.minPrice,
started: lastItem.started,
ended: lastItem.ended,
});
}
upsertPrice(price, resetMinPrice) {
if (resetMinPrice)
delete price['minPrice'];
const requiredFields = ['itemFk', 'started', 'ended', 'rate2', 'rate3'];
for (const field of requiredFields)
if (price[field] == undefined) return;
const query = 'FixedPrices/upsertFixedPrice';
this.$http.patch(query, price)
.then(res => {
this.vnApp.showSuccess(this.$t('Data saved!'));
Object.assign(price, res.data);
});
}
removePrice($index) {
const price = this.$.model.data[$index];
if (price.id) {
this.$http.delete(`FixedPrices/${price.id}`)
.then(() => {
this.$.model.remove($index);
this.vnApp.showSuccess(this.$t('Data saved!'));
});
} else
this.$.model.remove($index);
}
itemSearchFunc($search) {
return /^\d+$/.test($search)
? {id: $search}
: {name: {like: '%' + $search + '%'}};
}
exprBuilder(param, value) {
switch (param) {
case 'name':
return {'i.name': {like: `%${value}%`}};
case 'itemFk':
case 'warehouseFk':
case 'rate2':
case 'rate3':
param = `fp.${param}`;
return {[param]: value};
case 'minPrice':
param = `i.${param}`;
return {[param]: value};
}
}
}
ngModule.vnComponent('vnFixedPrice', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,173 +0,0 @@
import './index';
describe('fixed price', () => {
describe('Component vnFixedPrice', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$httpBackend = _$httpBackend_;
const $scope = $rootScope.$new();
const $element = angular.element('<vn-fixed-price></vn-fixed-price>');
controller = $componentController('vnFixedPrice', {$element, $scope});
controller.$ = {
model: {refresh: () => {}},
edit: {hide: () => {}}
};
}));
describe('get columns', () => {
it(`should return a set of columns`, () => {
let result = controller.columns;
let length = result.length;
let anyColumn = Object.keys(result[Math.floor(Math.random() * Math.floor(length))]);
expect(anyColumn).toContain('field', 'displayName');
});
});
describe('get checked', () => {
it(`should return a set of checked lines`, () => {
controller.$.model.data = [
{checked: true, id: 1},
{checked: true, id: 2},
{checked: true, id: 3},
{checked: false, id: 4},
];
let result = controller.checked;
expect(result.length).toEqual(3);
});
});
describe('reCheck()', () => {
it(`should recheck buys`, () => {
controller.$.model.data = [
{checked: false, id: 1},
{checked: false, id: 2},
{checked: false, id: 3},
{checked: false, id: 4},
];
controller.checkedFixedPrices = [1, 2];
controller.reCheck();
expect(controller.$.model.data[0].checked).toEqual(true);
expect(controller.$.model.data[1].checked).toEqual(true);
expect(controller.$.model.data[2].checked).toEqual(false);
expect(controller.$.model.data[3].checked).toEqual(false);
});
});
describe('saveChecked()', () => {
it(`should check buy`, () => {
const buyCheck = 3;
controller.checkedFixedPrices = [1, 2];
controller.saveChecked(buyCheck);
expect(controller.checkedFixedPrices[2]).toEqual(buyCheck);
});
it(`should uncheck buy`, () => {
const buyUncheck = 3;
controller.checkedFixedPrices = [1, 2, 3];
controller.saveChecked(buyUncheck);
expect(controller.checkedFixedPrices[2]).toEqual(undefined);
});
});
describe('onEditAccept()', () => {
it(`should perform a query to update columns`, () => {
controller.editedColumn = {field: 'my field', newValue: 'the new value'};
const query = 'FixedPrices/editFixedPrice';
$httpBackend.expectPOST(query).respond();
controller.onEditAccept();
$httpBackend.flush();
const result = controller.checked;
expect(result.length).toEqual(0);
});
});
describe('upsertPrice()', () => {
it('should do nothing if one or more required arguments are missing', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
controller.upsertPrice({});
expect(controller.vnApp.showSuccess).not.toHaveBeenCalled();
});
it('should perform an http request to update the price', () => {
const now = Date.vnNew();
jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expectPATCH('FixedPrices/upsertFixedPrice').respond();
controller.upsertPrice({
itemFk: 1,
started: now,
ended: now,
rate2: 1,
rate3: 2
});
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('removePrice()', () => {
it(`should only remove the created instance by the model as it doesn't have an ID yet`, () => {
const $index = 0;
controller.$ = {
model: {
remove: () => {},
data: [{
foo: 'bar'
}]
}
};
jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller.$.model, 'remove');
$httpBackend.expectGET('Warehouses').respond();
controller.removePrice($index);
expect(controller.vnApp.showSuccess).not.toHaveBeenCalled();
expect(controller.$.model.remove).toHaveBeenCalled();
});
it('should remove the instance performing an delete http request', () => {
const $index = 0;
controller.$ = {
model: {
remove: () => {},
data: [{
id: '1'
}]
}
};
jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller.$.model, 'remove');
const query = `FixedPrices/${controller.$.model.data[0].id}`;
$httpBackend.expectDELETE(query).respond();
controller.removePrice($index);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.$.model.remove).toHaveBeenCalled();
});
});
});
});

View File

@ -1,7 +0,0 @@
Fixed prices: Precios fijados
Search prices by item ID or code: Buscar por ID de artículo o código
Search fixed prices: Buscar precios fijados
Add fixed price: Añadir precio fijado
This row will be removed: Esta linea se eliminará
Edit fixed price(s): Editar precio(s) fijado(s)
Has min price: Tiene precio mínimo

View File

@ -1,46 +0,0 @@
@import "variables";
vn-fixed-price{
smart-table table{
[shrink-field]{
width: 90px;
max-width: 90px;
}
[shrink-field-expand]{
width: 150px;
max-width: 150px;
}
}
.minPrice {
align-items: center;
text-align: center;
vn-input-number {
width: 75px;
max-width: 75px;
}
}
smart-table table tbody > * > td .chip {
padding: 0px;
}
smart-table table tbody > * > td{
padding: 0px;
padding-left: 5px;
padding-right: 5px;
}
smart-table table tbody > * > td .chip.warning {
color: $color-font-bg
}
.vn-field > .container > .infix > .control > input {
color: inherit;
}
vn-input-number.inactive{
input {
color: $color-font-light !important;
}
}
}

View File

@ -1,27 +1,6 @@
export * from './module';
import './main';
import './index/';
import './search-panel';
import './diary';
import './create';
import './card';
import './descriptor';
import './descriptor-popover';
import './basic-data';
import './fetched-tags';
import './tags';
import './tax';
import './log';
import './request';
import './request-search-panel';
import './last-entries';
import './botanical';
import './barcode';
import './summary';
import './waste/index/';
import './fixed-price';
import './fixed-price-search-panel';
import './item-type';
import './item-shelving';

View File

@ -1,197 +0,0 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-card>
<smart-table
model="model"
view-config-id="itemsIndex"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-table>
<table>
<thead>
<tr>
<th shrink></th>
<th field="id">
<span translate>Identifier</span>
</th>
<th field="grouping">
<span translate>Grouping</span>
</th>
<th field="packing">
<span translate>Packing</span>
</th>
<th field="name">
<span translate>Description</span>
</th>
<th field="stems">
<span translate>Stems</span>
</th>
<th field="size">
<span translate>Size</span>
</th>
<th field="typeFk">
<span translate>Type</span>
</th>
<th field="category">
<span translate>Category</span>
</th>
<th field="intrastat">
<span translate>Intrastat</span>
</th>
<th field="origin">
<span translate>Origin</span>
</th>
<th field="buyerFk">
<span translate>Buyer</span>
</th>
<th field="weightByPiece">
<span translate>Weight/Piece</span>
</th>
<th field="stemMultiplier">
<span translate>Multiplier</span>
</th>
<th field="active">
<span translate>Active</span>
</th>
<th field="producer">
<span translate>Producer</span>
</th>
<th field="landed">
<span translate>Landed</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in model.data"
vn-anchor="::{
state: 'item.card.summary',
params: {id: item.id}
}">
<td>
<img
ng-src="{{::$root.imagePath('catalog', '50x50', item.id)}}"
zoom-image="{{::$root.imagePath('catalog', '1600x900', item.id)}}"
vn-click-stop
on-error-src/>
</td>
<td>
<span
vn-click-stop="itemDescriptor.show($event, item.id)"
class="link">
{{::item.id}}
</span>
</td>
<td>{{::item.grouping | dashIfEmpty}}</td>
<td>{{::item.packing | dashIfEmpty}}</td>
<td vn-fetched-tags>
<div>
<vn-one title="{{::item.name}}">{{::item.name}}</vn-one>
<vn-one ng-if="::item.subName">
<h3 title="{{::item.subName}}">{{::item.subName}}</h3>
</vn-one>
</div>
<vn-fetched-tags
max-length="6"
item="item"
tabindex="-1">
</vn-fetched-tags>
</td>
<td>{{::item.stems}}</td>
<td>{{::item.size}}</td>
<td title="{{::item.typeName}}">
{{::item.typeName}}
</td>
<td title="{{::item.category}}">
{{::item.category}}
</td>
<td title="{{::item.intrastat}}">
{{::item.intrastat}}
</td>
<td>{{::item.origin}}</td>
<td title="{{::item.userName}}">
<span
class="link"
vn-click-stop="workerDescriptor.show($event, item.buyerFk)">
{{::item.userName}}
</span>
</td>
<td>{{::item.weightByPiece}}</td>
<td>{{::item.stemMultiplier}}</td>
<td>
<vn-check
disabled="true"
ng-model="::item.isActive">
</vn-check>
</td>
<td>{{::item.producer | dashIfEmpty}}</td>
<td shrink-date>{{::item.landed | date:'dd/MM/yyyy'}}</td>
<td>
<vn-horizontal class="buttons">
<vn-icon-button
vn-click-stop="clone.show(item.id)"
vn-tooltip="Clone"
icon="icon-clone">
</vn-icon-button>
<vn-icon-button
vn-click-stop="$ctrl.preview(item)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-horizontal>
</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<a ui-sref="item.create" vn-tooltip="New item" vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>
<vn-item-descriptor-popover
vn-id="item-descriptor"
warehouse-fk="$ctrl.vnConfig.warehouseFk">
</vn-item-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="worker-descriptor">
</vn-worker-descriptor-popover>
<vn-confirm
vn-id="clone"
on-accept="$ctrl.onCloneAccept($data)"
question="Do you want to clone this item?"
message="All it's properties will be copied">
</vn-confirm>
<vn-popup vn-id="preview">
<vn-item-summary
item="$ctrl.itemSelected">
</vn-item-summary>
</vn-popup>
<vn-contextmenu
vn-id="contextmenu"
targets="['smart-table']"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-menu>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.filterBySelection()">
Filter by selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.excludeSelection()">
Exclude selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.removeFilter()">
Remove filter
</vn-item>
<vn-item translate
ng-click="contextmenu.removeAllFilters()">
Remove all filters
</vn-item>
</slot-menu>
</vn-contextmenu>

View File

@ -1,112 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.smartTableOptions = {
activeButtons: {
search: true,
shownColumns: true,
},
columns: [
{
field: 'category',
autocomplete: {
url: 'ItemCategories',
valueField: 'name',
}
},
{
field: 'origin',
autocomplete: {
url: 'Origins',
showField: 'code',
valueField: 'code'
}
},
{
field: 'typeFk',
autocomplete: {
url: 'ItemTypes',
}
},
{
field: 'intrastat',
autocomplete: {
url: 'Intrastats',
showField: 'description',
valueField: 'description'
}
},
{
field: 'buyerFk',
autocomplete: {
url: 'TicketRequests/getItemTypeWorker',
searchFunction: '{firstName: $search}',
showField: 'nickname',
valueField: 'id',
}
},
{
field: 'active',
searchable: false
},
{
field: 'landed',
searchable: false
},
]
};
}
exprBuilder(param, value) {
switch (param) {
case 'category':
return {'ic.name': value};
case 'buyerFk':
return {'it.workerFk': value};
case 'grouping':
return {'b.grouping': value};
case 'packing':
return {'b.packing': value};
case 'origin':
return {'ori.code': value};
case 'typeFk':
return {'i.typeFk': value};
case 'intrastat':
return {'intr.description': value};
case 'name':
return {'i.name': {like: `%${value}%`}};
case 'producer':
return {'pr.name': {like: `%${value}%`}};
case 'id':
case 'size':
case 'subname':
case 'isActive':
case 'weightByPiece':
case 'stemMultiplier':
case 'stems':
return {[`i.${param}`]: value};
}
}
onCloneAccept(itemFk) {
return this.$http.post(`Items/${itemFk}/clone`)
.then(res => {
this.$state.go('item.card.tags', {id: res.data.id});
});
}
preview(item) {
this.itemSelected = item;
this.$.preview.show();
}
}
ngModule.vnComponent('vnItemIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,30 +0,0 @@
import './index.js';
describe('Item', () => {
describe('Component vnItemIndex', () => {
let controller;
let $httpBackend;
let $scope;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element('<vn-item-index></vn-item-index>');
controller = $componentController('vnItemIndex', {$element, $scope});
}));
describe('onCloneAccept()', () => {
it('should perform a post query and then call go() then update itemSelected in the controller', () => {
jest.spyOn(controller.$state, 'go');
$httpBackend.expectRoute('POST', `Items/:id/clone`).respond({id: 99});
controller.onCloneAccept(1);
$httpBackend.flush();
expect(controller.$state.go).toHaveBeenCalledWith('item.card.tags', {id: 99});
});
});
});
});

View File

@ -1,2 +0,0 @@
picture: Foto
Buy requests: Peticiones de compra

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 24" style="enable-background:new 0 0 30 24;" xml:space="preserve" width="24px" heigth="24px">
<g>
<path d="M27,0H3C1.4,0,0,1.3,0,3v18c0,1.7,1.4,3,3,3h24c1.6,0,3-1.3,3-3V3C30,1.3,28.6,0,27,0z M27.9,19.8c0,1.4-1.2,2.6-2.6,2.6
H4.6c-1.4,0-2.6-1.2-2.6-2.6V4.2c0-1.4,1.2-2.6,2.6-2.6h20.7c1.4,0,2.6,1.2,2.6,2.6V19.8z"/>
<path d="M24.6,2.9H5.4c-1,0-1.9,0.8-1.9,1.9v14.4c0,1,0.8,1.9,1.9,1.9h19.1c1,0,1.9-0.8,1.9-1.9V4.8C26.4,3.8,25.6,2.9,24.6,2.9z"
/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 750 B

View File

@ -1,34 +0,0 @@
@import "variables";
vn-item-product {
display: block;
.id {
background-color: $color-main;
color: $color-font-dark;
margin-bottom: 0;
}
.image {
height: 112px;
width: 112px;
& > img {
max-height: 100%;
max-width: 100%;
border-radius: 3px;
}
}
vn-label-value:first-of-type section{
margin-top: 9px;
}
}
vn-item-index {
table {
img {
border-radius: 50%;
width: 50px;
height: 50px;
}
}
}

View File

@ -1,118 +0,0 @@
<vn-crud-model
vn-id="model"
url="ItemShelvingPlacementSupplyStocks"
link="{itemFk: $ctrl.$params.id}"
data="$ctrl.itemShelvingPlacementSupplyStocks"
auto-load="true">
</vn-crud-model>
<vn-card>
<smart-table
model="model"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-actions>
<div>
<div class="totalBox" style="text-align: center;">
<h6 translate>Total</h6>
<vn-label-value
label="Total labels"
value="{{$ctrl.labelTotal.toFixed(2)}}">
</vn-label-value>
</div>
</div>
<div class="vn-pa-md">
<vn-button
disabled="!$ctrl.checked.length"
ng-click="removeConfirm.show()"
icon="delete"
vn-tooltip="Remove selected lines"
vn-acl="replenisherBos">
</vn-button>
</div>
</slot-actions>
<slot-table>
<table>
<thead>
<tr>
<th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</th>
<th field="created">
<span translate>Created</span>
</th>
<th shrink field="itemFk">
<span translate>Item</span>
</th>
<th
field="longName">
<span translate>Concept</span>
</th>
<th
field="parking">
<span translate>Parking</span>
</th>
<th field="shelving">
<span translate>Shelving</span>
</th>
<th
field="label">
<span translate>Etiqueta</span>
</th>
<th
field="packing"
shrink>
<span translate>Packing</span>
</th>
</tr>
</thead>
<tbody>
<tr
ng-repeat="itemShelvingPlacementSupplyStock in $ctrl.itemShelvingPlacementSupplyStocks"
vn-repeat-last on-last="$ctrl.calculateTotals()">
<td shrink>
<vn-check
ng-model="itemShelvingPlacementSupplyStock.checked"
vn-click-stop>
</vn-check>
</td>
<td shrink-date>{{::itemShelvingPlacementSupplyStock.created | date: 'dd/MM/yyyy'}}</td>
<td>
{{::itemShelvingPlacementSupplyStock.itemFk}}
</td>
<td expand title="{{::itemShelvingPlacementSupplyStock.longName}}">
<span
vn-click-stop="itemDescriptor.show($event, itemShelvingPlacementSupplyStock.itemFk)"
class="link">
{{itemShelvingPlacementSupplyStock.longName}}
</span>
</td>
<td>
{{::itemShelvingPlacementSupplyStock.parking}}
</td>
<td>
{{::itemShelvingPlacementSupplyStock.shelving}}
</td>
<td>
{{(itemShelvingPlacementSupplyStock.stock / itemShelvingPlacementSupplyStock.packing).toFixed(2)}}
</td>
<td>
{{::itemShelvingPlacementSupplyStock.packing}}
</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<vn-item-descriptor-popover
vn-id="item-descriptor">
</vn-item-descriptor-popover>
<vn-confirm
vn-id="removeConfirm"
message="Selected lines will be deleted"
question="Are you sure you want to continue?"
on-accept="$ctrl.onRemove()">
</vn-confirm>

View File

@ -1,89 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.smartTableOptions = {
activeButtons: {
search: true
},
columns: [
{
field: 'parking',
autocomplete: {
url: 'Parkings',
showField: 'code',
valueField: 'code'
}
},
{
field: 'shelving',
autocomplete: {
url: 'Shelvings',
showField: 'code',
valueField: 'code'
}
},
{
field: 'created',
searchable: false
},
{
field: 'itemFk',
searchable: false
},
{
field: 'longName',
searchable: false
}
]
};
}
get checked() {
const itemShelvings = this.$.model.data || [];
const checkedLines = [];
for (let itemShelving of itemShelvings) {
if (itemShelving.checked)
checkedLines.push(itemShelving.itemShelvingFk);
}
return checkedLines;
}
calculateTotals() {
this.labelTotal = 0;
const itemShelvings = this.$.model.data || [];
itemShelvings.forEach(itemShelving => {
const label = itemShelving.stock / itemShelving.packing;
this.labelTotal += label;
});
}
onRemove() {
const params = {itemShelvingIds: this.checked};
const query = `ItemShelvings/deleteItemShelvings`;
this.$http.post(query, params)
.then(() => {
this.vnApp.showSuccess(this.$t('ItemShelvings removed'));
this.$.model.refresh();
});
}
exprBuilder(param, value) {
switch (param) {
case 'parking':
case 'shelving':
case 'label':
case 'packing':
return {[param]: value};
}
}
}
ngModule.vnComponent('vnItemShelving', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,81 +0,0 @@
import './index';
import crudModel from 'core/mocks/crud-model';
describe('item shelving', () => {
describe('Component vnItemShelving', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-item-shelving></vn-item-shelving>');
controller = $componentController('vnItemShelving', {$element});
controller.$.model = crudModel;
controller.$.model.data = [
{itemShelvingFk: 1, packing: 10, stock: 1},
{itemShelvingFk: 2, packing: 12, stock: 5},
{itemShelvingFk: 4, packing: 20, stock: 10}
];
const modelData = controller.$.model.data;
modelData[0].checked = true;
modelData[1].checked = true;
}));
describe('checked() getter', () => {
it('should return a the selected rows', () => {
const result = controller.checked;
expect(result).toEqual(expect.arrayContaining([1, 2]));
});
});
describe('calculateTotals()', () => {
it('should calculate the total of labels', () => {
controller.calculateTotals();
expect(controller.labelTotal).toEqual(1.0166666666666666);
});
});
describe('onRemove()', () => {
it('shoud remove the selected lines', () => {
jest.spyOn(controller.$.model, 'refresh');
const expectedParams = {itemShelvingIds: [1, 2]};
$httpBackend.expectPOST('ItemShelvings/deleteItemShelvings', expectedParams).respond(200);
controller.onRemove();
$httpBackend.flush();
expect(controller.$.model.refresh).toHaveBeenCalled();
});
});
describe('exprBuilder()', () => {
it('should search by parking', () => {
const expr = controller.exprBuilder('parking', '700-01');
expect(expr).toEqual({'parking': '700-01'});
});
it('should search by shelving', () => {
const expr = controller.exprBuilder('shelving', 'AAA');
expect(expr).toEqual({'shelving': 'AAA'});
});
it('should search by label', () => {
const expr = controller.exprBuilder('label', 0.17);
expect(expr).toEqual({'label': 0.17});
});
it('should search by packing', () => {
const expr = controller.exprBuilder('packing', 10);
expect(expr).toEqual({'packing': 10});
});
});
});
});

View File

@ -1,5 +0,0 @@
Shelving: Matrícula
Remove selected lines: Eliminar líneas seleccionadas
Selected lines will be deleted: Las líneas seleccionadas serán eliminadas
ItemShelvings removed: Carros eliminados
Total labels: Total etiquetas

View File

@ -1,62 +0,0 @@
<vn-watcher
vn-id="watcher"
url="ItemTypes"
data="$ctrl.itemType"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="Code"
ng-model="$ctrl.itemType.code"
rule
vn-focus>
</vn-textfield>
<vn-textfield
label="Name"
ng-model="$ctrl.itemType.name"
rule>
</vn-textfield>
<vn-autocomplete
label="Worker"
ng-model="$ctrl.itemType.workerFk"
url="Workers/search"
show-field="nickname"
value-field="id"
rule>
</vn-autocomplete>
<vn-autocomplete
label="Category"
ng-model="$ctrl.itemType.categoryFk"
url="ItemCategories"
show-field="name"
value-field="id"
rule>
</vn-autocomplete>
<vn-autocomplete
label="Temperature"
ng-model="$ctrl.itemType.temperatureFk"
url="Temperatures"
show-field="name"
value-field="code"
rule>
</vn-autocomplete>
</vn-vertical>
</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,12 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {}
ngModule.component('vnItemTypeBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
itemType: '<'
}
});

View File

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

View File

@ -1,23 +0,0 @@
import ngModule from '../../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
const filter = {
include: [
{relation: 'worker'},
{relation: 'category'},
{relation: 'itemPackingType'},
{relation: 'temperature'}
]
};
this.$http.get(`ItemTypes/${this.$params.id}`, {filter})
.then(res => this.itemType = res.data);
}
}
ngModule.vnComponent('vnItemTypeCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,27 +0,0 @@
import './index';
describe('component vnItemTypeCard', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnItemTypeCard', {$element: null});
}));
describe('reload()', () => {
it('should reload the controller data', () => {
controller.$params.id = 1;
const itemType = {id: 1};
$httpBackend.expectGET('ItemTypes/1').respond(itemType);
controller.reload();
$httpBackend.flush();
expect(controller.itemType).toEqual(itemType);
});
});
});

Some files were not shown because too many files have changed in this diff Show More