Merge branch 'dev' into 2785-getBalance-tests
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
3cb0859f85
|
@ -1 +0,0 @@
|
||||||
Delete me
|
|
|
@ -0,0 +1 @@
|
||||||
|
Delete this
|
|
@ -564,13 +564,13 @@ INSERT INTO `vn`.`zoneConfig` (`scope`) VALUES ('1');
|
||||||
|
|
||||||
INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agencyModeFk`, `description`, `m3`, `cost`, `started`, `finished`, `zoneFk`)
|
INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agencyModeFk`, `description`, `m3`, `cost`, `started`, `finished`, `zoneFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, '1899-12-30 12:15:00', 56, CURDATE(), 1, 1, 'first route', 1.8, 10, CURDATE(), CURDATE(), 1),
|
(1, '1899-12-30 12:15:00', 56, CURDATE(), 1, 1, 'first route', 1.8, 10, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1),
|
||||||
(2, '1899-12-30 13:20:00', 56, CURDATE(), 1, 2, 'second route', 0.2, 20, CURDATE(), CURDATE(), 9),
|
(2, '1899-12-30 13:20:00', 56, CURDATE(), 1, 2, 'second route', 0.2, 20, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 9),
|
||||||
(3, '1899-12-30 14:30:00', 56, CURDATE(), 2, 3, 'third route', 0.5, 30, CURDATE(), CURDATE(), 10),
|
(3, '1899-12-30 14:30:00', 56, CURDATE(), 2, 3, 'third route', 0.5, 30, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 10),
|
||||||
(4, '1899-12-30 15:45:00', 56, CURDATE(), 3, 4, 'fourth route', 0, 40, CURDATE(), CURDATE(), 12),
|
(4, '1899-12-30 15:45:00', 56, CURDATE(), 3, 4, 'fourth route', 0, 40, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 12),
|
||||||
(5, '1899-12-30 16:00:00', 56, CURDATE(), 4, 5, 'fifth route', 0.1, 50, CURDATE(), CURDATE(), 13),
|
(5, '1899-12-30 16:00:00', 56, CURDATE(), 4, 5, 'fifth route', 0.1, 50, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 13),
|
||||||
(6, NULL, 57, CURDATE(), 5, 7, 'sixth route', 1.7, 60, CURDATE(), CURDATE(), 3),
|
(6, NULL, 57, CURDATE(), 5, 7, 'sixth route', 1.7, 60, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 3),
|
||||||
(7, NULL, 57, CURDATE(), 6, 8, 'seventh route', 0, 70, CURDATE(), CURDATE(), 5);
|
(7, NULL, 57, CURDATE(), 6, 8, 'seventh route', 0, 70, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 5);
|
||||||
|
|
||||||
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`)
|
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2257,3 +2257,10 @@ INSERT INTO `vn`.`ticketRecalc`(`ticketFk`)
|
||||||
SELECT `id` FROM `vn`.`ticket`;
|
SELECT `id` FROM `vn`.`ticket`;
|
||||||
|
|
||||||
CALL `vn`.`ticket_doRecalc`();
|
CALL `vn`.`ticket_doRecalc`();
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`zoneAgencyMode`(`id`, `agencyModeFk`, `zoneFk`)
|
||||||
|
VALUES
|
||||||
|
(1, 1, 1),
|
||||||
|
(2, 1, 2),
|
||||||
|
(3, 6, 5),
|
||||||
|
(4, 7, 1);
|
|
@ -790,10 +790,11 @@ export default {
|
||||||
saveButton: 'vn-route-basic-data button[type=submit]'
|
saveButton: 'vn-route-basic-data button[type=submit]'
|
||||||
},
|
},
|
||||||
routeTickets: {
|
routeTickets: {
|
||||||
firstTicketPriority: 'vn-route-tickets vn-tr:nth-child(1) vn-textfield[ng-model="ticket.priority"]',
|
firstTicketPriority: 'vn-route-tickets vn-tr:nth-child(1) vn-input-number[ng-model="ticket.priority"]',
|
||||||
firstTicketCheckbox: 'vn-route-tickets vn-tr:nth-child(1) vn-check',
|
firstTicketCheckbox: 'vn-route-tickets vn-tr:nth-child(1) vn-check',
|
||||||
buscamanButton: 'vn-route-tickets vn-button[icon="icon-buscaman"]',
|
buscamanButton: 'vn-route-tickets vn-button[icon="icon-buscaman"]',
|
||||||
firstTicketDeleteButton: 'vn-route-tickets vn-tr:nth-child(1) vn-icon[icon="delete"]',
|
firstTicketDeleteButton: 'vn-route-tickets vn-tr:nth-child(1) vn-icon[icon="delete"]',
|
||||||
|
anyTicket: 'vn-route-tickets vn-tbody > vn-tr',
|
||||||
confirmButton: '.vn-confirm.shown button[response="accept"]'
|
confirmButton: '.vn-confirm.shown button[response="accept"]'
|
||||||
},
|
},
|
||||||
workerSummary: {
|
workerSummary: {
|
||||||
|
|
|
@ -376,7 +376,8 @@ describe('Ticket Edit sale path', () => {
|
||||||
expect(result).toBeFalsy();
|
expect(result).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update all sales discount', async() => {
|
// tickets no longer update their totals instantly, a task performed ever 5-10 mins does it. disabled this test until it changes.
|
||||||
|
xit('should update all sales discount', async() => {
|
||||||
await page.closePopup();
|
await page.closePopup();
|
||||||
await page.waitToClick(selectors.ticketSales.moreMenu);
|
await page.waitToClick(selectors.ticketSales.moreMenu);
|
||||||
await page.waitToClick(selectors.ticketSales.moreMenuUpdateDiscount);
|
await page.waitToClick(selectors.ticketSales.moreMenuUpdateDiscount);
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import selectors from '../../helpers/selectors.js';
|
import selectors from '../../helpers/selectors.js';
|
||||||
import getBrowser from '../../helpers/puppeteer';
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
// #1528 e2e claim/detail
|
describe('Route tickets path', () => {
|
||||||
xdescribe('Route basic Data path', () => {
|
|
||||||
let browser;
|
let browser;
|
||||||
let page;
|
let page;
|
||||||
|
|
||||||
|
@ -10,7 +9,7 @@ xdescribe('Route basic Data path', () => {
|
||||||
browser = await getBrowser();
|
browser = await getBrowser();
|
||||||
page = browser.page;
|
page = browser.page;
|
||||||
await page.loginAndModule('delivery', 'route');
|
await page.loginAndModule('delivery', 'route');
|
||||||
await page.accessToSearchResult('3');
|
await page.accessToSearchResult('2');
|
||||||
await page.accessToSection('route.card.tickets');
|
await page.accessToSection('route.card.tickets');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19,40 +18,32 @@ xdescribe('Route basic Data path', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should modify the first ticket priority', async() => {
|
it('should modify the first ticket priority', async() => {
|
||||||
await page.write(selectors.routeTickets.firstTicketPriority, '2');
|
await page.clearInput(selectors.routeTickets.firstTicketPriority);
|
||||||
|
await page.type(selectors.routeTickets.firstTicketPriority, '9');
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
const message = await page.waitForSnackbar();
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
expect(message.text).toContain('Data saved!');
|
expect(message.text).toContain('Data saved!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the buscamanButton is disabled', async() => {
|
it('should confirm the buscaman button is disabled', async() => {
|
||||||
const result = await page.evaluate(selector => {
|
await page.waitForSelector(`${selectors.routeTickets.buscamanButton}.disabled`);
|
||||||
return document.querySelector(selector);
|
|
||||||
}, `${selectors.routeTickets.buscamanButton} :disabled`);
|
|
||||||
|
|
||||||
expect(result).toBeTruthy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check the first ticket checkbox and confirm the buscamanButton button is no longer disabled', async() => {
|
it('should check the first ticket checkbox and confirm the buscamanButton button is no longer disabled', async() => {
|
||||||
await page.waitToClick(selectors.routeTickets.firstTicketCheckbox);
|
await page.waitForSelector(`${selectors.routeTickets.buscamanButton}.disabled`, {visible: false});
|
||||||
const result = await page.evaluate(selector => {
|
|
||||||
return document.querySelector(selector);
|
|
||||||
}, `${selectors.routeTickets.buscamanButton} :disabled`);
|
|
||||||
|
|
||||||
expect(result).toBeFalsy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check the route volume on the descriptor', async() => {
|
it('should check the route volume on the descriptor', async() => {
|
||||||
const result = await page.waitToGetProperty(selectors.routeDescriptor.volume, 'innerText');
|
const result = await page.waitToGetProperty(selectors.routeDescriptor.volume, 'innerText');
|
||||||
|
|
||||||
expect(result).toEqual('1.1 / 18 m³');
|
expect(result).toEqual('0.2 / 50 m³');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should count how many tickets are in route', async() => {
|
it('should count how many tickets are in route', async() => {
|
||||||
const result = await page.countElement('vn-route-tickets vn-textfield[ng-model="ticket.priority"]');
|
const result = await page.countElement(selectors.routeTickets.anyTicket);
|
||||||
|
|
||||||
expect(result).toEqual(11);
|
expect(result).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete the first ticket in route', async() => {
|
it('should delete the first ticket in route', async() => {
|
||||||
|
@ -63,23 +54,14 @@ xdescribe('Route basic Data path', () => {
|
||||||
expect(message.text).toContain('Ticket removed from route');
|
expect(message.text).toContain('Ticket removed from route');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should again delete the first ticket in route', async() => {
|
|
||||||
await page.waitToClick(selectors.routeTickets.firstTicketDeleteButton);
|
|
||||||
await page.waitToClick(selectors.routeTickets.confirmButton);
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('Ticket removed from route');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should now count how many tickets are in route to find one less', async() => {
|
it('should now count how many tickets are in route to find one less', async() => {
|
||||||
const result = await page.countElement('vn-route-tickets vn-textfield[ng-model="ticket.priority"]');
|
await page.waitForNumberOfElements(selectors.routeTickets.anyTicket, 0);
|
||||||
|
|
||||||
expect(result).toEqual(9);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the route volume on the descriptor has been updated by the changes made', async() => {
|
// #2862 updateVolume() route descriptor no actualiza volumen
|
||||||
|
xit('should confirm the route volume on the descriptor has been updated by the changes made', async() => {
|
||||||
const result = await page.waitToGetProperty(selectors.routeDescriptor.volume, 'innerText');
|
const result = await page.waitToGetProperty(selectors.routeDescriptor.volume, 'innerText');
|
||||||
|
|
||||||
expect(result).toEqual('0.9 / 18 m³');
|
expect(result).toEqual('0 / 50 m³');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import selectors from '../../helpers/selectors.js';
|
import selectors from '../../helpers/selectors.js';
|
||||||
import getBrowser from '../../helpers/puppeteer';
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
// #2833 Refactor account.basicData
|
describe('Account create and basic data path', () => {
|
||||||
xdescribe('Account create and basic data path', () => {
|
|
||||||
let browser;
|
let browser;
|
||||||
let page;
|
let page;
|
||||||
|
|
||||||
|
@ -37,40 +36,23 @@ xdescribe('Account create and basic data path', () => {
|
||||||
await page.waitForState('account.card.basicData');
|
await page.waitForState('account.card.basicData');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should edit the basic data', async() => {
|
it('should reload the section and check the name is as expected', async() => {
|
||||||
await page.overwrite(selectors.accountBasicData.name, 'Anna');
|
|
||||||
await page.overwrite(selectors.accountBasicData.nickname, 'Rogue');
|
|
||||||
await page.overwrite(selectors.accountBasicData.email, 'AnnaMarieLeBeau@verdnatura.es');
|
|
||||||
await page.autocompleteSearch(selectors.accountBasicData.language, 'english');
|
|
||||||
await page.waitToClick(selectors.accountBasicData.save);
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('Data saved!');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reload the section and check the name was edited successfully', async() => {
|
|
||||||
await page.reloadSection('account.card.basicData');
|
await page.reloadSection('account.card.basicData');
|
||||||
const result = await page.waitToGetProperty(selectors.accountBasicData.name, 'value');
|
const result = await page.waitToGetProperty(selectors.accountBasicData.name, 'value');
|
||||||
|
|
||||||
expect(result).toEqual('Anna');
|
expect(result).toEqual('Remy');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check the nickname was edited successfully', async() => {
|
it('should check the nickname is as expected', async() => {
|
||||||
const result = await page.waitToGetProperty(selectors.accountBasicData.nickname, 'value');
|
const result = await page.waitToGetProperty(selectors.accountBasicData.nickname, 'value');
|
||||||
|
|
||||||
expect(result).toEqual('Rogue');
|
expect(result).toEqual('Gambit');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check the email was edited successfully', async() => {
|
it('should check the email is as expected', async() => {
|
||||||
const result = await page.waitToGetProperty(selectors.accountBasicData.email, 'value');
|
const result = await page.waitToGetProperty(selectors.accountBasicData.email, 'value');
|
||||||
|
|
||||||
expect(result).toEqual('AnnaMarieLeBeau@verdnatura.es');
|
expect(result).toEqual('RemyEtienneLeBeau@verdnatura.es');
|
||||||
});
|
|
||||||
|
|
||||||
it('should check the language was edited successfully', async() => {
|
|
||||||
const result = await page.waitToGetProperty(selectors.accountBasicData.language, 'value');
|
|
||||||
|
|
||||||
expect(result).toEqual('English');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigate to the roles section to check the roles are correct', async() => {
|
it('should navigate to the roles section to check the roles are correct', async() => {
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
<div ng-transclude="before"></div>
|
<div ng-transclude="before"></div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
<h5>{{$ctrl.description}}</h5>
|
<h5 title="{{$ctrl.description}}">{{$ctrl.description}}</h5>
|
||||||
<div>
|
<div>
|
||||||
{{$ctrl.descriptor.id | id}}
|
{{$ctrl.descriptor.id | id}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -42,7 +42,7 @@ Travels: Envíos
|
||||||
Workers: Trabajadores
|
Workers: Trabajadores
|
||||||
Routes: Rutas
|
Routes: Rutas
|
||||||
Locator: Localizador
|
Locator: Localizador
|
||||||
Invoices out: Facturas emitidas
|
Invoices out: Fact. emitidas
|
||||||
Invoices in: Fact. recibidas
|
Invoices in: Fact. recibidas
|
||||||
Entries: Entradas
|
Entries: Entradas
|
||||||
Users: Usuarios
|
Users: Usuarios
|
||||||
|
|
|
@ -25,8 +25,8 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
if (ctx.data) {
|
if (ctx.data) {
|
||||||
const changes = pick(ctx.currentInstance, Object.keys(ctx.data));
|
const changes = pick(ctx.currentInstance, Object.keys(ctx.data));
|
||||||
newInstance = await fkToValue(ctx.data, ctx);
|
newInstance = ctx.data;
|
||||||
oldInstance = await fkToValue(changes, ctx);
|
oldInstance = changes;
|
||||||
|
|
||||||
if (ctx.where && !ctx.currentInstance) {
|
if (ctx.where && !ctx.currentInstance) {
|
||||||
const fields = Object.keys(ctx.data);
|
const fields = Object.keys(ctx.data);
|
||||||
|
@ -41,7 +41,7 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
// Get changes from created instance
|
// Get changes from created instance
|
||||||
if (ctx.isNewInstance)
|
if (ctx.isNewInstance)
|
||||||
newInstance = await fkToValue(ctx.instance.__data, ctx);
|
newInstance = ctx.instance.__data;
|
||||||
|
|
||||||
ctx.hookState.oldInstance = oldInstance;
|
ctx.hookState.oldInstance = oldInstance;
|
||||||
ctx.hookState.newInstance = newInstance;
|
ctx.hookState.newInstance = newInstance;
|
||||||
|
@ -134,47 +134,49 @@ module.exports = function(Self) {
|
||||||
if (value instanceof Object)
|
if (value instanceof Object)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (value === undefined || value === null) continue;
|
if (value === undefined) continue;
|
||||||
|
|
||||||
for (let relationName in relations) {
|
if (value) {
|
||||||
const relation = relations[relationName];
|
for (let relationName in relations) {
|
||||||
if (relation.keyFrom == key && key != 'id') {
|
const relation = relations[relationName];
|
||||||
const model = relation.modelTo;
|
if (relation.keyFrom == key && key != 'id') {
|
||||||
const modelName = relation.modelTo.modelName;
|
const model = relation.modelTo;
|
||||||
const properties = model && model.definition.properties;
|
const modelName = relation.modelTo.modelName;
|
||||||
const settings = model && model.definition.settings;
|
const properties = model && model.definition.properties;
|
||||||
|
const settings = model && model.definition.settings;
|
||||||
|
|
||||||
const recordSet = await appModels[modelName].findById(value, null, options);
|
const recordSet = await appModels[modelName].findById(value, null, options);
|
||||||
|
|
||||||
const hasShowField = settings.log && settings.log.showField;
|
const hasShowField = settings.log && settings.log.showField;
|
||||||
let showField = hasShowField && recordSet
|
let showField = hasShowField && recordSet
|
||||||
&& recordSet[settings.log.showField];
|
&& recordSet[settings.log.showField];
|
||||||
|
|
||||||
if (!showField) {
|
if (!showField) {
|
||||||
const showFieldNames = [
|
const showFieldNames = [
|
||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'code',
|
'code',
|
||||||
'nickname'
|
'nickname'
|
||||||
];
|
];
|
||||||
for (field of showFieldNames) {
|
for (field of showFieldNames) {
|
||||||
const propField = properties && properties[field];
|
const propField = properties && properties[field];
|
||||||
const recordField = recordSet && recordSet[field];
|
const recordField = recordSet && recordSet[field];
|
||||||
|
|
||||||
if (propField && recordField) {
|
if (propField && recordField) {
|
||||||
showField = field;
|
showField = field;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (showField && recordSet && recordSet[showField]) {
|
if (showField && recordSet && recordSet[showField]) {
|
||||||
value = recordSet[showField];
|
value = recordSet[showField];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = recordSet && recordSet.id || value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = recordSet && recordSet.id || value;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result[key] = value;
|
result[key] = value;
|
||||||
|
@ -259,6 +261,9 @@ module.exports = function(Self) {
|
||||||
removeUnloggable(definition, oldInstance);
|
removeUnloggable(definition, oldInstance);
|
||||||
removeUnloggable(definition, newInstance);
|
removeUnloggable(definition, newInstance);
|
||||||
|
|
||||||
|
oldInstance = await fkToValue(oldInstance, ctx);
|
||||||
|
newInstance = await fkToValue(newInstance, ctx);
|
||||||
|
|
||||||
// Prevent log with no new changes
|
// Prevent log with no new changes
|
||||||
const hasNewChanges = Object.keys(newInstance).length;
|
const hasNewChanges = Object.keys(newInstance).length;
|
||||||
if (!hasNewChanges) return;
|
if (!hasNewChanges) return;
|
||||||
|
|
|
@ -93,5 +93,7 @@
|
||||||
"New ticket request has been created": "New ticket request has been created *'{{description}}'* for day *{{shipped}}*, with a quantity of *{{quantity}}*",
|
"New ticket request has been created": "New ticket request has been created *'{{description}}'* for day *{{shipped}}*, with a quantity of *{{quantity}}*",
|
||||||
"There's a new urgent ticket": "There's a new urgent ticket: [{{title}}](https://cau.verdnatura.es/WorkOrder.do?woMode=viewWO&woID={{issueId}})",
|
"There's a new urgent ticket": "There's a new urgent ticket: [{{title}}](https://cau.verdnatura.es/WorkOrder.do?woMode=viewWO&woID={{issueId}})",
|
||||||
"Swift / BIC cannot be empty": "Swift / BIC cannot be empty",
|
"Swift / BIC cannot be empty": "Swift / BIC cannot be empty",
|
||||||
"Role name must be written in camelCase": "Role name must be written in camelCase"
|
"Role name must be written in camelCase": "Role name must be written in camelCase",
|
||||||
|
"Client assignment has changed": "I did change the salesperson ~*\"<{{previousWorkerName}}>\"*~ by *\"<{{currentWorkerName}}>\"* from the client [{{clientName}} ({{clientId}})]({{{url}}})",
|
||||||
|
"None": "None"
|
||||||
}
|
}
|
|
@ -167,8 +167,8 @@
|
||||||
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
|
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
|
||||||
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
|
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
|
||||||
"Sorts whole route": "Reordena ruta entera",
|
"Sorts whole route": "Reordena ruta entera",
|
||||||
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día <strong>{{shipped}}</strong>, con una cantidad de <strong>{{quantity}}</strong> y un precio de <strong>{{price}} €</strong>",
|
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*",
|
||||||
"New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día <strong>{{shipped}}</strong>, con una cantidad de <strong>{{quantity}}</strong>",
|
"New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*",
|
||||||
"Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",
|
"Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",
|
||||||
"This BIC already exist.": "Este BIC ya existe.",
|
"This BIC already exist.": "Este BIC ya existe.",
|
||||||
"That item doesn't exists": "Ese artículo no existe",
|
"That item doesn't exists": "Ese artículo no existe",
|
||||||
|
@ -176,5 +176,7 @@
|
||||||
"Invalid account": "Cuenta inválida",
|
"Invalid account": "Cuenta inválida",
|
||||||
"Compensation account is empty": "La cuenta para compensar está vacia",
|
"Compensation account is empty": "La cuenta para compensar está vacia",
|
||||||
"This genus already exist": "Este genus ya existe",
|
"This genus already exist": "Este genus ya existe",
|
||||||
"This specie already exist": "Esta especie ya existe"
|
"This specie already exist": "Esta especie ya existe",
|
||||||
|
"Client assignment has changed": "He cambiado el comercial ~*\"<{{previousWorkerName}}>\"*~ por *\"<{{currentWorkerName}}>\"* del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
|
||||||
|
"None": "Ninguno"
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
* @param {Object} instance - The model or context instance
|
* @param {Object} instance - The model or context instance
|
||||||
* @param {Object} changes - Object containing changes
|
* @param {Object} changes - Object containing changes
|
||||||
*/
|
*/
|
||||||
exports.translateValues = async(instance, changes) => {
|
exports.translateValues = async(instance, changes, options = {}) => {
|
||||||
const models = instance.app.models;
|
const models = instance.app.models;
|
||||||
function getRelation(instance, property) {
|
function getRelation(instance, property) {
|
||||||
const relations = instance.definition.settings.relations;
|
const relations = instance.definition.settings.relations;
|
||||||
|
@ -38,12 +38,20 @@ exports.translateValues = async(instance, changes) => {
|
||||||
|
|
||||||
const properties = Object.assign({}, changes);
|
const properties = Object.assign({}, changes);
|
||||||
for (let property in properties) {
|
for (let property in properties) {
|
||||||
|
const firstChar = property.substring(0, 1);
|
||||||
|
const isPrivate = firstChar == '$';
|
||||||
|
if (isPrivate) {
|
||||||
|
delete properties[property];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const relation = getRelation(instance, property);
|
const relation = getRelation(instance, property);
|
||||||
const value = properties[property];
|
const value = properties[property];
|
||||||
let finalValue = value;
|
const hasValue = value != null && value != undefined;
|
||||||
|
|
||||||
if (relation) {
|
let finalValue = value;
|
||||||
let fieldsToShow = ['alias', 'name', 'code', 'description'];
|
if (relation && hasValue) {
|
||||||
|
let fieldsToShow = ['nickname', 'name', 'code', 'description'];
|
||||||
const modelName = relation.model;
|
const modelName = relation.model;
|
||||||
const model = models[modelName];
|
const model = models[modelName];
|
||||||
const log = model.definition.settings.log;
|
const log = model.definition.settings.log;
|
||||||
|
@ -53,7 +61,7 @@ exports.translateValues = async(instance, changes) => {
|
||||||
|
|
||||||
const row = await model.findById(value, {
|
const row = await model.findById(value, {
|
||||||
fields: fieldsToShow
|
fields: fieldsToShow
|
||||||
});
|
}, options);
|
||||||
const newValue = getValue(row);
|
const newValue = getValue(row);
|
||||||
if (newValue) finalValue = newValue;
|
if (newValue) finalValue = newValue;
|
||||||
}
|
}
|
||||||
|
@ -76,7 +84,12 @@ exports.translateValues = async(instance, changes) => {
|
||||||
exports.getChanges = (original, changes) => {
|
exports.getChanges = (original, changes) => {
|
||||||
const oldChanges = {};
|
const oldChanges = {};
|
||||||
const newChanges = {};
|
const newChanges = {};
|
||||||
|
|
||||||
for (let property in changes) {
|
for (let property in changes) {
|
||||||
|
const firstChar = property.substring(0, 1);
|
||||||
|
const isPrivate = firstChar == '$';
|
||||||
|
if (isPrivate) return;
|
||||||
|
|
||||||
if (changes[property] != original[property]) {
|
if (changes[property] != original[property]) {
|
||||||
newChanges[property] = changes[property];
|
newChanges[property] = changes[property];
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,10 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.importToNewRefundTicket = async(ctx, id) => {
|
Self.importToNewRefundTicket = async(ctx, id, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const token = ctx.req.accessToken;
|
const token = ctx.req.accessToken;
|
||||||
const userId = token.userId;
|
const userId = token.userId;
|
||||||
const tx = await Self.beginTransaction({});
|
|
||||||
const filter = {
|
const filter = {
|
||||||
where: {id: id},
|
where: {id: id},
|
||||||
include: [
|
include: [
|
||||||
|
@ -63,29 +62,39 @@ module.exports = Self => {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let tx;
|
||||||
|
let myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let options = {transaction: tx};
|
|
||||||
const worker = await models.Worker.findOne({
|
const worker = await models.Worker.findOne({
|
||||||
where: {userFk: userId}
|
where: {userFk: userId}
|
||||||
}, options);
|
}, myOptions);
|
||||||
|
|
||||||
const obsevationType = await models.ObservationType.findOne({
|
const obsevationType = await models.ObservationType.findOne({
|
||||||
where: {description: 'comercial'}
|
where: {description: 'comercial'}
|
||||||
}, options);
|
}, myOptions);
|
||||||
|
|
||||||
const agencyMode = await models.AgencyMode.findOne({
|
const agencyMode = await models.AgencyMode.findOne({
|
||||||
where: {code: 'refund'}
|
where: {code: 'refund'}
|
||||||
}, options);
|
}, myOptions);
|
||||||
|
|
||||||
const state = await models.State.findOne({
|
const state = await models.State.findOne({
|
||||||
where: {code: 'DELIVERED'}
|
where: {code: 'DELIVERED'}
|
||||||
}, options);
|
}, myOptions);
|
||||||
|
|
||||||
const zone = await models.Zone.findOne({
|
const zone = await models.Zone.findOne({
|
||||||
where: {agencyModeFk: agencyMode.id}
|
where: {agencyModeFk: agencyMode.id}
|
||||||
}, options);
|
}, myOptions);
|
||||||
|
|
||||||
const claim = await models.Claim.findOne(filter, options);
|
const claim = await models.Claim.findOne(filter, myOptions);
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
|
|
||||||
const newRefundTicket = await models.Ticket.create({
|
const newRefundTicket = await models.Ticket.create({
|
||||||
|
@ -98,33 +107,33 @@ module.exports = Self => {
|
||||||
addressFk: claim.ticket().addressFk,
|
addressFk: claim.ticket().addressFk,
|
||||||
agencyModeFk: agencyMode.id,
|
agencyModeFk: agencyMode.id,
|
||||||
zoneFk: zone.id
|
zoneFk: zone.id
|
||||||
}, options);
|
}, myOptions);
|
||||||
|
|
||||||
await saveObservation({
|
await saveObservation({
|
||||||
description: `Reclama ticket: ${claim.ticketFk}`,
|
description: `Reclama ticket: ${claim.ticketFk}`,
|
||||||
ticketFk: newRefundTicket.id,
|
ticketFk: newRefundTicket.id,
|
||||||
observationTypeFk: obsevationType.id
|
observationTypeFk: obsevationType.id
|
||||||
}, options);
|
}, myOptions);
|
||||||
|
|
||||||
await models.TicketTracking.create({
|
await models.TicketTracking.create({
|
||||||
ticketFk: newRefundTicket.id,
|
ticketFk: newRefundTicket.id,
|
||||||
stateFk: state.id,
|
stateFk: state.id,
|
||||||
workerFk: worker.id
|
workerFk: worker.id
|
||||||
}, options);
|
}, myOptions);
|
||||||
|
|
||||||
const salesToRefund = await models.ClaimBeginning.find(salesFilter, options);
|
const salesToRefund = await models.ClaimBeginning.find(salesFilter, myOptions);
|
||||||
const createdSales = await addSalesToTicket(salesToRefund, newRefundTicket.id, options);
|
const createdSales = await addSalesToTicket(salesToRefund, newRefundTicket.id, myOptions);
|
||||||
await insertIntoClaimEnd(createdSales, id, worker.id, options);
|
await insertIntoClaimEnd(createdSales, id, worker.id, myOptions);
|
||||||
|
|
||||||
await Self.rawSql('CALL vn.ticketCalculateClon(?, ?)', [
|
await Self.rawSql('CALL vn.ticketCalculateClon(?, ?)', [
|
||||||
newRefundTicket.id, claim.ticketFk
|
newRefundTicket.id, claim.ticketFk
|
||||||
], options);
|
], myOptions);
|
||||||
|
|
||||||
await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
return newRefundTicket;
|
return newRefundTicket;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
if (tx) await tx.rollback();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,42 +1,43 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
const LoopBackContext = require('loopback-context');
|
const LoopBackContext = require('loopback-context');
|
||||||
|
const models = app.models;
|
||||||
|
|
||||||
describe('claimBeginning', () => {
|
describe('claimBeginning', () => {
|
||||||
const claimManagerId = 72;
|
const claimManagerId = 72;
|
||||||
let ticket;
|
|
||||||
let refundTicketSales;
|
|
||||||
let salesInsertedInClaimEnd;
|
|
||||||
|
|
||||||
const activeCtx = {
|
const activeCtx = {
|
||||||
accessToken: {userId: claimManagerId},
|
accessToken: {userId: claimManagerId},
|
||||||
};
|
};
|
||||||
const ctx = {req: activeCtx};
|
const ctx = {req: activeCtx};
|
||||||
|
|
||||||
afterAll(async done => {
|
|
||||||
try {
|
|
||||||
await app.models.Ticket.destroyById(ticket.id);
|
|
||||||
await app.models.Ticket.rawSql(`DELETE FROM vn.orderTicket WHERE ticketFk ='${ticket.id}';`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('importToNewRefundTicket()', () => {
|
describe('importToNewRefundTicket()', () => {
|
||||||
it('should create a new ticket with negative sales and insert the negative sales into claimEnd', async() => {
|
it('should create a new ticket with negative sales and insert the negative sales into claimEnd', async() => {
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
active: activeCtx
|
active: activeCtx
|
||||||
});
|
});
|
||||||
let claimId = 1;
|
let claimId = 1;
|
||||||
ticket = await app.models.ClaimBeginning.importToNewRefundTicket(ctx, claimId);
|
|
||||||
|
|
||||||
refundTicketSales = await app.models.Sale.find({where: {ticketFk: ticket.id}});
|
const tx = await models.Entry.beginTransaction({});
|
||||||
salesInsertedInClaimEnd = await app.models.ClaimEnd.find({where: {claimFk: claimId}});
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
expect(refundTicketSales.length).toEqual(1);
|
const ticket = await models.ClaimBeginning.importToNewRefundTicket(ctx, claimId, options);
|
||||||
expect(refundTicketSales[0].quantity).toEqual(-5);
|
|
||||||
expect(salesInsertedInClaimEnd[0].saleFk).toEqual(refundTicketSales[0].id);
|
const refundTicketSales = await models.Sale.find({
|
||||||
|
where: {ticketFk: ticket.id}
|
||||||
|
}, options);
|
||||||
|
const salesInsertedInClaimEnd = await models.ClaimEnd.find({
|
||||||
|
where: {claimFk: claimId}
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
expect(refundTicketSales.length).toEqual(1);
|
||||||
|
expect(refundTicketSales[0].quantity).toEqual(-5);
|
||||||
|
expect(salesInsertedInClaimEnd[0].saleFk).toEqual(refundTicketSales[0].id);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -227,36 +227,6 @@ module.exports = Self => {
|
||||||
await Self.app.models.ClientCredit.create(newCredit);
|
await Self.app.models.ClientCredit.create(newCredit);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const app = require('vn-loopback/server/server');
|
|
||||||
|
|
||||||
app.on('started', function() {
|
|
||||||
let account = app.models.Account;
|
|
||||||
|
|
||||||
account.observe('before save', async ctx => {
|
|
||||||
if (ctx.isNewInstance) return;
|
|
||||||
ctx.hookState.oldInstance = JSON.parse(JSON.stringify(ctx.currentInstance));
|
|
||||||
});
|
|
||||||
|
|
||||||
account.observe('after save', async ctx => {
|
|
||||||
let changes = ctx.data || ctx.instance;
|
|
||||||
if (!ctx.isNewInstance && changes) {
|
|
||||||
let oldData = ctx.hookState.oldInstance;
|
|
||||||
let hasChanges = oldData.name != changes.name || oldData.active != changes.active;
|
|
||||||
if (!hasChanges) return;
|
|
||||||
|
|
||||||
let userId = ctx.options.accessToken.userId;
|
|
||||||
let logRecord = {
|
|
||||||
originFk: oldData.id,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'Account',
|
|
||||||
oldInstance: {name: oldData.name, active: oldData.active},
|
|
||||||
newInstance: {name: changes.name, active: changes.active}
|
|
||||||
};
|
|
||||||
await Self.app.models.ClientLog.create(logRecord);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.observe('after save', async ctx => {
|
Self.observe('after save', async ctx => {
|
||||||
if (ctx.isNewInstance) return;
|
if (ctx.isNewInstance) return;
|
||||||
|
@ -303,8 +273,58 @@ module.exports = Self => {
|
||||||
query: params
|
query: params
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const workerIdBefore = oldInstance.salesPersonFk;
|
||||||
|
const workerIdAfter = newInstance.salesPersonFk;
|
||||||
|
const assignmentChanged = workerIdBefore != workerIdAfter;
|
||||||
|
if (assignmentChanged)
|
||||||
|
await Self.notifyAssignment(instance, workerIdBefore, workerIdAfter);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Send notification on client worker assignment
|
||||||
|
Self.notifyAssignment = async function notifyAssignment(client, previousWorkerId, currentWorkerId) {
|
||||||
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||||
|
const httpCtx = {req: loopBackContext.active};
|
||||||
|
const httpRequest = httpCtx.req.http.req;
|
||||||
|
const $t = httpRequest.__;
|
||||||
|
const headers = httpRequest.headers;
|
||||||
|
const origin = headers.origin;
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
let previousWorker = {name: $t('None')};
|
||||||
|
let currentWorker = {name: $t('None')};
|
||||||
|
if (previousWorkerId) {
|
||||||
|
const worker = await models.Worker.findById(previousWorkerId, {
|
||||||
|
include: {relation: 'user'}
|
||||||
|
});
|
||||||
|
previousWorker.user = worker && worker.user().name;
|
||||||
|
previousWorker.name = worker && worker.user().nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentWorkerId) {
|
||||||
|
const worker = await models.Worker.findById(currentWorkerId, {
|
||||||
|
include: {relation: 'user'}
|
||||||
|
});
|
||||||
|
currentWorker.user = worker && worker.user().name;
|
||||||
|
currentWorker.name = worker && worker.user().nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullUrl = `${origin}/#!/client/${client.id}/basic-data`;
|
||||||
|
const message = $t('Client assignment has changed', {
|
||||||
|
clientId: client.id,
|
||||||
|
clientName: client.name,
|
||||||
|
url: fullUrl,
|
||||||
|
previousWorkerName: previousWorker.name,
|
||||||
|
currentWorkerName: currentWorker.name
|
||||||
|
});
|
||||||
|
|
||||||
|
if (previousWorkerId)
|
||||||
|
await models.Chat.send(httpCtx, `@${previousWorker.user}`, message);
|
||||||
|
|
||||||
|
if (currentWorkerId)
|
||||||
|
await models.Chat.send(httpCtx, `@${currentWorker.user}`, message);
|
||||||
|
};
|
||||||
|
|
||||||
async function validateCreditChange(ctx, finalState) {
|
async function validateCreditChange(ctx, finalState) {
|
||||||
let models = Self.app.models;
|
let models = Self.app.models;
|
||||||
let userId = ctx.options.accessToken.userId;
|
let userId = ctx.options.accessToken.userId;
|
||||||
|
@ -341,4 +361,34 @@ module.exports = Self => {
|
||||||
if (count <= 0)
|
if (count <= 0)
|
||||||
throw new UserError('The role cannot set this credit amount');
|
throw new UserError('The role cannot set this credit amount');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
app.on('started', function() {
|
||||||
|
let account = app.models.Account;
|
||||||
|
|
||||||
|
account.observe('before save', async ctx => {
|
||||||
|
if (ctx.isNewInstance) return;
|
||||||
|
ctx.hookState.oldInstance = JSON.parse(JSON.stringify(ctx.currentInstance));
|
||||||
|
});
|
||||||
|
|
||||||
|
account.observe('after save', async ctx => {
|
||||||
|
let changes = ctx.data || ctx.instance;
|
||||||
|
if (!ctx.isNewInstance && changes) {
|
||||||
|
let oldData = ctx.hookState.oldInstance;
|
||||||
|
let hasChanges = oldData.name != changes.name || oldData.active != changes.active;
|
||||||
|
if (!hasChanges) return;
|
||||||
|
|
||||||
|
let userId = ctx.options.accessToken.userId;
|
||||||
|
let logRecord = {
|
||||||
|
originFk: oldData.id,
|
||||||
|
userFk: userId,
|
||||||
|
action: 'update',
|
||||||
|
changedModel: 'Account',
|
||||||
|
oldInstance: {name: oldData.name, active: oldData.active},
|
||||||
|
newInstance: {name: changes.name, active: changes.active}
|
||||||
|
};
|
||||||
|
await Self.app.models.ClientLog.create(logRecord);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('Client Model', () => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'},
|
||||||
|
[`__`]: value => {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ctx = {req: activeCtx};
|
||||||
|
const chatModel = app.models.Chat;
|
||||||
|
const client = {id: 101, name: 'Bruce Banner'};
|
||||||
|
const previousWorkerId = 106; // DavidCharlesHaller
|
||||||
|
const currentWorkerId = 107; // HankPym
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('notifyAssignment()', () => {
|
||||||
|
it('should call to the Chat send() method for both workers', async() => {
|
||||||
|
spyOn(chatModel, 'send').and.callThrough();
|
||||||
|
|
||||||
|
await app.models.Client.notifyAssignment(client, previousWorkerId, currentWorkerId);
|
||||||
|
|
||||||
|
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`);
|
||||||
|
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call to the Chat send() method for the previous worker', async() => {
|
||||||
|
spyOn(chatModel, 'send').and.callThrough();
|
||||||
|
|
||||||
|
await app.models.Client.notifyAssignment(client, null, currentWorkerId);
|
||||||
|
|
||||||
|
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call to the Chat send() method for the current worker', async() => {
|
||||||
|
spyOn(chatModel, 'send').and.callThrough();
|
||||||
|
|
||||||
|
await app.models.Client.notifyAssignment(client, previousWorkerId, null);
|
||||||
|
|
||||||
|
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -2,7 +2,6 @@ const app = require('vn-loopback/server/server');
|
||||||
const LoopBackContext = require('loopback-context');
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('entry import()', () => {
|
describe('entry import()', () => {
|
||||||
let newEntry;
|
|
||||||
const buyerId = 35;
|
const buyerId = 35;
|
||||||
const companyId = 442;
|
const companyId = 442;
|
||||||
const travelId = 1;
|
const travelId = 1;
|
||||||
|
@ -52,29 +51,32 @@ describe('entry import()', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const tx = await app.models.Entry.beginTransaction({});
|
const tx = await app.models.Entry.beginTransaction({});
|
||||||
const options = {transaction: tx};
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const newEntry = await app.models.Entry.create({
|
||||||
|
dated: new Date(),
|
||||||
|
supplierFk: supplierId,
|
||||||
|
travelFk: travelId,
|
||||||
|
companyFk: companyId,
|
||||||
|
observation: 'The entry',
|
||||||
|
ref: 'Entry ref'
|
||||||
|
}, options);
|
||||||
|
|
||||||
newEntry = await app.models.Entry.create({
|
await app.models.Entry.importBuys(ctx, newEntry.id, options);
|
||||||
dated: new Date(),
|
|
||||||
supplierFk: supplierId,
|
|
||||||
travelFk: travelId,
|
|
||||||
companyFk: companyId,
|
|
||||||
observation: 'The entry',
|
|
||||||
ref: 'Entry ref'
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
await app.models.Entry.importBuys(ctx, newEntry.id, options);
|
const updatedEntry = await app.models.Entry.findById(newEntry.id, null, options);
|
||||||
|
const entryBuys = await app.models.Buy.find({
|
||||||
|
where: {entryFk: newEntry.id}
|
||||||
|
}, options);
|
||||||
|
|
||||||
const updatedEntry = await app.models.Entry.findById(newEntry.id, null, options);
|
expect(updatedEntry.observation).toEqual(expectedObservation);
|
||||||
const entryBuys = await app.models.Buy.find({
|
expect(updatedEntry.ref).toEqual(expectedRef);
|
||||||
where: {entryFk: newEntry.id}
|
expect(entryBuys.length).toEqual(2);
|
||||||
}, options);
|
|
||||||
|
|
||||||
expect(updatedEntry.observation).toEqual(expectedObservation);
|
await tx.rollback();
|
||||||
expect(updatedEntry.ref).toEqual(expectedRef);
|
} catch (e) {
|
||||||
expect(entryBuys.length).toEqual(2);
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
// Restores
|
}
|
||||||
await tx.rollback();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,4 +2,5 @@ reference: Referencia
|
||||||
Observation: Observación
|
Observation: Observación
|
||||||
Box: Embalaje
|
Box: Embalaje
|
||||||
Import buys: Importar compras
|
Import buys: Importar compras
|
||||||
Some of the imported buys doesn't have an item: Algunas de las compras importadas no tienen un artículo
|
Some of the imported buys doesn't have an item: Algunas de las compras importadas no tienen un artículo
|
||||||
|
JSON files only: Solo ficheros JSON
|
|
@ -165,9 +165,13 @@
|
||||||
<vn-dialog class="edit"
|
<vn-dialog class="edit"
|
||||||
vn-id="edit"
|
vn-id="edit"
|
||||||
on-accept="$ctrl.onEditAccept()"
|
on-accept="$ctrl.onEditAccept()"
|
||||||
on-close="$ctrl.editedColumn = null"
|
on-close="$ctrl.editedColumn = null">
|
||||||
message="Edit buy(s)">
|
|
||||||
<tpl-body>
|
<tpl-body>
|
||||||
|
<span translate>Edit</span>
|
||||||
|
<span class="countLines">
|
||||||
|
{{::$ctrl.totalChecked}}
|
||||||
|
</span>
|
||||||
|
<span translate>buy(s)</span>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-two
|
vn-two
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
export default class Controller extends Section {
|
export default class Controller extends Section {
|
||||||
constructor($element, $) {
|
constructor($element, $) {
|
||||||
|
|
|
@ -12,3 +12,5 @@ Weight: Peso
|
||||||
Minimun amount: Cantidad mínima de compra
|
Minimun amount: Cantidad mínima de compra
|
||||||
Field to edit: Campo a editar
|
Field to edit: Campo a editar
|
||||||
PackageName: Cubo
|
PackageName: Cubo
|
||||||
|
Edit: Editar
|
||||||
|
buy(s): compra(s)
|
|
@ -0,0 +1,7 @@
|
||||||
|
.countLines {
|
||||||
|
flex: 0.15;
|
||||||
|
font-size: 24px;
|
||||||
|
color: orangered;
|
||||||
|
font-weight: bold;
|
||||||
|
max-width: 30px;
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ module.exports = Self => {
|
||||||
description: 'itemFk, id'
|
description: 'itemFk, id'
|
||||||
}],
|
}],
|
||||||
returns: {
|
returns: {
|
||||||
type: 'Array',
|
type: ['Object'],
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
|
@ -40,12 +40,19 @@ module.exports = Self => {
|
||||||
b.weight,
|
b.weight,
|
||||||
i.stems,
|
i.stems,
|
||||||
b.quantity,
|
b.quantity,
|
||||||
|
b.buyingValue +
|
||||||
|
b.freightValue +
|
||||||
|
b.comissionValue +
|
||||||
|
b.packageValue AS cost,
|
||||||
b.buyingValue,
|
b.buyingValue,
|
||||||
|
b.freightValue,
|
||||||
|
b.comissionValue,
|
||||||
|
b.packageValue,
|
||||||
b.packageFk ,
|
b.packageFk ,
|
||||||
s.id AS supplierFk,
|
s.id AS supplierFk,
|
||||||
s.name AS supplier
|
s.name AS supplier
|
||||||
FROM itemType it
|
FROM itemType it
|
||||||
RIGHT JOIN (entry e
|
RIGHT JOIN (entry e
|
||||||
LEFT JOIN supplier s ON s.id = e.supplierFk
|
LEFT JOIN supplier s ON s.id = e.supplierFk
|
||||||
RIGHT JOIN buy b ON b.entryFk = e.id
|
RIGHT JOIN buy b ON b.entryFk = e.id
|
||||||
LEFT JOIN item i ON i.id = b.itemFk
|
LEFT JOIN item i ON i.id = b.itemFk
|
||||||
|
|
|
@ -72,7 +72,17 @@
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number class="expendable">{{::entry.stems | dashIfEmpty}}</vn-td>
|
<vn-td number class="expendable">{{::entry.stems | dashIfEmpty}}</vn-td>
|
||||||
<vn-td number>{{::entry.quantity}}</vn-td>
|
<vn-td number>{{::entry.quantity}}</vn-td>
|
||||||
<vn-td number class="expendable">{{::entry.buyingValue | dashIfEmpty}}</vn-td>
|
<vn-td number
|
||||||
|
class="expendable">
|
||||||
|
<span
|
||||||
|
vn-tooltip="
|
||||||
|
{{::$ctrl.$t('Cost')}}: {{::entry.buyingValue| dashIfEmpty}}<br>
|
||||||
|
{{::$ctrl.$t('Package')}}: {{::entry.packageValue| dashIfEmpty}}<br>
|
||||||
|
{{::$ctrl.$t('Freight')}}: {{::entry.freightValue| dashIfEmpty}}<br>
|
||||||
|
{{::$ctrl.$t('Comission')}}: {{::entry.comissionValue| dashIfEmpty}}">
|
||||||
|
{{::entry.cost | dashIfEmpty}}
|
||||||
|
</span>
|
||||||
|
</vn-td>
|
||||||
<vn-td number>{{::entry.weight | dashIfEmpty}}</vn-td>
|
<vn-td number>{{::entry.weight | dashIfEmpty}}</vn-td>
|
||||||
<vn-td number>{{::entry.packageFk | dashIfEmpty}}</vn-td>
|
<vn-td number>{{::entry.packageFk | dashIfEmpty}}</vn-td>
|
||||||
<vn-td class="expendable" title="{{::entry.supplier | dashIfEmpty}}">{{::entry.supplier | dashIfEmpty}}</vn-td>
|
<vn-td class="expendable" title="{{::entry.supplier | dashIfEmpty}}">{{::entry.supplier | dashIfEmpty}}</vn-td>
|
||||||
|
|
|
@ -9,4 +9,7 @@ Ignored: Ignorado
|
||||||
Provider: Proveedor
|
Provider: Proveedor
|
||||||
Cube: Cubo
|
Cube: Cubo
|
||||||
Price Per Unit: Precio Por Unidad
|
Price Per Unit: Precio Por Unidad
|
||||||
Price Per Package: Precio Por Paquete
|
Price Per Package: Precio Por Paquete
|
||||||
|
Freight: Porte
|
||||||
|
Package: Embalaje
|
||||||
|
Comission: Comision
|
|
@ -0,0 +1,72 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('getSuggestedTickets', {
|
||||||
|
description: 'Returns an array of suggested tickets for the given route',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The route id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/getSuggestedTickets`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.getSuggestedTickets = async id => {
|
||||||
|
const ticketsInRoute = await Self.app.models.Ticket.find({
|
||||||
|
where: {routeFk: id},
|
||||||
|
fields: ['id']
|
||||||
|
});
|
||||||
|
const idsToExclude = ticketsInRoute.map(ticket => ticket.id);
|
||||||
|
|
||||||
|
const route = await Self.app.models.Route.findById(id);
|
||||||
|
|
||||||
|
const zoneAgencyModes = await Self.app.models.ZoneAgencyMode.find({
|
||||||
|
where: {
|
||||||
|
agencyModeFk: route.agencyModeFk
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const zoneIds = [];
|
||||||
|
for (let zoneAgencyMode of zoneAgencyModes)
|
||||||
|
zoneIds.push(zoneAgencyMode.zoneFk);
|
||||||
|
|
||||||
|
const minDate = new Date(route.finished);
|
||||||
|
minDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const maxDate = new Date(route.finished);
|
||||||
|
maxDate.setHours(23, 59, 59, 59);
|
||||||
|
|
||||||
|
let tickets = await Self.app.models.Ticket.find({
|
||||||
|
where: {
|
||||||
|
agencyModeFk: route.agencyModeFk,
|
||||||
|
zoneFk: {inq: zoneIds},
|
||||||
|
id: {nin: idsToExclude},
|
||||||
|
landed: {between: [minDate, maxDate]}
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'warehouse',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'address',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'street', 'postalCode', 'city'],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return tickets;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('route getSuggestedTickets()', () => {
|
||||||
|
it('should return an array of suggested tickets', async() => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 19},
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
|
||||||
|
const routeID = 1;
|
||||||
|
const ticketInRoute = await app.models.Ticket.findById(12);
|
||||||
|
|
||||||
|
await ticketInRoute.updateAttribute('routeFk', null);
|
||||||
|
|
||||||
|
const result = await app.models.Route.getSuggestedTickets(routeID);
|
||||||
|
|
||||||
|
const length = result.length;
|
||||||
|
const anyResult = result[Math.floor(Math.random() * Math.floor(length))];
|
||||||
|
|
||||||
|
expect(result.length).toEqual(1);
|
||||||
|
expect(anyResult.zoneFk).toEqual(1);
|
||||||
|
expect(anyResult.agencyModeFk).toEqual(1);
|
||||||
|
|
||||||
|
await ticketInRoute.updateAttribute('routeFk', routeID);
|
||||||
|
});
|
||||||
|
});
|
|
@ -4,7 +4,7 @@ const LoopBackContext = require('loopback-context');
|
||||||
describe('route insertTicket()', () => {
|
describe('route insertTicket()', () => {
|
||||||
const deliveryId = 56;
|
const deliveryId = 56;
|
||||||
let originalTicket;
|
let originalTicket;
|
||||||
const routeId = 2;
|
const routeId = 1;
|
||||||
const activeCtx = {
|
const activeCtx = {
|
||||||
accessToken: {userId: deliveryId},
|
accessToken: {userId: deliveryId},
|
||||||
};
|
};
|
||||||
|
@ -17,26 +17,18 @@ describe('route insertTicket()', () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async done => {
|
|
||||||
try {
|
|
||||||
await originalTicket.updateAttribute('routeFk', null);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add the ticket to a route', async() => {
|
it('should add the ticket to a route', async() => {
|
||||||
originalTicket = await app.models.Ticket.findById(14);
|
const ticketId = 12;
|
||||||
|
originalTicket = await app.models.Ticket.findById(ticketId);
|
||||||
|
await originalTicket.updateAttribute('routeFk', null);
|
||||||
|
|
||||||
const ticketId = 14;
|
|
||||||
const result = await app.models.Route.insertTicket(routeId, ticketId);
|
const result = await app.models.Route.insertTicket(routeId, ticketId);
|
||||||
|
|
||||||
expect(result.routeFk).toEqual(2);
|
expect(result.routeFk).toEqual(routeId);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw and error if the ticket is not suitable for the route', async() => {
|
it('should throw and error if the ticket is not suitable for the route', async() => {
|
||||||
const ticketId = 23;
|
const ticketId = 2;
|
||||||
let error;
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -7,6 +7,7 @@ module.exports = Self => {
|
||||||
require('../methods/route/getDeliveryPoint')(Self);
|
require('../methods/route/getDeliveryPoint')(Self);
|
||||||
require('../methods/route/insertTicket')(Self);
|
require('../methods/route/insertTicket')(Self);
|
||||||
require('../methods/route/clone')(Self);
|
require('../methods/route/clone')(Self);
|
||||||
|
require('../methods/route/getSuggestedTickets')(Self);
|
||||||
|
|
||||||
Self.validate('kmStart', validateDistance, {
|
Self.validate('kmStart', validateDistance, {
|
||||||
message: 'Distance must be lesser than 1000'
|
message: 'Distance must be lesser than 1000'
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<vn-icon
|
<vn-icon
|
||||||
ng-if="ticket.notes.length"
|
ng-if="ticket.notes.length"
|
||||||
title="::{{ticket.notes[0].description}}"
|
title="{{ticket.notes[0].description}}"
|
||||||
icon="insert_drive_file"
|
icon="insert_drive_file"
|
||||||
class="bright">
|
class="bright">
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
|
@ -110,10 +110,9 @@
|
||||||
question="Delete ticket from route?"
|
question="Delete ticket from route?"
|
||||||
on-accept="$ctrl.removeTicketFromRoute()">
|
on-accept="$ctrl.removeTicketFromRoute()">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="possibleTicketsModel"
|
vn-id="possibleTicketsModel"
|
||||||
url="Tickets"
|
url="Routes/{{$ctrl.$params.id}}/getSuggestedTickets"
|
||||||
filter="$ctrl.possibleTicketsFilter"
|
|
||||||
data="$ctrl.possibleTickets">
|
data="$ctrl.possibleTickets">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-dialog
|
<vn-dialog
|
||||||
|
|
|
@ -9,8 +9,6 @@ class Controller extends Section {
|
||||||
|
|
||||||
set route(value) {
|
set route(value) {
|
||||||
this._route = value;
|
this._route = value;
|
||||||
if (value)
|
|
||||||
this.buildPossibleTicketsFilter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isChecked() {
|
get isChecked() {
|
||||||
|
@ -22,32 +20,6 @@ class Controller extends Section {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildPossibleTicketsFilter() {
|
|
||||||
let minDate = new Date(this.route.finished);
|
|
||||||
minDate.setHours(0, 0, 0, 0);
|
|
||||||
|
|
||||||
let maxDate = new Date(this.route.finished);
|
|
||||||
maxDate.setHours(23, 59, 59, 59);
|
|
||||||
|
|
||||||
this.possibleTicketsFilter = {
|
|
||||||
where: {
|
|
||||||
zoneFk: this.route.zoneFk,
|
|
||||||
routeFk: null,
|
|
||||||
landed: {between: [minDate, maxDate]},
|
|
||||||
},
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
relation: 'warehouse',
|
|
||||||
scope: {
|
|
||||||
fields: ['name']
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
relation: 'address'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getHighestPriority() {
|
getHighestPriority() {
|
||||||
let highestPriority = Math.max(...this.$.model.data.map(tag => {
|
let highestPriority = Math.max(...this.$.model.data.map(tag => {
|
||||||
return tag.priority;
|
return tag.priority;
|
||||||
|
@ -134,14 +106,26 @@ class Controller extends Section {
|
||||||
setTicketsRoute() {
|
setTicketsRoute() {
|
||||||
let tickets = this.getSelectedItems(this.possibleTickets);
|
let tickets = this.getSelectedItems(this.possibleTickets);
|
||||||
if (tickets.length === 0) return;
|
if (tickets.length === 0) return;
|
||||||
for (let i = 0; i < tickets.length; i++) {
|
|
||||||
delete tickets[i].checked;
|
const updates = [];
|
||||||
tickets[i].routeFk = this.route.id;
|
|
||||||
|
for (let ticket of tickets) {
|
||||||
|
delete ticket.checked;
|
||||||
|
const update = {
|
||||||
|
where: {id: ticket.id},
|
||||||
|
data: {routeFk: this.route.id}
|
||||||
|
};
|
||||||
|
|
||||||
|
updates.push(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.$.possibleTicketsModel.save().then(() => {
|
const data = {creates: [], updates: updates, deletes: []};
|
||||||
this.$.model.data = this.$.model.data.concat(tickets);
|
|
||||||
});
|
return this.$http.post(`Tickets/crud`, data)
|
||||||
|
.then(() => {
|
||||||
|
this.$.model.data = this.$.model.data.concat(tickets);
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onDrop($event) {
|
onDrop($event) {
|
||||||
|
|
|
@ -37,42 +37,6 @@ describe('Route', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buildPossibleTicketsFilter()', () => {
|
|
||||||
it('should build the possible tickets filter', () => {
|
|
||||||
let expectedFilter = {
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
relation: 'warehouse',
|
|
||||||
scope: {
|
|
||||||
fields: ['name']
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
relation: 'address'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
where: {
|
|
||||||
landed: {
|
|
||||||
between: [
|
|
||||||
jasmine.any(Date),
|
|
||||||
jasmine.any(Date)
|
|
||||||
]
|
|
||||||
},
|
|
||||||
routeFk: null,
|
|
||||||
zoneFk: 67
|
|
||||||
}
|
|
||||||
};
|
|
||||||
controller.route = {
|
|
||||||
finished: new Date(),
|
|
||||||
routeFk: null,
|
|
||||||
zoneFk: 67
|
|
||||||
};
|
|
||||||
|
|
||||||
controller.buildPossibleTicketsFilter();
|
|
||||||
|
|
||||||
expect(controller.possibleTicketsFilter).toEqual(expectedFilter);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getHighestPriority()', () => {
|
describe('getHighestPriority()', () => {
|
||||||
it('should return the highest value found in priorities plus 1', () => {
|
it('should return the highest value found in priorities plus 1', () => {
|
||||||
controller.$.model = {data: [
|
controller.$.model = {data: [
|
||||||
|
@ -228,13 +192,13 @@ describe('Route', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setTicketsRoute()', () => {
|
describe('setTicketsRoute()', () => {
|
||||||
it('should perform a POST query to add tickets to the route', done => {
|
it('should perform a POST query to add tickets to the route', () => {
|
||||||
controller.$.possibleTicketsModel = {save: () => {}};
|
|
||||||
jest.spyOn(controller.$.possibleTicketsModel, 'save').mockReturnValue(Promise.resolve());
|
|
||||||
controller.$.model = {data: [
|
controller.$.model = {data: [
|
||||||
{id: 1, checked: false}
|
{id: 1, checked: false}
|
||||||
]};
|
]};
|
||||||
|
|
||||||
|
const existingTicket = controller.$.model.data[0];
|
||||||
|
|
||||||
controller.route = {id: 111};
|
controller.route = {id: 111};
|
||||||
|
|
||||||
controller.possibleTickets = [
|
controller.possibleTickets = [
|
||||||
|
@ -245,15 +209,16 @@ describe('Route', () => {
|
||||||
];
|
];
|
||||||
|
|
||||||
let expectedResult = [
|
let expectedResult = [
|
||||||
{checked: false, id: 1},
|
existingTicket,
|
||||||
{id: 3, routeFk: 111},
|
{id: 3},
|
||||||
{id: 5, routeFk: 111}
|
{id: 5}
|
||||||
];
|
];
|
||||||
|
|
||||||
controller.setTicketsRoute().then(() => {
|
$httpBackend.expectPOST(`Tickets/crud`).respond();
|
||||||
expect(controller.$.model.data).toEqual(expectedResult);
|
controller.setTicketsRoute();
|
||||||
done();
|
$httpBackend.flush();
|
||||||
}).catch(done.fail);
|
|
||||||
|
expect(controller.$.model.data).toEqual(expectedResult);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Accounts: Cuentas
|
|
@ -9,10 +9,10 @@
|
||||||
{"state": "supplier.index", "icon": "icon-supplier"}
|
{"state": "supplier.index", "icon": "icon-supplier"}
|
||||||
],
|
],
|
||||||
"card": [
|
"card": [
|
||||||
{"state": "supplier.card.account", "icon": "contact_support"},
|
|
||||||
{"state": "supplier.card.basicData", "icon": "settings"},
|
{"state": "supplier.card.basicData", "icon": "settings"},
|
||||||
{"state": "supplier.card.fiscalData", "icon": "account_balance"},
|
{"state": "supplier.card.fiscalData", "icon": "account_balance"},
|
||||||
{"state": "supplier.card.billingData", "icon": "icon-payment"},
|
{"state": "supplier.card.billingData", "icon": "icon-payment"},
|
||||||
|
{"state": "supplier.card.account", "icon": "contact_support"},
|
||||||
{"state": "supplier.card.contact", "icon": "contact_phone"},
|
{"state": "supplier.card.contact", "icon": "contact_phone"},
|
||||||
{"state": "supplier.card.log", "icon": "history"},
|
{"state": "supplier.card.log", "icon": "history"},
|
||||||
{"state": "supplier.card.consumption", "icon": "show_chart"}
|
{"state": "supplier.card.consumption", "icon": "show_chart"}
|
||||||
|
@ -104,7 +104,7 @@
|
||||||
"url": "/account",
|
"url": "/account",
|
||||||
"state": "supplier.card.account",
|
"state": "supplier.card.account",
|
||||||
"component": "vn-supplier-account",
|
"component": "vn-supplier-account",
|
||||||
"description": "Account",
|
"description": "Accounts",
|
||||||
"params": {
|
"params": {
|
||||||
"supplier": "$ctrl.supplier"
|
"supplier": "$ctrl.supplier"
|
||||||
},
|
},
|
||||||
|
|
|
@ -259,7 +259,7 @@ module.exports = Self => {
|
||||||
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)
|
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)
|
||||||
AND f.shipped >= CURDATE()`);
|
AND f.shipped >= CURDATE()`);
|
||||||
|
|
||||||
stmts.push('CALL ticketGetProblems()');
|
stmts.push('CALL ticketGetProblems(FALSE)');
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`
|
stmt = new ParameterizedSQL(`
|
||||||
SELECT
|
SELECT
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('ticket filter()', () => {
|
// #2868 Excluded until database export
|
||||||
|
xdescribe('ticket filter()', () => {
|
||||||
it('should return the tickets matching the filter', async() => {
|
it('should return the tickets matching the filter', async() => {
|
||||||
const ctx = {req: {accessToken: {userId: 9}}, args: {}};
|
const ctx = {req: {accessToken: {userId: 9}}, args: {}};
|
||||||
const filter = {order: 'id DESC'};
|
const filter = {order: 'id DESC'};
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
"name": "Ticket",
|
"name": "Ticket",
|
||||||
"base": "Loggable",
|
"base": "Loggable",
|
||||||
"log": {
|
"log": {
|
||||||
"model":"TicketLog"
|
"model":"TicketLog",
|
||||||
|
"showField": "id"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
|
|
|
@ -43,9 +43,9 @@
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</vn-tool-bar>
|
</vn-tool-bar>
|
||||||
<vn-one class="taxes" ng-if="$ctrl.sales.length > 0">
|
<vn-one class="taxes" ng-if="$ctrl.sales.length > 0">
|
||||||
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.subtotal | currency: 'EUR': 2}}</p>
|
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
|
||||||
<p><vn-label translate>VAT</vn-label> {{$ctrl.VAT | currency: 'EUR': 2}}</p>
|
<p><vn-label translate>VAT</vn-label> {{$ctrl.ticket.totalWithVat - $ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
|
||||||
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.total | currency: 'EUR': 2}}</strong></p>
|
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.ticket.totalWithVat | currency: 'EUR':2}}</strong></p>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-table model="model">
|
<vn-table model="model">
|
||||||
|
|
|
@ -24,7 +24,6 @@ class Controller extends Section {
|
||||||
|
|
||||||
set sales(value) {
|
set sales(value) {
|
||||||
this._sales = value;
|
this._sales = value;
|
||||||
this.refreshTotal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get ticketState() {
|
get ticketState() {
|
||||||
|
@ -33,17 +32,6 @@ class Controller extends Section {
|
||||||
return this.ticket.ticketState.state.code;
|
return this.ticket.ticketState.state.code;
|
||||||
}
|
}
|
||||||
|
|
||||||
get total() {
|
|
||||||
return this.subtotal + this.VAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSubTotal() {
|
|
||||||
if (!this.$params.id || !this.sales) return;
|
|
||||||
this.$http.get(`Tickets/${this.$params.id}`).then(res => {
|
|
||||||
this.subtotal = res.data.totalWithoutVat || 0.0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getSaleTotal(sale) {
|
getSaleTotal(sale) {
|
||||||
if (sale.quantity == null || sale.price == null)
|
if (sale.quantity == null || sale.price == null)
|
||||||
return null;
|
return null;
|
||||||
|
@ -59,19 +47,6 @@ class Controller extends Section {
|
||||||
.then(res => this.edit.mana = res.data);
|
.then(res => this.edit.mana = res.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
getVat() {
|
|
||||||
this.VAT = 0.0;
|
|
||||||
if (!this.$params.id || !this.sales) return;
|
|
||||||
this.$http.get(`Tickets/${this.$params.id}`).then(res => {
|
|
||||||
this.VAT = res.data.totalWithVat - res.data.totalWithoutVat || 0.0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshTotal() {
|
|
||||||
this.getSubTotal();
|
|
||||||
this.getVat();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns checked instances
|
* Returns checked instances
|
||||||
*
|
*
|
||||||
|
@ -158,8 +133,6 @@ class Controller extends Section {
|
||||||
const index = this.sales.indexOf(sale);
|
const index = this.sales.indexOf(sale);
|
||||||
this.sales.splice(index, 1);
|
this.sales.splice(index, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.refreshTotal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createClaim() {
|
createClaim() {
|
||||||
|
@ -221,7 +194,6 @@ class Controller extends Section {
|
||||||
this.$http.post(query, {newPrice}).then(res => {
|
this.$http.post(query, {newPrice}).then(res => {
|
||||||
sale.price = res.data.price;
|
sale.price = res.data.price;
|
||||||
this.edit = null;
|
this.edit = null;
|
||||||
this.refreshTotal();
|
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
}).finally(() => this.resetChanges());
|
}).finally(() => this.resetChanges());
|
||||||
}
|
}
|
||||||
|
@ -287,7 +259,6 @@ class Controller extends Section {
|
||||||
sale.discount = this.edit.discount;
|
sale.discount = this.edit.discount;
|
||||||
|
|
||||||
this.edit = null;
|
this.edit = null;
|
||||||
this.refreshTotal();
|
|
||||||
}).finally(() => this.resetChanges());
|
}).finally(() => this.resetChanges());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +372,6 @@ class Controller extends Section {
|
||||||
updateQuantity(sale) {
|
updateQuantity(sale) {
|
||||||
const data = {quantity: sale.quantity};
|
const data = {quantity: sale.quantity};
|
||||||
this.$http.post(`Sales/${sale.id}/updateQuantity`, data).then(() => {
|
this.$http.post(`Sales/${sale.id}/updateQuantity`, data).then(() => {
|
||||||
this.refreshTotal();
|
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
this.$.model.refresh();
|
this.$.model.refresh();
|
||||||
|
@ -444,7 +414,6 @@ class Controller extends Section {
|
||||||
sale.price = newSale.price;
|
sale.price = newSale.price;
|
||||||
sale.item = newSale.item;
|
sale.item = newSale.item;
|
||||||
|
|
||||||
this.refreshTotal();
|
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
}).finally(() => this.resetChanges());
|
}).finally(() => this.resetChanges());
|
||||||
}
|
}
|
||||||
|
@ -466,7 +435,6 @@ class Controller extends Section {
|
||||||
this.$http.post(query).then(() => {
|
this.$http.post(query).then(() => {
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
this.$.model.refresh();
|
this.$.model.refresh();
|
||||||
this.refreshTotal();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,28 +72,6 @@ describe('Ticket', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sales() setter', () => {
|
|
||||||
it('should set the sales data an then call the refreshTotal() method', () => {
|
|
||||||
jest.spyOn(controller, 'refreshTotal').mockReturnThis();
|
|
||||||
|
|
||||||
controller.sales = [{id: 1}];
|
|
||||||
|
|
||||||
expect(controller.refreshTotal).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getSubTotal()', () => {
|
|
||||||
it('should make an HTTP GET query and then set the subtotal property', () => {
|
|
||||||
const expectedResponse = {totalWithoutVat: 128};
|
|
||||||
|
|
||||||
$httpBackend.expect('GET', 'Tickets/1').respond(200, expectedResponse);
|
|
||||||
controller.getSubTotal();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.subtotal).toEqual(expectedResponse.totalWithoutVat);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getSaleTotal()', () => {
|
describe('getSaleTotal()', () => {
|
||||||
it('should return the sale total amount', () => {
|
it('should return the sale total amount', () => {
|
||||||
const sale = {
|
const sale = {
|
||||||
|
@ -122,21 +100,6 @@ describe('Ticket', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getVat()', () => {
|
|
||||||
it('should make an HTTP GET query and return the ticket VAT', () => {
|
|
||||||
controller.edit = {};
|
|
||||||
const expectedResponse = {totalWithVat: 1000, totalWithoutVat: 999};
|
|
||||||
|
|
||||||
const expectedVAT = expectedResponse.totalWithVat - expectedResponse.totalWithoutVat;
|
|
||||||
|
|
||||||
$httpBackend.expect('GET', 'Tickets/1').respond(200, expectedResponse);
|
|
||||||
controller.getVat();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.VAT).toEqual(expectedVAT);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('selectedSales()', () => {
|
describe('selectedSales()', () => {
|
||||||
it('should return a list of selected sales', () => {
|
it('should return a list of selected sales', () => {
|
||||||
controller.sales[1].checked = true;
|
controller.sales[1].checked = true;
|
||||||
|
@ -276,7 +239,6 @@ describe('Ticket', () => {
|
||||||
|
|
||||||
describe('removeSelectedSales()', () => {
|
describe('removeSelectedSales()', () => {
|
||||||
it('should remove the selected sales from the controller sale property', () => {
|
it('should remove the selected sales from the controller sale property', () => {
|
||||||
jest.spyOn(controller, 'refreshTotal').mockReturnThis();
|
|
||||||
jest.spyOn(controller, 'resetChanges').mockReturnThis();
|
jest.spyOn(controller, 'resetChanges').mockReturnThis();
|
||||||
|
|
||||||
const firstSale = controller.sales[0];
|
const firstSale = controller.sales[0];
|
||||||
|
@ -288,7 +250,6 @@ describe('Ticket', () => {
|
||||||
|
|
||||||
expect(controller.sales.length).toEqual(1);
|
expect(controller.sales.length).toEqual(1);
|
||||||
expect(lastSale.id).toEqual(4);
|
expect(lastSale.id).toEqual(4);
|
||||||
expect(controller.refreshTotal).toHaveBeenCalledWith();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -355,7 +316,6 @@ describe('Ticket', () => {
|
||||||
|
|
||||||
describe('updatePrice()', () => {
|
describe('updatePrice()', () => {
|
||||||
it('should make an HTTP POST query, update the sale price and then call to the resetChanges() method', () => {
|
it('should make an HTTP POST query, update the sale price and then call to the resetChanges() method', () => {
|
||||||
jest.spyOn(controller, 'refreshTotal').mockReturnThis();
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
|
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
|
||||||
jest.spyOn(controller, 'resetChanges').mockReturnThis();
|
jest.spyOn(controller, 'resetChanges').mockReturnThis();
|
||||||
|
|
||||||
|
@ -372,7 +332,6 @@ describe('Ticket', () => {
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
expect(selectedSale.price).toEqual(2);
|
expect(selectedSale.price).toEqual(2);
|
||||||
expect(controller.refreshTotal).toHaveBeenCalledWith();
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
expect(controller.$.editPricePopover.hide).toHaveBeenCalledWith();
|
expect(controller.$.editPricePopover.hide).toHaveBeenCalledWith();
|
||||||
expect(controller.resetChanges).toHaveBeenCalledWith();
|
expect(controller.resetChanges).toHaveBeenCalledWith();
|
||||||
|
@ -451,7 +410,6 @@ describe('Ticket', () => {
|
||||||
it('should make an HTTP POST query, update the sales discount and then call to the resetChanges() method', () => {
|
it('should make an HTTP POST query, update the sales discount and then call to the resetChanges() method', () => {
|
||||||
jest.spyOn(controller, 'resetChanges').mockReturnThis();
|
jest.spyOn(controller, 'resetChanges').mockReturnThis();
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
|
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
|
||||||
jest.spyOn(controller, 'refreshTotal').mockReturnThis();
|
|
||||||
|
|
||||||
const expectedDiscount = 10;
|
const expectedDiscount = 10;
|
||||||
const firstSelectedSale = controller.sales[0];
|
const firstSelectedSale = controller.sales[0];
|
||||||
|
@ -473,7 +431,6 @@ describe('Ticket', () => {
|
||||||
|
|
||||||
expect(firstSelectedSale.discount).toEqual(expectedDiscount);
|
expect(firstSelectedSale.discount).toEqual(expectedDiscount);
|
||||||
expect(secondSelectedSale.discount).toEqual(expectedDiscount);
|
expect(secondSelectedSale.discount).toEqual(expectedDiscount);
|
||||||
expect(controller.refreshTotal).toHaveBeenCalledWith();
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
expect(controller.resetChanges).toHaveBeenCalledWith();
|
expect(controller.resetChanges).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
|
@ -622,7 +579,6 @@ describe('Ticket', () => {
|
||||||
|
|
||||||
describe('updateQuantity()', () => {
|
describe('updateQuantity()', () => {
|
||||||
it('should make a POST query saving sale quantity', () => {
|
it('should make a POST query saving sale quantity', () => {
|
||||||
jest.spyOn(controller, 'refreshTotal').mockReturnThis();
|
|
||||||
jest.spyOn(controller, 'resetChanges').mockReturnThis();
|
jest.spyOn(controller, 'resetChanges').mockReturnThis();
|
||||||
|
|
||||||
const selectedSale = controller.sales[0];
|
const selectedSale = controller.sales[0];
|
||||||
|
@ -634,7 +590,6 @@ describe('Ticket', () => {
|
||||||
controller.updateQuantity(selectedSale);
|
controller.updateQuantity(selectedSale);
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
expect(controller.refreshTotal).toHaveBeenCalledWith();
|
|
||||||
expect(controller.resetChanges).toHaveBeenCalledWith();
|
expect(controller.resetChanges).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -659,7 +614,6 @@ describe('Ticket', () => {
|
||||||
describe('addSale()', () => {
|
describe('addSale()', () => {
|
||||||
it('should make a POST query adding a new sale', () => {
|
it('should make a POST query adding a new sale', () => {
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
|
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
|
||||||
jest.spyOn(controller, 'refreshTotal').mockReturnThis();
|
|
||||||
jest.spyOn(controller, 'resetChanges').mockReturnThis();
|
jest.spyOn(controller, 'resetChanges').mockReturnThis();
|
||||||
|
|
||||||
const newSale = {itemFk: 4, quantity: 10};
|
const newSale = {itemFk: 4, quantity: 10};
|
||||||
|
@ -681,7 +635,6 @@ describe('Ticket', () => {
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
expect(controller.refreshTotal).toHaveBeenCalledWith();
|
|
||||||
expect(controller.resetChanges).toHaveBeenCalledWith();
|
expect(controller.resetChanges).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -712,7 +665,6 @@ describe('Ticket', () => {
|
||||||
it('should make an HTTP post query ', () => {
|
it('should make an HTTP post query ', () => {
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
|
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
|
||||||
jest.spyOn(controller.$.model, 'refresh').mockReturnThis();
|
jest.spyOn(controller.$.model, 'refresh').mockReturnThis();
|
||||||
jest.spyOn(controller, 'refreshTotal').mockReturnThis();
|
|
||||||
|
|
||||||
const selectedSale = controller.sales[0];
|
const selectedSale = controller.sales[0];
|
||||||
selectedSale.checked = true;
|
selectedSale.checked = true;
|
||||||
|
@ -723,7 +675,6 @@ describe('Ticket', () => {
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
expect(controller.$.model.refresh).toHaveBeenCalledWith();
|
expect(controller.$.model.refresh).toHaveBeenCalledWith();
|
||||||
expect(controller.refreshTotal).toHaveBeenCalledWith();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,7 @@ You have to allow pop-ups in your web browser to use this functionality:
|
||||||
Debes permitir los pop-pups en tu navegador para que esta herramienta funcione correctamente
|
Debes permitir los pop-pups en tu navegador para que esta herramienta funcione correctamente
|
||||||
Disc: Dto
|
Disc: Dto
|
||||||
Available: Disponible
|
Available: Disponible
|
||||||
What is the day of receipt of the ticket?: ¿Cual es del día de recepción del pedido?
|
What is the day of receipt of the ticket?: ¿Cual es el día de preparación del pedido?
|
||||||
Add claim: Crear reclamación
|
Add claim: Crear reclamación
|
||||||
Claim: Reclamación
|
Claim: Reclamación
|
||||||
Transfer lines: Transferir líneas
|
Transfer lines: Transferir líneas
|
||||||
|
|
|
@ -11,6 +11,9 @@
|
||||||
"Zone": {
|
"Zone": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"ZoneAgencyMode": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"ZoneClosure": {
|
"ZoneClosure": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"name": "ZoneAgencyMode",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "zoneAgencyMode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"agencyModeFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"zoneFk": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"acls": [
|
||||||
|
{
|
||||||
|
"accessType": "READ",
|
||||||
|
"principalType": "ROLE",
|
||||||
|
"principalId": "$everyone",
|
||||||
|
"permission": "ALLOW"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="vn-mx-xs" v-if="attachment.component">
|
<div class="vn-mx-xs" v-if="attachment.component">
|
||||||
<a target="_blank" class="vn-py-sm vn-px-md" v-bind:href="path">
|
<a target="_blank" class="vn-py-sm vn-px-md" v-bind:href="attachmentPath">
|
||||||
<div class="text">{{attachment.filename}}</div>
|
<div class="text">{{attachment.filename}}</div>
|
||||||
<div class="icon vn-pl-md">▼</div>
|
<div class="icon vn-pl-md">▼</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'attachment',
|
name: 'attachment',
|
||||||
computed: {
|
computed: {
|
||||||
path() {
|
attachmentPath() {
|
||||||
const filename = this.attachment.filename;
|
const filename = this.attachment.filename;
|
||||||
const component = this.attachment.component;
|
const component = this.attachment.component;
|
||||||
if (this.attachment.cid)
|
if (this.attachment.cid)
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
subject: Avis initial de solde débiteur
|
||||||
|
title: Avis initial de solde débiteur
|
||||||
|
sections:
|
||||||
|
introduction:
|
||||||
|
title: Madame, Monsieur,
|
||||||
|
description: Sauf erreur ou omission de notre part, nous constatons que votre
|
||||||
|
compte client présente à ce jour un solde débiteur.
|
||||||
|
checkExtract: Ce montant correspond à nos factures restées impayées, ci-joint en annexe.
|
||||||
|
Notre service administratif se fera un plaisir de clarifier toutes les questions que vous
|
||||||
|
pourriez avoir, et vous fournira également tout document que vous nous demandez.
|
||||||
|
checkValidData: Si lors de la vérification des données fournies, elles sont correctes et
|
||||||
|
l’échéance étant dépassée, nous vous demandons de bien vouloir régulariser cette situation.
|
||||||
|
payMethod: Si vous ne souhaitez pas vous rendre personnellement à nos bureaux, vous
|
||||||
|
pouvez effectuer le paiement par virement bancaire sur le compte qui apparaît en
|
||||||
|
bas du relevé, en indiquant votre numéro de client, ou vous pouvez effectuer le
|
||||||
|
paiement en ligne avec une carte bleue sur notre site Internet.
|
||||||
|
conclusion: Dans le cas où votre règlement aurait été adressé entre temps,
|
||||||
|
nous vous prions de ne pas tenir compte de la présente.
|
||||||
|
Nous vous remercions par avance de votre aimable coopération.
|
||||||
|
transferAccount: Coordonées pour virement bancaire
|
|
@ -12,4 +12,4 @@ claim: Reclamación {0}
|
||||||
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 integrados. <br/> Tlf: 96 166 77 88 - Ana Gómez (Ext. 2113) <em>(agomezf@integra2.es)</em>'
|
de integrados. <br/> Tlf: 96 166 77 88 - Ana Gómez (Ext. 2113) <em>(agomez@integra2.com)</em>'
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
title: Relevé de compte
|
||||||
|
claimId: Réclamation
|
||||||
|
clientId: Client
|
||||||
|
clientData: Données client
|
||||||
|
date: Date
|
||||||
|
concept: Objet
|
||||||
|
invoiced: Facturé
|
||||||
|
payed: Payé
|
||||||
|
balance: Solde
|
||||||
|
client: Client {0}
|
Loading…
Reference in New Issue