fix: refs #7283 remove
gitea/salix/pipeline/pr-dev This commit looks good Details

This commit is contained in:
Carlos Satorres 2024-09-02 13:36:03 +02:00
parent 8fee3dd706
commit 155597bf85
115 changed files with 5 additions and 6892 deletions

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,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"
show-field="firstName"
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);
});
});
});

View File

@ -1,62 +0,0 @@
<vn-watcher
vn-id="watcher"
url="ItemTypes"
data="$ctrl.itemType"
insert-mode="true"
form="form">
</vn-watcher>
<form
name="form"
vn-http-submit="$ctrl.onSubmit()"
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"
show-field="firstName"
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="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="item.itemType">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,15 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onSubmit() {
return this.$.watcher.submit().then(res =>
this.$state.go('item.itemType.card.basicData', {id: res.data.id})
);
}
}
ngModule.component('vnItemTypeCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,34 +0,0 @@
import './index';
describe('component vnItemTypeCreate', () => {
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: '1234'}});
}
};
}
};
const $element = angular.element('<vn-item-type-create></vn-item-type-create>');
controller = $componentController('vnItemTypeCreate', {$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.itemType.card.basicData', {id: '1234'});
});
});
});

View File

@ -1,25 +0,0 @@
<vn-descriptor-content
module="item"
base-state="item.itemType"
description="$ctrl.itemType.code">
<slot-body>
<div class="attributes">
<vn-label-value
label="Code"
value="{{$ctrl.itemType.code}}">
</vn-label-value>
<vn-label-value
label="Name"
value="{{$ctrl.itemType.name}}">
</vn-label-value>
<vn-label-value
label="Worker"
value="{{$ctrl.itemType.worker.firstName}} {{$ctrl.itemType.worker.lastName}}">
</vn-label-value>
<vn-label-value
label="Category"
value="{{$ctrl.itemType.category.name}}">
</vn-label-value>
</div>
</slot-body>
</vn-descriptor-content>

View File

@ -1,20 +0,0 @@
import ngModule from '../../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get itemType() {
return this.entity;
}
set itemType(value) {
this.entity = value;
}
}
ngModule.component('vnItemTypeDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
itemType: '<'
}
});

View File

@ -1,8 +0,0 @@
import './main';
import './index/';
import './summary';
import './card';
import './descriptor';
import './create';
import './basic-data';
import './search-panel';

View File

@ -1,43 +0,0 @@
<vn-crud-model
vn-id="model"
auto-load="true"
url="ItemTypes"
data="itemTypes"
order="name">
</vn-crud-model>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<div class="vn-list separated">
<a
ng-repeat="itemType in itemTypes track by itemType.id"
ui-sref="item.itemType.card.summary(::{id: itemType.id})"
ui-sref-opts="{inherit: false}"
translate-attr="{title: 'View itemType'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::itemType.code}}</h6>
<div>{{::itemType.name}}</div>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
vn-click-stop="$ctrl.preview(itemType)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-item-section>
</a>
</div>
</vn-card>
</vn-data-viewer>
<vn-popup vn-id="summary">
<vn-item-type-summary item-type="$ctrl.selectedItemType"></vn-item-type-summary>
</vn-popup>
<a ui-sref="item.itemType.create"
vn-tooltip="New itemType"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -1,14 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
preview(itemType) {
this.selectedItemType = itemType;
this.$.summary.show();
}
}
ngModule.component('vnItemTypeIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,34 +0,0 @@
import './index';
describe('Item', () => {
describe('Component vnItemTypeIndex', () => {
let controller;
let $window;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, _$window_) => {
$window = _$window_;
const $element = angular.element('<vn-item-type-summary></vn-item-type-summary>');
controller = $componentController('vnItemTypeIndex', {$element});
}));
describe('preview()', () => {
it('should show the dialog summary', () => {
controller.$.summary = {show: () => {}};
jest.spyOn(controller.$.summary, 'show');
const itemType = {id: 1};
const event = new MouseEvent('click', {
view: $window,
bubbles: true,
cancelable: true
});
controller.preview(event, itemType);
expect(controller.$.summary.show).toHaveBeenCalledWith();
});
});
});
});

View File

@ -1,2 +0,0 @@
Item Type: Familia
New itemType: Nueva familia

View File

@ -1,18 +0,0 @@
<vn-crud-model
vn-id="model"
url="ItemTypes"
filter="::$ctrl.filter"
limit="20">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
info="Search itemType by id, name or code"
panel="vn-item-type-search-panel"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)"
base-state="item.itemType">
</vn-searchbar>
</vn-portal>
<ui-view>
<vn-item-type-index></vn-item-type-index>
</ui-view>

View File

@ -1,24 +0,0 @@
import ngModule from '../../module';
import ModuleMain from 'salix/components/module-main';
export default class ItemType extends ModuleMain {
exprBuilder(param, value) {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {id: value}
: {or: [
{name: {like: `%${value}%`}},
{code: {like: `%${value}%`}}
]};
case 'name':
case 'code':
return {[param]: {like: `%${value}%`}};
}
}
}
ngModule.vnComponent('vnItemType', {
controller: ItemType,
template: require('./index.html')
});

View File

@ -1,31 +0,0 @@
import './index';
describe('Item', () => {
describe('Component vnItemType', () => {
let controller;
beforeEach(ngModule('item'));
beforeEach(inject($componentController => {
const $element = angular.element('<vn-item-type></vn-item-type>');
controller = $componentController('vnItemType', {$element});
}));
describe('exprBuilder()', () => {
it('should return a filter based on a search by id', () => {
const filter = controller.exprBuilder('search', '123');
expect(filter).toEqual({id: '123'});
});
it('should return a filter based on a search by name or code', () => {
const filter = controller.exprBuilder('search', 'Alstroemeria');
expect(filter).toEqual({or: [
{name: {like: '%Alstroemeria%'}},
{code: {like: '%Alstroemeria%'}},
]});
});
});
});
});

View File

@ -1 +0,0 @@
Search itemType by id, name or code: Buscar familia por id, nombre o código

View File

@ -1,22 +0,0 @@
<div class="search-panel">
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield
vn-one
label="Name"
ng-model="filter.name"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Code"
ng-model="filter.code">
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

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

View File

@ -1,50 +0,0 @@
<vn-card class="summary">
<h5>
<span>{{summary.id}} - {{summary.name}} - {{summary.worker.firstName}} {{summary.worker.lastName}}</span>
</h5>
<vn-horizontal class="vn-pa-md">
<vn-one>
<h4 translate>Basic data</h4>
<vn-label-value
label="Id"
value="{{summary.id}}">
</vn-label-value>
<vn-label-value
label="Code"
value="{{summary.code}}">
</vn-label-value>
<vn-label-value
label="Name"
value="{{summary.name}}">
</vn-label-value>
<vn-label-value
label="Worker"
value="{{summary.worker.firstName}} {{summary.worker.lastName}}">
</vn-label-value>
<vn-label-value
label="Category"
value="{{summary.category.name}}">
</vn-label-value>
<vn-label-value
label="Temperature"
value="{{summary.temperature.name}}">
</vn-label-value>
<vn-label-value
label="Life"
value="{{summary.life}}">
</vn-label-value>
<vn-label-value
label="Promo"
value="{{summary.promo}}">
</vn-label-value>
<vn-label-value
label="Item packing type"
value="{{summary.itemPackingType.description}}">
</vn-label-value>
<vn-label-value
label="Is unconventional size"
value="{{summary.isUnconventionalSize}}">
</vn-label-value>
</vn-one>
</vn-horizontal>
</vn-card>

View File

@ -1,33 +0,0 @@
import ngModule from '../../module';
import Component from 'core/lib/component';
class Controller extends Component {
set itemType(value) {
this._itemType = value;
this.$.summary = null;
if (!value) return;
const filter = {
include: [
{relation: 'worker'},
{relation: 'category'},
{relation: 'itemPackingType'},
{relation: 'temperature'}
]
};
this.$http.get(`ItemTypes/${value.id}`, {filter})
.then(res => this.$.summary = res.data);
}
get itemType() {
return this._itemType;
}
}
ngModule.component('vnItemTypeSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
itemType: '<'
}
});

View File

@ -1,4 +0,0 @@
Life: Vida
Promo: Promoción
Item packing type: Tipo de embalaje
Is unconventional size: Es de tamaño poco convencional

View File

@ -1,138 +0,0 @@
<vn-crud-model
vn-id="model"
url="Items/lastEntriesFilter"
filter="::$ctrl.filter"
data="entries"
auto-load="true"
order="landed DESC, buyFk DESC"
limit="20">
</vn-crud-model>
<vn-card class="vn-w-md vn-pa-md">
<vn-horizontal>
<vn-date-picker class="vn-pa-xs"
vn-one
label="Since"
vn-one
label="Since"
ng-model="$ctrl.dateFrom">
</vn-date-picker>
<vn-date-picker class="vn-pa-xs"
vn-one
label="To"
ng-model="$ctrl.dateTo">
</vn-date-picker>
</vn-horizontal>
</vn-card>
<vn-data-viewer
model="model"
class="vn-mb-xl vn-w-xl vn-pa-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th vn-tooltip="Ignored" center>Ig</vn-th>
<vn-th field="warehouseFk">Warehouse</vn-th>
<vn-th field="landed">Landed</vn-th>
<vn-th number>Entry</vn-th>
<vn-th vn-tooltip="Grouping / Packing" expand>PVP</vn-th>
<vn-th number class="expendable">Label</vn-th>
<vn-th number>Packing</vn-th>
<vn-th number>Grouping</vn-th>
<vn-th number class="expendable">Stems</vn-th>
<vn-th field="quantity" number>Quantity</vn-th>
<vn-th number class="expendable">Cost</vn-th>
<vn-th number>Kg.</vn-th>
<vn-th field="packagingFk" number>Cube</vn-th>
<vn-th field="supplierFk" class="expendable">Provider</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="entry in entries">
<vn-td center>
<vn-check
ng-model="entry.isIgnored"
disabled="true">
</vn-check>
</vn-td>
<vn-td title="{{::entry.warehouse| dashIfEmpty}}">{{::entry.warehouse| dashIfEmpty}}</vn-td>
<vn-td shrink-datetime>{{::entry.landed | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-td shrink>
<span
vn-click-stop="entryDescriptor.show($event, entry.entryFk)"
class="link">
{{::entry.entryFk | dashIfEmpty}}
</span>
</vn-td>
<vn-td title="Grouping / Packing">
{{::entry.price2 | currency: 'EUR':2 | dashIfEmpty}} / {{::entry.price3 | currency: 'EUR':2 | dashIfEmpty}}
</vn-td>
<vn-td number class="expendable">{{entry.stickers | dashIfEmpty}}</vn-td>
<vn-td number>
<vn-chip translate-attr="{title: 'Packing'}" ng-class="{'message': entry.groupingMode == 'packing'}">
<span>{{::entry.packing | dashIfEmpty}}</span>
</vn-chip>
</vn-td>
<vn-td number>
<vn-chip translate-attr="{title: 'Grouping'}" ng-class="{'message': entry.groupingMode == 'grouping'}">
<span>{{::entry.grouping | dashIfEmpty}}</span>
</vn-chip>
</vn-td>
<vn-td number class="expendable">{{::entry.stems | dashIfEmpty}}</vn-td>
<vn-td number>{{::entry.quantity}}</vn-td>
<vn-td number
class="expendable">
<span
vn-tooltip="
{{::$ctrl.$t('Cost')}}: {{::entry.buyingValue | currency: 'EUR':3 | dashIfEmpty}}<br>
{{::$ctrl.$t('Package')}}: {{::entry.packageValue | currency: 'EUR':3 | dashIfEmpty}}<br>
{{::$ctrl.$t('Freight')}}: {{::entry.freightValue | currency: 'EUR':3 | dashIfEmpty}}<br>
{{::$ctrl.$t('Comission')}}: {{::entry.comissionValue | currency: 'EUR':3 | dashIfEmpty}}">
{{::entry.cost | currency: 'EUR':3 | dashIfEmpty}}
</span>
</vn-td>
<vn-td number>{{::entry.weight | dashIfEmpty}}</vn-td>
<vn-td number>{{::entry.packagingFk | dashIfEmpty}}</vn-td>
<vn-td class="expendable" title="{{::entry.supplier | dashIfEmpty}}">{{::entry.supplier | dashIfEmpty}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-vertical>
</vn-card>
</vn-data-viewer>
<vn-entry-descriptor-popover
vn-id="entryDescriptor">
</vn-entry-descriptor-popover>
<vn-contextmenu vn-id="contextmenu" targets="['vn-data-viewer']" model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-menu>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.filterBySelection()">
Filter by selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.excludeSelection()">
Exclude selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.removeFilter()">
Remove filter
</vn-item>
<vn-item translate
ng-click="contextmenu.removeAllFilters()">
Remove all filters
</vn-item>
<vn-item translate
ng-if="contextmenu.isActionAllowed()"
ng-click="contextmenu.copyValue()">
Copy value
</vn-item>
</slot-menu>
</vn-contextmenu>

View File

@ -1,105 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
const from = Date.vnNew();
from.setDate(from.getDate() - 75);
from.setHours(0, 0, 0, 0);
const to = Date.vnNew();
to.setDate(to.getDate() + 10);
to.setHours(23, 59, 59, 59);
this.filter = {
where: {
itemFk: this.$params.id,
landed: {
between: [from, to]
}
}
};
this._dateFrom = from;
this._dateTo = to;
}
set dateFrom(value) {
this._dateFrom = value;
if (!value) return;
const from = new Date(value);
from.setHours(0, 0, 0, 0);
const to = new Date(this._dateTo);
to.setHours(23, 59, 59, 59);
this.filter.where.landed = {
between: [from, to]
};
this.$.model.refresh();
}
set dateTo(value) {
this._dateTo = value;
if (!value) return;
const from = new Date(this._dateFrom);
from.setHours(0, 0, 0, 0);
const to = new Date(value);
to.setHours(23, 59, 59, 59);
this.filter.where.landed = {
between: [from, to]
};
this.$.model.refresh();
}
get dateFrom() {
return this._dateFrom;
}
get dateTo() {
return this._dateTo;
}
exprBuilder(param, value) {
switch (param) {
case 'id':
case 'quantity':
case 'packagingFk':
return {[`b.${param}`]: value};
case 'supplierFk':
return {[`s.id`]: value};
case 'warehouseFk':
return {'tr.warehouseInFk': value};
case 'landed':
return {'tr.landed': {
between: this.dateRange(value)}
};
}
}
dateRange(value) {
const minHour = new Date(value);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(value);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnItemLastEntries', {
template: require('./index.html'),
controller: Controller,
bindings: {
item: '<'
}
});

View File

@ -1,15 +0,0 @@
Since: Desde
Landed: F. entrega
Stems: Tallos
Quantity: Cantidad
Cost: Coste
Label: Etiquetas
Entry: Entrada
Ignored: Ignorado
Provider: Proveedor
Cube: Cubo
Price Per Unit: Precio Por Unidad
Price Per Package: Precio Por Paquete
Freight: Porte
Package: Embalaje
Comission: Comision

View File

@ -1,23 +0,0 @@
<vn-crud-model
vn-id="model"
url="Items/filter"
user-params="::$ctrl.filterParams"
limit="12"
order="isActive DESC, name, id"
data="items"
auto-load="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
panel="vn-item-search-panel"
info="Search items by id, name or barcode"
suggested-filter="$ctrl.filterParams"
filter="$ctrl.filterParams"
model="model">
</vn-searchbar>
</vn-portal>
<vn-portal slot="menu">
<vn-left-menu></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -3,11 +3,10 @@ import ModuleMain from 'salix/components/module-main';
export default class Items extends ModuleMain {
constructor($element, $) {
super($element, $);
this.filterParams = {
isActive: true,
isFloramondo: false
};
}
async $onInit() {
this.$state.go('home');
window.location.href = await this.vnApp.getUrl(`claim/`);
}
}

View File

@ -1,104 +0,0 @@
<div class="search-panel">
<vn-crud-model
auto-load="true"
url="Warehouses"
data="warehouses">
</vn-crud-model>
<form id="manifold-form" ng-submit="$ctrl.onSearch()">
<vn-horizontal class="vn-px-lg vn-pt-lg">
<vn-textfield
vn-one
label="General search"
ng-model="filter.search"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-textfield
vn-one
label="Ticket id"
ng-model="filter.ticketFk">
</vn-textfield>
<vn-autocomplete
vn-one
ng-model="filter.attenderFk"
url="TicketRequests/getItemTypeWorker"
search-function="{firstName: $search}"
show-field="nickname"
value-field="id"
label="Atender">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-textfield
vn-one
label="Client id"
ng-model="filter.clientFk">
</vn-textfield>
<vn-autocomplete
vn-one
label="Warehouse"
ng-model="filter.warehouseFk"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-worker-autocomplete
vn-one
ng-model="filter.requesterFk"
departments="['VT']"
label="Salesperson">
</vn-worker-autocomplete>
</vn-horizontal>
<section class="vn-px-md">
<vn-horizontal class="manifold-panel vn-pa-md">
<vn-date-picker
vn-one
label="From"
ng-model="filter.from"
on-change="$ctrl.from = value">
</vn-date-picker>
<vn-date-picker
vn-one
label="To"
ng-model="filter.to"
on-change="$ctrl.to = value">
</vn-date-picker>
<vn-none class="or vn-px-md" translate>Or</vn-none>
<vn-input-number
vn-one
min="0"
step="1"
label="Days onward"
ng-model="filter.scopeDays"
on-change="$ctrl.scopeDays = value"
display-controls="true">
</vn-input-number>
<vn-icon color-marginal
icon="info"
vn-tooltip="Cannot choose a range of dates and days onward at the same time">
</vn-icon>
</vn-horizontal>
</section>
<vn-horizontal class="vn-px-lg">
<vn-check vn-one
triple-state="true"
label="For me"
ng-model="filter.mine">
</vn-check>
<vn-autocomplete
vn-one
ng-model="filter.state"
data="$ctrl.states"
value-field="code"
label="State">
<tpl-item>{{name}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-px-lg vn-pb-lg vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -1,48 +0,0 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
class Controller extends SearchPanel {
constructor($element, $) {
super($element, $);
this.states = [
{code: 'pending', name: this.$t('Pending')},
{code: 'accepted', name: this.$t('Accepted')},
{code: 'denied', name: this.$t('Denied')}
];
}
get from() {
return this._from;
}
set from(value) {
this._from = value;
this.filter.scopeDays = null;
}
get to() {
return this._to;
}
set to(value) {
this._to = value;
this.filter.scopeDays = null;
}
get scopeDays() {
return this._scopeDays;
}
set scopeDays(value) {
this._scopeDays = value;
this.filter.from = null;
this.filter.to = null;
}
}
ngModule.vnComponent('vnRequestSearchPanel', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,48 +0,0 @@
import './index';
describe(' Component vnRequestSearchPanel', () => {
let controller;
beforeEach(ngModule('item'));
beforeEach(inject($componentController => {
controller = $componentController('vnRequestSearchPanel', {$element: null});
controller.$t = () => {};
controller.filter = {};
}));
describe('from() setter', () => {
it('should clear the scope days when setting the from property', () => {
controller.filter.scopeDays = 1;
controller.from = Date.vnNew();
expect(controller.filter.scopeDays).toBeNull();
expect(controller.from).toBeDefined();
});
});
describe('to() setter', () => {
it('should clear the scope days when setting the to property', () => {
controller.filter.scopeDays = 1;
controller.to = Date.vnNew();
expect(controller.filter.scopeDays).toBeNull();
expect(controller.to).toBeDefined();
});
});
describe('scopeDays() setter', () => {
it('should clear the date range when setting the scopeDays property', () => {
controller.filter.from = Date.vnNew();
controller.filter.to = Date.vnNew();
controller.scopeDays = 1;
expect(controller.filter.from).toBeNull();
expect(controller.filter.to).toBeNull();
expect(controller.scopeDays).toBeDefined();
});
});
});

View File

@ -1,4 +0,0 @@
Ink: Tinta
Origin: Origen
Producer: Productor
For me: Para mi

View File

@ -1,172 +0,0 @@
<vn-crud-model
vn-id="model"
url="TicketRequests/filter"
user-params="::$ctrl.filterParams"
limit="20"
data="requests"
order="shippedDate ASC, isOk ASC"
auto-load="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
panel="vn-request-search-panel"
info="Search request by id or alias"
suggested-filter="$ctrl.filterParams"
filter="$ctrl.filterParams"
fetch-params="$ctrl.fetchParams($params)"
model="model"
auto-state="false">
</vn-searchbar>
</vn-portal>
<vn-data-viewer model="model">
<vn-card>
<vn-table model="model" auto-load="false">
<vn-thead>
<vn-tr>
<vn-th field="ticketFk" number>Ticket ID</vn-th>
<vn-th field="shipped" expand>Shipped</vn-th>
<vn-th field="description" filter-enabled="false" expand>Description</vn-th>
<vn-th field="requesterFk">Requester</vn-th>
<vn-th field="quantity" number editable>Requested</vn-th>
<vn-th field="price" number>Price</vn-th>
<vn-th field="attenderName">Atender</vn-th>
<vn-th>Item</vn-th>
<vn-th field="saleQuantity">Achieved</vn-th>
<vn-th field="description" filter-enabled="false">Concept</vn-th>
<vn-th field="isOk">State</vn-th>
<vn-th></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="request in requests">
<vn-td number>
<span class="link"
ng-click="ticketDescriptor.show($event, request.ticketFk)">
{{request.ticketFk}}
</span>
</vn-td>
<vn-td expand>
<span title="{{::request.shipped | date: 'dd/MM/yyyy'}}"
class="chip {{$ctrl.compareDate(request.shipped)}}">
{{::request.shipped | date: 'dd/MM/yyyy'}}
</span>
</vn-td>
<vn-td title="{{::request.description}}" expand>{{::request.description}}</vn-td>
<vn-td number>
<span
class="link"
ng-click="workerDescriptor.show($event, request.requesterFk)">
{{::request.requesterName}}
</span>
</vn-td>
<vn-td number>{{::request.quantity}}</vn-td>
<vn-td number>{{::request.price | currency: 'EUR':2}}</vn-td>
<vn-td>
<span
class="link"
ng-click="workerDescriptor.show($event, request.attenderFk)">
{{::request.attenderName}}
</span>
</vn-td>
<vn-td-editable disabled="request.isOk != null" number>
<text>{{request.itemFk}}</text>
<field>
<vn-input-number class="dense" vn-focus
ng-model="request.itemFk">
</vn-input-number>
</field>
</vn-td-editable>
<vn-td-editable disabled="!request.itemFk || request.isOk != null" number>
<text number>{{request.saleQuantity}}</text>
<field>
<vn-input-number class="dense" vn-focus
ng-model="request.saleQuantity"
on-change="$ctrl.changeQuantity(request)">
</vn-input-number>
</field>
</vn-td-editable>
<vn-td expand>
<span
class="link"
ng-click="itemDescriptor.show($event, request.itemFk)"
title="{{request.itemDescription}}">
{{request.itemDescription}}
</span>
</vn-td>
<vn-td translate>{{$ctrl.getState(request.isOk)}}</vn-td>
<vn-td>
<vn-icon
ng-if="request.response.length"
translate-attr="{title: request.response}"
icon="insert_drive_file"
class="bright">
</vn-icon>
<vn-icon-button
ng-if="request.isOk == null"
icon="thumb_down"
ng-click="denyDialog.show(request)"
translate-attr="{title: 'Discard'}"
tabindex="-1">
</vn-icon-button>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-worker-descriptor-popover
vn-id="worker-descriptor">
</vn-worker-descriptor-popover>
<vn-ticket-descriptor-popover
vn-id="ticket-descriptor">
</vn-ticket-descriptor-popover>
<vn-item-descriptor-popover
vn-id="item-descriptor"
warehouse-fk="$ctrl.vnConfig.warehouseFk">
</vn-item-descriptor-popover>
<vn-dialog
vn-id="denyDialog"
on-accept="$ctrl.onDenyAccept($data)"
on-close="$ctrl.denyObservation = null"
message="Specify the reasons to deny this request">
<tpl-body>
<vn-textarea
ng-model="$ctrl.denyObservation"
vn-focus>
</vn-textarea>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>
<vn-contextmenu vn-id="contextmenu" targets="['vn-data-viewer']" model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-menu>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.filterBySelection()">
Filter by selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.excludeSelection()">
Exclude selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.removeFilter()">
Remove filter
</vn-item>
<vn-item translate
ng-click="contextmenu.removeAllFilters()">
Remove all filters
</vn-item>
<vn-item translate
ng-if="contextmenu.isActionAllowed()"
ng-click="contextmenu.copyValue()">
Copy value
</vn-item>
</slot-menu>
</vn-contextmenu>

View File

@ -1,143 +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, $);
if (!this.$state.q) {
const today = Date.vnNew();
today.setHours(0, 0, 0, 0);
const nextWeek = Date.vnNew();
nextWeek.setHours(23, 59, 59, 59);
nextWeek.setDate(nextWeek.getDate() + 7);
this.filterParams = {
from: today,
to: nextWeek,
state: 'pending'
};
}
}
fetchParams($params) {
if (!Object.entries($params).length)
$params.scopeDays = 1;
if (typeof $params.scopeDays === 'number') {
const from = Date.vnNew();
from.setHours(0, 0, 0, 0);
const to = new Date(from.getTime());
to.setDate(to.getDate() + $params.scopeDays);
to.setHours(23, 59, 59, 999);
Object.assign($params, {from, to});
}
return $params;
}
getState(isOk) {
if (isOk === null)
return 'Pending';
else if (isOk)
return 'Accepted';
else
return 'Denied';
}
confirmRequest(request) {
if (request.itemFk && request.saleQuantity) {
let params = {
itemFk: request.itemFk,
quantity: request.saleQuantity
};
let query = `TicketRequests/${request.id}/confirm`;
this.$http.post(query, params).then(res => {
request.itemDescription = res.data.concept;
request.isOk = true;
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
}
changeQuantity(request) {
if (request.saleFk) {
let params = {
quantity: request.saleQuantity
};
let endpoint = `Sales/${request.saleFk}`;
this.$http.patch(endpoint, params)
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
.then(() => this.confirmRequest(request));
} else
this.confirmRequest(request);
}
compareDate(date) {
let today = Date.vnNew();
today.setHours(0, 0, 0, 0);
let timeTicket = new Date(date);
timeTicket.setHours(0, 0, 0, 0);
let comparation = today - timeTicket;
if (comparation == 0)
return 'warning';
if (comparation < 0)
return 'success';
}
onDenyAccept(request) {
let params = {
observation: this.denyObservation
};
return this.$http.post(`TicketRequests/${request.id}/deny`, params)
.then(res => {
const newRequest = res.data;
request.isOk = newRequest.isOk;
request.attenderFk = newRequest.attenderFk;
request.response = newRequest.response;
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
exprBuilder(param, value) {
switch (param) {
case 'ticketFk':
case 'quantity':
case 'price':
case 'isOk':
return {[`tr.${param}`]: value};
case 'attenderName':
return {[`ua.name`]: value};
case 'shipped':
return {'t.shipped': {
between: this.dateRange(value)}
};
}
}
dateRange(value) {
const minHour = new Date(value);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(value);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
}
}
ngModule.vnComponent('vnItemRequest', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,120 +0,0 @@
import './index.js';
describe('Item', () => {
describe('Component vnItemRequest', () => {
let $scope;
let controller;
let $httpBackend;
beforeEach(ngModule('item'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
controller = $componentController('vnItemRequest', {$element: null, $scope});
}));
afterAll(() => {
$scope.$destroy();
$element.remove();
});
describe('getState()', () => {
it(`should return an string depending to the isOK value`, () => {
let isOk = null;
let result = controller.getState(isOk);
expect(result).toEqual('Pending');
isOk = 1;
result = controller.getState(isOk);
expect(result).toEqual('Accepted');
isOk = 0;
result = controller.getState(isOk);
expect(result).toEqual('Denied');
});
});
describe('confirmRequest()', () => {
it(`should do nothing if the request does't have itemFk or saleQuantity`, () => {
let request = {};
jest.spyOn(controller.vnApp, 'showSuccess');
controller.confirmRequest(request);
expect(controller.vnApp.showSuccess).not.toHaveBeenCalled();
});
it('should perform a query and call vnApp.showSuccess() and refresh if the conditions are met', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
const expectedResult = {concept: 'Melee Weapon'};
let request = {itemFk: 1, saleQuantity: 1, id: 1};
$httpBackend.expectPOST(`TicketRequests/${request.id}/confirm`).respond(expectedResult);
controller.confirmRequest(request);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('changeQuantity()', () => {
it(`should call confirmRequest() if there's no sale id in the request`, () => {
let request = {};
jest.spyOn(controller, 'confirmRequest');
controller.changeQuantity(request);
expect(controller.confirmRequest).toHaveBeenCalledWith(jasmine.any(Object));
});
it(`should perform a query and call vnApp.showSuccess() if the conditions are met`, () => {
let request = {saleFk: 1, saleQuantity: 1};
jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expectPATCH(`Sales/${request.saleFk}`).respond();
controller.changeQuantity(request);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('compareDate()', () => {
it(`should return "success" if receives a future date`, () => {
let date = '3019-02-18T11:00:00.000Z';
let result = controller.compareDate(date);
expect(result).toEqual('success');
});
it(`should return "warning" if date is today`, () => {
let date = Date.vnNew();
let result = controller.compareDate(date);
expect(result).toEqual('warning');
});
});
describe('onDenyAccept()', () => {
it(`should deny the request`, () => {
const request = {
id: 1,
response: 'new'
};
$httpBackend.expectPOST(`TicketRequests/${request.id}/deny`)
.respond({response: 'denied'});
controller.onDenyAccept(request);
$httpBackend.flush();
expect(request.response).toBe('denied');
});
});
});
});

View File

@ -1,9 +0,0 @@
Discard: Descartar
Specify the reasons to deny this request: Especifica las razones para descartar la petición
Buy requests: Peticiones de compra
Search request by id or alias: Buscar peticiones por identificador o alias
Requested: Solicitado
Achieved: Conseguido
Pending: Pendiente
Accepted: Aceptada
Denied: Rechazada

View File

@ -1,15 +0,0 @@
@import "variables";
vn-item-request {
vn-dialog[vn-id="denyReason"] {
button.close {
display: none
}
vn-button {
margin: 0 auto
}
vn-textarea {
width: 100%
}
}
}

View File

@ -6,25 +6,7 @@
"dependencies": ["worker", "client", "ticket"],
"menus": {
"main": [
{"state": "item.index", "icon": "icon-item"},
{"state": "item.request", "icon": "icon-buyrequest"},
{"state": "item.waste.index", "icon": "icon-claims"},
{"state": "item.fixedPrice", "icon": "icon-fixedPrice"},
{"state": "item.itemType", "icon": "contact_support"}
],
"card": [
{"state": "item.card.basicData", "icon": "settings"},
{"state": "item.card.tags", "icon": "icon-tags"},
{"state": "item.card.last-entries", "icon": "icon-regentry"},
{"state": "item.card.tax", "icon": "icon-tax"},
{"state": "item.card.botanical", "icon": "local_florist"},
{"state": "item.card.shelving", "icon": "icon-inventory"},
{"state": "item.card.itemBarcode", "icon": "icon-barcode"},
{"state": "item.card.diary", "icon": "icon-transaction"},
{"state": "item.card.log", "icon": "history"}
],
"itemType": [
{"state": "item.itemType.card.basicData", "icon": "settings"}
{"state": "item.index", "icon": "icon-item"}
]
},
"keybindings": [
@ -44,187 +26,11 @@
"component": "vn-item-index",
"description": "Items"
},
{
"url": "/create",
"state": "item.create",
"component": "vn-item-create",
"description": "New item"
},
{
"url": "/:id",
"state": "item.card",
"abstract": true,
"component": "vn-item-card"
},
{
"url" : "/basic-data",
"state": "item.card.basicData",
"component": "vn-item-basic-data",
"description": "Basic data",
"params": {
"item": "$ctrl.item"
},
"acl": ["buyer"]
},
{
"url" : "/tags",
"state": "item.card.tags",
"component": "vn-item-tags",
"description": "Tags",
"params": {
"item-tags": "$ctrl.itemTags"
},
"acl": ["buyer", "replenisher"]
},
{
"url" : "/tax",
"state": "item.card.tax",
"component": "vn-item-tax",
"description": "Tax",
"acl": ["administrative","buyer"]
},
{
"url" : "/botanical",
"state": "item.card.botanical",
"component": "vn-item-botanical",
"description": "Botanical",
"params": {
"item": "$ctrl.item"
},
"acl": ["buyer"]
},
{
"url" : "/shelving",
"state": "item.card.shelving",
"component": "vn-item-shelving",
"description": "Shelvings",
"params": {
"item": "$ctrl.item"
},
"acl": ["employee"]
},
{
"url" : "/barcode",
"state": "item.card.itemBarcode",
"component": "vn-item-barcode",
"description": "Barcodes",
"params": {
"item": "$ctrl.item"
},
"acl": ["buyer","replenisher"]
},
{
"url" : "/summary",
"state": "item.card.summary",
"component": "vn-item-summary",
"description": "Summary",
"params": {
"item": "$ctrl.item"
}
},
{
"url" : "/diary?warehouseFk&lineFk",
"state": "item.card.diary",
"component": "vn-item-diary",
"description": "Diary",
"params": {
"item": "$ctrl.item"
},
"acl": ["employee"]
},
{
"url" : "/last-entries",
"state": "item.card.last-entries",
"component": "vn-item-last-entries",
"description": "Last entries",
"params": {
"item": "$ctrl.item"
},
"acl": ["employee"]
},
{
"url" : "/log",
"state": "item.card.log",
"component": "vn-item-log",
"description": "Log"
},
{
"url" : "/request?q",
"state": "item.request",
"component": "vn-item-request",
"description": "Buy requests",
"params": {
"item": "$ctrl.item"
},
"acl": ["employee"]
},
{
"url": "/waste",
"state": "item.waste",
"component": "ui-view",
"abstract": true
},
{
"url" : "/index",
"state": "item.waste.index",
"component": "vn-item-waste-index",
"description": "Waste breakdown",
"acl": ["buyer"]
},
{
"url" : "/detail?buyer&family",
"state": "item.waste.detail",
"component": "vn-item-waste-detail",
"description": "Waste breakdown by item",
"acl": ["buyer"]
},
{
"url" : "/fixed-price?q",
"state": "item.fixedPrice",
"component": "vn-fixed-price",
"description": "Fixed prices",
"acl": ["buyer"]
},
{
"url" : "/item-type?q",
"state": "item.itemType",
"component": "vn-item-type",
"description": "Item Type",
"acl": ["buyer"]
},
{
"url": "/create",
"state": "item.itemType.create",
"component": "vn-item-type-create",
"description": "New itemType",
"acl": ["buyer"]
},
{
"url": "/:id",
"state": "item.itemType.card",
"component": "vn-item-type-card",
"abstract": true,
"description": "Detail"
},
{
"url": "/summary",
"state": "item.itemType.card.summary",
"component": "vn-item-type-summary",
"description": "Summary",
"params": {
"item-type": "$ctrl.itemType"
},
"acl": ["buyer"]
},
{
"url": "/basic-data",
"state": "item.itemType.card.basicData",
"component": "vn-item-type-basic-data",
"description": "Basic data",
"params": {
"item-type": "$ctrl.itemType"
},
"acl": ["buyer"]
}
]
}

View File

@ -1,181 +0,0 @@
<mg-ajax path="Tags" options="mgIndex as tags"></mg-ajax>
<div class="search-panel">
<form ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield
vn-one
label="General search"
ng-model="filter.search"
info="Search items by id, name or barcode"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
vn-focus
url="ItemCategories"
label="Category"
show-field="name"
value-field="id"
ng-model="filter.categoryFk">
</vn-autocomplete>
<vn-autocomplete vn-one
url="ItemTypes"
label="Type"
where="{categoryFk: filter.categoryFk}"
show-field="name"
value-field="id"
ng-model="filter.typeFk"
fields="['categoryFk']"
include="'category'">
<tpl-item>
<div>{{name}}</div>
<div class="text-caption text-secondary">
{{category.name}}
</div>
</tpl-item>>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
ng-model="filter.buyerFk"
url="TicketRequests/getItemTypeWorker"
search-function="{firstName: $search}"
show-field="nickname"
value-field="id"
label="Buyer">
</vn-autocomplete>
<vn-autocomplete
vn-one
label="Supplier"
ng-model="filter.supplierFk"
url="Suppliers"
fields="['name','nickname']"
search-function="{or: [{nickname: {like: '%'+ $search +'%'}}, {name: {like: '%'+ $search +'%'}}]}"
show-field="name"
value-field="id">
<tpl-item>{{name}}: {{nickname}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-pt-sm">
<vn-one class="text-subtitle1" translate>
Tags
</vn-one>
<vn-icon-button
vn-none
vn-bind="+"
vn-tooltip="Add tag"
icon="add_circle"
ng-click="filter.tags.push({})">
</vn-icon-button>
</vn-horizontal>
<vn-horizontal ng-repeat="itemTag in filter.tags">
<vn-autocomplete
vn-id="tag"
vn-one
ng-model="itemTag.tagFk"
data="tags.model"
show-field="name"
label="Tag"
on-change="itemTag.value = null">
</vn-autocomplete>
<vn-textfield
vn-one
ng-show="tag.selection.isFree || tag.selection.isFree == undefined"
vn-id="text"
label="Value"
ng-model="itemTag.value">
</vn-textfield>
<vn-autocomplete
vn-one
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"
rule>
</vn-autocomplete>
<vn-icon-button
vn-none
vn-tooltip="Remove tag"
icon="delete"
ng-click="filter.tags.splice($index, 1)"
tabindex="-1">
</vn-icon-button>
</vn-horizontal>
<vn-horizontal class="vn-pt-sm">
<vn-one class="text-subtitle1" translate>
More fields
</vn-one>
<vn-icon-button
vn-none
vn-bind="+"
vn-tooltip="Add field"
icon="add_circle"
ng-click="$ctrl.fieldFilters.push({})">
</vn-icon-button>
</vn-horizontal>
<vn-horizontal ng-repeat="fieldFilter in $ctrl.fieldFilters">
<vn-autocomplete
vn-one
label="Field"
ng-model="fieldFilter.name"
data="$ctrl.moreFields"
value-field="name"
show-field="label"
show-filter="false"
translate-fields="['label']"
selection="info"
on-change="fieldFilter.value = null">
</vn-autocomplete>
<vn-one ng-switch="info.type">
<div ng-switch-when="Number">
<vn-input-number
label="Value"
ng-model="fieldFilter.value">
</vn-input-number>
</div>
<div ng-switch-when="Boolean">
<vn-check
label="Value"
ng-model="fieldFilter.value">
</vn-check>
</div>
<div ng-switch-when="Date">
<vn-date-picker
label="Value"
ng-model="fieldFilter.value">
</vn-date-picker>
</div>
<div ng-switch-default>
<vn-textfield
label="Value"
ng-model="fieldFilter.value">
</vn-textfield>
</div>
</vn-one>
<vn-icon-button
vn-none
vn-tooltip="Remove field"
icon="delete"
ng-click="$ctrl.removeField($index, fieldFilter.name)"
tabindex="-1">
</vn-icon-button>
</vn-horizontal>
<vn-horizontal>
<vn-check
vn-one
label="Floramondo"
ng-model="filter.isFloramondo"
triple-state="true">
</vn-check>
</vn-horizontal>
<vn-horizontal class="vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -1,67 +0,0 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
class Controller extends SearchPanel {
constructor($element, $) {
super($element, $);
let model = 'Item';
let moreFields = ['id', 'description', 'name', 'isActive'];
let properties;
let validations = window.validations;
if (validations && validations[model])
properties = validations[model].properties;
else
properties = {};
this.moreFields = [];
for (let field of moreFields) {
let prop = properties[field];
this.moreFields.push({
name: field,
label: prop ? prop.description : field,
type: prop ? prop.type : null
});
}
}
get filter() {
let filter = this.$.filter;
for (let fieldFilter of this.fieldFilters)
filter[fieldFilter.name] = fieldFilter.value;
return filter;
}
set filter(value) {
if (!value)
value = {};
if (!value.tags)
value.tags = [{}];
this.fieldFilters = [];
for (let field of this.moreFields) {
if (value[field.name] != undefined) {
this.fieldFilters.push({
name: field.name,
value: value[field.name],
info: field
});
}
}
this.$.filter = value;
}
removeField(index, field) {
this.fieldFilters.splice(index, 1);
delete this.$.filter[field];
}
}
ngModule.vnComponent('vnItemSearchPanel', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,60 +0,0 @@
import './index.js';
describe('Item', () => {
describe('Component vnItemSearchPanel', () => {
let $element;
let controller;
beforeEach(ngModule('item'));
beforeEach(inject($componentController => {
$element = angular.element(`<div></div>`);
controller = $componentController('vnItemSearchPanel', {$element});
}));
describe('filter() setter', () => {
it(`should set the tags property to the scope filter with an empty array`, () => {
const expectedFilter = {
tags: [{}]
};
controller.filter = null;
expect(controller.filter).toEqual(expectedFilter);
});
it(`should set the tags property to the scope filter with an array of tags`, () => {
const expectedFilter = {
description: 'My item',
tags: [{}]
};
const expectedFieldFilter = [{
info: {
label: 'description',
name: 'description',
type: null
},
name: 'description',
value: 'My item'
}];
controller.filter = {
description: 'My item'
};
expect(controller.filter).toEqual(expectedFilter);
expect(controller.fieldFilters).toEqual(expectedFieldFilter);
});
});
describe('removeField()', () => {
it(`should remove the description property from the fieldFilters and from the scope filter`, () => {
const expectedFilter = {tags: [{}]};
controller.filter = {description: 'My item'};
controller.removeField(0, 'description');
expect(controller.filter).toEqual(expectedFilter);
expect(controller.fieldFilters).toEqual([]);
});
});
});
});

View File

@ -1,8 +0,0 @@
Ink: Tinta
Origin: Origen
Producer: Productor
With visible: Con visible
Field: Campo
More fields: Más campos
Add field: Añadir campo
Remove field: Quitar campo

View File

@ -1,227 +0,0 @@
<vn-card class="summary">
<h5>
<a ng-if="::$ctrl.item.id"
vn-tooltip="Go to the item"
ui-sref="item.card.summary({id: {{::$ctrl.item.id}}})"
name="goToSummary">
<vn-icon-button icon="launch"></vn-icon-button>
</a>
<span>{{$ctrl.item.id}} - {{$ctrl.summary.item.name}}</span>
</h5>
<vn-horizontal>
<vn-one>
<img style="width: 100%; display: block;"
ng-src="{{$root.imagePath('catalog', '200x200', $ctrl.item.id)}}"
zoom-image="{{$root.imagePath('catalog', '1600x900', $ctrl.item.id)}}" on-error-src/>
<vn-horizontal class="item-state">
<vn-one>
<p translate>Visible</p>
<p>{{$ctrl.summary.visible}}</p>
</vn-one>
<vn-one>
<p translate>Available</p>
<p>{{$ctrl.summary.available}}</p>
</vn-one>
<vn-one>
<p>
<vn-icon
ng-if="$ctrl.warehouseText != null"
icon="info_outline"
vn-tooltip="{{$ctrl.warehouseText}}"
pointer>
</vn-icon>
</p>
</vn-one>
</vn-horizontal>
</vn-one>
<vn-one name="basicData">
<h4 ng-show="$ctrl.isBuyer">
<a
ui-sref="item.card.basicData({id:$ctrl.item.id})"
target="_self">
<span translate vn-tooltip="Go to">Basic data</span>
</a>
</h4>
<h4
translate
ng-show="!$ctrl.isBuyer">
Basic data
</h4>
<vn-label-value label="Name"
value="{{$ctrl.summary.item.name}}">
</vn-label-value>
<vn-label-value label="Full name"
value="{{$ctrl.summary.item.longName}}">
</vn-label-value>
<vn-label-value label="Item family"
value="{{$ctrl.summary.item.itemType.name}}">
</vn-label-value>
<vn-label-value label="Size"
value="{{$ctrl.summary.item.size}}">
</vn-label-value>
<vn-label-value label="Origin"
value="{{$ctrl.summary.item.origin.name}}">
</vn-label-value>
<vn-label-value label="stems"
value="{{$ctrl.summary.item.stems}}">
</vn-label-value>
<vn-label-value label="Multiplier"
value="{{$ctrl.summary.item.stemMultiplier}}">
</vn-label-value>
<vn-label-value label="Buyer">
<span
ng-click="workerDescriptor.show($event, $ctrl.summary.item.itemType.worker.id)"
class="link">
{{$ctrl.summary.item.itemType.worker.user.name}}
</span>
</vn-label-value>
<vn-horizontal>
<vn-check
label="Do photo"
disabled="true"
vn-one
ng-model="$ctrl.item.isPhotoRequested"
info="This item does need a photo">
</vn-check>
</vn-horizontal>
</vn-one>
<vn-one name="otherData">
<h4 ng-show="$ctrl.isBuyer">
<a
ui-sref="item.card.basicData({id:$ctrl.item.id})"
target="_self">
<span translate vn-tooltip="Go to">Other data</span>
</a>
</h4>
<h4
translate
ng-show="!$ctrl.isBuyer">
Other data
</h4>
<vn-label-value label="Intrastat code"
value="{{$ctrl.summary.item.intrastat.id}}">
</vn-label-value>
<vn-label-value label="Intrastat"
value="{{$ctrl.summary.item.intrastat.description}}">
</vn-label-value>
<vn-label-value label="Reference"
value="{{$ctrl.summary.item.comment}}">
</vn-label-value>
<vn-label-value label="Relevancy"
value="{{$ctrl.summary.item.relevancy}}">
</vn-label-value>
<vn-label-value label="Weight/Piece"
value="{{$ctrl.summary.item.weightByPiece}}">
</vn-label-value>
<vn-label-value label="Units/Box"
value="{{$ctrl.summary.item.packingOut}}">
</vn-label-value>
<vn-label-value label="Expense"
value="{{$ctrl.summary.item.expense.name}}">
</vn-label-value>
<vn-label-value label="Generic"
value="{{$ctrl.summary.item.genericFk}}">
</vn-label-value>
<vn-label-value label="Recycled Plastic"
value="{{$ctrl.summary.item.recycledPlastic}}">
</vn-label-value>
<vn-label-value label="Non recycled plastic"
value="{{$ctrl.summary.item.nonRecycledPlastic}}">
</vn-label-value>
</vn-one>
<vn-one name="tags">
<h4 ng-show="$ctrl.isBuyer || $ctrl.isReplenisher">
<a
ui-sref="item.card.tags({id:$ctrl.item.id})"
target="_self">
<span translate vn-tooltip="Go to">Tags</span>
</a>
</h4>
<h4
translate
ng-show="!$ctrl.isBuyer && !$ctrl.isReplenisher">
Tags
</h4>
<vn-label-value
label="{{tag.priority}} {{tag.tag.name}}"
ng-repeat="tag in $ctrl.summary.tags track by tag.id"
value="{{tag.value}}">
</vn-label-value>
</vn-one>
<vn-one name="description" ng-if="$ctrl.summary.item.description">
<h4 ng-show="$ctrl.isBuyer">
<a
ui-sref="item.card.basicData({id:$ctrl.item.id})"
target="_self">
<span translate vn-tooltip="Go to">Description</span>
</a>
</h4>
<h4
translate
ng-show="!$ctrl.isBuyer">
Description
</h4>
<p>
{{$ctrl.summary.item.description}}
</p>
</vn-one>
<vn-one name="tax">
<h4 ng-show="$ctrl.isBuyer || $ctrl.isAdministrative">
<a
ui-sref="item.card.tax({id:$ctrl.item.id})"
target="_self">
<span translate vn-tooltip="Go to">Tax</span>
</a>
</h4>
<h4
translate
ng-show="!$ctrl.isBuyer && !$ctrl.isAdministrative">
Tax
</h4>
<vn-label-value label="{{tax.country.name}}"
ng-repeat="tax in $ctrl.summary.item.taxes"
value="{{tax.taxClass.description}}">
</vn-label-value>
</vn-one>
<vn-one name="botanical">
<h4 ng-show="$ctrl.isBuyer">
<a
ui-sref="item.card.botanical({id:$ctrl.item.id})"
target="_self">
<span translate vn-tooltip="Go to">Botanical</span>
</a>
</h4>
<h4
translate
ng-show="!$ctrl.isBuyer">
Botanical
</h4>
<vn-label-value label="Genus"
value="{{$ctrl.summary.botanical.genus.name}}">
</vn-label-value>
<vn-label-value label="Specie"
value="{{$ctrl.summary.botanical.specie.name}}">
</vn-label-value>
</vn-one>
<vn-one name="barcode">
<h4 ng-show="$ctrl.isBuyer || $ctrl.isReplenisher">
<a
ui-sref="item.card.itemBarcode({id:$ctrl.item.id})"
target="_self">
<span translate vn-tooltip="Go to">Barcode</span>
</a>
</h4>
<h4
translate
ng-show="!$ctrl.isBuyer && !$ctrl.isReplenisher">
Barcode
</h4>
<p ng-repeat="barcode in $ctrl.summary.item.itemBarcode track by $index">
<b>{{barcode.code}}</b>
</p>
</vn-one>
</vn-horizontal>
</vn-card>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>

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