Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 4927-permitir-razonSocial-duplicada

This commit is contained in:
Alexandre Riera 2023-01-12 14:09:39 +01:00
commit f01b729a61
40 changed files with 654 additions and 367 deletions

View File

@ -9,10 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- [General](Inicio) Permite recuperar la contraseña - [General](Inicio) Permite recuperar la contraseña
- [Artículo](Datos Básicos) Añadido campo Unidades/Caja
### Changed ### Changed
- [Tickets](Líneas preparadas) Actualizada sección para que sea más visual
- [Supplier](Crear/Editar) Permite añadir Proveedores con la misma razón social pero con países distintos - [Supplier](Crear/Editar) Permite añadir Proveedores con la misma razón social pero con países distintos
### Fixed
- [General] Al utilizar el traductor de Google se descuadraban los iconos
### Removed ### Removed
- [Tickets](Control clientes) Eliminada sección - [Tickets](Control clientes) Eliminada sección

View File

@ -29,8 +29,12 @@ module.exports = Self => {
filter = mergeFilters(filter, {where}); filter = mergeFilters(filter, {where});
const stmt = new ParameterizedSQL( const stmt = new ParameterizedSQL(
`SELECT * FROM campaign`); `SELECT * FROM (`);
stmt.merge('SELECT * FROM campaign');
stmt.merge(conn.makeWhere(filter.where)); stmt.merge(conn.makeWhere(filter.where));
stmt.merge('ORDER BY dated ASC');
stmt.merge('LIMIT 10000000000000000000');
stmt.merge(') sub');
stmt.merge('GROUP BY code'); stmt.merge('GROUP BY code');
stmt.merge(conn.makePagination(filter)); stmt.merge(conn.makePagination(filter));

View File

@ -0,0 +1,2 @@
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`)
VALUES ('ItemShelvingSale','*','*','ALLOW','employee');

View File

@ -0,0 +1,4 @@
SET FOREIGN_KEY_CHECKS = 0;
ALTER TABLE `vn`.`report` MODIFY COLUMN id tinyint(3) unsigned NOT NULL AUTO_INCREMENT;
ALTER TABLE `vn`.`printer` MODIFY COLUMN id tinyint(3) unsigned NOT NULL AUTO_INCREMENT;
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -1143,10 +1143,10 @@ INSERT INTO `vn`.`itemShelving` (`itemFk`, `shelvingFk`, `visible`, `grouping`,
INSERT INTO `vn`.`itemShelvingSale` (`itemShelvingFk`, `saleFk`, `quantity`, `created`, `userFk`) INSERT INTO `vn`.`itemShelvingSale` (`itemShelvingFk`, `saleFk`, `quantity`, `created`, `userFk`)
VALUES VALUES
('1', '1', '1', '', '1106'), ('1', '1', '1', util.VN_CURDATE(), '1106'),
('2', '2', '5', '', '1106'), ('2', '2', '5', util.VN_CURDATE(), '1106'),
('1', '7', '1', '', '1106'), ('1', '7', '1', util.VN_CURDATE(), '1106'),
('2', '8', '5', '', '1106'); ('2', '8', '5', util.VN_CURDATE(), '1106');
INSERT INTO `vncontrol`.`accion`(`accion_id`, `accion`) INSERT INTO `vncontrol`.`accion`(`accion_id`, `accion`)
VALUES VALUES
@ -1779,7 +1779,7 @@ INSERT INTO `vn`.`claimDestination`(`id`, `description`, `addressFk`)
INSERT INTO `vn`.`claimDevelopment`(`id`, `claimFk`, `claimResponsibleFk`, `workerFk`, `claimReasonFk`, `claimResultFk`, `claimRedeliveryFk`, `claimDestinationFk`) INSERT INTO `vn`.`claimDevelopment`(`id`, `claimFk`, `claimResponsibleFk`, `workerFk`, `claimReasonFk`, `claimResultFk`, `claimRedeliveryFk`, `claimDestinationFk`)
VALUES VALUES
(1, 1, 1, 21, 1, 1, 2, 5), (1, 1, 1, 21, 1, 1, 2, 5),
(2, 1, 1, 21, 7, 2, 2, 5), (2, 1, 2, 21, 7, 2, 2, 5),
(3, 2, 7, 21, 9, 3, 2, 5), (3, 2, 7, 21, 9, 3, 2, 5),
(4, 3, 7, 21, 15, 8, 2, 5), (4, 3, 7, 21, 15, 8, 2, 5),
(5, 4, 7, 21, 7, 8, 2, 5); (5, 4, 7, 21, 7, 8, 2, 5);
@ -2652,7 +2652,7 @@ INSERT INTO `vn`.`mdbVersion` (`app`, `branchFk`, `version`)
INSERT INTO `vn`.`accountingConfig` (`id`, `minDate`, `maxDate`) INSERT INTO `vn`.`accountingConfig` (`id`, `minDate`, `maxDate`)
VALUES VALUES
(1, '2022-01-01', '2023-01-01'); (1, CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR)), '-01-01'), CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL +1 YEAR)), '-01-01'));
INSERT INTO `vn`.`saleGroup` (`userFk`, `parkingFk`, `sectorFk`) INSERT INTO `vn`.`saleGroup` (`userFk`, `parkingFk`, `sectorFk`)

View File

@ -463,6 +463,7 @@ export default {
generic: 'vn-autocomplete[ng-model="$ctrl.item.genericFk"]', generic: 'vn-autocomplete[ng-model="$ctrl.item.genericFk"]',
isFragile: 'vn-check[ng-model="$ctrl.item.isFragile"]', isFragile: 'vn-check[ng-model="$ctrl.item.isFragile"]',
longName: 'vn-textfield[ng-model="$ctrl.item.longName"]', longName: 'vn-textfield[ng-model="$ctrl.item.longName"]',
packingOut: 'vn-input-number[ng-model="$ctrl.item.packingOut"]',
isActiveCheckbox: 'vn-check[label="Active"]', isActiveCheckbox: 'vn-check[label="Active"]',
priceInKgCheckbox: 'vn-check[label="Price in kg"]', priceInKgCheckbox: 'vn-check[label="Price in kg"]',
newIntrastatButton: 'vn-item-basic-data vn-icon-button[vn-tooltip="New intrastat"] > button', newIntrastatButton: 'vn-item-basic-data vn-icon-button[vn-tooltip="New intrastat"] > button',

View File

@ -2,68 +2,32 @@ import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
describe('Worker summary path', () => { describe('Worker summary path', () => {
const workerId = 3;
let browser; let browser;
let page; let page;
beforeAll(async() => { beforeAll(async() => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('employee', 'worker'); await page.loginAndModule('employee', 'worker');
const httpDataResponse = page.waitForResponse(response => {
return response.status() === 200 && response.url().includes(`Workers/${workerId}`);
});
await page.accessToSearchResult('agencyNick'); await page.accessToSearchResult('agencyNick');
await httpDataResponse;
}); });
afterAll(async() => { afterAll(async() => {
await browser.close(); await browser.close();
}); });
it('should reach the employee summary section', async() => { it('should reach the employee summary section and check all properties', async() => {
await page.waitForState('worker.card.summary'); expect(await page.getProperty(selectors.workerSummary.header, 'innerText')).toEqual('agency agency');
}); expect(await page.getProperty(selectors.workerSummary.id, 'innerText')).toEqual('3');
expect(await page.getProperty(selectors.workerSummary.email, 'innerText')).toEqual('agency@verdnatura.es');
it('should check the summary contains the name and userName on the header', async() => { expect(await page.getProperty(selectors.workerSummary.department, 'innerText')).toEqual('CAMARA');
const result = await page.waitToGetProperty(selectors.workerSummary.header, 'innerText'); expect(await page.getProperty(selectors.workerSummary.userId, 'innerText')).toEqual('3');
expect(await page.getProperty(selectors.workerSummary.userName, 'innerText')).toEqual('agency');
expect(result).toEqual('agency agency'); expect(await page.getProperty(selectors.workerSummary.role, 'innerText')).toEqual('agency');
}); expect(await page.getProperty(selectors.workerSummary.extension, 'innerText')).toEqual('1101');
it('should check the summary contains the basic data id', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.id, 'innerText');
expect(result).toEqual('3');
});
it('should check the summary contains the basic data email', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.email, 'innerText');
expect(result).toEqual('agency@verdnatura.es');
});
it('should check the summary contains the basic data department', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.department, 'innerText');
expect(result).toEqual('CAMARA');
});
it('should check the summary contains the user data id', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.userId, 'innerText');
expect(result).toEqual('3');
});
it('should check the summary contains the user data name', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.userName, 'innerText');
expect(result).toEqual('agency');
});
it('should check the summary contains the user data role', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.role, 'innerText');
expect(result).toEqual('agency');
});
it('should check the summary contains the user data extension', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.extension, 'innerText');
expect(result).toEqual('1101');
}); });
}); });

View File

@ -2,13 +2,18 @@ import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
describe('Worker basic data path', () => { describe('Worker basic data path', () => {
const workerId = 1106;
let browser; let browser;
let page; let page;
beforeAll(async() => { beforeAll(async() => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('hr', 'worker'); await page.loginAndModule('hr', 'worker');
const httpDataResponse = page.waitForResponse(response => {
return response.status() === 200 && response.url().includes(`Workers/${workerId}`);
});
await page.accessToSearchResult('David Charles Haller'); await page.accessToSearchResult('David Charles Haller');
await httpDataResponse;
await page.accessToSection('worker.card.basicData'); await page.accessToSection('worker.card.basicData');
}); });
@ -16,35 +21,20 @@ describe('Worker basic data path', () => {
await browser.close(); await browser.close();
}); });
it('should edit the form', async() => { it('should edit the form and then reload the section and check the data was edited', async() => {
await page.clearInput(selectors.workerBasicData.name); await page.overwrite(selectors.workerBasicData.name, 'David C.');
await page.write(selectors.workerBasicData.name, 'David C.'); await page.overwrite(selectors.workerBasicData.surname, 'H.');
await page.clearInput(selectors.workerBasicData.surname); await page.overwrite(selectors.workerBasicData.phone, '444332211');
await page.write(selectors.workerBasicData.surname, 'H.'); await page.click(selectors.workerBasicData.saveButton);
await page.clearInput(selectors.workerBasicData.phone);
await page.write(selectors.workerBasicData.phone, '444332211');
await page.waitToClick(selectors.workerBasicData.saveButton);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!'); expect(message.text).toContain('Data saved!');
});
it('should reload the section then check the name was edited', async() => {
await page.reloadSection('worker.card.basicData'); await page.reloadSection('worker.card.basicData');
const result = await page.waitToGetProperty(selectors.workerBasicData.name, 'value');
expect(result).toEqual('David C.'); expect(await page.waitToGetProperty(selectors.workerBasicData.name, 'value')).toEqual('David C.');
}); expect(await page.waitToGetProperty(selectors.workerBasicData.surname, 'value')).toEqual('H.');
expect(await page.waitToGetProperty(selectors.workerBasicData.phone, 'value')).toEqual('444332211');
it('should the surname was edited', async() => {
const result = await page.waitToGetProperty(selectors.workerBasicData.surname, 'value');
expect(result).toEqual('H.');
});
it('should the phone was edited', async() => {
const result = await page.waitToGetProperty(selectors.workerBasicData.phone, 'value');
expect(result).toEqual('444332211');
}); });
}); });

View File

@ -16,19 +16,16 @@ describe('Worker pbx path', () => {
await browser.close(); await browser.close();
}); });
it('should receive an error when the extension exceeds 4 characters', async() => { it('should receive an error when the extension exceeds 4 characters and then sucessfully save the changes', async() => {
await page.write(selectors.workerPbx.extension, '55555'); await page.write(selectors.workerPbx.extension, '55555');
await page.waitToClick(selectors.workerPbx.saveButton); await page.click(selectors.workerPbx.saveButton);
const message = await page.waitForSnackbar(); let message = await page.waitForSnackbar();
expect(message.text).toContain('Extension format is invalid'); expect(message.text).toContain('Extension format is invalid');
});
it('should sucessfully save the changes', async() => { await page.overwrite(selectors.workerPbx.extension, '4444');
await page.clearInput(selectors.workerPbx.extension); await page.click(selectors.workerPbx.saveButton);
await page.write(selectors.workerPbx.extension, '4444'); message = await page.waitForSnackbar();
await page.waitToClick(selectors.workerPbx.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved! User must access web'); expect(message.text).toContain('Data saved! User must access web');
}); });

View File

@ -21,43 +21,40 @@ describe('Worker time control path', () => {
const fourPm = '16:00'; const fourPm = '16:00';
const hankPymId = 1107; const hankPymId = 1107;
it('should go to the next month', async() => { it('should go to the next month, go to current month and go 1 month in the past', async() => {
const date = new Date(); let date = new Date();
date.setMonth(date.getMonth() + 1); date.setMonth(date.getMonth() + 1);
const month = date.toLocaleString('default', {month: 'long'}); let month = date.toLocaleString('default', {month: 'long'});
await page.waitToClick(selectors.workerTimeControl.nextMonthButton); await page.click(selectors.workerTimeControl.nextMonthButton);
const result = await page.waitToGetProperty(selectors.workerTimeControl.monthName, 'innerText'); let result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');
expect(result).toContain(month); expect(result).toContain(month);
});
it('should go to current month', async() => { date = new Date();
const date = new Date(); month = date.toLocaleString('default', {month: 'long'});
const month = date.toLocaleString('default', {month: 'long'});
await page.waitToClick(selectors.workerTimeControl.previousMonthButton); await page.click(selectors.workerTimeControl.previousMonthButton);
const result = await page.waitToGetProperty(selectors.workerTimeControl.monthName, 'innerText'); result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');
expect(result).toContain(month); expect(result).toContain(month);
});
it('should go 1 month in the past', async() => { date = new Date();
const date = new Date();
date.setMonth(date.getMonth() - 1); date.setMonth(date.getMonth() - 1);
const timestamp = Math.round(date.getTime() / 1000); const timestamp = Math.round(date.getTime() / 1000);
const month = date.toLocaleString('default', {month: 'long'}); month = date.toLocaleString('default', {month: 'long'});
await page.loginAndModule('salesBoss', 'worker'); await page.loginAndModule('salesBoss', 'worker');
await page.goto(`http://localhost:5000/#!/worker/${hankPymId}/time-control?timestamp=${timestamp}`); await page.goto(`http://localhost:5000/#!/worker/${hankPymId}/time-control?timestamp=${timestamp}`);
await page.waitToClick(selectors.workerTimeControl.secondWeekDay); await page.click(selectors.workerTimeControl.secondWeekDay);
const result = await page.waitToGetProperty(selectors.workerTimeControl.monthName, 'innerText'); result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');
expect(result).toContain(month); expect(result).toContain(month);
}); });
it(`should return error when insert 'out' of first entry`, async() => { it(`should return error when insert 'out' of first entry`, async() => {
pending('https://redmine.verdnatura.es/issues/4707');
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton); await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton);
await page.pickTime(selectors.workerTimeControl.dialogTimeInput, eightAm); await page.pickTime(selectors.workerTimeControl.dialogTimeInput, eightAm);
await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out'); await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out');
@ -68,6 +65,7 @@ describe('Worker time control path', () => {
}); });
it(`should insert 'in' monday`, async() => { it(`should insert 'in' monday`, async() => {
pending('https://redmine.verdnatura.es/issues/4707');
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton); await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton);
await page.pickTime(selectors.workerTimeControl.dialogTimeInput, eightAm); await page.pickTime(selectors.workerTimeControl.dialogTimeInput, eightAm);
await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'in'); await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'in');
@ -78,6 +76,7 @@ describe('Worker time control path', () => {
}); });
it(`should insert 'out' monday`, async() => { it(`should insert 'out' monday`, async() => {
pending('https://redmine.verdnatura.es/issues/4707');
await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton); await page.waitToClick(selectors.workerTimeControl.mondayAddTimeButton);
await page.pickTime(selectors.workerTimeControl.dialogTimeInput, fourPm); await page.pickTime(selectors.workerTimeControl.dialogTimeInput, fourPm);
await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out'); await page.autocompleteSearch(selectors.workerTimeControl.dialogTimeDirection, 'out');
@ -88,11 +87,13 @@ describe('Worker time control path', () => {
}); });
it(`should check Hank Pym worked 8:20 hours`, async() => { it(`should check Hank Pym worked 8:20 hours`, async() => {
pending('https://redmine.verdnatura.es/issues/4707');
await page.waitForTextInElement(selectors.workerTimeControl.mondayWorkedHours, '08:20 h.'); await page.waitForTextInElement(selectors.workerTimeControl.mondayWorkedHours, '08:20 h.');
await page.waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '08:20 h.'); await page.waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '08:20 h.');
}); });
it('should remove first entry of monday', async() => { it('should remove first entry of monday', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
await page.waitForTextInElement(selectors.workerTimeControl.firstEntryOfMonday, eightAm); await page.waitForTextInElement(selectors.workerTimeControl.firstEntryOfMonday, eightAm);
await page.waitForTextInElement(selectors.workerTimeControl.secondEntryOfMonday, fourPm); await page.waitForTextInElement(selectors.workerTimeControl.secondEntryOfMonday, fourPm);
await page.waitToClick(selectors.workerTimeControl.firstEntryOfMondayDelete); await page.waitToClick(selectors.workerTimeControl.firstEntryOfMondayDelete);
@ -103,13 +104,16 @@ describe('Worker time control path', () => {
}); });
it(`should be the 'out' the first entry of monday`, async() => { it(`should be the 'out' the first entry of monday`, async() => {
pending('https://redmine.verdnatura.es/issues/4707');
const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfMonday, 'innerText'); const result = await page.waitToGetProperty(selectors.workerTimeControl.firstEntryOfMonday, 'innerText');
expect(result).toEqual(fourPm); expect(result).toEqual(fourPm);
}); });
it('should change week of month', async() => { it('should change week of month', async() => {
await page.waitToClick(selectors.workerTimeControl.thrirdWeekDay); await page.click(selectors.workerTimeControl.thrirdWeekDay);
await page.waitForTextInElement(selectors.workerTimeControl.mondayWorkedHours, '00:00 h.'); const result = await page.getProperty(selectors.workerTimeControl.mondayWorkedHours, 'innerText');
expect(result).toEqual('00:00 h.');
}); });
}); });

View File

@ -1,16 +1,25 @@
/* eslint-disable max-len */
import selectors from '../../helpers/selectors.js'; import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
describe('Worker calendar path', () => { describe('Worker calendar path', () => {
let reasonableTimeBetweenClicks = 400; const reasonableTimeBetweenClicks = 300;
const date = new Date();
const lastYear = (date.getFullYear() - 1).toString();
let browser; let browser;
let page; let page;
async function accessAs(user) {
await page.loginAndModule(user, 'worker');
await page.accessToSearchResult('Charles Xavier');
await page.accessToSection('worker.card.calendar');
}
beforeAll(async() => { beforeAll(async() => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('hr', 'worker'); accessAs('hr');
await page.accessToSearchResult('Charles Xavier');
await page.accessToSection('worker.card.calendar');
}); });
afterAll(async() => { afterAll(async() => {
@ -21,48 +30,40 @@ describe('Worker calendar path', () => {
it('should set two days as holidays on the calendar and check the total holidays increased by 1.5', async() => { it('should set two days as holidays on the calendar and check the total holidays increased by 1.5', async() => {
await page.waitToClick(selectors.workerCalendar.holidays); await page.waitToClick(selectors.workerCalendar.holidays);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.penultimateMondayOfJanuary); await page.click(selectors.workerCalendar.penultimateMondayOfJanuary);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.absence); await page.click(selectors.workerCalendar.absence);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.lastMondayOfMarch); await page.click(selectors.workerCalendar.lastMondayOfMarch);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.halfHoliday); await page.click(selectors.workerCalendar.halfHoliday);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.fistMondayOfMay); await page.click(selectors.workerCalendar.fistMondayOfMay);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.furlough); await page.click(selectors.workerCalendar.furlough);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.secondTuesdayOfMay); await page.click(selectors.workerCalendar.secondTuesdayOfMay);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.secondWednesdayOfMay); await page.click(selectors.workerCalendar.secondWednesdayOfMay);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.secondThursdayOfMay); await page.click(selectors.workerCalendar.secondThursdayOfMay);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.halfFurlough); await page.click(selectors.workerCalendar.halfFurlough);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.secondFridayOfJun);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.click(selectors.workerCalendar.secondFridayOfJun);
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText'); expect(await page.getProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText')).toContain(' 1.5 ');
expect(result).toContain(' 1.5 ');
}); });
}); });
describe(`as salesBoss`, () => { describe(`as salesBoss`, () => {
it(`should log in and get to Charles Xavier's calendar`, async() => { it(`should log in, get to Charles Xavier's calendar, undo what was done here, and check the total holidays used are back to what it was`, async() => {
await page.loginAndModule('salesBoss', 'worker'); accessAs('salesBoss');
await page.accessToSearchResult('Charles Xavier');
await page.accessToSection('worker.card.calendar');
});
it('should undo what was done here', async() => {
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.holidays); await page.waitToClick(selectors.workerCalendar.holidays);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.penultimateMondayOfJanuary); await page.waitToClick(selectors.workerCalendar.penultimateMondayOfJanuary);
@ -90,45 +91,24 @@ describe('Worker calendar path', () => {
await page.waitToClick(selectors.workerCalendar.halfFurlough); await page.waitToClick(selectors.workerCalendar.halfFurlough);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.secondFridayOfJun); await page.waitToClick(selectors.workerCalendar.secondFridayOfJun);
});
it('should check the total holidays used are back to what it was', async() => { expect(await page.getProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText')).toContain(' 0 ');
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
expect(result).toContain(' 0 ');
}); });
}); });
describe(`as Charles Xavier`, () => { describe(`as Charles Xavier`, () => {
it(`should log in and get to his calendar`, async() => { it('should log in and get to his calendar, make a futile attempt to add holidays, check the total holidays used are now the initial ones and use the year selector to go to the previous year', async() => {
await page.loginAndModule('CharlesXavier', 'worker'); accessAs('CharlesXavier');
await page.accessToSearchResult('Charles Xavier');
await page.accessToSection('worker.card.calendar');
});
it('should make a futile attempt to add holidays', async() => {
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.holidays); await page.waitToClick(selectors.workerCalendar.holidays);
await page.waitForTimeout(reasonableTimeBetweenClicks); await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.penultimateMondayOfJanuary);
});
it('should check the total holidays used are now the initial ones', async() => { await page.click(selectors.workerCalendar.penultimateMondayOfJanuary);
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
expect(result).toContain(' 0 '); expect(await page.getProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText')).toContain(' 0 ');
});
it('should use the year selector to go to the previous year', async() => {
const date = new Date();
const lastYear = (date.getFullYear() - 1).toString();
await page.autocompleteSearch(selectors.workerCalendar.year, lastYear); await page.autocompleteSearch(selectors.workerCalendar.year, lastYear);
await page.waitForTimeout(reasonableTimeBetweenClicks); expect(await page.getProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText')).toContain(' 0 ');
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
expect(result).toContain(' 0 ');
}); });
}); });
}); });

View File

@ -35,6 +35,7 @@ describe('Item Edit basic data path', () => {
await page.waitToClick(selectors.itemBasicData.isActiveCheckbox); await page.waitToClick(selectors.itemBasicData.isActiveCheckbox);
await page.waitToClick(selectors.itemBasicData.priceInKgCheckbox); await page.waitToClick(selectors.itemBasicData.priceInKgCheckbox);
await page.waitToClick(selectors.itemBasicData.isFragile); await page.waitToClick(selectors.itemBasicData.isFragile);
await page.write(selectors.itemBasicData.packingOut, '5');
await page.waitToClick(selectors.itemBasicData.submitBasicDataButton); await page.waitToClick(selectors.itemBasicData.submitBasicDataButton);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
@ -128,4 +129,11 @@ describe('Item Edit basic data path', () => {
expect(result).toBe('checked'); expect(result).toBe('checked');
}); });
it(`should confirm the item packingOut was edited`, async() => {
const result = await page
.waitToGetProperty(selectors.itemBasicData.packingOut, 'value');
expect(result).toEqual('5');
});
}); });

View File

@ -26,7 +26,7 @@ class Icon {
Icon.$inject = ['$attrs']; Icon.$inject = ['$attrs'];
ngModule.vnComponent('vnIcon', { ngModule.vnComponent('vnIcon', {
template: '<i class="{{::$ctrl.iconClass}} unselectable">{{::$ctrl.iconContent}}</i>', template: '<i class="{{::$ctrl.iconClass}} unselectable notranslate">{{::$ctrl.iconContent}}</i>',
controller: Icon, controller: Icon,
bindings: { bindings: {
icon: '@' icon: '@'

View File

@ -29,7 +29,7 @@ vn-table {
& > tbody { & > tbody {
display: table-row-group; display: table-row-group;
} }
& > vn-tfoot, & > vn-tfoot,
& > .vn-tfoot, & > .vn-tfoot,
& > tfoot { & > tfoot {
border-top: $border; border-top: $border;
@ -42,7 +42,7 @@ vn-table {
height: 48px; height: 48px;
} }
vn-thead, .vn-thead, vn-thead, .vn-thead,
vn-tbody, .vn-tbody, vn-tbody, .vn-tbody,
vn-tfoot, .vn-tfoot, vn-tfoot, .vn-tfoot,
thead, tbody, tfoot { thead, tbody, tfoot {
& > * { & > * {
@ -153,6 +153,18 @@ vn-table {
background-color: $color-font-bg-dark; background-color: $color-font-bg-dark;
color: $color-font-bg; color: $color-font-bg;
} }
&.dark-notice {
background-color: $color-notice;
color: $color-font-bg;
}
&.yellow {
background-color: $color-yellow;
color: $color-font-bg;
}
&.pink {
background-color: $color-pink;
color: $color-font-bg;
}
} }
vn-icon-menu { vn-icon-menu {
display: inline-block; display: inline-block;
@ -194,7 +206,7 @@ vn-table.scrollable > .vn-table,
} }
vn-thead th, vn-thead th,
vn-thead vn-th, vn-thead vn-th,
thead vn-th, thead vn-th,
thead th { thead th {
border-bottom: $border; border-bottom: $border;
@ -217,4 +229,4 @@ vn-table.scrollable.lg,
.tableWrapper { .tableWrapper {
overflow-x: auto; overflow-x: auto;
} }

View File

@ -101,6 +101,8 @@ $color-marginal: #222;
$color-success: #a3d131; $color-success: #a3d131;
$color-notice: #32b1ce; $color-notice: #32b1ce;
$color-alert: #fa3939; $color-alert: #fa3939;
$color-pink: #ff99cc;
$color-yellow: #ffff00;
$color-button: $color-secondary; $color-button: $color-secondary;
$color-spacer: rgba(255, 255, 255, .3); $color-spacer: rgba(255, 255, 255, .3);

118
front/package-lock.json generated
View File

@ -1,15 +1,8 @@
{ {
"name": "salix-front", "name": "salix-front",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 1, "lockfileVersion": 2,
"requires": true, "requires": true,
<<<<<<< HEAD
"dependencies": {
"@uirouter/angularjs": {
"version": "1.0.29",
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.29.tgz",
"integrity": "sha512-RImWnBarNixkMto0o8stEaGwZmvhv5cnuOLXyMU2pY8MP2rgEF74ZNJTLeJCW14LR7XDUxVH8Mk8bPI6lxedmQ==",
=======
"packages": { "packages": {
"": { "": {
"name": "salix-front", "name": "salix-front",
@ -32,8 +25,7 @@
}, },
"node_modules/@uirouter/angularjs": { "node_modules/@uirouter/angularjs": {
"version": "1.0.30", "version": "1.0.30",
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.30.tgz", "license": "MIT",
"integrity": "sha512-qkc3RFZc91S5K0gc/QVAXc9LGDPXjR04vDgG/11j8+yyZEuQojXxKxdLhKIepiPzqLmGRVqzBmBc27gtqaEeZg==",
"dependencies": { "dependencies": {
"@uirouter/core": "6.0.8" "@uirouter/core": "6.0.8"
}, },
@ -46,17 +38,14 @@
}, },
"node_modules/@uirouter/core": { "node_modules/@uirouter/core": {
"version": "6.0.8", "version": "6.0.8",
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.8.tgz", "license": "MIT",
"integrity": "sha512-Gc/BAW47i4L54p8dqYCJJZuv2s3tqlXQ0fvl6Zp2xrblELPVfxmjnc0eurx3XwfQdaqm3T6uls6tQKkof/4QMw==",
"engines": { "engines": {
"node": ">=4.0.0" "node": ">=4.0.0"
} }
}, },
"node_modules/angular": { "node_modules/angular": {
"version": "1.8.3", "version": "1.8.3",
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz", "license": "MIT"
"integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw==",
"deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
}, },
"node_modules/angular-animate": { "node_modules/angular-animate": {
"version": "1.8.2", "version": "1.8.2",
@ -74,8 +63,7 @@
}, },
"node_modules/angular-translate": { "node_modules/angular-translate": {
"version": "2.19.0", "version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.19.0.tgz", "license": "MIT",
"integrity": "sha512-Z/Fip5uUT2N85dPQ0sMEe1JdF5AehcDe4tg/9mWXNDVU531emHCg53ZND9Oe0dyNiGX5rWcJKmsL1Fujus1vGQ==",
"dependencies": { "dependencies": {
"angular": "^1.8.0" "angular": "^1.8.0"
}, },
@ -85,8 +73,7 @@
}, },
"node_modules/angular-translate-loader-partial": { "node_modules/angular-translate-loader-partial": {
"version": "2.19.0", "version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.19.0.tgz", "license": "MIT",
"integrity": "sha512-NnMw13LMV4bPQmJK7/pZOZAnPxe0M5OtUHchADs5Gye7V7feonuEnrZ8e1CKhBlv9a7IQyWoqcBa4Lnhg8gk5w==",
"dependencies": { "dependencies": {
"angular-translate": "~2.19.0" "angular-translate": "~2.19.0"
} }
@ -133,8 +120,7 @@
}, },
"node_modules/moment": { "node_modules/moment": {
"version": "2.29.4", "version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", "license": "MIT",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"engines": { "engines": {
"node": "*" "node": "*"
} }
@ -179,97 +165,51 @@
"dependencies": { "dependencies": {
"@uirouter/angularjs": { "@uirouter/angularjs": {
"version": "1.0.30", "version": "1.0.30",
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.30.tgz",
"integrity": "sha512-qkc3RFZc91S5K0gc/QVAXc9LGDPXjR04vDgG/11j8+yyZEuQojXxKxdLhKIepiPzqLmGRVqzBmBc27gtqaEeZg==",
>>>>>>> dev
"requires": { "requires": {
"@uirouter/core": "6.0.8" "@uirouter/core": "6.0.8"
} }
}, },
"@uirouter/core": { "@uirouter/core": {
<<<<<<< HEAD "version": "6.0.8"
"version": "6.0.7",
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.7.tgz",
"integrity": "sha512-KUTJxL+6q0PiBnFx4/Z+Hsyg0pSGiaW5yZQeJmUxknecjpTbnXkLU8H2EqRn9N2B+qDRa7Jg8RcgeNDPY72O1w=="
}, },
"angular": { "angular": {
"version": "1.8.2", "version": "1.8.3"
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.2.tgz",
"integrity": "sha512-IauMOej2xEe7/7Ennahkbb5qd/HFADiNuLSESz9Q27inmi32zB0lnAsFeLEWcox3Gd1F6YhNd1CP7/9IukJ0Gw=="
=======
"version": "6.0.8",
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.8.tgz",
"integrity": "sha512-Gc/BAW47i4L54p8dqYCJJZuv2s3tqlXQ0fvl6Zp2xrblELPVfxmjnc0eurx3XwfQdaqm3T6uls6tQKkof/4QMw=="
},
"angular": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz",
"integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw=="
>>>>>>> dev
}, },
"angular-animate": { "angular-animate": {
"version": "1.8.2", "version": "1.8.2"
"resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz",
"integrity": "sha512-Jbr9+grNMs9Kj57xuBU3Ju3NOPAjS1+g2UAwwDv7su1lt0/PLDy+9zEwDiu8C8xJceoTbmBNKiWGPJGBdCQLlA=="
}, },
"angular-moment": { "angular-moment": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/angular-moment/-/angular-moment-1.3.0.tgz",
"integrity": "sha512-KG8rvO9MoaBLwtGnxTeUveSyNtrL+RNgGl1zqWN36+HDCCVGk2DGWOzqKWB6o+eTTbO3Opn4hupWKIElc8XETA==",
"requires": { "requires": {
"moment": ">=2.8.0 <3.0.0" "moment": ">=2.8.0 <3.0.0"
} }
}, },
"angular-translate": { "angular-translate": {
<<<<<<< HEAD
"version": "2.18.4",
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.4.tgz",
"integrity": "sha512-KohNrkH6J9PK+VW0L/nsRTcg5Fw70Ajwwe3Jbfm54Pf9u9Fd+wuingoKv+h45mKf38eT+Ouu51FPua8VmZNoCw==",
=======
"version": "2.19.0", "version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.19.0.tgz",
"integrity": "sha512-Z/Fip5uUT2N85dPQ0sMEe1JdF5AehcDe4tg/9mWXNDVU531emHCg53ZND9Oe0dyNiGX5rWcJKmsL1Fujus1vGQ==",
>>>>>>> dev
"requires": { "requires": {
"angular": "^1.8.0" "angular": "^1.8.0"
} }
}, },
"angular-translate-loader-partial": { "angular-translate-loader-partial": {
<<<<<<< HEAD
"version": "2.18.4",
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.18.4.tgz",
"integrity": "sha512-bsjR+FbB0sdA2528E/ugwKdlPPQhA1looxLxI3otayBTFXBpED33besfSZhYAISLgNMSL038vSssfRUen9qD8w==",
=======
"version": "2.19.0", "version": "2.19.0",
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.19.0.tgz",
"integrity": "sha512-NnMw13LMV4bPQmJK7/pZOZAnPxe0M5OtUHchADs5Gye7V7feonuEnrZ8e1CKhBlv9a7IQyWoqcBa4Lnhg8gk5w==",
>>>>>>> dev
"requires": { "requires": {
"angular-translate": "~2.19.0" "angular-translate": "~2.19.0"
} }
}, },
"argparse": { "argparse": {
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": { "requires": {
"sprintf-js": "~1.0.2" "sprintf-js": "~1.0.2"
} }
}, },
"croppie": { "croppie": {
"version": "2.6.5", "version": "2.6.5"
"resolved": "https://registry.npmjs.org/croppie/-/croppie-2.6.5.tgz",
"integrity": "sha512-IlChnVUGG5T3w2gRZIaQgBtlvyuYnlUWs2YZIXXR3H9KrlO1PtBT3j+ykxvy9eZIWhk+V5SpBmhCQz5UXKrEKQ=="
}, },
"esprima": { "esprima": {
"version": "4.0.1", "version": "4.0.1"
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
}, },
"js-yaml": { "js-yaml": {
"version": "3.14.1", "version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"requires": { "requires": {
"argparse": "^1.0.7", "argparse": "^1.0.7",
"esprima": "^4.0.0" "esprima": "^4.0.0"
@ -277,45 +217,27 @@
}, },
"mg-crud": { "mg-crud": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/mg-crud/-/mg-crud-1.1.2.tgz",
"integrity": "sha512-mAR6t0aQHKnT0QHKHpLOi0kNPZfO36iMpIoiLjFHxuio6mIJyuveBJ4VNlNXJRxLh32/FLADEb41/sYo7QUKFw==",
"requires": { "requires": {
"angular": "^1.6.1" "angular": "^1.6.1"
} }
}, },
"moment": { "moment": {
<<<<<<< HEAD "version": "2.29.4"
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
=======
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
>>>>>>> dev
}, },
"oclazyload": { "oclazyload": {
"version": "0.6.3", "version": "0.6.3"
"resolved": "https://registry.npmjs.org/oclazyload/-/oclazyload-0.6.3.tgz",
"integrity": "sha512-HpOSYUgjtt6sTB/C6+FWsExR+9HCnXKsUA96RWkDXfv11C8Cc9X2DlR0WIZwFIiG6FQU0pwB5dhoYyut8bFAOQ=="
}, },
"require-yaml": { "require-yaml": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/require-yaml/-/require-yaml-0.0.1.tgz",
"integrity": "sha512-M6eVEgLPRbeOhgSCnOTtdrOOEQzbXRchg24Xa13c39dMuraFKdI9emUo97Rih0YEFzSICmSKg8w4RQp+rd9pOQ==",
"requires": { "requires": {
"js-yaml": "^4.1.0" "js-yaml": ""
}, },
"dependencies": { "dependencies": {
"argparse": { "argparse": {
"version": "2.0.1", "version": "2.0.1"
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
}, },
"js-yaml": { "js-yaml": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"requires": { "requires": {
"argparse": "^2.0.1" "argparse": "^2.0.1"
} }
@ -323,14 +245,10 @@
} }
}, },
"sprintf-js": { "sprintf-js": {
"version": "1.0.3", "version": "1.0.3"
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
}, },
"validator": { "validator": {
"version": "6.3.0", "version": "6.3.0"
"resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz",
"integrity": "sha512-BylxTwhqwjQI5MDJF7amCy/L0ejJO+74DvCsLV52Lq3+3bhVcVMKqNqOiNcQJm2G48u9EAcw4xFERAmFbwXM9Q=="
} }
} }
} }

View File

@ -23,7 +23,7 @@ module.exports = Self => {
{ {
arg: 'search', arg: 'search',
type: 'string', type: 'string',
description: `If it's and integer searchs by id, otherwise it searchs by client name`, description: `If it's a number searchs by id, otherwise it searchs by client name`,
http: {source: 'query'} http: {source: 'query'}
}, },
{ {
@ -34,31 +34,31 @@ module.exports = Self => {
}, },
{ {
arg: 'id', arg: 'id',
type: 'integer', type: 'number',
description: 'The claim id', description: 'The claim id',
http: {source: 'query'} http: {source: 'query'}
}, },
{ {
arg: 'clientFk', arg: 'clientFk',
type: 'integer', type: 'number',
description: 'The client id', description: 'The client id',
http: {source: 'query'} http: {source: 'query'}
}, },
{ {
arg: 'claimStateFk', arg: 'claimStateFk',
type: 'integer', type: 'number',
description: 'The claim state id', description: 'The claim state id',
http: {source: 'query'} http: {source: 'query'}
}, },
{ {
arg: 'salesPersonFk', arg: 'salesPersonFk',
type: 'integer', type: 'number',
description: 'The salesPerson id', description: 'The salesPerson id',
http: {source: 'query'} http: {source: 'query'}
}, },
{ {
arg: 'attenderFk', arg: 'attenderFk',
type: 'integer', type: 'number',
description: 'The attender worker id', description: 'The attender worker id',
http: {source: 'query'} http: {source: 'query'}
}, },
@ -67,6 +67,18 @@ module.exports = Self => {
type: 'date', type: 'date',
description: 'The to date filter', description: 'The to date filter',
http: {source: 'query'} http: {source: 'query'}
},
{
arg: 'itemFk',
type: 'number',
description: 'The item id',
http: {source: 'query'}
},
{
arg: 'claimResponsibleFk',
type: 'number',
description: 'The claimResponsible id',
http: {source: 'query'}
} }
], ],
returns: { returns: {
@ -80,33 +92,58 @@ module.exports = Self => {
}); });
Self.filter = async(ctx, filter, options) => { Self.filter = async(ctx, filter, options) => {
const models = Self.app.models;
const conn = Self.dataSource.connector; const conn = Self.dataSource.connector;
const args = ctx.args;
const myOptions = {}; const myOptions = {};
let to; let to;
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
const where = buildFilter(ctx.args, (param, value) => { let claimIdsByItemFk = [];
let claimIdsByClaimResponsibleFk = [];
if (args.itemFk) {
query = `SELECT cb.claimFk
FROM claimBeginning cb
LEFT JOIN sale s ON s.id = cb.saleFk
WHERE s.itemFk = ?`;
const claims = await Self.rawSql(query, [args.itemFk], myOptions);
claimIdsByItemFk = claims.map(claim => claim.claimFk);
}
if (args.claimResponsibleFk) {
const claims = await models.ClaimDevelopment.find({
fields: ['claimFk'],
where: {claimResponsibleFk: args.claimResponsibleFk}
}, myOptions);
claimIdsByClaimResponsibleFk = claims.map(claim => claim.claimFk);
}
const where = buildFilter(args, (param, value) => {
switch (param) { switch (param) {
case 'search': case 'search':
return /^\d+$/.test(value) return /^\d+$/.test(value)
? {'cl.id': value} ? {'cl.id': value}
: { : {
or: [ or: [
{'cl.clientName': {like: `%${value}%`}} {'c.name': {like: `%${value}%`}}
] ]
}; };
case 'clientName': case 'clientName':
return {'cl.clientName': {like: `%${value}%`}}; return {'c.name': {like: `%${value}%`}};
case 'clientFk': case 'clientFk':
return {'cl.clientFk': value};
case 'id': case 'id':
case 'claimStateFk': case 'claimStateFk':
case 'priority': case 'priority':
return {[`cl.${param}`]: value}; return {[`cl.${param}`]: value};
case 'itemFk':
return {'cl.id': {inq: claimIdsByItemFk}};
case 'claimResponsibleFk':
return {'cl.id': {inq: claimIdsByClaimResponsibleFk}};
case 'salesPersonFk': case 'salesPersonFk':
return {'cl.salesPersonFk': value}; return {'c.salesPersonFk': value};
case 'attenderFk': case 'attenderFk':
return {'cl.workerFk': value}; return {'cl.workerFk': value};
case 'created': case 'created':
@ -118,29 +155,23 @@ module.exports = Self => {
} }
}); });
filter = mergeFilters(ctx.args.filter, {where}); filter = mergeFilters(args.filter, {where});
const stmts = []; const stmts = [];
const stmt = new ParameterizedSQL( const stmt = new ParameterizedSQL(
`SELECT * `SELECT
FROM ( cl.id,
SELECT cl.clientFk,
cl.id, c.name AS clientName,
cl.clientFk, cl.workerFk,
c.name AS clientName, u.name AS workerName,
cl.workerFk, cs.description,
u.name AS workerName, cl.created
cs.description, FROM claim cl
cl.created, LEFT JOIN client c ON c.id = cl.clientFk
cs.priority, LEFT JOIN account.user u ON u.id = cl.workerFk
cl.claimStateFk, LEFT JOIN claimState cs ON cs.id = cl.claimStateFk`
c.salesPersonFk
FROM claim cl
LEFT JOIN client c ON c.id = cl.clientFk
LEFT JOIN worker w ON w.id = cl.workerFk
LEFT JOIN account.user u ON u.id = w.userFk
LEFT JOIN claimState cs ON cs.id = cl.claimStateFk ) cl`
); );
stmt.merge(conn.makeSuffix(filter)); stmt.merge(conn.makeSuffix(filter));

View File

@ -57,4 +57,44 @@ describe('claim filter()', () => {
throw e; throw e;
} }
}); });
it('should return 3 results filtering by item id', async() => {
const tx = await app.models.Claim.beginTransaction({});
try {
const options = {transaction: tx};
const result = await app.models.Claim.filter({args: {filter: {}, itemFk: 2}}, null, options);
expect(result.length).toEqual(3);
expect(result[0].id).toEqual(1);
expect(result[1].id).toEqual(2);
expect(result[2].id).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return 3 results filtering by claimResponsible id', async() => {
const tx = await app.models.Claim.beginTransaction({});
try {
const options = {transaction: tx};
const result = await app.models.Claim.filter({args: {filter: {}, claimResponsibleFk: 7}}, null, options);
expect(result.length).toEqual(3);
expect(result[0].id).toEqual(2);
expect(result[1].id).toEqual(3);
expect(result[2].id).toEqual(4);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
}); });

View File

@ -58,8 +58,28 @@
ng-model="filter.created"> ng-model="filter.created">
</vn-date-picker> </vn-date-picker>
</vn-horizontal> </vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one class="dense"
label="Item"
url="Items/withName"
ng-model="filter.itemFk"
show-field="name"
value-field="id"
search-function="$ctrl.itemSearchFunc($search)"
order="id DESC">
<tpl-item>{{::id}} - {{::name}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete
vn-one
ng-model="filter.claimResponsibleFk"
url="ClaimResponsibles"
show-field="description"
value-field="id"
label="Responsible">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal class="vn-mt-lg"> <vn-horizontal class="vn-mt-lg">
<vn-submit label="Search"></vn-submit> <vn-submit label="Search"></vn-submit>
</vn-horizontal> </vn-horizontal>
</form> </form>
</div> </div>

View File

@ -1,7 +1,14 @@
import ngModule from '../module'; import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel'; import SearchPanel from 'core/components/searchbar/search-panel';
class Controller extends SearchPanel {
itemSearchFunc($search) {
return /^\d+$/.test($search)
? {id: $search}
: {name: {like: '%' + $search + '%'}};
}
}
ngModule.vnComponent('vnClaimSearchPanel', { ngModule.vnComponent('vnClaimSearchPanel', {
template: require('./index.html'), template: require('./index.html'),
controller: SearchPanel controller: Controller
}); });

View File

@ -4,7 +4,7 @@ import Dialog from 'core/components/dialog';
class Controller extends Dialog { class Controller extends Dialog {
constructor($element, $, $transclude, vnReport) { constructor($element, $, $transclude, vnReport) {
super($element, $, $transclude); super($element, $, $transclude);
this.viewReceipt = true;
this.vnReport = vnReport; this.vnReport = vnReport;
this.receipt = {}; this.receipt = {};
} }

View File

@ -75,6 +75,7 @@ describe('Client', () => {
jest.spyOn(controller.vnReport, 'show'); jest.spyOn(controller.vnReport, 'show');
controller.$params = {id: 1101}; controller.$params = {id: 1101};
controller.viewReceipt = false;
$httpBackend.expect('POST', `Clients/1101/createReceipt`).respond({id: 1}); $httpBackend.expect('POST', `Clients/1101/createReceipt`).respond({id: 1});
controller.responseHandler('accept'); controller.responseHandler('accept');

View File

@ -11,7 +11,6 @@ describe('InvoiceOut createPdf()', () => {
const ctx = {req: activeCtx}; const ctx = {req: activeCtx};
it('should create a new PDF file and set true the hasPdf property', async() => { it('should create a new PDF file and set true the hasPdf property', async() => {
pending('https://redmine.verdnatura.es/issues/4875');
const invoiceId = 1; const invoiceId = 1;
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx

View File

@ -13,7 +13,6 @@ describe('InvoiceOut downloadZip()', () => {
}; };
it('should return part of link to dowloand the zip', async() => { it('should return part of link to dowloand the zip', async() => {
pending('https://redmine.verdnatura.es/issues/4875');
const tx = await models.InvoiceOut.beginTransaction({}); const tx = await models.InvoiceOut.beginTransaction({});
try { try {
@ -31,6 +30,8 @@ describe('InvoiceOut downloadZip()', () => {
}); });
it('should return an error if the size of the files is too large', async() => { it('should return an error if the size of the files is too large', async() => {
pending('https://redmine.verdnatura.es/issues/5035');
const tx = await models.InvoiceOut.beginTransaction({}); const tx = await models.InvoiceOut.beginTransaction({});
let error; let error;

View File

@ -51,7 +51,6 @@ describe('InvoiceOut filter()', () => {
}); });
it('should return the invoice out matching hasPdf', async() => { it('should return the invoice out matching hasPdf', async() => {
pending('https://redmine.verdnatura.es/issues/4875');
const tx = await models.InvoiceOut.beginTransaction({}); const tx = await models.InvoiceOut.beginTransaction({});
const options = {transaction: tx}; const options = {transaction: tx};
@ -67,7 +66,7 @@ describe('InvoiceOut filter()', () => {
const result = await models.InvoiceOut.filter(ctx, {id: invoiceOut.id}, options); const result = await models.InvoiceOut.filter(ctx, {id: invoiceOut.id}, options);
expect(result.length).toEqual(1); expect(result.length).toBeGreaterThanOrEqual(1);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -17,12 +17,12 @@
target="_blank" target="_blank"
name="showInvoicePdf" name="showInvoicePdf"
translate> translate>
Show as PDF as PDF
</a> </a>
<vn-item <vn-item
ng-click="$ctrl.showCsvInvoice()" ng-click="$ctrl.showCsvInvoice()"
translate> translate>
Show as CSV as CSV
</vn-item> </vn-item>
</vn-list> </vn-list>
</vn-menu> </vn-menu>

View File

@ -2,6 +2,8 @@ Show invoice...: Ver factura...
Send invoice...: Enviar factura... Send invoice...: Enviar factura...
Send PDF invoice: Enviar factura en PDF Send PDF invoice: Enviar factura en PDF
Send CSV invoice: Enviar factura en CSV Send CSV invoice: Enviar factura en CSV
as PDF: como PDF
as CSV: como CSV
Delete Invoice: Eliminar factura Delete Invoice: Eliminar factura
Clone Invoice: Clonar factura Clone Invoice: Clonar factura
Book invoice: Asentar factura Book invoice: Asentar factura

View File

@ -0,0 +1,49 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
module.exports = Self => {
Self.remoteMethod('filter', {
description: 'Returns all item shelving sale matching with the filter',
accessType: 'READ',
accepts: [{
arg: 'filter',
type: 'object',
description: 'Filter defining where and paginated data'
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/filter`,
verb: 'GET'
}
});
Self.filter = async(filter, options) => {
const conn = Self.dataSource.connector;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const stmt = new ParameterizedSQL(`
SELECT iss.created,
iss.saleFk,
iss.quantity,
iss.userFk,
ish.shelvingFk,
p.code,
u.name
FROM itemShelvingSale iss
LEFT JOIN itemShelving ish ON iss.itemShelvingFk = ish.id
LEFT JOIN shelving s ON ish.shelvingFk = s.code
LEFT JOIN parking p ON s.parkingFk = p.id
LEFT JOIN account.user u ON u.id = iss.userFk`
);
stmt.merge(conn.makeSuffix(filter));
return conn.executeStmt(stmt);
};
};

View File

@ -0,0 +1,3 @@
module.exports = Self => {
require('../methods/item-shelving-sale/filter')(Self);
};

View File

@ -12,21 +12,12 @@
"id": true, "id": true,
"description": "Identifier" "description": "Identifier"
}, },
"shelve": {
"type": "string"
},
"shelvingFk": { "shelvingFk": {
"type": "string" "type": "string"
}, },
"itemFk": { "itemFk": {
"type": "number" "type": "number"
}, },
"deep": {
"type": "number"
},
"quantity": {
"type": "number"
},
"created": { "created": {
"type": "date" "type": "date"
} }
@ -41,6 +32,11 @@
"type": "belongsTo", "type": "belongsTo",
"model": "Account", "model": "Account",
"foreignKey": "userFk" "foreignKey": "userFk"
} },
"shelving": {
"type": "belongsTo",
"model": "Shelving",
"foreignKey": "shelvingFk"
}
} }
} }

View File

@ -1,6 +1,6 @@
<mg-ajax <mg-ajax
path="Items/{{patch.params.id}}" path="Items/{{patch.params.id}}"
options="vnPatch" options="vnPatch"
override="{filter: {include: [{relation: 'itemType'}, {relation: 'origin'}, {relation: 'ink'}, {relation: 'producer'}, {relation: 'expense'}]}}"> override="{filter: {include: [{relation: 'itemType'}, {relation: 'origin'}, {relation: 'ink'}, {relation: 'producer'}, {relation: 'expense'}]}}">
</mg-ajax> </mg-ajax>
<vn-watcher <vn-watcher
@ -26,8 +26,8 @@
rule rule
vn-focus> vn-focus>
</vn-textfield> </vn-textfield>
<vn-textfield <vn-textfield
vn-one label="Full name" vn-one label="Full name"
ng-model="$ctrl.item.longName" ng-model="$ctrl.item.longName"
rule rule
info="Full name calculates based on tags 1-3. Is not recommended to change it manually"> info="Full name calculates based on tags 1-3. Is not recommended to change it manually">
@ -95,7 +95,7 @@
<vn-input-number <vn-input-number
vn-one vn-one
min="0" min="0"
label="Relevancy" label="Relevancy"
ng-model="$ctrl.item.relevancy" ng-model="$ctrl.item.relevancy"
rule> rule>
</vn-input-number> </vn-input-number>
@ -108,15 +108,15 @@
</vn-input-number> </vn-input-number>
<vn-input-number <vn-input-number
vn-one vn-one
min="0" min="0"
label="stems" label="stems"
ng-model="$ctrl.item.stems" ng-model="$ctrl.item.stems"
rule> rule>
</vn-input-number> </vn-input-number>
<vn-input-number <vn-input-number
vn-one vn-one
min="0" min="0"
label="Multiplier" label="Multiplier"
ng-model="$ctrl.item.stemMultiplier"> ng-model="$ctrl.item.stemMultiplier">
</vn-input-number> </vn-input-number>
</vn-horizontal> </vn-horizontal>
@ -124,10 +124,17 @@
<vn-input-number <vn-input-number
vn-one vn-one
min="0" min="0"
label="Weight/Piece" label="Weight/Piece"
ng-model="$ctrl.item.weightByPiece" ng-model="$ctrl.item.weightByPiece"
rule> rule>
</vn-input-number> </vn-input-number>
<vn-input-number
vn-one
min="0"
label="Units/Box"
ng-model="$ctrl.item.packingOut"
rule>
</vn-input-number>
<vn-autocomplete <vn-autocomplete
vn-one vn-one
label="Generic" label="Generic"
@ -192,7 +199,7 @@
</form> </form>
<!-- Create custom agent dialog --> <!-- Create custom agent dialog -->
<vn-dialog class="edit" <vn-dialog class="edit"
vn-id="intrastat" vn-id="intrastat"
on-accept="$ctrl.onIntrastatAccept()" on-accept="$ctrl.onIntrastatAccept()"
message="New intrastat"> message="New intrastat">
@ -225,12 +232,12 @@
<tpl-body class="itemFilter"> <tpl-body class="itemFilter">
<vn-horizontal> <vn-horizontal>
<vn-textfield <vn-textfield
label="Name" label="Name"
ng-model="$ctrl.itemFilterParams.name" ng-model="$ctrl.itemFilterParams.name"
vn-focus> vn-focus>
</vn-textfield> </vn-textfield>
<vn-textfield <vn-textfield
label="Size" label="Size"
ng-model="$ctrl.itemFilterParams.size"> ng-model="$ctrl.itemFilterParams.size">
</vn-textfield> </vn-textfield>
<vn-autocomplete <vn-autocomplete
@ -262,7 +269,7 @@
</vn-button> </vn-button>
</vn-horizontal> </vn-horizontal>
<vn-crud-model <vn-crud-model
vn-id="itemsModel" vn-id="itemsModel"
url="Items/withName" url="Items/withName"
filter="$ctrl.itemFilter" filter="$ctrl.itemFilter"
data="items" data="items"
@ -305,4 +312,4 @@
warehouse-fk="$ctrl.vnConfig.warehouseFk"> warehouse-fk="$ctrl.vnConfig.warehouseFk">
</vn-item-descriptor-popover> </vn-item-descriptor-popover>
</tpl-body> </tpl-body>
</vn-dialog> </vn-dialog>

View File

@ -44,6 +44,7 @@ Weight/Piece: Peso/tallo
Search items by id, name or barcode: Buscar articulos por identificador, nombre o codigo de barras Search items by id, name or barcode: Buscar articulos por identificador, nombre o codigo de barras
SalesPerson: Comercial SalesPerson: Comercial
Concept: Concepto Concept: Concepto
Units/Box: Unidades/Caja
# Sections # Sections
Items: Artículos Items: Artículos
@ -61,4 +62,4 @@ Item diary: Registro de compra-venta
Last entries: Últimas entradas Last entries: Últimas entradas
Tags: Etiquetas Tags: Etiquetas
Waste breakdown: Desglose de mermas Waste breakdown: Desglose de mermas
Waste breakdown by item: Desglose de mermas por artículo Waste breakdown by item: Desglose de mermas por artículo

View File

@ -0,0 +1,33 @@
module.exports = Self => {
Self.remoteMethodCtx('salePreparingList', {
description: 'Returns a list with the lines of a ticket and its different states of preparation',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'The ticket id',
http: {source: 'path'}
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/:id/salePreparingList`,
verb: 'GET'
}
});
Self.salePreparingList = async(ctx, id, options) => {
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
query = `CALL vn.salePreparingList(?)`;
const [sales] = await Self.rawSql(query, [id], myOptions);
return sales;
};
};

View File

@ -1,5 +1,6 @@
module.exports = Self => { module.exports = Self => {
require('../methods/sale/getClaimableFromTicket')(Self); require('../methods/sale/getClaimableFromTicket')(Self);
require('../methods/sale/salePreparingList')(Self);
require('../methods/sale/reserve')(Self); require('../methods/sale/reserve')(Self);
require('../methods/sale/deleteSales')(Self); require('../methods/sale/deleteSales')(Self);
require('../methods/sale/updatePrice')(Self); require('../methods/sale/updatePrice')(Self);

View File

@ -1,10 +1,11 @@
<vn-crud-model <vn-crud-model
vn-id="model" vn-id="model"
url="SaleTrackings/listSaleTracking" url="sales"
filter="::$ctrl.filter"
link="{ticketFk: $ctrl.$params.id}" link="{ticketFk: $ctrl.$params.id}"
limit="20" limit="20"
data="sales" data="$ctrl.sales"
order="itemFk DESC" order="concept ASC"
auto-load="true"> auto-load="true">
</vn-crud-model> </vn-crud-model>
<vn-data-viewer model="model"> <vn-data-viewer model="model">
@ -12,31 +13,27 @@
<vn-table model="model"> <vn-table model="model">
<vn-thead> <vn-thead>
<vn-tr> <vn-tr>
<vn-th shrink></vn-th> <vn-th field="isChecked" center>Is checked</vn-th>
<vn-th field="itemFk" number>Item</vn-th> <vn-th field="itemFk" number>Item</vn-th>
<vn-th expand>Description</vn-th> <vn-th field="concept">Description</vn-th>
<vn-th field="quantity" number>Quantity</vn-th> <vn-th field="quantity" number>Quantity</vn-th>
<vn-th field="originalQuantity" number>Original</vn-th> <vn-th></vn-th>
<vn-th field="workerFk">Worker</vn-th>
<vn-th field="state" shrink>State</vn-th>
<vn-th field="created" expand>Created</vn-th>
</vn-tr> </vn-tr>
</vn-thead> </vn-thead>
<vn-tbody> <vn-tbody>
<vn-tr ng-repeat="sale in sales"> <vn-tr ng-repeat="sale in $ctrl.sales">
<vn-td shrink> <vn-td center>
<vn-icon <span class="chip {{$ctrl.chipHasSaleGroupDetail(sale.preparingList.hasSaleGroupDetail)}} vn-mx-xs chip2" vn-tooltip="has saleGroupDetail"></span>
class="bright" <span class="chip {{$ctrl.chipIsPreviousSelected(sale.preparingList.isPreviousSelected)}} vn-ml-xs" vn-tooltip="is previousSelected"></span>
icon="warning" <span class="chip {{$ctrl.chipIsPrevious(sale.preparingList.isPrevious)}} vn-mr-xs" vn-tooltip="is previous"></span>
ng-if="sale.quantity != sale.originalQuantity" <span class="chip {{$ctrl.chipIsPrepared(sale.preparingList.isPrepared)}} vn-mx-xs" vn-tooltip="is prepared"></span>
vn-tooltip="The quantity do not match"> <span class="chip {{$ctrl.chipIsControled(sale.preparingList.isControled)}} vn-mx-xs" vn-tooltip="is controled"></span>
</vn-icon>
</vn-td> </vn-td>
<vn-td number> <vn-td number>
<span <span
ng-click="itemDescriptor.show($event, sale.itemFk, sale.id)" ng-click="$ctrl.showItemDescriptor($event, sale)"
class="link"> class="link">
{{sale.itemFk | zeroFill:6}} {{::sale.itemFk | zeroFill:6}}
</span> </span>
</vn-td> </vn-td>
<vn-td vn-fetched-tags> <vn-td vn-fetched-tags>
@ -53,16 +50,18 @@
</vn-fetched-tags> </vn-fetched-tags>
</vn-td> </vn-td>
<vn-td number>{{::sale.quantity}}</vn-td> <vn-td number>{{::sale.quantity}}</vn-td>
<vn-td number>{{::sale.originalQuantity}}</vn-td> <vn-td actions>
<vn-td expand> <vn-icon-button
<span vn-click-stop="$ctrl.showSaleTracking(sale)"
class="link" vn-tooltip="Sale tracking"
ng-click="workerDescriptor.show($event, sale.workerFk)"> icon="history">
{{::sale.userNickname | dashIfEmpty}} </vn-icon-button>
</span> <vn-icon-button
vn-click-stop="$ctrl.showItemShelvingSale(sale)"
vn-tooltip="ItemShelvings sale"
icon="icon-inventory">
</vn-icon-button>
</vn-td> </vn-td>
<vn-td shrink>{{::sale.state}}</vn-td>
<vn-td expand>{{::sale.created | date: 'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr> </vn-tr>
</vn-tbody> </vn-tbody>
</vn-table> </vn-table>
@ -70,8 +69,99 @@
</vn-data-viewer> </vn-data-viewer>
<vn-item-descriptor-popover <vn-item-descriptor-popover
vn-id="item-descriptor" vn-id="item-descriptor"
warehouse-fk="$ctrl.ticket.warehouseFk"> warehouse-fk="$ctrl.ticket.warehouseFk"
ticket-fk="$ctrl.ticket.id">
</vn-item-descriptor-popover> </vn-item-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="worker-descriptor"> <vn-popup vn-id="saleTracking">
</vn-worker-descriptor-popover> <vn-crud-model
vn-id="modelSaleTracking"
url="SaleTrackings/listSaleTracking"
link="{saleFk: $ctrl.saleId}"
limit="20"
data="saleTrackings"
order="itemFk DESC"
auto-load="true">
</vn-crud-model>
<vn-data-viewer model="modelSaleTracking">
<vn-card class="vn-w-lg">
<vn-table model="modelSaleTracking">
<vn-thead>
<vn-tr>
<vn-th field="quantity" number>Quantity</vn-th>
<vn-th field="originalQuantity" number>Original</vn-th>
<vn-th field="workerFk">Worker</vn-th>
<vn-th field="state" shrink>State</vn-th>
<vn-th field="created" expand>Created</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="sale in saleTrackings">
<vn-td number>{{::sale.quantity}}</vn-td>
<vn-td number>{{::sale.originalQuantity}}</vn-td>
<vn-td expand>
<span
class="link"
ng-click="workerDescriptor.show($event, sale.workerFk)">
{{::sale.userNickname | dashIfEmpty}}
</span>
</vn-td>
<vn-td shrink>{{::sale.state}}</vn-td>
<vn-td expand>{{::sale.created | date: 'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-item-descriptor-popover
vn-id="item-descriptor"
warehouse-fk="$ctrl.ticket.warehouseFk">
</vn-item-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="worker-descriptor">
</vn-worker-descriptor-popover>
</vn-popup>
<vn-popup vn-id="itemShelvingSale">
<vn-crud-model
vn-id="modelSaleTracking"
url="ItemShelvingSales/filter"
link="{saleFk: $ctrl.saleId}"
limit="20"
data="$ctrl.itemShelvingSales"
auto-load="true">
</vn-crud-model>
<vn-data-viewer model="modelSaleTracking">
<vn-card class="vn-w-lg">
<vn-table model="modelSaleTracking">
<vn-thead>
<vn-tr>
<vn-th field="quantity" number>Quantity</vn-th>
<vn-th field="workerFk">Worker</vn-th>
<vn-th field="shelving" shrink>Shelving</vn-th>
<vn-th field="parking" shrink>Parking</vn-th>
<vn-th field="created" expand>Created</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="itemShelvingSale in $ctrl.itemShelvingSales">
<vn-td number>{{::itemShelvingSale.quantity}}</vn-td>
<vn-td expand>
<span
class="link"
ng-click="workerDescriptor.show($event, itemShelvingSale.userFk)">
{{::itemShelvingSale.name | dashIfEmpty}}
</span>
</vn-td>
<vn-td shrink>{{::itemShelvingSale.shelvingFk}}</vn-td>
<vn-td shrink>{{::itemShelvingSale.code}}</vn-td>
<vn-td expand>{{::itemShelvingSale.created | date: 'dd/MM/yyyy HH:mm'}}</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-popup>

View File

@ -1,12 +1,100 @@
import ngModule from '../module'; import ngModule from '../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {} class Controller extends Section {
constructor($element, $) {
super($element, $);
this.filter = {
include: [
{
relation: 'item'
}, {
relation: 'saleTracking',
scope: {
fields: ['isChecked']
}
}
]
};
}
get sales() {
return this._sales;
}
set sales(value) {
this._sales = value;
if (value) {
const query = `Sales/${this.$params.id}/salePreparingList`;
this.$http.get(query)
.then(res => {
this.salePreparingList = res.data;
for (const salePreparing of this.salePreparingList) {
for (const sale of this.sales) {
if (salePreparing.saleFk == sale.id)
sale.preparingList = salePreparing;
}
}
});
}
}
showItemDescriptor(event, sale) {
this.quicklinks = {
btnThree: {
icon: 'icon-transaction',
state: `item.card.diary({
id: ${sale.itemFk},
warehouseFk: ${this.ticket.warehouseFk},
lineFk: ${sale.id}
})`,
tooltip: 'Item diary'
}
};
this.$.itemDescriptor.show(event.target, sale.itemFk);
}
chipHasSaleGroupDetail(hasSaleGroupDetail) {
if (hasSaleGroupDetail) return 'pink';
else return 'message';
}
chipIsPreviousSelected(isPreviousSelected) {
if (isPreviousSelected) return 'notice';
else return 'message';
}
chipIsPrevious(isPrevious) {
if (isPrevious) return 'dark-notice';
else return 'message';
}
chipIsPrepared(isPrepared) {
if (isPrepared) return 'warning';
else return 'message';
}
chipIsControled(isControled) {
if (isControled) return 'yellow';
else return 'message';
}
showSaleTracking(sale) {
this.saleId = sale.id;
this.$.saleTracking.show();
}
showItemShelvingSale(sale) {
this.saleId = sale.id;
this.$.itemShelvingSale.show();
}
}
ngModule.vnComponent('vnTicketSaleTracking', { ngModule.vnComponent('vnTicketSaleTracking', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller, controller: Controller,
bindings: { bindings: {
ticket: '<', ticket: '<'
}, }
}); });

View File

@ -0,0 +1,6 @@
ItemShelvings sale: Carros línea
has saleGroupDetail: tiene detalle grupo lineas
is previousSelected: es previa seleccionada
is previous: es previa
is prepared: esta preparado
is controled: esta controlado

View File

@ -0,0 +1,7 @@
@import "variables";
.chip {
display: inline-block;
min-width: 15px;
min-height: 25px;
}

View File

@ -10,6 +10,7 @@ describe('workerTimeControl sendMail()', () => {
const ctx = {req: activeCtx, args: {}}; const ctx = {req: activeCtx, args: {}};
it('should fill time control of a worker without records in Journey and with rest', async() => { it('should fill time control of a worker without records in Journey and with rest', async() => {
pending('https://redmine.verdnatura.es/issues/4903');
const tx = await models.WorkerTimeControl.beginTransaction({}); const tx = await models.WorkerTimeControl.beginTransaction({});
try { try {
@ -34,6 +35,7 @@ describe('workerTimeControl sendMail()', () => {
}); });
it('should fill time control of a worker without records in Journey and without rest', async() => { it('should fill time control of a worker without records in Journey and without rest', async() => {
pending('https://redmine.verdnatura.es/issues/4903');
const workdayOf20Hours = 3; const workdayOf20Hours = 3;
const tx = await models.WorkerTimeControl.beginTransaction({}); const tx = await models.WorkerTimeControl.beginTransaction({});
@ -61,6 +63,7 @@ describe('workerTimeControl sendMail()', () => {
}); });
it('should fill time control of a worker with records in Journey and with rest', async() => { it('should fill time control of a worker with records in Journey and with rest', async() => {
pending('https://redmine.verdnatura.es/issues/4903');
const tx = await models.WorkerTimeControl.beginTransaction({}); const tx = await models.WorkerTimeControl.beginTransaction({});
try { try {
@ -92,6 +95,7 @@ describe('workerTimeControl sendMail()', () => {
}); });
it('should fill time control of a worker with records in Journey and without rest', async() => { it('should fill time control of a worker with records in Journey and without rest', async() => {
pending('https://redmine.verdnatura.es/issues/4903');
const tx = await models.WorkerTimeControl.beginTransaction({}); const tx = await models.WorkerTimeControl.beginTransaction({});
try { try {

View File

@ -201,6 +201,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
describe('WorkerTimeControl_clockIn calls', () => { describe('WorkerTimeControl_clockIn calls', () => {
it('should fail to add a time entry if the target user has an absence that day', async() => { it('should fail to add a time entry if the target user has an absence that day', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId; const workerId = hankPymId;
const date = new Date(); const date = new Date();
@ -247,6 +248,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
describe('direction errors', () => { describe('direction errors', () => {
it('should throw an error when trying "in" direction twice', async() => { it('should throw an error when trying "in" direction twice', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId; const workerId = hankPymId;
@ -276,6 +278,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
}); });
it('should throw an error when trying "in" direction after insert "in" and "middle"', async() => { it('should throw an error when trying "in" direction after insert "in" and "middle"', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId; const workerId = hankPymId;
@ -309,6 +312,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
}); });
it('Should throw an error when trying "out" before closing a "middle" couple', async() => { it('Should throw an error when trying "out" before closing a "middle" couple', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId; const workerId = hankPymId;
@ -342,6 +346,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
}); });
it('should throw an error when trying "middle" after "out"', async() => { it('should throw an error when trying "middle" after "out"', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId; const workerId = hankPymId;
@ -375,6 +380,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
}); });
it('should throw an error when trying "out" direction twice', async() => { it('should throw an error when trying "out" direction twice', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId; const workerId = hankPymId;
@ -446,6 +452,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
}); });
it('should not fail as the 12h rest is fulfilled', async() => { it('should not fail as the 12h rest is fulfilled', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId; const workerId = hankPymId;
@ -482,6 +489,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
describe('for 3500kg drivers with enforced 9h rest', () => { describe('for 3500kg drivers with enforced 9h rest', () => {
it('should throw an error when the 9h enforced rest is not fulfilled', async() => { it('should throw an error when the 9h enforced rest is not fulfilled', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = jessicaJonesId; const workerId = jessicaJonesId;
@ -516,6 +524,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
}); });
it('should not fail when the 9h enforced rest is fulfilled', async() => { it('should not fail when the 9h enforced rest is fulfilled', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = jessicaJonesId; const workerId = jessicaJonesId;
@ -552,6 +561,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
describe('for 36h weekly rest', () => { describe('for 36h weekly rest', () => {
it('should throw an error when the 36h weekly rest is not fulfilled', async() => { it('should throw an error when the 36h weekly rest is not fulfilled', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId; const workerId = hankPymId;
@ -586,6 +596,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
}); });
it('should throw an error when the 36h weekly rest is not fulfilled again', async() => { it('should throw an error when the 36h weekly rest is not fulfilled again', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId; const workerId = hankPymId;
@ -619,6 +630,7 @@ describe('workerTimeControl add/delete timeEntry()', () => {
describe('for 72h weekly rest', () => { describe('for 72h weekly rest', () => {
it('should throw when the 72h weekly rest is not fulfilled yet', async() => { it('should throw when the 72h weekly rest is not fulfilled yet', async() => {
pending('https://redmine.verdnatura.es/issues/4707');
activeCtx.accessToken.userId = salesBossId; activeCtx.accessToken.userId = salesBossId;
const workerId = hankPymId; const workerId = hankPymId;