Merge branch 'dev' into 3686-client_create
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Carlos Jimenez Ruiz 2022-03-28 09:33:24 +00:00
commit 29f8fd1fc6
36 changed files with 625 additions and 339 deletions

View File

@ -0,0 +1,8 @@
CREATE TABLE `vn`.`claimConfig` (
`id` int(11) NOT NULL,
`pickupContact` varchar(250),
PRIMARY KEY (`id`)
);
INSERT INTO vn.claimConfig (id, pickupContact)
VALUES(1, 'Email: cmorenoa@logista.com Telf: 961594250 Extensión: 206');

View File

@ -0,0 +1,2 @@
ALTER TABLE `vn`.`claimState` ADD `hasToNotify` TINYINT DEFAULT 0 NULL;
UPDATE `vn`.`claimState` SET `hasToNotify` = 1 WHERE `code` IN ('canceled', 'incomplete');

View File

@ -812,25 +812,25 @@ INSERT INTO `vn`.`itemFamily`(`code`, `description`)
('VT', 'Sales'); ('VT', 'Sales');
INSERT INTO `vn`.`item`(`id`, `typeFk`, `size`, `inkFk`, `stems`, `originFk`, `description`, `producerFk`, `intrastatFk`, `expenceFk`, INSERT INTO `vn`.`item`(`id`, `typeFk`, `size`, `inkFk`, `stems`, `originFk`, `description`, `producerFk`, `intrastatFk`, `expenceFk`,
`comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`) `comment`, `relevancy`, `image`, `subName`, `minPrice`, `stars`, `family`, `isFloramondo`, `genericFk`, `itemPackingTypeFk`, `hasMinPrice`)
VALUES VALUES
(1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'VT', 0, NULL, 'V'), (1, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '1', NULL, 0, 1, 'VT', 0, NULL, 'V', 0),
(2, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H'), (2, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '2', NULL, 0, 2, 'VT', 0, NULL, 'H', 0),
(3, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL), (3, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '3', NULL, 0, 5, 'VT', 0, NULL, NULL, 0),
(4, 1, 60, 'YEL', 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL), (4, 1, 60, 'YEL', 1, 1, 'Increases block', 1, 05080000, 4751000000, NULL, 0, '4', NULL, 0, 3, 'VT', 0, NULL, NULL, 0),
(5, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '5', NULL, 0, 3, 'VT', 0, NULL, NULL), (5, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '5', NULL, 0, 3, 'VT', 0, NULL, NULL, 0),
(6, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '6', NULL, 0, 4, 'VT', 0, NULL, NULL), (6, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '6', NULL, 0, 4, 'VT', 0, NULL, NULL, 0),
(7, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '7', NULL, 0, 4, 'VT', 0, NULL, NULL), (7, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '7', NULL, 0, 4, 'VT', 0, NULL, NULL, 0),
(8, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '8', NULL, 0, 5, 'VT', 0, NULL, NULL), (8, 2, 70, 'YEL', 1, 1, NULL, 1, 06021010, 2000000000, NULL, 0, '8', NULL, 0, 5, 'VT', 0, NULL, NULL, 0),
(9, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '9', NULL, 0, 4, 'VT', 1, NULL, NULL), (9, 2, 70, 'BLU', 1, 2, NULL, 1, 06021010, 2000000000, NULL, 0, '9', NULL, 0, 4, 'VT', 1, NULL, NULL, 0),
(10, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '10', NULL, 0, 4, 'VT', 0, NULL, NULL), (10, 1, 60, 'YEL', 1, 3, NULL, 1, 05080000, 4751000000, NULL, 0, '10', NULL, 0, 4, 'VT', 0, NULL, NULL, 0),
(11, 1, 60, 'YEL', 1, 1, NULL, 1, 05080000, 4751000000, NULL, 0, '11', NULL, 0, 4, 'VT', 0, NULL, NULL), (11, 1, 60, 'YEL', 1, 1, NULL, 1, 05080000, 4751000000, NULL, 0, '11', NULL, 0, 4, 'VT', 0, NULL, NULL, 0),
(12, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '12', NULL, 0, 3, 'VT', 0, NULL, NULL), (12, 3, 30, 'RED', 1, 2, NULL, 2, 06021010, 4751000000, NULL, 0, '12', NULL, 0, 3, 'VT', 0, NULL, NULL, 0),
(13, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '13', NULL, 0, 2, 'VT', 1, NULL, NULL), (13, 5, 30, 'RED', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '13', NULL, 1, 2, 'VT', 1, NULL, NULL, 1),
(14, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 4, 'VT', 1, NULL, NULL), (14, 5, 90, 'BLU', 1, 2, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 4, 'VT', 1, NULL, NULL, 0),
(15, 4, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL), (15, 4, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0),
(16, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL), (16, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'EMB', 0, NULL, NULL, 0),
(71, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'VT', 0, NULL, NULL); (71, 6, NULL, NULL, NULL, 1, NULL, NULL, 06021010, 4751000000, NULL, 0, '', NULL, 0, 0, 'VT', 0, NULL, NULL, 0);
-- Update the taxClass after insert of the items -- Update the taxClass after insert of the items
UPDATE `vn`.`itemTaxCountry` SET `taxClassFk` = 2 UPDATE `vn`.`itemTaxCountry` SET `taxClassFk` = 2
@ -840,7 +840,7 @@ INSERT INTO `vn`.`priceFixed`(`id`, `itemFk`, `rate0`, `rate1`, `rate2`, `rate3`
VALUES VALUES
(1, 1, 0, 0, 2.5, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 0, 1, CURDATE()), (1, 1, 0, 0, 2.5, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 0, 1, CURDATE()),
(2, 3, 10, 10, 10, 10, CURDATE(), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 0, 1, CURDATE()), (2, 3, 10, 10, 10, 10, CURDATE(), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 0, 1, CURDATE()),
(3, 5, 8.5, 10, 7.5, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 1, 2, CURDATE()); (3, 13, 8.5, 10, 7.5, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 1, 2, CURDATE());
INSERT INTO `vn`.`expeditionBoxVol`(`boxFk`, `m3`, `ratio`) INSERT INTO `vn`.`expeditionBoxVol`(`boxFk`, `m3`, `ratio`)
VALUES VALUES
@ -1700,15 +1700,15 @@ INSERT INTO `vn`.`clientSample`(`id`, `clientFk`, `typeFk`, `created`, `workerFk
(4, 1102, 2, CURDATE(), 18, 18, 567), (4, 1102, 2, CURDATE(), 18, 18, 567),
(5, 1102, 3, CURDATE(), 19, 19, 567); (5, 1102, 3, CURDATE(), 19, 19, 567);
INSERT INTO `vn`.`claimState`(`id`, `code`, `description`, `roleFk`, `priority`) INSERT INTO `vn`.`claimState`(`id`, `code`, `description`, `roleFk`, `priority`, `hasToNotify`)
VALUES VALUES
( 1, 'pending', 'Pendiente', 1, 1), ( 1, 'pending', 'Pendiente', 1, 1, 0),
( 2, 'managed', 'Gestionado', 1, 5), ( 2, 'managed', 'Gestionado', 1, 5, 0),
( 3, 'resolved', 'Resuelto', 72, 7), ( 3, 'resolved', 'Resuelto', 72, 7, 0),
( 4, 'canceled', 'Anulado', 72, 6), ( 4, 'canceled', 'Anulado', 72, 6, 1),
( 5, 'incomplete', 'Incompleta', 72, 3), ( 5, 'incomplete', 'Incompleta', 72, 3, 1),
( 6, 'mana', 'Mana', 1, 4), ( 6, 'mana', 'Mana', 1, 4, 0),
( 7, 'lack', 'Faltas', 1, 2); ( 7, 'lack', 'Faltas', 1, 2, 0);
INSERT INTO `vn`.`claim`(`id`, `ticketCreated`, `claimStateFk`, `observation`, `clientFk`, `workerFk`, `responsibility`, `isChargedToMana`, `created`, `packages`) INSERT INTO `vn`.`claim`(`id`, `ticketCreated`, `claimStateFk`, `observation`, `clientFk`, `workerFk`, `responsibility`, `isChargedToMana`, `created`, `packages`)
VALUES VALUES

View File

@ -346,16 +346,17 @@ export default {
saveFieldsButton: '.vn-popover.shown vn-button[label="Save"] > button' saveFieldsButton: '.vn-popover.shown vn-button[label="Save"] > button'
}, },
itemFixedPrice: { itemFixedPrice: {
add: 'vn-fixed-price vn-icon[icon="add_circle"]', add: 'vn-fixed-price vn-icon-button[icon="add_circle"]',
fourthFixedPrice: 'vn-fixed-price vn-tr:nth-child(4)', fourthFixedPrice: 'vn-fixed-price tr:nth-child(5)',
fourthItemID: 'vn-fixed-price vn-tr:nth-child(4) vn-autocomplete[ng-model="price.itemFk"]', fourthItemID: 'vn-fixed-price tr:nth-child(5) vn-autocomplete[ng-model="price.itemFk"]',
fourthWarehouse: 'vn-fixed-price vn-tr:nth-child(4) vn-autocomplete[ng-model="price.warehouseFk"]', fourthWarehouse: 'vn-fixed-price tr:nth-child(5) vn-autocomplete[ng-model="price.warehouseFk"]',
fourthPPU: 'vn-fixed-price vn-tr:nth-child(4) > vn-td-editable:nth-child(4)', fourthPPU: 'vn-fixed-price tr:nth-child(5) > td:nth-child(4)',
fourthPPP: 'vn-fixed-price vn-tr:nth-child(4) > vn-td-editable:nth-child(5)', fourthPPP: 'vn-fixed-price tr:nth-child(5) > td:nth-child(5)',
fourthMinPrice: 'vn-fixed-price vn-tr:nth-child(4) > vn-td-editable:nth-child(6)', fourthHasMinPrice: 'vn-fixed-price tr:nth-child(5) > td:nth-child(6) > vn-check[ng-model="price.hasMinPrice"]',
fourthStarted: 'vn-fixed-price vn-tr:nth-child(4) vn-date-picker[ng-model="price.started"]', fourthMinPrice: 'vn-fixed-price tr:nth-child(5) > td:nth-child(6) > vn-input-number[ng-model="price.minPrice"]',
fourthEnded: 'vn-fixed-price vn-tr:nth-child(4) vn-date-picker[ng-model="price.ended"]', fourthStarted: 'vn-fixed-price tr:nth-child(5) vn-date-picker[ng-model="price.started"]',
fourthDeleteIcon: 'vn-fixed-price vn-tr:nth-child(4) > vn-td:nth-child(9) > vn-icon-button[icon="delete"]' fourthEnded: 'vn-fixed-price tr:nth-child(5) vn-date-picker[ng-model="price.ended"]',
fourthDeleteIcon: 'vn-fixed-price tr:nth-child(5) > td:nth-child(9) > vn-icon-button[icon="delete"]'
}, },
itemCreateView: { itemCreateView: {
temporalName: 'vn-item-create vn-textfield[ng-model="$ctrl.item.provisionalName"]', temporalName: 'vn-item-create vn-textfield[ng-model="$ctrl.item.provisionalName"]',

View File

@ -16,33 +16,17 @@ describe('Item fixed prices path', () => {
}); });
it('should click on the add new foxed price button', async() => { it('should click on the add new foxed price button', async() => {
await page.doSearch();
await page.waitToClick(selectors.itemFixedPrice.add); await page.waitToClick(selectors.itemFixedPrice.add);
await page.waitForSelector(selectors.itemFixedPrice.fourthFixedPrice); await page.waitForSelector(selectors.itemFixedPrice.fourthFixedPrice);
}); });
it('should fill the fixed price data', async() => { it('should fill the fixed price data', async() => {
const now = new Date(); const now = new Date();
const searchValue = 'Chest ammo box';
await page.waitToClick(selectors.itemFixedPrice.fourthItemID);
await page.write('body > div > div > div.content > div.filter.ng-scope > vn-textfield', searchValue);
try {
await page.waitForFunction(searchValue => {
const element = document.querySelector('li.active');
if (element)
return element.innerText.toLowerCase().includes(searchValue.toLowerCase());
}, {}, searchValue);
} catch (error) {
const builtSelector = await page.selectorFormater(selectors.ticketSales.moreMenuState);
const inputValue = await page.evaluate(() => {
return document.querySelector('.vn-drop-down.shown vn-textfield input').value;
});
throw new Error(`${builtSelector} value is ${inputValue}! ${error}`);
}
await page.keyboard.press('Enter');
await page.autocompleteSearch(selectors.itemFixedPrice.fourthWarehouse, 'Warehouse one'); await page.autocompleteSearch(selectors.itemFixedPrice.fourthWarehouse, 'Warehouse one');
await page.writeOnEditableTD(selectors.itemFixedPrice.fourthPPU, '20'); await page.write(selectors.itemFixedPrice.fourthPPU, '1');
await page.writeOnEditableTD(selectors.itemFixedPrice.fourthPPP, '10'); await page.write(selectors.itemFixedPrice.fourthPPP, '1');
await page.writeOnEditableTD(selectors.itemFixedPrice.fourthMinPrice, '5'); await page.write(selectors.itemFixedPrice.fourthMinPrice, '1');
await page.pickDate(selectors.itemFixedPrice.fourthStarted, now); await page.pickDate(selectors.itemFixedPrice.fourthStarted, now);
await page.pickDate(selectors.itemFixedPrice.fourthEnded, now); await page.pickDate(selectors.itemFixedPrice.fourthEnded, now);
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
@ -53,7 +37,9 @@ describe('Item fixed prices path', () => {
it('should reload the section and check the created price has the expected ID', async() => { it('should reload the section and check the created price has the expected ID', async() => {
await page.accessToSection('item.index'); await page.accessToSection('item.index');
await page.accessToSection('item.fixedPrice'); await page.accessToSection('item.fixedPrice');
const result = await page.getProperty('vn-fixed-price > div > vn-card > vn-table > div > vn-tbody > vn-tr:nth-child(4) > vn-td:nth-child(1) > span', 'innerText'); await page.doSearch();
const result = await page.waitToGetProperty(selectors.itemFixedPrice.fourthItemID, 'value');
expect(result).toContain('13'); expect(result).toContain('13');
}); });

View File

@ -32,6 +32,9 @@ export default class SmartTable extends Component {
this._options = options; this._options = options;
if (!options) return; if (!options) return;
if (options.defaultSearch)
this.displaySearch();
const activeButtons = options.activeButtons; const activeButtons = options.activeButtons;
const missingId = activeButtons && activeButtons.shownColumns && !this.viewConfigId; const missingId = activeButtons && activeButtons.shownColumns && !this.viewConfigId;
if (missingId) if (missingId)

View File

@ -70,6 +70,7 @@
"Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})", "Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})",
"Claim will be picked": "The product from the claim [({{claimId}})]({{{claimUrl}}}) from the client *{{clientName}}* will be picked", "Claim will be picked": "The product from the claim [({{claimId}})]({{{claimUrl}}}) from the client *{{clientName}}* will be picked",
"Claim state has changed to incomplete": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *incomplete*", "Claim state has changed to incomplete": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *incomplete*",
"Claim state has changed to canceled": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *canceled*",
"This ticket is not an stowaway anymore": "The ticket id [{{ticketId}}]({{{ticketUrl}}}) is not an stowaway anymore", "This ticket is not an stowaway anymore": "The ticket id [{{ticketId}}]({{{ticketUrl}}}) is not an stowaway anymore",
"Customs agent is required for a non UEE member": "Customs agent is required for a non UEE member", "Customs agent is required for a non UEE member": "Customs agent is required for a non UEE member",
"Incoterms is required for a non UEE member": "Incoterms is required for a non UEE member", "Incoterms is required for a non UEE member": "Incoterms is required for a non UEE member",

View File

@ -137,6 +137,7 @@
"Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})", "Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})",
"Claim will be picked": "Se recogerá el género de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}*", "Claim will be picked": "Se recogerá el género de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}*",
"Claim state has changed to incomplete": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *incompleta*", "Claim state has changed to incomplete": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *incompleta*",
"Claim state has changed to canceled": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *anulado*",
"This ticket is not an stowaway anymore": "El ticket id [{{ticketId}}]({{{ticketUrl}}}) ha dejado de ser un polizón", "This ticket is not an stowaway anymore": "El ticket id [{{ticketId}}]({{{ticketUrl}}}) ha dejado de ser un polizón",
"Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}", "Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}",
"ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto", "ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto",

View File

@ -47,7 +47,7 @@ describe('Update Claim', () => {
expect(error.message).toEqual(`You don't have enough privileges to change that field`); expect(error.message).toEqual(`You don't have enough privileges to change that field`);
}); });
it(`should success to update the claim within privileges `, async() => { it(`should success to update the claimState to 'canceled' and send a rocket message`, async() => {
const tx = await app.models.Claim.beginTransaction({}); const tx = await app.models.Claim.beginTransaction({});
try { try {
@ -55,13 +55,15 @@ describe('Update Claim', () => {
const newClaim = await app.models.Claim.create(originalData, options); const newClaim = await app.models.Claim.create(originalData, options);
const chatModel = app.models.Chat;
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
const canceledState = 4; const canceledState = 4;
const claimManagerId = 72; const claimManagerId = 72;
const ctx = { const ctx = {
req: { req: {
accessToken: { accessToken: {userId: claimManagerId},
userId: claimManagerId headers: {origin: 'http://localhost'}
}
}, },
args: { args: {
observation: 'valid observation', observation: 'valid observation',
@ -69,11 +71,56 @@ describe('Update Claim', () => {
hasToPickUp: false hasToPickUp: false
} }
}; };
ctx.req.__ = (value, params) => {
return params.nickname;
};
await app.models.Claim.updateClaim(ctx, newClaim.id, options); await app.models.Claim.updateClaim(ctx, newClaim.id, options);
let updatedClaim = await app.models.Claim.findById(newClaim.id, null, options); let updatedClaim = await app.models.Claim.findById(newClaim.id, null, options);
expect(updatedClaim.observation).toEqual(ctx.args.observation); expect(updatedClaim.observation).toEqual(ctx.args.observation);
expect(chatModel.sendCheckingPresence).toHaveBeenCalled();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it(`should success to update the claimState to 'incomplete' and send a rocket message`, async() => {
const tx = await app.models.Claim.beginTransaction({});
try {
const options = {transaction: tx};
const newClaim = await app.models.Claim.create(originalData, options);
const chatModel = app.models.Chat;
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
const incompleteState = 5;
const claimManagerId = 72;
const ctx = {
req: {
accessToken: {userId: claimManagerId},
headers: {origin: 'http://localhost'}
},
args: {
observation: 'valid observation',
claimStateFk: incompleteState,
hasToPickUp: false
}
};
ctx.req.__ = (value, params) => {
return params.nickname;
};
await app.models.Claim.updateClaim(ctx, newClaim.id, options);
let updatedClaim = await app.models.Claim.findById(newClaim.id, null, options);
expect(updatedClaim.observation).toEqual(ctx.args.observation);
expect(chatModel.sendCheckingPresence).toHaveBeenCalled();
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -96,9 +96,12 @@ module.exports = Self => {
// When claimState has been changed // When claimState has been changed
if (args.claimStateFk) { if (args.claimStateFk) {
const newState = await models.ClaimState.findById(args.claimStateFk, null, options); const newState = await models.ClaimState.findById(args.claimStateFk, null, options);
if (newState.hasToNotify) {
if (newState.code == 'incomplete') if (newState.code == 'incomplete')
notifyStateChange(ctx, salesPerson.id, claim); notifyStateChange(ctx, salesPerson.id, claim, newState.code);
if (newState.code == 'canceled')
notifyStateChange(ctx, claim.workerFk, claim, newState.code);
}
} }
if (tx) await tx.commit(); if (tx) await tx.commit();
@ -125,11 +128,12 @@ module.exports = Self => {
return canUpdate; return canUpdate;
} }
async function notifyStateChange(ctx, workerId, claim) { async function notifyStateChange(ctx, workerId, claim, state) {
const origin = ctx.req.headers.origin;
const models = Self.app.models; const models = Self.app.models;
const origin = ctx.req.headers.origin;
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
const message = $t('Claim state has changed to incomplete', {
const message = $t(`Claim state has changed to ${state}`, {
claimId: claim.id, claimId: claim.id,
clientName: claim.client().name, clientName: claim.client().name,
claimUrl: `${origin}/#!/claim/${claim.id}/summary` claimUrl: `${origin}/#!/claim/${claim.id}/summary`

View File

@ -13,20 +13,24 @@
}, },
"properties": { "properties": {
"id": { "id": {
"type": "Number", "type": "number",
"id": true, "id": true,
"description": "Identifier" "description": "Identifier"
}, },
"code": { "code": {
"type": "String", "type": "string",
"required": true "required": true
}, },
"description": { "description": {
"type": "String", "type": "string",
"required": true "required": true
}, },
"priority": { "priority": {
"type": "Number", "type": "number",
"required": true
},
"hasToNotify": {
"type": "boolean",
"required": true "required": true
} }
}, },

View File

@ -39,17 +39,25 @@ class Controller extends Descriptor {
loadData() { loadData() {
const filter = { const filter = {
include: [ include: [
{ {relation: 'supplier'},
relation: 'company', {relation: 'invoiceInDueDay'},
{relation: 'company',
scope: { scope: {
fields: ['id', 'code'] fields: ['id', 'code']
} }
} }
] ]
}; };
return this.getData(`InvoiceIns/${this.id}`, {filter}) return this.getData(`InvoiceIns/${this.id}`, {filter})
.then(res => this.entity = res.data); .then(res => {
this.entity = res.data;
this.invoiceIn.amount = res.data.invoiceInDueDay.reduce(
(accumulator, currentValue) => {
return accumulator + (currentValue['amount'] || 0);
}, 0);
});
} }
checkToBook() { checkToBook() {

View File

@ -12,12 +12,25 @@ describe('vnInvoiceInDescriptor', () => {
controller = $componentController('vnInvoiceInDescriptor', {$element}); controller = $componentController('vnInvoiceInDescriptor', {$element});
controller.invoiceIn = {id: 1}; controller.invoiceIn = {id: 1};
$httpBackend.when('GET', `InvoiceIns/${controller.invoiceIn.id}`).respond({id: 1});
})); }));
describe('loadData()', () => { describe('loadData()', () => {
it(`should perform a get query to store the invoice in data into the controller`, () => { it(`should perform a get query to store the invoice in data into the controller`, () => {
expect(controller.invoiceIn).toEqual({id: 1}); const invoiceIn = {
id: 1,
invoiceInDueDay: [
{amount: 1},
{amount: 2}
]
};
const expectedAmount = invoiceIn.invoiceInDueDay[0].amount + invoiceIn.invoiceInDueDay[1].amount;
$httpBackend.when('GET', `InvoiceIns/${controller.invoiceIn.id}`).respond(invoiceIn);
controller.loadData();
$httpBackend.flush();
expect(controller.invoiceIn.id).toEqual(invoiceIn.id);
expect(controller.invoiceIn.amount).toEqual(expectedAmount);
}); });
}); });

View File

@ -90,7 +90,7 @@ describe('fixed price filter()', () => {
} }
}); });
it('should return no results filtering by hasMinPrice', async() => { it('should return 1 result filtering by hasMinPrice', async() => {
const tx = await models.FixedPrice.beginTransaction({}); const tx = await models.FixedPrice.beginTransaction({});
try { try {
@ -103,7 +103,7 @@ describe('fixed price filter()', () => {
}; };
const result = await models.FixedPrice.filter(ctx, null, options); const result = await models.FixedPrice.filter(ctx, null, options);
expect(result.length).toEqual(0); expect(result.length).toEqual(1);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -3,8 +3,8 @@
url="FixedPrices/filter" url="FixedPrices/filter"
limit="20" limit="20"
data="prices" data="prices"
auto-load="true" order="itemFk"
order="itemFk"> auto-load="false">
</vn-crud-model> </vn-crud-model>
<vn-crud-model <vn-crud-model
auto-load="true" auto-load="true"
@ -18,147 +18,166 @@
panel="vn-fixed-price-search-panel" panel="vn-fixed-price-search-panel"
info="Search prices by item ID or code" info="Search prices by item ID or code"
placeholder="Search fixed prices" placeholder="Search fixed prices"
filter="{}"
model="model"> model="model">
</vn-searchbar> </vn-searchbar>
</vn-portal> </vn-portal>
<div class="vn-w-lg"> <div class="vn-w-xl">
<vn-card> <vn-card>
<vn-table model="model"> <smart-table
<vn-thead>
<vn-tr>
<vn-th field="itemFk" shrink>Item ID</vn-th>
<vn-th field="itemFk">Description</vn-th>
<vn-th field="warehouseFk">Warehouse</vn-th>
<vn-th field="rate2">P.P.U.</vn-th>
<vn-th field="rate3">P.P.P.</vn-th>
<vn-th field="minPrice">Min price</vn-th>
<vn-th field="started" style="width: 90px">Started</vn-th>
<vn-th field="ended" style="width: 90px">Ended</vn-th>
<vn-th shrink></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="price in prices">
<vn-td shrink>
<span
ng-if="price.itemFk"
ng-click="itemDescriptor.show($event, price.itemFk)"
class="link">
{{price.itemFk}}
</span>
<vn-autocomplete
class="dense"
ng-if="!price.itemFk"
vn-focus
url="Items/withName"
ng-model="price.itemFk"
show-field="name"
value-field="id"
search-function="$ctrl.itemSearchFunc($search)"
on-change="$ctrl.upsertPrice(price)"
order="id DESC"
tabindex="1">
<tpl-item>
{{::id}} - {{::name}}
</tpl-item>
</vn-autocomplete>
</vn-td>
<vn-td vn-fetched-tags>
<div>
<vn-one title="{{price.name}}">{{price.name}}</vn-one>
<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>
</vn-td>
<vn-td>
<vn-autocomplete
vn-one
label="Warehouse"
ng-model="price.warehouseFk"
url="Warehouses"
on-change="$ctrl.upsertPrice(price)"
tabindex="2">
</vn-autocomplete>
</vn-td>
<vn-td-editable number>
<text>{{price.rate2 | currency: 'EUR':2}}</text>
<field>
<vn-input-number
class="dense"
vn-focus
ng-model="price.rate2"
on-change="$ctrl.upsertPrice(price)">
</vn-input-number>
</field>
</vn-td-editable>
<vn-td-editable number>
<text>{{price.rate3 | currency: 'EUR':2}}</text>
<field>
<vn-input-number
class="dense"
vn-focus
ng-model="price.rate3"
on-change="$ctrl.upsertPrice(price)">
</vn-input-number>
</field>
</vn-td-editable>
<vn-td-editable number>
<text>{{(price.hasMinPrice ? (price.minPrice | currency: 'EUR':2) : "-")}}</text>
<field>
<vn-input-number
class="dense"
vn-focus
ng-model="price.minPrice"
on-change="$ctrl.upsertPrice(price)"
step="0.01">
</vn-input-number>
</field>
</vn-td-editable>
<vn-td>
<vn-date-picker
vn-one
label="Started"
ng-model="price.started"
on-change="$ctrl.upsertPrice(price)">
</vn-date-picker>
</vn-td>
<vn-td>
<vn-date-picker
vn-one
label="Ended"
ng-model="price.ended"
on-change="$ctrl.upsertPrice(price)">
</vn-date-picker>
</vn-td>
<vn-td shrink>
<vn-icon-button
icon="delete"
vn-tooltip="Delete"
ng-click="deleteFixedPrice.show({$index})">
</vn-icon-button>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
<div class="vn-pa-md">
<vn-icon-button
vn-tooltip="Add fixed price"
icon="add_circle"
vn-bind="+"
ng-click="model.insert()">
</vn-icon-button>
</div>
<vn-pagination
model="model" model="model"
class="vn-pt-md"> options="$ctrl.smartTableOptions"
</vn-pagination> expr-builder="$ctrl.exprBuilder(param, value)">
<slot-table>
<table>
<thead>
<tr>
<th field="itemFk">
<span translate>Item ID</span>
</th>
<th field="itemName">
<span translate>Description</span>
</th>
<th field="warehouseFk">
<span translate>Warehouse</span>
</th>
<th
field="rate2"
vn-tooltip="Price By Unit">
<span translate>P.P.U.</span>
</th>
<th
field="rate3"
vn-tooltip="Price By Package">
<span translate>P.P.P.</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 shrink></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="price in prices">
<td shrink-field>
<vn-autocomplete
vn-focus
class="dense"
url="Items/withName"
ng-model="price.itemFk"
show-field="name"
value-field="id"
search-function="$ctrl.itemSearchFunc($search)"
on-change="$ctrl.upsertPrice(price)"
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">
{{price.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-expand>
<vn-autocomplete
vn-one
ng-model="price.warehouseFk"
data="warehouses"
on-change="$ctrl.upsertPrice(price)"
tabindex="2">
</vn-autocomplete>
</td>
<td shrink-field>
<vn-input-number
ng-model="price.rate2"
on-change="$ctrl.upsertPrice(price)"
step="0.01">
</vn-input-number>
</td>
<td shrink-field>
<vn-input-number
ng-model="price.rate3"
on-change="$ctrl.upsertPrice(price)"
step="0.01">
</vn-input-number>
</td>
<td shrink-field-expand class="minPrice">
<vn-check
vn-one
ng-model="price.hasMinPrice">
</vn-check>
<vn-input-number
disabled="!price.hasMinPrice"
ng-model="price.minPrice"
on-change="$ctrl.upsertPrice(price)"
step="0.01">
</vn-input-number>
</td>
<td shrink-date>
<vn-date-picker
vn-one
ng-model="price.started"
on-change="$ctrl.upsertPrice(price)">
</vn-date-picker>
</td>
<td shrink-date>
<vn-date-picker
vn-one
ng-model="price.ended"
on-change="$ctrl.upsertPrice(price)">
</vn-date-picker>
</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>
<vn-pagination
model="model"
class="vn-pt-md"
scroll-selector="vn-item-price-fixed vn-table"
scroll-offset="100">
</vn-pagination>
</slot-table>
</smart-table>
</vn-card> </vn-card>
</div> </div>
<vn-item-descriptor-popover <vn-item-descriptor-popover

View File

@ -5,13 +5,69 @@ import './style.scss';
export default class Controller extends Section { export default class Controller extends Section {
constructor($element, $) { constructor($element, $) {
super($element, $); super($element, $);
this.smartTableOptions = {
activeButtons: {
search: true
},
defaultSearch: true,
columns: [
{
field: 'itemName',
autocomplete: {
url: 'Items',
showField: 'name',
valueField: 'id'
}
},
{
field: 'warehouseFk',
autocomplete: {
url: 'Warehouses',
showField: 'name',
valueField: 'id',
}
},
{
field: 'started',
searchable: false
},
{
field: 'ended',
searchable: false
}
]
};
} }
/**
* Inserts a new instance
*/
add() { add() {
this.$.model.insert({}); if (!this.$.model.data || this.$.model.data.length == 0) {
this.$.model.data = [];
this.$.model.proxiedData = [];
this.$.model.insert({});
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) { upsertPrice(price) {
@ -46,6 +102,22 @@ export default class Controller extends Section {
? {id: $search} ? {id: $search}
: {name: {like: '%' + $search + '%'}}; : {name: {like: '%' + $search + '%'}};
} }
exprBuilder(param, value) {
switch (param) {
case 'itemName':
return {'i.id': 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', { ngModule.vnComponent('vnFixedPrice', {

View File

@ -55,6 +55,8 @@ describe('fixed price', () => {
jest.spyOn(controller.vnApp, 'showSuccess'); jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller.$.model, 'remove'); jest.spyOn(controller.$.model, 'remove');
$httpBackend.expectGET('Warehouses').respond();
controller.removePrice($index); controller.removePrice($index);
expect(controller.vnApp.showSuccess).not.toHaveBeenCalled(); expect(controller.vnApp.showSuccess).not.toHaveBeenCalled();

View File

@ -3,3 +3,5 @@ Search prices by item ID or code: Buscar por ID de artículo o código
Search fixed prices: Buscar precios fijados Search fixed prices: Buscar precios fijados
Add fixed price: Añadir precio fijado Add fixed price: Añadir precio fijado
This row will be removed: Esta linea se eliminará This row will be removed: Esta linea se eliminará
Price By Unit: Precio Por Unidad
Price By Package: Precio Por Paquete

View File

@ -1,5 +1,20 @@
@import "variables"; @import "variables";
smart-table table{
vn-table vn-date-picker { [shrink-field]{
max-width: 90px; width: 80px;
max-width: 80px;
}
[shrink-field-expand]{
width: 150px;
max-width: 150px;
}
}
.minPrice {
align-items: center;
text-align: center;
vn-input-number {
width: 90px;
max-width: 90px;
}
} }

View File

@ -40,7 +40,8 @@ module.exports = Self => {
IFNULL(sc.workerSubstitute, c.salesPersonFk) AS salesPersonFk, IFNULL(sc.workerSubstitute, c.salesPersonFk) AS salesPersonFk,
c.id AS clientFk, c.id AS clientFk,
c.name AS clientName, c.name AS clientName,
s.lastUpdate AS dated, TIME(v.stamp) AS hour,
DATE(v.stamp) AS dated,
wtc.workerFk wtc.workerFk
FROM hedera.userSession s FROM hedera.userSession s
JOIN hedera.visitUser v ON v.id = s.userVisitFk JOIN hedera.visitUser v ON v.id = s.userVisitFk

View File

@ -2,7 +2,8 @@
vn-id="model" vn-id="model"
url="SalesMonitors/clientsFilter" url="SalesMonitors/clientsFilter"
limit="6" limit="6"
order="dated DESC" filter="$ctrl.filter"
order="dated DESC, hour DESC"
auto-load="true"> auto-load="true">
</vn-crud-model> </vn-crud-model>
<vn-horizontal class="header"> <vn-horizontal class="header">
@ -15,54 +16,81 @@
vn-tooltip="Minimize/Maximize" vn-tooltip="Minimize/Maximize"
ng-click="$ctrl.main.toggle()"> ng-click="$ctrl.main.toggle()">
</vn-icon> </vn-icon>
<vn-icon
icon="refresh"
vn-tooltip="Refresh"
ng-click="model.refresh()">
</vn-icon>
</vn-none> </vn-none>
</vn-horizontal> </vn-horizontal>
<vn-card vn-id="card"> <vn-card vn-id="card">
<vn-table model="model" class="scrollable sm"> <smart-table
<vn-thead>
<vn-tr>
<vn-th field="dated">Hour</vn-th>
<vn-th field="salesPersonFk" class="expendable">Salesperson</vn-th>
<vn-th field="clientFk">Client</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="visit in model.data">
<vn-td shrink-date>
<span class="chip">
{{::visit.dated | date: 'HH:mm'}}
</span>
</vn-td>
<vn-td class="shrink expendable">
<span
title="{{::visit.salesPerson}}"
vn-click-stop="workerDescriptor.show($event, visit.salesPersonFk)"
class="link">
{{::visit.salesPerson | dashIfEmpty}}
</span>
</vn-td>
<vn-td>
<span
title="{{::visit.clientName}}"
vn-click-stop="clientDescriptor.show($event, visit.clientFk)"
class="link">
{{::visit.clientName}}
</span>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
<vn-pagination
model="model" model="model"
class="vn-pt-xs" options="$ctrl.smartTableOptions"
scroll-selector="vn-monitor-sales-clients vn-table" expr-builder="$ctrl.exprBuilder(param, value)"
scroll-offset="100"> class="scrollable sm">
</vn-pagination> <slot-actions>
<vn-horizontal>
<vn-date-picker
class="vn-pa-xs"
label="From"
ng-model="$ctrl.dateFrom"
on-change="$ctrl.addFilterDate()">
</vn-date-picker>
<vn-date-picker
class="vn-pa-xs"
label="To"
ng-model="$ctrl.dateTo"
on-change="$ctrl.addFilterDate()">
</vn-date-picker>
</vn-horizontal>
</slot-actions>
<slot-table>
<table>
<thead>
<tr>
<th field="dated">
<span translate>Date</span>
</th>
<th field="hour">
<span translate>Hour</span>
</th>
<th field="salesPersonFk" class="expendable">
<span translate>Salesperson</span>
</th>
<th field="clientFk">
<span translate>Client</span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="visit in model.data">
<td shrink-date>
<span class="chip">
{{::visit.dated | date:'dd/MM/yy'}}
</span>
</td>
<td shrink-date>
<span class="chip">
{{::visit.hour | date: 'HH:mm'}}
</span>
</td>
<td class="shrink expendable">
<span
title="{{::visit.salesPerson}}"
vn-click-stop="workerDescriptor.show($event, visit.salesPersonFk)"
class="link">
{{::visit.salesPerson | dashIfEmpty}}
</span>
</td>
<td>
<span
title="{{::visit.clientName}}"
vn-click-stop="clientDescriptor.show($event, visit.clientFk)"
class="link">
{{::visit.clientName}}
</span>
</td>
</tr>
</tbody>
<table>
<slot-table>
</smart-table>
</vn-card> </vn-card>
<vn-worker-descriptor-popover <vn-worker-descriptor-popover
vn-id="workerDescriptor"> vn-id="workerDescriptor">
@ -70,32 +98,3 @@
<vn-client-descriptor-popover <vn-client-descriptor-popover
vn-id="clientDescriptor"> vn-id="clientDescriptor">
</vn-client-descriptor-popover> </vn-client-descriptor-popover>
<vn-contextmenu vn-id="contextmenu" targets="['vn-monitor-sales-clients vn-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>
<vn-item translate
ng-if="contextmenu.isActionAllowed()"
ng-click="contextmenu.copyValue()">
Copy value
</vn-item>
</slot-menu>
</vn-contextmenu>

View File

@ -2,26 +2,89 @@ import ngModule from '../../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
export default class Controller extends Section { export default class Controller extends Section {
constructor($element, $) {
super($element, $);
const date = new Date();
this.dateFrom = date;
this.dateTo = date;
this.filter = {
where: {
'v.stamp': {
between: this.dateRange()
}
}
};
this.smartTableOptions = {
activeButtons: {
search: true
},
columns: [
{
field: 'clientFk',
autocomplete: {
url: 'Clients',
showField: 'name',
valueField: 'id'
}
},
{
field: 'salesPersonFk',
autocomplete: {
url: 'Workers/activeWithInheritedRole',
where: `{role: 'salesPerson'}`,
searchFunction: '{firstName: $search}',
showField: 'nickname',
valueField: 'id',
}
},
{
field: 'dated',
searchable: false
},
{
field: 'hour',
searchable: false
}
]
};
}
exprBuilder(param, value) { exprBuilder(param, value) {
switch (param) { switch (param) {
case 'dated':
return {'s.lastUpdate': {
between: this.dateRange(value)}
};
case 'clientFk': case 'clientFk':
return {[`c.id`]: value};
case 'salesPersonFk': case 'salesPersonFk':
return {[`c.${param}`]: value}; return {[`c.${param}`]: value};
} }
} }
dateRange(value) { dateRange() {
const minHour = new Date(value); let from = this.dateFrom;
let to = this.dateTo;
if (!from)
from = new Date();
if (!to)
to = new Date();
const minHour = new Date(from);
minHour.setHours(0, 0, 0, 0); minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(value); const maxHour = new Date(to);
maxHour.setHours(23, 59, 59, 59); maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour]; return [minHour, maxHour];
} }
addFilterDate() {
this.$.model.filter = {
where: {
'v.stamp': {
between: this.dateRange()
}
}
};
this.$.model.refresh();
}
} }
ngModule.vnComponent('vnMonitorSalesClients', { ngModule.vnComponent('vnMonitorSalesClients', {

View File

@ -21,7 +21,7 @@
<vn-list> <vn-list>
<vn-item <vn-item
ng-if="!$ctrl.hasDocuwareFile" ng-if="!$ctrl.hasDocuwareFile"
ng-click="$ctrl.showPdfDeliveryNote()" ng-click="$ctrl.showPdfDeliveryNote('deliveryNote')"
translate> translate>
as PDF as PDF
</vn-item> </vn-item>
@ -32,6 +32,12 @@
translate> translate>
as PDF as PDF
</a> </a>
<vn-item
ng-if="!$ctrl.hasDocuwareFile"
ng-click="$ctrl.showPdfDeliveryNote('withoutPrices')"
translate>
as PDF without prices
</vn-item>
<vn-item <vn-item
ng-click="$ctrl.showCsvDeliveryNote()" ng-click="$ctrl.showCsvDeliveryNote()"
translate> translate>
@ -44,7 +50,6 @@
vn-click-stop="sendDeliveryNoteMenu.show($event, 'left')" vn-click-stop="sendDeliveryNoteMenu.show($event, 'left')"
translate> translate>
Send Delivery Note... Send Delivery Note...
<vn-menu vn-id="sendDeliveryNoteMenu"> <vn-menu vn-id="sendDeliveryNoteMenu">
<vn-list> <vn-list>
<vn-item <vn-item
@ -60,6 +65,11 @@
</vn-list> </vn-list>
</vn-menu> </vn-menu>
</vn-item> </vn-item>
<vn-item
ng-click="$ctrl.showPdfDeliveryNote('proforma')"
translate>
Show Proforma
</vn-item>
<vn-item <vn-item
ng-click="deleteConfirmation.show()" ng-click="deleteConfirmation.show()"
ng-show="$ctrl.isEditable" ng-show="$ctrl.isEditable"

View File

@ -116,10 +116,11 @@ class Controller extends Section {
}); });
} }
showPdfDeliveryNote() { showPdfDeliveryNote(type) {
this.vnReport.show('delivery-note', { this.vnReport.show('delivery-note', {
recipientId: this.ticket.client.id, recipientId: this.ticket.client.id,
ticketId: this.id, ticketId: this.id,
type: type
}); });
} }

View File

@ -122,14 +122,15 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
describe('showPdfDeliveryNote()', () => { describe('showPdfDeliveryNote()', () => {
it('should open a new window showing a delivery note PDF document', () => { it('should open a new window showing a delivery note PDF document', () => {
jest.spyOn(window, 'open').mockReturnThis(); jest.spyOn(window, 'open').mockReturnThis();
const type = 'deliveryNote';
const expectedParams = { const expectedParams = {
ticketId: ticket.id, ticketId: ticket.id,
recipientId: ticket.client.id recipientId: ticket.client.id,
type: type
}; };
const serializedParams = $httpParamSerializer(expectedParams); const serializedParams = $httpParamSerializer(expectedParams);
const expectedPath = `api/report/delivery-note?${serializedParams}`; const expectedPath = `api/report/delivery-note?${serializedParams}`;
controller.showPdfDeliveryNote(); controller.showPdfDeliveryNote(type);
expect(window.open).toHaveBeenCalledWith(expectedPath); expect(window.open).toHaveBeenCalledWith(expectedPath);
}); });

View File

@ -2,7 +2,9 @@ Show Delivery Note...: Ver albarán...
Send Delivery Note...: Enviar albarán... Send Delivery Note...: Enviar albarán...
as PDF: como PDF as PDF: como PDF
as CSV: como CSV as CSV: como CSV
as PDF without prices: como PDF sin precios
Send PDF: Enviar PDF Send PDF: Enviar PDF
Send CSV: Enviar CSV Send CSV: Enviar CSV
Send CSV Delivery Note: Enviar albarán en CSV Send CSV Delivery Note: Enviar albarán en CSV
Send PDF Delivery Note: Enviar albarán en PDF Send PDF Delivery Note: Enviar albarán en PDF
Show Proforma: Ver proforma

View File

@ -80,6 +80,7 @@
</div> </div>
<p v-html="$t('sections.agency.description')"></p> <p v-html="$t('sections.agency.description')"></p>
<p>{{claimConfig.pickupContact}}</p>
</div> </div>
</div> </div>
<!-- Footer block --> <!-- Footer block -->

View File

@ -7,6 +7,7 @@ module.exports = {
async serverPrefetch() { async serverPrefetch() {
this.client = await this.fetchClient(this.claimId); this.client = await this.fetchClient(this.claimId);
this.sales = await this.fetchSales(this.claimId); this.sales = await this.fetchSales(this.claimId);
this.claimConfig = await this.fetchClaimConfig();
if (!this.client) if (!this.client)
throw new Error('Something went wrong'); throw new Error('Something went wrong');
@ -25,6 +26,9 @@ module.exports = {
fetchSales(claimId) { fetchSales(claimId) {
return this.rawSqlFromDef('sales', [claimId]); return this.rawSqlFromDef('sales', [claimId]);
}, },
fetchClaimConfig() {
return this.findOneFromDef('claimConfig');
},
}, },
components: { components: {
'report-header': reportHeader.build(), 'report-header': reportHeader.build(),

View File

@ -14,4 +14,4 @@ phone: Teléfono
sections: sections:
agency: agency:
description: 'Para agilizar su recogida, por favor, póngase en contacto con la oficina description: 'Para agilizar su recogida, por favor, póngase en contacto con la oficina
de Logista Parcel. <br/> Tlf: 96 166 77 88 - Ana Gómez (Ext. 2113) <em>(atcsalidas.i2valencia@integra2.es)</em>' de Logista Parcel.'

View File

@ -0,0 +1,2 @@
SELECT pickupContact
FROM claimConfig;

View File

@ -15,7 +15,7 @@
<div class="columns"> <div class="columns">
<div class="size50"> <div class="size50">
<div class="size75 vn-mt-ml"> <div class="size75 vn-mt-ml">
<h1 class="title uppercase">{{$t('title')}}</h1> <h1 class="title uppercase">{{$t(type)}}</h1>
<table class="row-oriented ticket-info"> <table class="row-oriented ticket-info">
<tbody> <tbody>
<tr> <tr>
@ -23,7 +23,7 @@
<th>{{client.id}}</th> <th>{{client.id}}</th>
</tr> </tr>
<tr> <tr>
<td class="font gray uppercase">{{$t('ticketId')}}</td> <td class="font gray uppercase">{{$t(type)}}</td>
<th>{{ticket.id}}</th> <th>{{ticket.id}}</th>
</tr> </tr>
<tr> <tr>
@ -77,10 +77,10 @@
<th width="5%">{{$t('reference')}}</th> <th width="5%">{{$t('reference')}}</th>
<th class="number">{{$t('quantity')}}</th> <th class="number">{{$t('quantity')}}</th>
<th width="50%">{{$t('concept')}}</th> <th width="50%">{{$t('concept')}}</th>
<th class="number">{{$t('price')}}</th> <th class="number" v-if="showPrices">{{$t('price')}}</th>
<th class="centered" width="5%">{{$t('discount')}}</th> <th class="centered" width="5%" v-if="showPrices">{{$t('discount')}}</th>
<th class="centered">{{$t('vat')}}</th> <th class="centered" v-if="showPrices">{{$t('vat')}}</th>
<th class="number">{{$t('amount')}}</th> <th class="number" v-if="showPrices">{{$t('amount')}}</th>
</tr> </tr>
</thead> </thead>
<tbody v-for="sale in sales" class="no-page-break"> <tbody v-for="sale in sales" class="no-page-break">
@ -88,10 +88,10 @@
<td width="5%">{{sale.itemFk | zerofill('000000')}}</td> <td width="5%">{{sale.itemFk | zerofill('000000')}}</td>
<td class="number">{{sale.quantity}}</td> <td class="number">{{sale.quantity}}</td>
<td width="50%">{{sale.concept}}</td> <td width="50%">{{sale.concept}}</td>
<td class="number">{{sale.price | currency('EUR', $i18n.locale)}}</td> <td class="number" v-if="showPrices">{{sale.price | currency('EUR', $i18n.locale)}}</td>
<td class="centered" width="5%">{{(sale.discount / 100) | percentage}}</td> <td class="centered" width="5%" v-if="showPrices">{{(sale.discount / 100) | percentage}}</td>
<td class="centered">{{sale.vatType}}</td> <td class="centered" v-if="showPrices">{{sale.vatType}}</td>
<td class="number">{{sale.price * sale.quantity * (1 - sale.discount / 100) | currency('EUR', $i18n.locale)}}</td> <td class="number" v-if="showPrices">{{sale.price * sale.quantity * (1 - sale.discount / 100) | currency('EUR', $i18n.locale)}}</td>
</tr> </tr>
<tr class="description font light-gray"> <tr class="description font light-gray">
<td colspan="7"> <td colspan="7">
@ -107,7 +107,7 @@
</td> </td>
</tr> </tr>
</tbody> </tbody>
<tfoot> <tfoot v-if="showPrices">
<tr> <tr>
<td colspan="6" class="font bold"> <td colspan="6" class="font bold">
<span class="pull-right">{{$t('subtotal')}}</span> <span class="pull-right">{{$t('subtotal')}}</span>
@ -181,7 +181,7 @@
</div> </div>
<!-- End of packages block --> <!-- End of packages block -->
</div> </div>
<div class="columns vn-mt-xl"> <div class="columns vn-mt-xl" v-if="showPrices">
<!-- Taxes block --> <!-- Taxes block -->
<div id="taxes" class="size50 pull-right no-page-break" v-if="taxes"> <div id="taxes" class="size50 pull-right no-page-break" v-if="taxes">
<table class="column-oriented"> <table class="column-oriented">
@ -281,7 +281,7 @@
<!-- Footer block --> <!-- Footer block -->
<report-footer id="pageFooter" <report-footer id="pageFooter"
v-bind:company-code="ticket.companyCode" v-bind:company-code="ticket.companyCode"
v-bind:left-text="$t('ticket', [ticket.id])" v-bind:left-text="footerType"
v-bind:center-text="client.socialName" v-bind:center-text="client.socialName"
v-bind="$props"> v-bind="$props">
</report-footer> </report-footer>

View File

@ -44,6 +44,13 @@ module.exports = {
}); });
return total; return total;
},
showPrices() {
return this.type != 'withoutPrices';
},
footerType() {
const translatedType = this.$t(this.type);
return `${translatedType} ${this.ticketId}`;
} }
}, },
methods: { methods: {
@ -119,6 +126,10 @@ module.exports = {
ticketId: { ticketId: {
type: [Number, String], type: [Number, String],
required: true required: true
},
type: {
type: String,
required: true
} }
} }
}; };

View File

@ -1,6 +1,7 @@
reportName: delivery-note reportName: delivery-note
title: Delivery note deliveryNote: Delivery note
ticketId: Delivery note proforma: Proforma
withoutPrices: Delivery note
clientId: Client clientId: Client
deliveryAddress: Delivery address deliveryAddress: Delivery address
fiscalData: Fiscal data fiscalData: Fiscal data
@ -17,7 +18,6 @@ total: Total
subtotal: Subtotal subtotal: Subtotal
vatType: VAT Type vatType: VAT Type
digitalSignature: Digital signature digitalSignature: Digital signature
ticket: Delivery note {0}
plantPassport: Plant passport plantPassport: Plant passport
packages: Packages packages: Packages
services: services:

View File

@ -1,6 +1,7 @@
reportName: albaran reportName: albaran
title: Albarán deliveryNote: Albarán
ticketId: Albarán proforma: Proforma
withoutPrices: Albarán
clientId: Cliente clientId: Cliente
deliveryAddress: Dirección de entrega deliveryAddress: Dirección de entrega
fiscalData: Datos fiscales fiscalData: Datos fiscales

View File

@ -1,6 +1,7 @@
reportName: bon-de-livraison reportName: bon-de-livraison
title: Bon de livraison deliveryNote: Bon de livraison
ticketId: BL proforma: Proforma
withoutPrices: Bon de livraison
clientId: Client clientId: Client
deliveryAddress: Adresse de livraison deliveryAddress: Adresse de livraison
fiscalData: Coordonnées fiscalData: Coordonnées

View File

@ -1,6 +1,7 @@
reportName: nota-de-entrega reportName: nota-de-entrega
title: Nota de Entrega deliveryNote: Nota de Entrega
ticketId: Nota de Entrega proforma: Proforma
withoutPrices: Nota de Entrega
clientId: Cliente clientId: Cliente
deliveryAddress: Morada de Entrega deliveryAddress: Morada de Entrega
fiscalData: Dados Fiscais fiscalData: Dados Fiscais