2502-supplier_summary + e2e #425
|
@ -23,21 +23,23 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.sendCheckingPresence = async(ctx, workerId, message) => {
|
||||
if (!workerId) return false;
|
||||
Self.sendCheckingPresence = async(ctx, recipientId, message) => {
|
||||
if (!recipientId) return false;
|
||||
|
||||
const models = Self.app.models;
|
||||
const account = await models.Account.findById(workerId);
|
||||
const account = await models.Account.findById(recipientId);
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
|
||||
if (recipientId == userId) return false;
|
||||
|
||||
if (!account)
|
||||
throw new Error(`Could not send message "${message}" to worker id ${workerId} from user ${userId}`);
|
||||
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
|
||||
|
||||
const query = `SELECT worker_isWorking(?) isWorking`;
|
||||
const [result] = await Self.rawSql(query, [workerId]);
|
||||
const [result] = await Self.rawSql(query, [recipientId]);
|
||||
|
||||
if (!result.isWorking) {
|
||||
const workerDepartment = await models.WorkerDepartment.findById(workerId, {
|
||||
const workerDepartment = await models.WorkerDepartment.findById(recipientId, {
|
||||
include: {
|
||||
relation: 'department'
|
||||
}
|
||||
|
@ -46,7 +48,7 @@ module.exports = Self => {
|
|||
const channelName = department && department.chatName;
|
||||
|
||||
if (channelName)
|
||||
return Self.send(ctx, `#${channelName}`, `@${account.name} => ${message}`);
|
||||
return Self.send(ctx, `#${channelName}`, `@${account.name} ➔ ${message}`);
|
||||
}
|
||||
|
||||
return Self.send(ctx, `@${account.name}`, message);
|
||||
|
|
|
@ -38,7 +38,7 @@ describe('chat sendCheckingPresence()', () => {
|
|||
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.message).toEqual('Fake notification sent');
|
||||
expect(chatModel.send).toHaveBeenCalledWith(ctx, '#cooler', '@HankPym => I changed something');
|
||||
expect(chatModel.send).toHaveBeenCalledWith(ctx, '#cooler', '@HankPym ➔ I changed something');
|
||||
|
||||
// restores
|
||||
await department.updateAttribute('chatName', null);
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
USE `vn`;
|
||||
DROP procedure IF EXISTS `ticket_componentPreview`;
|
||||
|
||||
DELIMITER $$
|
||||
USE `vn`$$
|
||||
CREATE DEFINER=`root`@`%` PROCEDURE `ticket_componentPreview`(
|
||||
vTicketFk INT,
|
||||
vLanded DATE,
|
||||
vAddressFk INT,
|
||||
vZoneFk INT,
|
||||
vWarehouseFk SMALLINT)
|
||||
BEGIN
|
||||
/**
|
||||
* Calcula los componentes de los articulos de un ticket
|
||||
*
|
||||
* @param vTicketFk id del ticket
|
||||
* @param vLanded nueva fecha de entrega
|
||||
* @param vAddressFk nuevo consignatario
|
||||
* @param vZoneFk nueva zona
|
||||
* @param vWarehouseFk nuevo warehouse
|
||||
*
|
||||
* @return tmp.ticketComponentPreview (warehouseFk, itemFk, componentFk, cost)
|
||||
*/
|
||||
DECLARE vHasDataChanged BOOL DEFAULT FALSE;
|
||||
DECLARE vHasAddressChanged BOOL;
|
||||
DECLARE vHasZoneChanged BOOL DEFAULT FALSE;
|
||||
DECLARE vHasWarehouseChanged BOOL DEFAULT FALSE;
|
||||
|
||||
DECLARE vShipped DATE;
|
||||
DECLARE vAddressTypeRateFk INT DEFAULT NULL;
|
||||
DECLARE vAgencyModeTypeRateFk INT DEFAULT NULL;
|
||||
|
||||
DECLARE vHasChangeAll BOOL DEFAULT FALSE;
|
||||
|
||||
SELECT DATE(landed) <> vLanded,
|
||||
addressFk <> vAddressFk,
|
||||
zoneFk <> vZoneFk,
|
||||
warehouseFk <> vWarehouseFk
|
||||
INTO
|
||||
vHasDataChanged,
|
||||
vHasAddressChanged,
|
||||
vHasZoneChanged,
|
||||
vHasWarehouseChanged
|
||||
FROM vn.ticket t
|
||||
WHERE t.id = vTicketFk;
|
||||
|
||||
IF vHasDataChanged OR vHasWarehouseChanged THEN
|
||||
SET vHasChangeAll = TRUE;
|
||||
END IF;
|
||||
|
||||
IF vHasAddressChanged THEN
|
||||
SET vAddressTypeRateFk = 5;
|
||||
END IF;
|
||||
|
||||
IF vHasZoneChanged THEN
|
||||
SET vAgencyModeTypeRateFk = 6;
|
||||
END IF;
|
||||
|
||||
SELECT TIMESTAMPADD(DAY, -travelingDays, vLanded) INTO vShipped
|
||||
FROM zone
|
||||
WHERE id = vZoneFk;
|
||||
|
||||
CALL buyUltimate(vWarehouseFk, vShipped);
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.ticketLot;
|
||||
CREATE TEMPORARY TABLE tmp.ticketLot ENGINE = MEMORY (
|
||||
SELECT
|
||||
vWarehouseFk AS warehouseFk,
|
||||
NULL AS available,
|
||||
s.itemFk,
|
||||
bu.buyFk,
|
||||
vZoneFk zoneFk
|
||||
FROM sale s
|
||||
LEFT JOIN tmp.buyUltimate bu ON bu.itemFk = s.itemFk
|
||||
WHERE s.ticketFk = vTicketFk
|
||||
GROUP BY bu.warehouseFk, bu.itemFk);
|
||||
|
||||
CALL catalog_componentPrepare();
|
||||
CALL catalog_componentCalculate(vZoneFk, vAddressFk, vShipped, vWarehouseFk);
|
||||
|
||||
REPLACE INTO tmp.ticketComponent (warehouseFk, itemFk, componentFk, cost)
|
||||
SELECT t.warehouseFk, s.itemFk, sc.componentFk, sc.value
|
||||
FROM saleComponent sc
|
||||
JOIN sale s ON s.id = sc.saleFk
|
||||
JOIN ticket t ON t.id = s.ticketFk
|
||||
JOIN `component` c ON c.id = sc.componentFk
|
||||
WHERE s.ticketFk = vTicketFk
|
||||
AND (c.isRenewable = FALSE
|
||||
OR
|
||||
(NOT vHasChangeAll
|
||||
AND (NOT (c.typeFk <=> vAddressTypeRateFk
|
||||
OR c.typeFk <=> vAgencyModeTypeRateFk))));
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.ticketComponentPreview;
|
||||
CREATE TEMPORARY TABLE tmp.ticketComponentPreview
|
||||
SELECT * FROM tmp.ticketComponent;
|
||||
|
||||
CALL catalog_componentPurge();
|
||||
DROP TEMPORARY TABLE tmp.buyUltimate;
|
||||
|
||||
IF vShipped IS NULL THEN
|
||||
CALL util.throw('NO_ZONE_AVAILABLE');
|
||||
END IF;
|
||||
END$$
|
||||
|
||||
DELIMITER ;
|
||||
|
|
@ -399,16 +399,16 @@ let actions = {
|
|||
pickDate: async function(selector, date) {
|
||||
date = date || new Date();
|
||||
|
||||
const tzoffset = date.getTimezoneOffset() * 60000;
|
||||
const localIso = (new Date(date.getTime() - tzoffset))
|
||||
.toISOString();
|
||||
const timeZoneOffset = date.getTimezoneOffset() * 60000;
|
||||
const localDate = (new Date(date.getTime() - timeZoneOffset))
|
||||
.toISOString().substr(0, 10);
|
||||
|
||||
await this.wait(selector);
|
||||
await this.evaluate((selector, localIso) => {
|
||||
await this.evaluate((selector, localDate) => {
|
||||
let input = document.querySelector(selector).$ctrl.input;
|
||||
input.value = localIso.substr(0, 10);
|
||||
input.value = localDate;
|
||||
input.dispatchEvent(new Event('change'));
|
||||
}, selector, localIso);
|
||||
}, selector, localDate);
|
||||
},
|
||||
|
||||
pickTime: async function(selector, time) {
|
||||
|
|
|
@ -83,6 +83,9 @@ export default {
|
|||
equalizationTaxCheckbox: 'vn-client-fiscal-data vn-check[ng-model="$ctrl.client.isEqualizated"]',
|
||||
address: 'vn-client-fiscal-data vn-textfield[ng-model="$ctrl.client.street"]',
|
||||
postcode: 'vn-client-fiscal-data vn-datalist[ng-model="$ctrl.client.postcode"]',
|
||||
sageTax: 'vn-client-fiscal-data vn-autocomplete[ng-model="$ctrl.client.sageTaxTypeFk"]',
|
||||
sageTransaction: 'vn-client-fiscal-data vn-autocomplete[ng-model="$ctrl.client.sageTransactionTypeFk"]',
|
||||
transferor: 'vn-client-fiscal-data vn-autocomplete[ng-model="$ctrl.client.transferorFk"]',
|
||||
city: 'vn-client-fiscal-data vn-datalist[ng-model="$ctrl.client.city"]',
|
||||
province: 'vn-client-fiscal-data vn-autocomplete[ng-model="$ctrl.client.provinceFk"]',
|
||||
country: 'vn-client-fiscal-data vn-autocomplete[ng-model="$ctrl.client.countryFk"]',
|
||||
|
@ -199,6 +202,21 @@ export default {
|
|||
firstDocWorker: 'vn-client-dms-index vn-td:nth-child(8) > span',
|
||||
firstDocWorkerDescriptor: '.vn-popover.shown vn-worker-descriptor'
|
||||
},
|
||||
clientCreditInsurance: {
|
||||
addNewContract: 'vn-client-credit-insurance-index vn-float-button[ui-sref="client.card.creditInsurance.create"]',
|
||||
newCreditClassification: 'vn-client-credit-insurance-create vn-input-number[ng-model="$ctrl.creditClassification.credit"]',
|
||||
newInsuranceCredit: 'vn-client-credit-insurance-insurance-create vn-input-number[ng-model="$ctrl.insurance.credit"]',
|
||||
newClassificationGrade: 'vn-client-credit-insurance-create vn-input-number[ng-model="$ctrl.creditClassification.grade"]',
|
||||
newInsuranceGrade: 'vn-client-credit-insurance-insurance-create vn-input-number[ng-model="$ctrl.insurance.grade"]',
|
||||
newClassificationStartingDate: 'vn-client-credit-insurance-create vn-date-picker[ng-model="$ctrl.creditClassification.started"]',
|
||||
newInsuranceStartingDate: 'vn-client-credit-insurance-insurance-create vn-date-picker[ng-model="$ctrl.insurance.created"]',
|
||||
endCurrentContract: 'vn-client-credit-insurance-index vn-icon-button[icon="lock"]',
|
||||
firstContratViewCreditButton: 'vn-client-credit-insurance-index vn-card > vn-horizontal:nth-child(1) vn-icon-button[icon="desktop_windows"]',
|
||||
addNewCredit: 'vn-client-credit-insurance-insurance-index vn-float-button vn-icon[icon="add"]',
|
||||
saveNewContract: 'vn-client-credit-insurance-create vn-submit',
|
||||
saveNewInsuranceCredit: 'vn-client-credit-insurance-insurance-create button[type="submit"]',
|
||||
anyCreditInsuranceLine: 'vn-client-credit-insurance-insurance-index vn-tbody > vn-tr',
|
||||
},
|
||||
clientContacts: {
|
||||
addContactButton: 'vn-client-contact vn-icon[icon="add_circle"]',
|
||||
name: 'vn-client-contact vn-textfield[ng-model="contact.name"]',
|
||||
|
|
|
@ -66,6 +66,9 @@ describe('Client Edit fiscalData path', () => {
|
|||
await page.autocompleteSearch(selectors.clientFiscalData.province, 'Province one');
|
||||
await page.clearInput(selectors.clientFiscalData.city);
|
||||
await page.write(selectors.clientFiscalData.city, 'Valencia');
|
||||
await page.autocompleteSearch(selectors.clientFiscalData.sageTax, 'operaciones no sujetas');
|
||||
await page.autocompleteSearch(selectors.clientFiscalData.sageTransaction, 'regularización de inversiones');
|
||||
await page.autocompleteSearch(selectors.clientFiscalData.transferor, 'Max Eisenhardt');
|
||||
await page.clearInput(selectors.clientFiscalData.postcode);
|
||||
await page.write(selectors.clientFiscalData.postcode, '46000');
|
||||
await page.waitToClick(selectors.clientFiscalData.activeCheckbox);
|
||||
|
@ -188,6 +191,24 @@ describe('Client Edit fiscalData path', () => {
|
|||
expect(result).toContain('46000');
|
||||
});
|
||||
|
||||
it('should confirm the sageTax have been edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.clientFiscalData.sageTax, 'value');
|
||||
|
||||
expect(result).toEqual('Operaciones no sujetas');
|
||||
});
|
||||
|
||||
it('should confirm the sageTransaction have been edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.clientFiscalData.sageTransaction, 'value');
|
||||
|
||||
expect(result).toEqual('Regularización de inversiones');
|
||||
});
|
||||
|
||||
it('should confirm the transferor have been edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.clientFiscalData.transferor, 'value');
|
||||
|
||||
expect(result).toEqual('Max Eisenhardt');
|
||||
});
|
||||
|
||||
it('should confirm the city have been autocompleted', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.clientFiscalData.city, 'value');
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Client credit insurance path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
let previousMonth = new Date();
|
||||
previousMonth.setMonth(previousMonth.getMonth() - 1);
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('insurance', 'client');
|
||||
await page.accessToSearchResult('Tony Stark');
|
||||
await page.accessToSection('client.card.creditInsurance.index');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should open the create a new credit contract form', async() => {
|
||||
await page.waitToClick(selectors.clientCreditInsurance.addNewContract);
|
||||
await page.waitForState('client.card.creditInsurance.create');
|
||||
});
|
||||
|
||||
it('should create a new credit contract', async() => {
|
||||
await page.write(selectors.clientCreditInsurance.newCreditClassification, '1000');
|
||||
await page.write(selectors.clientCreditInsurance.newClassificationGrade, '1');
|
||||
await page.pickDate(selectors.clientCreditInsurance.newClassificationStartingDate, previousMonth);
|
||||
await page.waitToClick(selectors.clientCreditInsurance.saveNewContract);
|
||||
await page.waitForState('client.card.creditInsurance.index');
|
||||
});
|
||||
|
||||
it(`should verify the addNewContract button is not present since there's an active contract`, async() => {
|
||||
await page.waitForSelector(selectors.clientCreditInsurance.addNewContract, {hidden: true});
|
||||
});
|
||||
|
||||
it(`should click the view credits button`, async() => {
|
||||
await page.waitToClick(selectors.clientCreditInsurance.firstContratViewCreditButton);
|
||||
await page.waitForState('client.card.creditInsurance.insurance.index');
|
||||
});
|
||||
|
||||
it('should click the add new credit button which opens the new credit form', async() => {
|
||||
await page.waitToClick(selectors.clientCreditInsurance.addNewCredit);
|
||||
await page.waitForState('client.card.creditInsurance.insurance.create');
|
||||
});
|
||||
|
||||
it('should fill the form but provide no grade to the new credit hence fail', async() => {
|
||||
await page.write(selectors.clientCreditInsurance.newInsuranceCredit, '2000');
|
||||
await page.pickDate(selectors.clientCreditInsurance.newInsuranceStartingDate, previousMonth);
|
||||
await page.waitToClick(selectors.clientCreditInsurance.saveNewInsuranceCredit);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toEqual('The grade must be similar to the last one');
|
||||
});
|
||||
|
||||
it('should provide a correct grade and succesfully save a new credit', async() => {
|
||||
await page.write(selectors.clientCreditInsurance.newInsuranceGrade, '999');
|
||||
await page.waitToClick(selectors.clientCreditInsurance.saveNewInsuranceCredit);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toEqual('Data saved!');
|
||||
});
|
||||
|
||||
it('should be redirected to the credit index', async() => {
|
||||
await page.waitForState('client.card.creditInsurance.insurance.index');
|
||||
});
|
||||
|
||||
it('should check the amount of credits is the expected', async() => {
|
||||
const result = await page.countElement(selectors.clientCreditInsurance.anyCreditInsuranceLine);
|
||||
|
||||
expect(result).toEqual(2);
|
||||
});
|
||||
|
||||
it('should navigate to the credit insurance section', async() => {
|
||||
await page.waitToClick(`vn-left-menu li > a[ui-sref="client.card.creditInsurance.index"]`);
|
||||
await page.waitForState('client.card.creditInsurance.index');
|
||||
});
|
||||
|
||||
it('should bring the current contract to an end', async() => {
|
||||
await page.waitToClick(selectors.clientCreditInsurance.endCurrentContract);
|
||||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
});
|
||||
|
||||
it(`should verify the addNewContract button is now present since there's no active contract`, async() => {
|
||||
await page.waitForSelector(selectors.clientCreditInsurance.addNewContract, {visible: true});
|
||||
});
|
||||
|
||||
it(`should verify the endCurrentContract button is not present since there's no active contract`, async() => {
|
||||
await page.waitForSelector(selectors.clientCreditInsurance.endCurrentContract, {hidden: true});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,70 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
export function stringifyParams(data) {
|
||||
const params = Object.assign({}, data.params);
|
||||
for (let param in params)
|
||||
params[param] = JSON.stringify(params[param]);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
export function changeState($state, event, data) {
|
||||
const params = stringifyParams(data);
|
||||
$state.go(data.state, params);
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
export function openNewTab($state, $window, event, data) {
|
||||
const params = stringifyParams(data);
|
||||
const href = $state.href(data.state, params);
|
||||
$window.open(href);
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows changing state for nested anchor
|
||||
*
|
||||
* @param {Object} $state
|
||||
* @param {Object} $window
|
||||
* @return {Object} The directive
|
||||
*/
|
||||
export function directive($state, $window) {
|
||||
let ctrlPressed = false;
|
||||
|
||||
$window.addEventListener('keydown', event => {
|
||||
if (event.key == 'Control')
|
||||
ctrlPressed = true;
|
||||
});
|
||||
|
||||
$window.addEventListener('keyup', event => {
|
||||
if (event.key == 'Control')
|
||||
ctrlPressed = false;
|
||||
});
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function($scope, $element, $attrs) {
|
||||
const data = $scope.$eval($attrs.vnAnchor);
|
||||
$element.on('click', event => {
|
||||
if (ctrlPressed)
|
||||
openNewTab($state, $window, event, data);
|
||||
else
|
||||
changeState($state, event, data);
|
||||
});
|
||||
|
||||
$element.on('mousedown', event => {
|
||||
const mouseWheel = 1;
|
||||
if (event.button == mouseWheel)
|
||||
openNewTab($state, $window, event, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
directive.$inject = ['$state', '$window'];
|
||||
|
||||
ngModule.directive('vnAnchor', directive);
|
|
@ -15,3 +15,4 @@ import './smart-table';
|
|||
import './droppable';
|
||||
import './http-click';
|
||||
import './http-submit';
|
||||
import './anchor';
|
||||
|
|
|
@ -72,5 +72,13 @@
|
|||
"Deleted absence": "The worker <strong>{{author}}</strong> has deleted an absence of type '{{absenceType}}' to <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> for day {{dated}}.",
|
||||
"I have deleted the ticket id": "I have deleted the ticket id [{{id}}]({{{url}}})",
|
||||
"I have restored the ticket id": "I have restored the ticket id [{{id}}]({{{url}}})",
|
||||
"Changed this data from the ticket": "I have changed the data from the ticket [{{ticketId}}]({{{ticketUrl}}}): ```{{{changes}}}```"
|
||||
"Changed this data from the ticket": "I have changed the data from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
|
||||
"agencyModeFk": "Agency",
|
||||
"clientFk": "Client",
|
||||
"zoneFk": "Zone",
|
||||
"warehouseFk": "Warehouse",
|
||||
"shipped": "Shipped",
|
||||
"landed": "Landed",
|
||||
"addressFk": "Address",
|
||||
"companyFk": "Company"
|
||||
}
|
|
@ -121,11 +121,11 @@
|
|||
"Swift / BIC can't be empty": "Swift / BIC no puede estar vacío",
|
||||
"Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios",
|
||||
"Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios",
|
||||
"MESSAGE_BOUGHT_UNITS": "Se ha comprado {{quantity}} unidades de {{concept}} (#{{itemId}}) para el ticket id [{{ticketId}}]({{{url}}})",
|
||||
"MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} (#{{clientId}})]({{{url}}}) a *{{credit}} €*",
|
||||
"MESSAGE_CHANGED_PAYMETHOD": "He cambiado la forma de pago del cliente [{{clientName}} (#{{clientId}})]({{{url}}})",
|
||||
"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}}*",
|
||||
"MESSAGE_BOUGHT_UNITS": "Se ha comprado {{quantity}} unidades de {{concept}} ({{itemId}}) para el ticket id [{{ticketId}}]({{{url}}})",
|
||||
"MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*",
|
||||
"MESSAGE_CHANGED_PAYMETHOD": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
|
||||
"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}}*",
|
||||
"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}}",
|
||||
"ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto",
|
||||
|
@ -140,7 +140,6 @@
|
|||
"Role already assigned": "Role already assigned",
|
||||
"Invalid role name": "Invalid role name",
|
||||
"Role name must be written in camelCase": "Role name must be written in camelCase",
|
||||
"can't be set": "can't be set",
|
||||
"Email already exists": "Email already exists",
|
||||
"User already exists": "User already exists",
|
||||
"Absence change notification on the labour calendar": "Notificacion de cambio de ausencia en el calendario laboral",
|
||||
|
@ -149,5 +148,13 @@
|
|||
"I have deleted the ticket id": "He eliminado el ticket id [{{id}}]({{{url}}})",
|
||||
"I have restored the ticket id": "He restaurado el ticket id [{{id}}]({{{url}}})",
|
||||
"You can only restore a ticket within the first hour after deletion": "Únicamente puedes restaurar el ticket dentro de la primera hora después de su eliminación",
|
||||
"Changed this data from the ticket": "He cambiado estos datos del ticket [{{ticketId}}]({{{ticketUrl}}}): ```{{{changes}}}```"
|
||||
"Changed this data from the ticket": "He cambiado estos datos del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}",
|
||||
"agencyModeFk": "Agencia",
|
||||
"clientFk": "Cliente",
|
||||
"zoneFk": "Zona",
|
||||
"warehouseFk": "Almacén",
|
||||
"shipped": "F. envío",
|
||||
"landed": "F. entrega",
|
||||
"addressFk": "Consignatario",
|
||||
"companyFk": "Empresa"
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* Translates to a readable values
|
||||
* @param {Object} instance - The model or context instance
|
||||
* @param {Object} changes - Object containing changes
|
||||
*/
|
||||
exports.translateValues = async(instance, changes) => {
|
||||
const models = instance.app.models;
|
||||
|
||||
function getRelation(instance, property) {
|
||||
const relations = instance.definition.settings.relations;
|
||||
for (let relationName in relations) {
|
||||
const relation = relations[relationName];
|
||||
if (relation.foreignKey == property)
|
||||
return relation;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function getValue(rawData) {
|
||||
const row = JSON.parse(JSON.stringify(rawData));
|
||||
for (column in row)
|
||||
return row[column];
|
||||
}
|
||||
|
||||
function formatDate(date) {
|
||||
return new Intl.DateTimeFormat('es', {
|
||||
year: '2-digit',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
const properties = Object.assign({}, changes);
|
||||
for (let property in properties) {
|
||||
const relation = getRelation(instance, property);
|
||||
const value = properties[property];
|
||||
|
||||
let finalValue = value;
|
||||
if (relation) {
|
||||
const model = relation.model;
|
||||
const row = await models[model].findById(value, {
|
||||
fields: ['alias', 'name', 'code', 'description']
|
||||
});
|
||||
const newValue = getValue(row);
|
||||
if (newValue) finalValue = newValue;
|
||||
}
|
||||
|
||||
if (finalValue instanceof Date)
|
||||
finalValue = formatDate(finalValue);
|
||||
|
||||
properties[property] = finalValue;
|
||||
}
|
||||
|
||||
return properties;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the changes between two objects
|
||||
* @param {Object} original - Original object
|
||||
* @param {Object} changes - New changes
|
||||
* @return {Object} Old and new values
|
||||
*/
|
||||
exports.getChanges = (original, changes) => {
|
||||
const oldChanges = {};
|
||||
const newChanges = {};
|
||||
for (let property in changes) {
|
||||
if (changes[property] != original[property]) {
|
||||
newChanges[property] = changes[property];
|
||||
|
||||
if (original[property] != undefined)
|
||||
oldChanges[property] = original[property];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
old: oldChanges,
|
||||
new: newChanges
|
||||
};
|
||||
};
|
|
@ -29,7 +29,6 @@ describe('Client updateFiscalData', () => {
|
|||
const ctx = {req: {accessToken: {userId: 5}}};
|
||||
ctx.args = {postcode: 46680};
|
||||
|
||||
|
||||
const client = await app.models.Client.findById(clientId);
|
||||
|
||||
expect(client.postcode).toEqual('46460');
|
||||
|
|
|
@ -43,6 +43,18 @@ module.exports = Self => {
|
|||
arg: 'provinceFk',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
arg: 'sageTaxTypeFk',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
arg: 'sageTransactionTypeFk',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
arg: 'transferorFk',
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
arg: 'hasToInvoiceByAddress',
|
||||
type: 'boolean'
|
||||
|
|
|
@ -83,6 +83,12 @@
|
|||
"SmsConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"SageTaxType": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"SageTransactionType": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"TpvError": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
|
|
@ -118,6 +118,18 @@
|
|||
},
|
||||
"created": {
|
||||
"type": "Date"
|
||||
},
|
||||
"sageTaxTypeFk": {
|
||||
"type": "number",
|
||||
"mysql": {
|
||||
"columnName": "taxTypeSageFk"
|
||||
}
|
||||
},
|
||||
"sageTransactionTypeFk": {
|
||||
"type": "number",
|
||||
"mysql": {
|
||||
"columnName": "transactionTypeSageFk"
|
||||
}
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
@ -200,6 +212,20 @@
|
|||
"type": "hasOne",
|
||||
"model": "ClaimRatio",
|
||||
"foreignKey": "clientFk"
|
||||
},
|
||||
"transferor": {
|
||||
"type": "belongsTo",
|
||||
"model": "Client",
|
||||
"foreignKey": "transferorFk"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scopes": {
|
||||
"isActive": {
|
||||
"where": {
|
||||
"isActive": {
|
||||
"neq": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "SageTaxType",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "sage.TiposIva"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"id": true,
|
||||
"description": "Identifier",
|
||||
"mysql": {
|
||||
"columnName": "CodigoIva"
|
||||
}
|
||||
},
|
||||
"vat": {
|
||||
"type": "string",
|
||||
"mysql": {
|
||||
"columnName": "Iva"
|
||||
}
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "SageTransactionType",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "sage.TiposTransacciones"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"id": true,
|
||||
"description": "Identifier",
|
||||
"mysql": {
|
||||
"columnName": "CodigoTransaccion"
|
||||
}
|
||||
},
|
||||
"transaction": {
|
||||
"type": "string",
|
||||
"mysql": {
|
||||
"columnName": "Transaccion"
|
||||
}
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
url="CreditClassifications/{{post.params.classificationId}}/insurances"
|
||||
url="CreditClassifications/{{$ctrl.$params.classificationId}}/insurances"
|
||||
data="$ctrl.insurance"
|
||||
insert-mode="true"
|
||||
form="form">
|
||||
|
@ -34,7 +34,7 @@
|
|||
<vn-submit label="Save"></vn-submit>
|
||||
<vn-button
|
||||
label="Cancel"
|
||||
ui-sref="client.card.creditInsurance.insurance.index({classificationId: post.params.classificationId})">
|
||||
ui-sref="client.card.creditInsurance.insurance.index({classificationId: $ctrl.$params.classificationId})">
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
|
@ -1,4 +1,5 @@
|
|||
Simple ticket: Ticket simple
|
||||
View consumer report: Ver informe de consumo
|
||||
From date: Fecha desde
|
||||
To date: Fecha hasta
|
||||
To date: Fecha hasta
|
||||
Go to user: Ir al usuario
|
|
@ -18,6 +18,18 @@
|
|||
data="countries"
|
||||
order="country">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="SageTaxTypes"
|
||||
data="sageTaxTypes"
|
||||
order="vat">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
auto-load="true"
|
||||
url="SageTransactionTypes"
|
||||
data="sageTransactionTypes"
|
||||
order="transaction">
|
||||
</vn-crud-model>
|
||||
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
|
@ -44,6 +56,35 @@
|
|||
rule>
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one
|
||||
ng-model="$ctrl.client.sageTaxTypeFk"
|
||||
data="sageTaxTypes"
|
||||
show-field="vat"
|
||||
value-field="id"
|
||||
label="Sage tax type"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
ng-model="$ctrl.client.sageTransactionTypeFk"
|
||||
data="sageTransactionTypes"
|
||||
show-field="transaction"
|
||||
value-field="id"
|
||||
label="Sage transaction type"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one
|
||||
ng-model="$ctrl.client.transferorFk"
|
||||
url="Clients/isActive"
|
||||
search-function="$ctrl.transferorSearchFunction($search)"
|
||||
where="{id: {neq: $ctrl.client.id}}"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Previous client"
|
||||
info="In case of a company succession, specify the grantor company"
|
||||
rule>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-datalist vn-one
|
||||
label="Postcode"
|
||||
|
|
|
@ -163,6 +163,12 @@ export default class Controller extends Section {
|
|||
this.client.provinceFk = response.provinceFk;
|
||||
this.client.countryFk = response.countryFk;
|
||||
}
|
||||
|
||||
transferorSearchFunction($search) {
|
||||
return /^\d+$/.test($search)
|
||||
? {id: $search}
|
||||
: {name: {like: '%' + $search + '%'}};
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnClientFiscalData', {
|
||||
|
|
|
@ -5,4 +5,8 @@ Frozen: Congelado
|
|||
In order to invoice, this field is not consulted, but the consignee's ET. When modifying this field if the invoice by address option is not checked, the change will be automatically propagated to all addresses, otherwise the user will be asked if he wants to propagate it or not.: Para facturar no se consulta este campo, sino el RE de consignatario. Al modificar este campo si no esta marcada la casilla Facturar por consignatario, se propagará automáticamente el cambio a todos los consignatarios, en caso contrario preguntará al usuario si quiere o no propagar.
|
||||
You can use letters and spaces: Se pueden utilizar letras y espacios
|
||||
Found a client with this data: Se ha encontrado un cliente con estos datos
|
||||
Found a client with this phone or email: El cliente con id <a href="#!/client/{{clientId}}/summary" target="_blank">{{clientId}}</a> ya tiene este teléfono o email. <br/> ¿Quieres continuar?
|
||||
Found a client with this phone or email: El cliente con id <a href="#!/client/{{clientId}}/summary" target="_blank">{{clientId}}</a> ya tiene este teléfono o email. <br/> ¿Quieres continuar?
|
||||
Sage tax type: Tipo de impuesto Sage
|
||||
Sage transaction type: Tipo de transacción Sage
|
||||
Previous client: Cliente anterior
|
||||
In case of a company succession, specify the grantor company: En el caso de que haya habido una sucesión de empresa, indicar la empresa cedente
|
|
@ -40,8 +40,7 @@
|
|||
vn-tooltip="Client frozen"
|
||||
icon="icon-frozen">
|
||||
</vn-icon-button>
|
||||
<vn-icon-button
|
||||
ng-click="$ctrl.filterTickets(client, $event)"
|
||||
<vn-icon-button vn-anchor="{state: 'ticket.index', params: {q: {clientFk: client.id}}}"
|
||||
vn-tooltip="Client tickets"
|
||||
icon="icon-ticket">
|
||||
</vn-icon-button>
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
<vn-autocomplete vn-three
|
||||
ng-show="tag.selection.isFree === false"
|
||||
url="{{$ctrl.sourceTables[itemTag.id].url}}"
|
||||
search-function="{name: {like: '%'+ $search +'%'}}"
|
||||
search-function="{name: {like: $search +'%'}}"
|
||||
label="Value"
|
||||
ng-model="itemTag.value"
|
||||
show-field="{{$ctrl.sourceTables[itemTag.id].field}}"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
const diff = require('object-diff');
|
||||
const loggable = require('vn-loopback/util/log');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('componentUpdate', {
|
||||
|
@ -103,10 +103,8 @@ module.exports = Self => {
|
|||
delete updatedTicket.ctx;
|
||||
delete updatedTicket.option;
|
||||
|
||||
// Force unroute
|
||||
// Force to unroute ticket
|
||||
const hasToBeUnrouted = true;
|
||||
const changedProperties = diff(originalTicket, updatedTicket);
|
||||
|
||||
const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
const res = await Self.rawSql(query, [
|
||||
id,
|
||||
|
@ -123,14 +121,18 @@ module.exports = Self => {
|
|||
option
|
||||
]);
|
||||
|
||||
const changes = loggable.getChanges(originalTicket, updatedTicket);
|
||||
const oldProperties = await loggable.translateValues(Self, changes.old);
|
||||
const newProperties = await loggable.translateValues(Self, changes.new);
|
||||
|
||||
await models.TicketLog.create({
|
||||
originFk: id,
|
||||
userFk: userId,
|
||||
action: 'update',
|
||||
changedModel: 'Ticket',
|
||||
changedModelId: id,
|
||||
oldInstance: originalTicket,
|
||||
newInstance: changedProperties
|
||||
oldInstance: oldProperties,
|
||||
newInstance: newProperties
|
||||
});
|
||||
|
||||
const salesPersonId = originalTicket.client().salesPersonFk;
|
||||
|
@ -138,20 +140,11 @@ module.exports = Self => {
|
|||
const origin = ctx.req.headers.origin;
|
||||
|
||||
let changesMade = '';
|
||||
for (let change in changedProperties) {
|
||||
let value = changedProperties[change];
|
||||
if (value instanceof Date) {
|
||||
value = new Intl.DateTimeFormat('es', {
|
||||
year: '2-digit',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
}).format(value);
|
||||
}
|
||||
for (let change in newProperties) {
|
||||
let value = newProperties[change];
|
||||
let oldValue = oldProperties[change];
|
||||
|
||||
changesMade += `${change}: ${value}\r\n`;
|
||||
changesMade += `\r\n~${$t(change)}: ${oldValue}~ ➔ *${$t(change)}: ${value}*`;
|
||||
}
|
||||
|
||||
const message = $t('Changed this data from the ticket', {
|
||||
|
|
|
@ -50,8 +50,8 @@ describe('ticket componentUpdate()', () => {
|
|||
req: {
|
||||
accessToken: {userId: userID},
|
||||
headers: {origin: 'http://localhost'},
|
||||
__: (value, params) => {
|
||||
return params.nickname;
|
||||
__: value => {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
</vn-td>
|
||||
<vn-td actions>
|
||||
<vn-icon-button
|
||||
vn-click-stop="$ctrl.goToLines(ticket.id)"
|
||||
vn-anchor="{state: 'ticket.card.sale', params: {id: ticket.id}}"
|
||||
vn-tooltip="Go to lines"
|
||||
icon="icon-lines">
|
||||
</vn-icon-button>
|
||||
|
|
|
@ -114,11 +114,6 @@ export default class Controller extends Section {
|
|||
return 'warning';
|
||||
}
|
||||
|
||||
goToLines(ticketFk) {
|
||||
let url = this.$state.href('ticket.card.sale', {id: ticketFk}, {absolute: true});
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
preview(ticket) {
|
||||
this.selectedTicket = ticket;
|
||||
this.$.summary.show();
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
// 2066
|
||||
xdescribe('Worker absences()', () => {
|
||||
describe('Worker absences()', () => {
|
||||
it('should get the absence calendar for a full year contract', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 106}}};
|
||||
let workerFk = 106;
|
||||
|
@ -84,14 +83,16 @@ xdescribe('Worker absences()', () => {
|
|||
yearStart.setDate(1);
|
||||
|
||||
const yearEnd = new Date();
|
||||
yearEnd.setHours(23, 59, 59, 59);
|
||||
yearEnd.setMonth(11);
|
||||
yearEnd.setDate(31);
|
||||
const currentYear = yearEnd.getFullYear();
|
||||
yearEnd.setFullYear(currentYear + 1);
|
||||
yearEnd.setHours(0, 0, 0, 0);
|
||||
yearEnd.setMonth(0);
|
||||
yearEnd.setDate(1);
|
||||
const startedTime = yearStart.getTime();
|
||||
const endedTime = yearEnd.getTime();
|
||||
const dayTimestamp = 1000 * 60 * 60 * 24;
|
||||
|
||||
const daysInYear = Math.floor((endedTime - startedTime) / dayTimestamp);
|
||||
const daysInYear = Math.round((endedTime - startedTime) / dayTimestamp);
|
||||
|
||||
// sets the holidays per year to the amount of days in the current year
|
||||
let holidaysConfig = await app.models.WorkCenterHoliday.findOne({
|
||||
|
@ -102,17 +103,11 @@ xdescribe('Worker absences()', () => {
|
|||
|
||||
let originalHolidaysValue = holidaysConfig.days;
|
||||
|
||||
await app.models.WorkCenterHoliday.updateAll(
|
||||
{
|
||||
workCenterFk: 1,
|
||||
year: today.getFullYear()
|
||||
},
|
||||
{
|
||||
days: daysInYear
|
||||
}
|
||||
);
|
||||
await holidaysConfig.updateAttribute('days', daysInYear);
|
||||
|
||||
// normal test begins
|
||||
const contract = await app.models.WorkerLabour.findById(106);
|
||||
const userId = 106;
|
||||
const contract = await app.models.WorkerLabour.findById(userId);
|
||||
const contractStartDate = contract.started;
|
||||
|
||||
const startingContract = new Date();
|
||||
|
@ -121,14 +116,13 @@ xdescribe('Worker absences()', () => {
|
|||
startingContract.setDate(1);
|
||||
|
||||
await app.models.WorkerLabour.rawSql(
|
||||
`UPDATE postgresql.business SET date_start = ? WHERE business_id = ?`,
|
||||
[startingContract, contract.businessFk]
|
||||
`UPDATE postgresql.business SET date_start = ?, date_end = ? WHERE business_id = ?`,
|
||||
[startingContract, yearEnd, contract.businessFk]
|
||||
);
|
||||
|
||||
let ctx = {req: {accessToken: {userId: 106}}};
|
||||
let workerFk = 106;
|
||||
let ctx = {req: {accessToken: {userId: userId}}};
|
||||
|
||||
let result = await app.models.Calendar.absences(ctx, workerFk, yearStart, yearEnd);
|
||||
let result = await app.models.Calendar.absences(ctx, userId, yearStart, yearEnd);
|
||||
let calendar = result[0];
|
||||
let absences = result[1];
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<vn-item-section side>
|
||||
<vn-icon-button
|
||||
ng-click="$ctrl.goToTimeControl($event, worker.id)"
|
||||
vn-tooltip="Preview"
|
||||
vn-tooltip="Time control"
|
||||
icon="access_time">
|
||||
</vn-icon-button>
|
||||
<vn-icon-button
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
"babel-preset-es2015": "^6.24.1",
|
||||
"css-loader": "^2.1.0",
|
||||
"del": "^2.2.2",
|
||||
"eslint": "^5.14.0",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-config-google": "^0.11.0",
|
||||
"eslint-plugin-jasmine": "^2.10.1",
|
||||
"fancy-log": "^1.3.2",
|
||||
|
|
Loading…
Reference in New Issue