refs #5525 mod yml y sql #1491

Merged
carlossa merged 41 commits from 5525-ibanSEPA-CORE into dev 2024-01-16 13:14:39 +00:00
216 changed files with 3146 additions and 1138 deletions
Showing only changes of commit 3bc26b228e - Show all commits

View File

@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2330.01] - 2023-07-27
## [2338.01] - 2023-09-21
### Added
@ -13,6 +14,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
## [2336.01] - 2023-09-07
### Added
### Changed
### Fixed
## [2334.01] - 2023-08-24
### Added
- (General -> Errores) Botón para enviar cau con los datos del error
## [2332.01] - 2023-08-10
### Added
- (Trabajadores -> Gestión documental) Soporte para Docuware
- (General -> Agencia) Soporte para Viaexpress
- (Tickets -> SMS) Nueva sección en Lilium
### Changed
- (General -> Tickets) Devuelve el motivo por el cual no es editable
- (Desplegables -> Trabajadores) Mejorados
- (General -> Clientes) Razón social y dirección en mayúsculas
### Fixed
- (Clientes -> SMS) Al pasar el ratón por encima muestra el mensaje completo
## [2330.01] - 2023-07-27
@ -24,10 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- (General -> Iconos) Añadidos nuevos iconos
- (Clientes -> Razón social) Nuevas restricciones por pais
### Fixed
- (Clientes -> Razón social) Permite crear clientes con la misma razón social según el país
## [2328.01] - 2023-07-13

View File

@ -71,11 +71,10 @@ module.exports = Self => {
}
try {
const response = await Self.get(fileCabinet, filter);
const [documents] = response.Items;
if (!documents) return false;
const [response] = await Self.get(fileCabinet, filter);
if (!response) return false;
return {id: documents.Id};
return {id: response['Document ID']};
} catch (error) {
return false;
}

View File

@ -65,7 +65,7 @@ module.exports = Self => {
const email = new Email('delivery-note', params);
const docuwareFile = await models.Docuware.download(ctx, id, 'deliveryNote');
const docuwareFile = await models.Docuware.download(id, 'deliveryNote');
return email.send({
overrideAttachments: true,

View File

@ -16,19 +16,9 @@ describe('docuware download()', () => {
it('should return the document data', async() => {
const docuwareId = 1;
const response = {
Items: [
{
Id: docuwareId,
Fields: [
{
FieldName: 'ESTADO',
Item: 'Firmado'
}
]
}
]
};
const response = [{
'Document ID': docuwareId
}];
spyOn(docuwareModel, 'get').and.returnValue((new Promise(resolve => resolve(response))));
const result = await models.Docuware.checkFile(ticketId, fileCabinetName, null, true);

View File

@ -111,7 +111,7 @@ module.exports = Self => {
throw new UserError('Action not allowed on the test environment');
// delete old
const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, false);
const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false);
if (docuwareFile) {
const deleteJson = {
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]

View File

@ -0,0 +1,63 @@
const smtp = require('vn-print/core/smtp');
const config = require('vn-print/core/config');
module.exports = Self => {
Self.remoteMethodCtx('sendToSupport', {
description: 'Send mail to support',
accessType: 'WRITE',
accepts: [
{
arg: 'reason',
type: 'string',
description: 'The reason'
},
{
arg: 'additionalData',
type: 'object',
required: true,
description: 'The additional data'
}
],
returns: {
type: 'object',
root: true
},
http: {
path: `/send-to-support`,
verb: 'POST'
}
});
Self.sendToSupport = async(ctx, reason, additionalData) => {
const emailUser =
await Self.app.models.EmailUser.findById(ctx.req.accessToken.userId, {fields: ['email']});
let html = `<strong>Motivo</strong>:<br/>${reason}<br/>`;
html += `<strong>Usuario</strong>:<br/>${ctx.req.accessToken.userId} ${emailUser.email}<br/>`;
for (const data in additionalData)
html += `<strong>${data}</strong>:<br/>${tryParse(additionalData[data])}<br/>`;
const subjectReason = JSON.parse(additionalData?.httpRequest)?.data?.error;
smtp.send({
to: `${config.app.reportEmail}, ${emailUser.email}`,
subject:
'[Support-Salix] ' +
additionalData?.frontPath + ' ' +
subjectReason?.name + ':' +
subjectReason?.message,
html
});
};
function tryParse(value) {
try {
try {
value = JSON.parse(value);
} catch {}
return JSON.stringify(value, null, '&nbsp;').split('\n').join('<br>');
} catch {
return value;
}
}
};

View File

@ -0,0 +1,45 @@
const axios = require('axios');
const {DOMParser} = require('xmldom');
module.exports = Self => {
Self.remoteMethod('internationalExpedition', {
description: 'Create an expedition and return a label',
accessType: 'WRITE',
accepts: [{
arg: 'expeditionFk',
type: 'number',
required: true
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/internationalExpedition`,
verb: 'POST'
}
});
Self.internationalExpedition = async expeditionFk => {
const models = Self.app.models;
const viaexpressConfig = await models.ViaexpressConfig.findOne({
fields: ['url']
});
const renderedXml = await models.ViaexpressConfig.renderer(expeditionFk);
const response = await axios.post(`${viaexpressConfig.url}ServicioVxClientes.asmx`, renderedXml, {
headers: {
'Content-Type': 'application/soap+xml; charset=utf-8'
}
});
const xmlString = response.data;
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlString, 'text/xml');
const referenciaVxElement = xmlDoc.getElementsByTagName('ReferenciaVx')[0];
const referenciaVx = referenciaVxElement.textContent;
return referenciaVx;
};
};

View File

@ -0,0 +1,126 @@
const fs = require('fs');
const ejs = require('ejs');
module.exports = Self => {
Self.remoteMethod('renderer', {
description: 'Renders the data from an XML',
accessType: 'READ',
accepts: [{
arg: 'expeditionFk',
type: 'number',
required: true
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/renderer`,
verb: 'GET'
}
});
Self.renderer = async expeditionFk => {
const models = Self.app.models;
const viaexpressConfig = await models.ViaexpressConfig.findOne({
fields: ['client', 'user', 'password', 'defaultWeight', 'deliveryType']
});
const expedition = await models.Expedition.findOne({
fields: ['id', 'ticketFk'],
where: {id: expeditionFk},
include: [
{
relation: 'ticket',
scope: {
fields: ['shipped', 'addressFk', 'clientFk', 'companyFk'],
include: [
{
relation: 'client',
scope: {
fields: ['mobile', 'phone', 'email']
}
},
{
relation: 'address',
scope: {
fields: [
'nickname',
'street',
'postalCode',
'city',
'mobile',
'phone',
'provinceFk'
],
include: {
relation: 'province',
scope: {
fields: ['name', 'countryFk'],
include: {
relation: 'country',
scope: {
fields: ['code'],
}
}
}
}
}
},
{
relation: 'company',
scope: {
fields: ['clientFk'],
include: {
relation: 'client',
scope: {
fields: ['socialName', 'mobile', 'phone', 'email', 'defaultAddressFk'],
include: {
relation: 'defaultAddress',
scope: {
fields: [
'street',
'postalCode',
'city',
'mobile',
'phone',
'provinceFk'
],
include: {
relation: 'province',
scope: {
fields: ['name']
}
}
}
}
}
}
}
}
]
}
}
]
});
const ticket = expedition.ticket();
const sender = ticket.company().client();
const shipped = ticket.shipped.toISOString();
const data = {
viaexpressConfig,
sender,
senderAddress: sender.defaultAddress(),
client: ticket.client(),
address: ticket.address(),
shipped
};
const template = fs.readFileSync(__dirname + '/template.ejs', 'utf-8');
const renderedXml = ejs.render(template, data);
return renderedXml;
};
};

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<PutExpedicionInternacional xmlns="http://82.223.6.71:82">
<ObjetoEnvio>
<Peso><%= viaexpressConfig.defaultWeight %></Peso>
<Bultos>1</Bultos>
<Reembolso>0</Reembolso>
<Fecha><%= shipped %></Fecha>
<ConRetorno>0</ConRetorno>
<Tipo><%= viaexpressConfig.deliveryType %></Tipo>
<Debidos>0</Debidos>
<Asegurado>0</Asegurado>
<Imprimir>0</Imprimir>
<ConDevolucionAlbaran>0</ConDevolucionAlbaran>
<Intradia>0</Intradia>
<Observaciones></Observaciones>
<AlbaranRemitente></AlbaranRemitente>
<Modo>0</Modo>
<TextoAgencia></TextoAgencia>
<Terminal></Terminal>
<ObjetoRemitente>
<RazonSocial><%= sender.socialName %></RazonSocial>
<Domicilio><%= senderAddress.street %></Domicilio>
<Cpostal><%= senderAddress.postalCode %></Cpostal>
<Poblacion><%= senderAddress.city %></Poblacion>
<Provincia><%= senderAddress.province().name %></Provincia>
<Contacto></Contacto>
<Telefono><%= senderAddress.mobile || senderAddress.phone || sender.mobile || sender.phone %></Telefono>
<Email><%= sender.email %></Email>
</ObjetoRemitente>
<ObjetoDestinatario>
<RazonSocial><%= address.nickname %></RazonSocial>
<Domicilio><%= address.street %></Domicilio>
<Cpostal><%= address.postalCode %></Cpostal>
<Poblacion><%= address.city %></Poblacion>
<Municipio></Municipio>
<Provincia><%= address.province().name %></Provincia>
<Contacto></Contacto>
<Telefono><%= address.mobile || address.phone || client.mobile || client.phone %></Telefono>
<Email><%= client.email %></Email>
<Pais><%= address.province().country().code %></Pais>
</ObjetoDestinatario>
<ObjetoLogin>
<IdCliente><%= viaexpressConfig.client %></IdCliente>
<Usuario><%= viaexpressConfig.user %></Usuario>
<Password><%= viaexpressConfig.password %></Password>
</ObjetoLogin>
</ObjetoEnvio>
</PutExpedicionInternacional>
</soap12:Body>
</soap12:Envelope>

View File

@ -47,7 +47,7 @@ module.exports = Self => {
const user = await Self.findById(userId, {fields: ['hasGrant']}, myOptions);
const userToUpdate = await Self.findById(id, {
fields: ['id', 'name', 'hasGrant', 'roleFk', 'password'],
fields: ['id', 'name', 'hasGrant', 'roleFk', 'password', 'email'],
include: {
relation: 'role',
scope: {

View File

@ -7,6 +7,11 @@ module.exports = Self => {
type: 'string',
description: 'The user name or email',
required: true
},
{
arg: 'app',
type: 'string',
description: 'The directory for mail'
}
],
http: {
@ -15,7 +20,7 @@ module.exports = Self => {
}
});
Self.recoverPassword = async function(user) {
Self.recoverPassword = async function(user, app) {
const models = Self.app.models;
const usesEmail = user.indexOf('@') !== -1;
@ -29,7 +34,7 @@ module.exports = Self => {
}
try {
await Self.resetPassword({email: user, emailTemplate: 'recover-password'});
await Self.resetPassword({email: user, emailTemplate: 'recover-password', app});
} catch (err) {
if (err.code === 'EMAIL_NOT_FOUND')
return;

View File

@ -53,19 +53,13 @@ module.exports = Self => {
return Self.validateLogin(user, password);
};
Self.passExpired = async(vnUser, myOptions) => {
Self.passExpired = async vnUser => {
const today = Date.vnNew();
today.setHours(0, 0, 0, 0);
if (vnUser.passExpired && vnUser.passExpired.getTime() <= today.getTime()) {
const $ = Self.app.models;
const changePasswordToken = await $.AccessToken.create({
scopes: ['changePassword'],
userId: vnUser.id
}, myOptions);
const err = new UserError('Pass expired', 'passExpired');
changePasswordToken.twoFactor = vnUser.twoFactor ? true : false;
err.details = {token: changePasswordToken};
err.details = {userId: vnUser.id, twoFactor: vnUser.twoFactor ? true : false};
throw err;
}
};

View File

@ -150,6 +150,9 @@
},
"PrintConfig": {
"dataSource": "vn"
},
"ViaexpressConfig": {
"dataSource": "vn"
}
}

View File

@ -27,5 +27,12 @@
"where" :{
"expired": null
}
},
"relations": {
"client": {
"type": "belongsTo",
"model": "Client",
"foreignKey": "clientFk"
}
}
}

View File

@ -1,4 +1,5 @@
module.exports = Self => {
require('../methods/osticket/osTicketReportEmail')(Self);
require('../methods/osticket/closeTicket')(Self);
require('../methods/osticket/sendToSupport')(Self);
};

View File

@ -0,0 +1,4 @@
module.exports = Self => {
require('../methods/viaexpress-config/internationalExpedition')(Self);
require('../methods/viaexpress-config/renderer')(Self);
};

View File

@ -0,0 +1,34 @@
{
"name": "ViaexpressConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "viaexpressConfig"
}
},
"properties": {
"id": {
"type": "number",
"required": true
},
"url": {
"type": "string",
"required": true
},
"client": {
"type": "string"
},
"user": {
"type": "string"
},
"password": {
"type": "string"
},
"defaultWeight": {
"type": "number"
},
"deliveryType": {
"type": "string"
}
}
}

View File

@ -20,7 +20,7 @@ module.exports = function(Self) {
Self.validatesFormatOf('email', {
message: 'Invalid email',
allowNull: true,
allowBlank: true,
allowBlank: false,
with: /^[\w|.|-]+@[\w|-]+(\.[\w|-]+)*(,[\w|.|-]+@[\w|-]+(\.[\w|-]+)*)*$/
});
@ -96,11 +96,21 @@ module.exports = function(Self) {
const headers = httpRequest.headers;
const origin = headers.origin;
const defaultHash = '/reset-password?access_token=$token$';
const recoverHashes = {
hedera: 'verificationToken=$token$'
};
const app = info.options?.app;
let recoverHash = app ? recoverHashes[app] : defaultHash;
recoverHash = recoverHash.replace('$token$', info.accessToken.id);
const user = await Self.app.models.VnUser.findById(info.user.id);
const params = {
recipient: info.email,
lang: user.lang,
url: `${origin}/#!/reset-password?access_token=${info.accessToken.id}`
url: origin + '/#!' + recoverHash
};
const options = Object.assign({}, info.options);
@ -115,6 +125,14 @@ module.exports = function(Self) {
Self.validateLogin = async function(user, password) {
let loginInfo = Object.assign({password}, Self.userUses(user));
token = await Self.login(loginInfo, 'user');
const userToken = await token.user.get();
try {
await Self.app.models.Account.sync(userToken.name, password);
} catch (err) {
console.warn(err);
}
return {token: token.id, ttl: token.ttl};
};

View File

@ -31,7 +31,6 @@ RUN sed -i -e 's/@mockDate/'"$MOCKDATE"'/g' mockDate.sql \
&& gosu mysql docker-structure.sh
COPY changes ./changes
COPY dump/fixtures.sql ./
ARG STAMP=unknown
RUN gosu mysql docker-fixtures.sh
RUN echo "[INFO] -> Import finished" \

View File

@ -1,4 +0,0 @@
/**
* Hay una versión en salix que machacará toda esta función/procedimiento avisa
* a ___ de los cambios que quieres hacer.
*/

View File

@ -0,0 +1,4 @@
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('SaleTracking', 'deleteSaleGroupDetail', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
('SaleTracking', 'replaceOrCreate', 'WRITE', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,4 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('ViaexpressConfig', 'internationalExpedition', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
('ViaexpressConfig', 'renderer', 'READ', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,2 @@
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
VALUES ('Ticket','canEditWeekly','WRITE','ALLOW','ROLE','buyer');

View File

@ -0,0 +1,10 @@
CREATE TABLE `vn`.`viaexpressConfig` (
id int auto_increment NOT NULL,
url varchar(100) NOT NULL,
client varchar(100) NOT NULL,
user varchar(100) NOT NULL,
password varchar(100) NOT NULL,
defaultWeight decimal(10,2) NOT NULL,
deliveryType varchar(5) NOT NULL,
CONSTRAINT viaexpressConfig_PK PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;

View File

@ -0,0 +1,87 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`client_create`(
vFirstname VARCHAR(50),
vSurnames VARCHAR(50),
vFi VARCHAR(9),
vAddress TEXT,
vPostcode CHAR(5),
vCity VARCHAR(25),
vProvinceFk SMALLINT(5),
vCompanyFk SMALLINT(5),
vPhone VARCHAR(11),
vEmail VARCHAR(255),
vUserFk INT
)
BEGIN
/**
* Create new client
*
* @params vFirstname firstName
* @params vSurnames surnames
* @params vFi company code from accounting transactions
* @params vAddress address
* @params vPostcode postCode
* @params vCity city
* @params vProvinceFk province
* @params vCompanyFk company in which he has become a client
* @params vPhone telephone number
* @params vEmail email address
* @params vUserFk user id
*/
DECLARE vPayMethodFk INT;
DECLARE vDueDay INT;
DECLARE vDefaultCredit DECIMAL(10, 2);
DECLARE vIsTaxDataChecked TINYINT(1);
DECLARE vHasCoreVnl BOOLEAN;
DECLARE vMandateTypeFk INT;
SELECT defaultPayMethodFk,
defaultDueDay,
defaultCredit,
defaultIsTaxDataChecked,
defaultHasCoreVnl,
defaultMandateTypeFk
INTO vPayMethodFk,
vDueDay,
vDefaultCredit,
vIsTaxDataChecked,
vHasCoreVnl,
vMandateTypeFk
FROM clientConfig;
INSERT INTO `client`
SET id = vUserFk,
name = CONCAT(vFirstname, ' ', vSurnames),
street = vAddress,
fi = TRIM(vFi),
phone = vPhone,
email = vEmail,
provinceFk = vProvinceFk,
city = vCity,
postcode = vPostcode,
socialName = UPPER(CONCAT(vSurnames, ' ', vFirstname)),
payMethodFk = vPayMethodFk,
dueDay = vDueDay,
credit = vDefaultCredit,
isTaxDataChecked = vIsTaxDataChecked,
hasCoreVnl = vHasCoreVnl,
isEqualizated = FALSE
ON duplicate KEY UPDATE
payMethodFk = vPayMethodFk,
dueDay = vDueDay,
credit = vDefaultCredit,
isTaxDataChecked = vIsTaxDataChecked,
hasCoreVnl = vHasCoreVnl,
isActive = TRUE;
INSERT INTO mandate (clientFk, companyFk, mandateTypeFk)
SELECT vUserFk, vCompanyFk, vMandateTypeFk
WHERE NOT EXISTS (
SELECT id
FROM mandate
WHERE clientFk = vUserFk
AND companyFk = vCompanyFk
AND mandateTypeFk = vMandateTypeFk
);
END$$
DELIMITER ;

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('TicketSms', 'find', 'READ', 'ALLOW', 'ROLE', 'salesPerson');

View File

View File

@ -0,0 +1,6 @@
UPDATE `salix`.`ACL`
SET principalId='salesPerson'
WHERE
model='Ticket'
AND property='setDeleted'
AND accessType='WRITE';

View File

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('Worker', 'search', 'READ', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,2 @@
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
VALUES ('ExpeditionState','addExpeditionState','WRITE','ALLOW','ROLE','delivery');

View File

@ -0,0 +1,32 @@
INSERT INTO `account`.`role` (`id`, `name`, `description`, `hasLogin`)
VALUES ('claimViewer','Trabajadores que consulta las reclamaciones ',1);
INSERT INTO `account`.`roleInherit` (`role`,`inheritsFrom`)
SELECT `r`.`id`, `r2`.`id`
FROM `account`.`role` `r`
JOIN `account`.`role` `r2` ON `r2`.`name` = 'claimViewer'
WHERE `r`.`name` IN (
'salesPerson',
'buyer',
'deliveryBoss',
'handmadeBoss'
)
DELETE FROM `salix`.`ACL`
WHERE `model`= 'claim'
AND `property` IN (
'filter',
'find',
'findById',
'getSummary'
);
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
VALUES ('Claim','filter','READ','ALLOW','ROLE','claimViewer');
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
VALUES ('Claim','find','READ','ALLOW','ROLE','claimViewer');
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
VALUES ('Claim','findById','READ','ALLOW','ROLE','claimViewer');
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
VALUES ('Claim','getSummary','READ','ALLOW','ROLE','claimViewer');

View File

@ -0,0 +1,3 @@
UPDATE `vn`.`department`
SET code='VN'
WHERE name='VERDNATURA';

View File

@ -0,0 +1,7 @@
DELETE FROM `vn`.`saleGroupDetail` WHERE id IN (468106,468104,468107,468105,495210,495208,495207,495209,462879,462880,447186,450623,450622,455606,455605,455827,455829,455828,459067,460689,460691,460690,460692,462408,463403,463405,463404,463129,463127,463126,463128,468098,468096,468099,468097,468310,468314,468313,475654,468325,473248,474803,474739,475042,475052,475047,475041,475051,475046,475040,475050,475045,475039,475049,475044,475038,475048,475043,474888,474892,474890,474887,474891,474889,481109,481107,481105,481108,481106,481110,479008,490787,490792,490791,485295,485294,485293,485528,490796,487853,487959,491303,490789,490914,490913,492305,492310,492307,492304,492309,492306,492303,492308,494111,494110,494480,494482,494481,494483,495202,495200,495199,495201,497209,499765,499763,499767,499764,499768,499766,502014,502013,508820,508819,508818,463133,463131,463130,463132,468102,468100,468103,468101,468311,468316,468315,468327,474894,474898,474896,474893,474897,474895,495206,495204,495203,495205,499771,499769,499773,499770,499774,499772);
ALTER TABLE `vn`.`saleGroupDetail` ADD CONSTRAINT saleGroupDetail_UN UNIQUE KEY (saleFk);
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`)
VALUES
('SaleGroupDetail','deleteById','WRITE','ALLOW','employee');

View File

View File

@ -22,12 +22,8 @@ module.exports = class Docker {
* @param {String} networkName Name of the container network
*/
async run(ci, networkName = 'jenkins') {
let d = new Date();
let pad = v => v < 10 ? '0' + v : v;
let stamp = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
log('Building container image...');
await this.execP(`docker build --build-arg STAMP=${stamp} -t salix-db ./db`);
await this.execP(`docker build -t salix-db ./db`);
log('Image built.');
let dockerArgs;

File diff suppressed because one or more lines are too long

View File

@ -360,18 +360,18 @@ INSERT INTO `vn`.`contactChannel`(`id`, `name`)
INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city`,`postcode`,`phone`,`mobile`,`isRelevant`,`email`,`iban`,`dueDay`,`accountingAccount`,`isEqualizated`,`provinceFk`,`hasToInvoice`,`credit`,`countryFk`,`isActive`,`gestdocFk`,`quality`,`payMethodFk`,`created`,`isToBeMailed`,`contactChannelFk`,`hasSepaVnl`,`hasCoreVnl`,`hasCoreVnh`,`riskCalculated`,`clientTypeFk`, `hasToInvoiceByAddress`,`isTaxDataChecked`,`isFreezed`,`creditInsurance`,`isCreatedAsServed`,`hasInvoiceSimplified`,`salesPersonFk`,`isVies`,`eypbc`, `businessTypeFk`)
VALUES
(1101, 'Bruce Wayne', '84612325V', 'Batman', 'Alfred', '1007 Mountain Drive, Gotham', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1102, 'Petter Parker', '87945234L', 'Spider man', 'Aunt May', '20 Ingram Street, Queens, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'PetterParker@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1103, 'Clark Kent', '06815934E', 'Super man', 'lois lane', '344 Clinton Street, Apartament 3-D', 'Gotham', 46460, 1111111111, 222222222, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 0, 19, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1104, 'Tony Stark', '06089160W', 'Iron man', 'Pepper Potts', '10880 Malibu Point, 90265', 'Gotham', 46460, 1111111111, 222222222, 1, 'TonyStark@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1105, 'Max Eisenhardt', '251628698', 'Magneto', 'Rogue', 'Unknown Whereabouts', 'Gotham', 46460, 1111111111, 222222222, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 300, 8, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1, 'florist'),
(1106, 'DavidCharlesHaller', '53136686Q', 'Legion', 'Charles Xavier', 'City of New York, New York, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'DavidCharlesHaller@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1, 'florist'),
(1107, 'Hank Pym', '09854837G', 'Ant man', 'Hawk', 'Anthill, San Francisco, California', 'Gotham', 46460, 1111111111, 222222222, 1, 'HankPym@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 19, 0, 1, 'florist'),
(1108, 'Charles Xavier', '22641921P', 'Professor X', 'Beast', '3800 Victory Pkwy, Cincinnati, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist'),
(1109, 'Bruce Banner', '16104829E', 'Hulk', 'Black widow', 'Somewhere in New York', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist'),
(1110, 'Jessica Jones', '58282869H', 'Jessica Jones', 'Luke Cage', 'NYCC 2015 Poster', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist'),
(1111, 'Missing', NULL, 'Missing man', 'Anton', 'The space, Universe far away', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others'),
(1112, 'Trash', NULL, 'Garbage man', 'Unknown name', 'New York city, Underground', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others');
(1101, 'Bruce Wayne', '84612325V', 'BATMAN', 'Alfred', '1007 MOUNTAIN DRIVE, GOTHAM', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1102, 'Petter Parker', '87945234L', 'SPIDER MAN', 'Aunt May', '20 INGRAM STREET, QUEENS, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'PetterParker@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1103, 'Clark Kent', '06815934E', 'SUPER MAN', 'lois lane', '344 CLINTON STREET, APARTAMENT 3-D', 'Gotham', 46460, 1111111111, 222222222, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 0, 19, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1104, 'Tony Stark', '06089160W', 'IRON MAN', 'Pepper Potts', '10880 MALIBU POINT, 90265', 'Gotham', 46460, 1111111111, 222222222, 1, 'TonyStark@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist'),
(1105, 'Max Eisenhardt', '251628698', 'MAGNETO', 'Rogue', 'UNKNOWN WHEREABOUTS', 'Gotham', 46460, 1111111111, 222222222, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 300, 8, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1, 'florist'),
(1106, 'DavidCharlesHaller', '53136686Q', 'LEGION', 'Charles Xavier', 'CITY OF NEW YORK, NEW YORK, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'DavidCharlesHaller@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1, 'florist'),
(1107, 'Hank Pym', '09854837G', 'ANT MAN', 'Hawk', 'ANTHILL, SAN FRANCISCO, CALIFORNIA', 'Gotham', 46460, 1111111111, 222222222, 1, 'HankPym@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 19, 0, 1, 'florist'),
(1108, 'Charles Xavier', '22641921P', 'PROFESSOR X', 'Beast', '3800 VICTORY PKWY, CINCINNATI, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist'),
(1109, 'Bruce Banner', '16104829E', 'HULK', 'Black widow', 'SOMEWHERE IN NEW YORK', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist'),
(1110, 'Jessica Jones', '58282869H', 'JESSICA JONES', 'Luke Cage', 'NYCC 2015 POSTER', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist'),
(1111, 'Missing', NULL, 'MISSING MAN', 'Anton', 'THE SPACE, UNIVERSE FAR AWAY', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others'),
(1112, 'Trash', NULL, 'GARBAGE MAN', 'Unknown name', 'NEW YORK CITY, UNDERGROUND', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others');
INSERT INTO `vn`.`client`(`id`, `name`, `fi`, `socialName`, `contact`, `street`, `city`, `postcode`, `isRelevant`, `email`, `iban`,`dueDay`,`accountingAccount`, `isEqualizated`, `provinceFk`, `hasToInvoice`, `credit`, `countryFk`, `isActive`, `gestdocFk`, `quality`, `payMethodFk`,`created`, `isTaxDataChecked`)
SELECT id, name, CONCAT(RPAD(CONCAT(id,9),8,id),'A'), CONCAT(name, 'Social'), CONCAT(name, 'Contact'), CONCAT(name, 'Street'), 'GOTHAM', 46460, 1, CONCAT(name,'@mydomain.com'), NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5, util.VN_CURDATE(), 1
@ -579,13 +579,13 @@ INSERT INTO `vn`.`supplierAccount`(`id`, `supplierFk`, `iban`, `bankEntityFk`)
VALUES
(241, 442, 'ES111122333344111122221111', 128);
INSERT INTO `vn`.`company`(`id`, `code`, `supplierAccountFk`, `workerManagerFk`, `companyCode`, `sage200Company`, `expired`, `companyGroupFk`, `phytosanitary`)
INSERT INTO `vn`.`company`(`id`, `code`, `supplierAccountFk`, `workerManagerFk`, `companyCode`, `sage200Company`, `expired`, `companyGroupFk`, `phytosanitary` , `clientFk`)
VALUES
(69 , 'CCs', NULL, 30, NULL, 0, NULL, 1, NULL),
(442 , 'VNL', 241, 30, 2 , 1, NULL, 2, 'VNL Company - Plant passport'),
(567 , 'VNH', NULL, 30, NULL, 4, NULL, 1, 'VNH Company - Plant passport'),
(791 , 'FTH', NULL, 30, NULL, 3, '2015-11-30', 1, NULL),
(1381, 'ORN', NULL, 30, NULL, 7, NULL, 1, 'ORN Company - Plant passport');
(69 , 'CCs', NULL, 30, NULL, 0, NULL, 1, NULL , NULL),
(442 , 'VNL', 241, 30, 2 , 1, NULL, 2, 'VNL Company - Plant passport' , 1101),
(567 , 'VNH', NULL, 30, NULL, 4, NULL, 1, 'VNH Company - Plant passport' , NULL),
(791 , 'FTH', NULL, 30, NULL, 3, '2015-11-30', 1, NULL , NULL),
(1381, 'ORN', NULL, 30, NULL, 7, NULL, 1, 'ORN Company - Plant passport' , NULL);
INSERT INTO `vn`.`taxArea` (`code`, `claveOperacionFactura`, `CodigoTransaccion`)
VALUES
@ -2955,6 +2955,6 @@ INSERT INTO `vn`.`invoiceInSerial` (`code`, `description`, `cplusTerIdNifFk`, `t
('W', 'Vanaheim', 1, 'WORLD');
INSERT INTO `hedera`.`imageConfig` (`id`, `maxSize`, `useXsendfile`, `url`)
INSERT INTO `hedera`.`imageConfig` (`id`, `maxSize`, `useXsendfile`, `url`)
VALUES
(1, 0, 0, 'marvel.com');

View File

@ -65,7 +65,6 @@ TABLES=(
sample
state
ticketUpdateAction
time
volumeConfig
workCenter
companyI18n
@ -98,13 +97,6 @@ TABLES=(
)
dump_tables ${TABLES[@]}
TABLES=(
postgresql
labour_agreement
media_type
)
dump_tables ${TABLES[@]}
TABLES=(
sage
TiposIva

View File

@ -7,7 +7,6 @@ SCHEMAS=(
edi
hedera
pbx
postgresql
sage
salix
stock
@ -23,7 +22,6 @@ IGNORETABLES=(
--ignore-table=bs.productionIndicators
--ignore-table=bs.VentasPorCliente
--ignore-table=bs.v_ventas
--ignore-table=postgresql.currentWorkersStats
--ignore-table=vn.accounting__
--ignore-table=vn.agencyModeZone
--ignore-table=vn.agencyProvince

View File

@ -54,7 +54,6 @@ xdescribe('worker workerTimeControl_check()', () => {
});
it('should throw an error if the worker with a special category has not finished the 9h break', async() => {
// dayBreak to 9h in postgresql.professional_category
const workerId = 1110;
const tabletId = 1;
let stmts = [];
@ -91,7 +90,6 @@ xdescribe('worker workerTimeControl_check()', () => {
});
it('should check f the worker with a special category has finished the 9h break', async() => {
// dayBreak to 9h in postgresql.professional_category
const workerId = 1110;
const tabletId = 1;
let stmts = [];
@ -239,12 +237,6 @@ xdescribe('worker workerTimeControl_check()', () => {
stmts.push('START TRANSACTION');
stmt = new ParameterizedSQL(`INSERT INTO postgresql.calendar_employee(businessFk,calendar_state_id,date)
VALUES
(?,1,CURDATE())`, [
workerId
]);
stmts.push(stmt);
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
VALUES
(?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),

View File

@ -632,6 +632,7 @@ let actions = {
await this.write(selector, value.toString());
break;
case 'vn-autocomplete':
case 'vn-worker-autocomplete':
if (value)
await this.autocompleteSearch(selector, value.toString());
else
@ -667,6 +668,7 @@ let actions = {
switch (tagName) {
case 'vn-textfield':
case 'vn-autocomplete':
case 'vn-worker-autocomplete':
case 'vn-input-time':
case 'vn-datalist':
el = await input.$('input');

View File

@ -187,7 +187,7 @@ export default {
country: 'vn-client-create vn-autocomplete[ng-model="$ctrl.client.countryFk"]',
userName: 'vn-client-create vn-textfield[ng-model="$ctrl.client.userName"]',
email: 'vn-client-create vn-textfield[ng-model="$ctrl.client.email"]',
salesPerson: 'vn-client-create vn-autocomplete[ng-model="$ctrl.client.salesPersonFk"]',
salesPerson: 'vn-client-create vn-worker-autocomplete[ng-model="$ctrl.client.salesPersonFk"]',
saveNewProvicenButton: '#saveProvince',
saveNewCityButton: '#saveCity',
saveNewPoscode: '#savePostcode',
@ -199,7 +199,7 @@ export default {
email: 'vn-client-basic-data vn-textfield[ng-model="$ctrl.client.email"]',
phone: 'vn-client-basic-data vn-textfield[ng-model="$ctrl.client.phone"]',
mobile: 'vn-client-basic-data vn-textfield[ng-model="$ctrl.client.mobile"]',
salesPerson: 'vn-client-basic-data vn-autocomplete[ng-model="$ctrl.client.salesPersonFk"]',
salesPerson: 'vn-client-basic-data vn-worker-autocomplete[ng-model="$ctrl.client.salesPersonFk"]',
channel: 'vn-client-basic-data vn-autocomplete[ng-model="$ctrl.client.contactChannelFk"]',
transferor: 'vn-client-basic-data vn-autocomplete[ng-model="$ctrl.client.transferorFk"]',
businessType: 'vn-client-basic-data vn-autocomplete[ng-model="$ctrl.client.businessTypeFk"]',
@ -735,7 +735,7 @@ export default {
},
createStateView: {
state: 'vn-autocomplete[ng-model="$ctrl.stateFk"]',
worker: 'vn-autocomplete[ng-model="$ctrl.workerFk"]',
worker: 'vn-worker-autocomplete[ng-model="$ctrl.workerFk"]',
saveStateButton: `button[type=submit]`
},
claimsIndex: {
@ -781,12 +781,12 @@ export default {
firstClaimReason: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimReasonFk"]',
firstClaimResult: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimResultFk"]',
firstClaimResponsible: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimResponsibleFk"]',
firstClaimWorker: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.workerFk"]',
firstClaimWorker: 'vn-claim-development vn-horizontal:nth-child(1) vn-worker-autocomplete[ng-model="claimDevelopment.workerFk"]',
firstClaimRedelivery: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimRedeliveryFk"]',
secondClaimReason: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimReasonFk"]',
secondClaimResult: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimResultFk"]',
secondClaimResponsible: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimResponsibleFk"]',
secondClaimWorker: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.workerFk"]',
secondClaimWorker: 'vn-claim-development vn-horizontal:nth-child(2) vn-worker-autocomplete[ng-model="claimDevelopment.workerFk"]',
secondClaimRedelivery: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimRedeliveryFk"]',
saveDevelopmentButton: 'button[type=submit]'
},
@ -854,7 +854,7 @@ export default {
},
createRouteView: {
worker: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.workerFk"]',
worker: 'vn-route-create vn-worker-autocomplete[ng-model="$ctrl.route.workerFk"]',
createdDatePicker: 'vn-route-create vn-date-picker[ng-model="$ctrl.route.created"]',
vehicleAuto: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.vehicleFk"]',
agency: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.agencyModeFk"]',
@ -875,7 +875,7 @@ export default {
},
routeTickets: {
firstTicketPriority: 'vn-route-tickets vn-tr:nth-child(1) vn-input-number[ng-model="ticket.priority"]',
firstTicketPriority: 'vn-route-tickets vn-tr:nth-child(1) vn-td-editable',
firstTicketCheckbox: 'vn-route-tickets vn-tr:nth-child(1) vn-check',
buscamanButton: 'vn-route-tickets vn-button[icon="icon-buscaman"]',
firstTicketDeleteButton: 'vn-route-tickets vn-tr:nth-child(1) vn-icon[icon="delete"]',
@ -976,7 +976,7 @@ export default {
street: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.street"]',
user: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.name"]',
email: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.email"]',
boss: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bossFk"]',
boss: 'vn-worker-create vn-worker-autocomplete[ng-model="$ctrl.worker.bossFk"]',
role: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.roleFk"]',
iban: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.iban"]',
createButton: 'vn-worker-create vn-submit[label="Create"]',

View File

@ -73,8 +73,8 @@ describe('Client create path', () => {
it(`should attempt to create a new user with all it's data but wrong email`, async() => {
await page.write(selectors.createClientView.name, 'Carol Danvers');
await page.write(selectors.createClientView.socialName, 'AVG tax');
await page.write(selectors.createClientView.street, 'Many places');
await page.write(selectors.createClientView.socialName, 'AVG TAX');
await page.write(selectors.createClientView.street, 'MANY PLACES');
await page.clearInput(selectors.createClientView.email);
await page.write(selectors.createClientView.email, 'incorrect email format');
await page.waitToClick(selectors.createClientView.createButton);

View File

@ -61,7 +61,7 @@ describe('Client Edit fiscalData path', () => {
await page.clearInput(selectors.clientFiscalData.fiscalId);
await page.write(selectors.clientFiscalData.fiscalId, 'INVALID!');
await page.clearInput(selectors.clientFiscalData.address);
await page.write(selectors.clientFiscalData.address, 'Somewhere edited');
await page.write(selectors.clientFiscalData.address, 'SOMEWHERE EDITED');
await page.autocompleteSearch(selectors.clientFiscalData.country, 'España');
await page.autocompleteSearch(selectors.clientFiscalData.province, 'Province one');
await page.clearInput(selectors.clientFiscalData.city);
@ -190,7 +190,7 @@ describe('Client Edit fiscalData path', () => {
const verifiedData = await page.checkboxState(selectors.clientFiscalData.verifiedDataCheckbox);
expect(fiscalId).toEqual('94980061C');
expect(address).toEqual('Somewhere edited');
expect(address).toEqual('SOMEWHERE EDITED');
expect(postcode).toContain('46000');
expect(sageTax).toEqual('Operaciones no sujetas');
expect(sageTransaction).toEqual('Regularización de inversiones');

View File

@ -28,7 +28,7 @@ describe('Client lock verified data path', () => {
it('should edit the social name', async() => {
await page.waitForSelector(selectors.clientFiscalData.socialName);
await page.clearInput(selectors.clientFiscalData.socialName);
await page.write(selectors.clientFiscalData.socialName, 'Captain America Civil War');
await page.write(selectors.clientFiscalData.socialName, 'CAPTAIN AMERICA CIVIL WAR');
await page.waitToClick(selectors.clientFiscalData.saveButton);
const message = await page.waitForSnackbar();
@ -39,7 +39,7 @@ describe('Client lock verified data path', () => {
await page.reloadSection('client.card.fiscalData');
const result = await page.waitToGetProperty(selectors.clientFiscalData.socialName, 'value');
expect(result).toEqual('Captain America Civil War');
expect(result).toEqual('CAPTAIN AMERICA CIVIL WAR');
});
});
@ -88,7 +88,7 @@ describe('Client lock verified data path', () => {
await page.reloadSection('client.card.fiscalData');
const result = await page.waitToGetProperty(selectors.clientFiscalData.socialName, 'value');
expect(result).toEqual('Ant man and the Wasp');
expect(result).toEqual('ANT MAN AND THE WASP');
});
});
@ -142,7 +142,7 @@ describe('Client lock verified data path', () => {
await page.reloadSection('client.card.fiscalData');
const result = await page.waitToGetProperty(selectors.clientFiscalData.socialName, 'value');
expect(result).toEqual('new social name edition');
expect(result).toEqual('NEW SOCIAL NAME EDITION');
});
});

View File

@ -36,7 +36,7 @@ describe('Client summary path', () => {
it('should display fiscal address details', async() => {
const result = await page.waitToGetProperty(selectors.clientSummary.street, 'innerText');
expect(result).toContain('20 Ingram Street');
expect(result).toContain('20 INGRAM STREET');
});
it('should display some fiscal data', async() => {

View File

@ -23,7 +23,7 @@ describe('Worker create path', () => {
await page.write(selectors.workerCreate.fi, '78457139E');
await page.write(selectors.workerCreate.phone, '12356789');
await page.write(selectors.workerCreate.postcode, '46680');
await page.write(selectors.workerCreate.street, 'S/ Doomstadt');
await page.write(selectors.workerCreate.street, 'S/ DOOMSTADT');
await page.write(selectors.workerCreate.email, 'doctorDoom@marvel.com');
await page.write(selectors.workerCreate.iban, 'ES9121000418450200051332');

View File

@ -59,7 +59,7 @@ describe('Ticket Create new tracking state path', () => {
const result = await page
.waitToGetProperty(selectors.createStateView.worker, 'value');
expect(result).toEqual('salesPersonNick');
expect(result).toEqual('salesPerson');
});
it(`should succesfully create a valid state`, async() => {

View File

@ -10,7 +10,7 @@ describe('Ticket create path', () => {
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'ticket');
await page.loginAndModule('salesPerson', 'ticket');
});
afterAll(async() => {

View File

@ -18,8 +18,7 @@ describe('Route tickets path', () => {
});
it('should modify the first ticket priority', async() => {
await page.clearInput(selectors.routeTickets.firstTicketPriority);
await page.type(selectors.routeTickets.firstTicketPriority, '9');
await page.writeOnEditableTD(selectors.routeTickets.firstTicketPriority, '9');
await page.keyboard.press('Enter');
const message = await page.waitForSnackbar();

View File

@ -18,7 +18,7 @@
<label>
<span translate>{{$ctrl.label}}</span>
<span class="required">*</span>
</label>
</label>
</div>
<div class="icons pre">
<vn-icon
@ -50,4 +50,4 @@
on-select="$ctrl.onDropDownSelect(item)"
on-data-ready="$ctrl.onDataReady()"
on-close-start="$ctrl.onDropDownClose()">
</vn-drop-down>
</vn-drop-down>

View File

@ -17,10 +17,9 @@ import './style.scss';
* @event change Thrown when value is changed
*/
export default class Autocomplete extends Field {
constructor($element, $, $compile, $transclude) {
super($element, $, $compile);
constructor($element, $, $transclude) {
super($element, $, $transclude);
this.$transclude = $transclude;
this.$compile = $compile;
this._selection = null;
this.input = this.element.querySelector('input');
}
@ -153,7 +152,14 @@ export default class Autocomplete extends Field {
filter.include = this.include;
let json = encodeURIComponent(JSON.stringify(filter));
this.$http.get(`${this.url}?filter=${json}`).then(
let url;
if (this.url.includes('?'))
url = `${this.url}&filter=${json}`;
else
url = `${this.url}?filter=${json}`;
this.$http.get(url).then(
json => this.onSelectionRequest(json.data),
() => this.onSelectionRequest()
);
@ -282,7 +288,7 @@ export default class Autocomplete extends Field {
this.refreshSelection();
}
}
Autocomplete.$inject = ['$element', '$scope', '$compile', '$transclude'];
Autocomplete.$inject = ['$element', '$scope', '$transclude'];
ngModule.vnComponent('vnAutocomplete', {
template: require('./index.html'),

View File

@ -13,12 +13,12 @@
class="dropdown"
ng-click="$ctrl.onContainerClick($event)">
</ul>
<div
ng-if="$ctrl.statusText"
<div
ng-if="$ctrl.statusText"
ng-click="$ctrl.onLoadMoreClick($event)"
class="status"
translate>
{{$ctrl.statusText}}
</div>
</div>
</default>
</default>

View File

@ -3,8 +3,8 @@ import FormInput from '../form-input';
import './style.scss';
export default class Field extends FormInput {
constructor($element, $scope) {
super($element, $scope);
constructor($element, $scope, $transclude) {
super($element, $scope, $transclude);
this.prefix = null;
this.suffix = null;
@ -197,7 +197,7 @@ export default class Field extends FormInput {
});
}
}
Field.$inject = ['$element', '$scope'];
Field.$inject = ['$element', '$scope', '$transclude'];
ngModule.vnComponent('vnField', {
template: require('./index.html'),

View File

@ -51,7 +51,9 @@ import './textarea';
import './th';
import './treeview';
import './wday-picker';
import './worker-autocomplete';
import './datalist';
import './contextmenu';
import './rating';
import './smart-table';
import './support-dialog';

View File

@ -339,8 +339,9 @@ export default class SmartTable extends Component {
if (!header) return;
const tbody = this.element.querySelector('tbody');
const columns = header.querySelectorAll('th');
if (!tbody) return;
const columns = header.querySelectorAll('th');
const hasSearchRow = tbody.querySelector('tr#searchRow');
if (hasSearchRow) {
if (this.$inputsScope)

View File

@ -1 +1,5 @@
<div id="shapes"></div>
<div id="shapes"></div>
<vn-support-dialog
vn-id="support-dialog"
additional-data="$ctrl.additionalData">
</vn-support-dialog>

View File

@ -27,6 +27,18 @@ export default class Controller extends Component {
setTimeout(() => element.classList.add('shown'), 30);
shape.element = element;
if (data.additionalData && this.vnToken.token) {
this.additionalData = data.additionalData;
let supportButton = document.createElement('i');
supportButton.setAttribute('class', 'material-icons clickable');
supportButton.addEventListener('click', () => this.$.supportDialog.show());
element.appendChild(supportButton);
let buttonIcon = 'support_agent';
buttonIcon = document.createTextNode(buttonIcon);
supportButton.appendChild(buttonIcon);
}
if (shape.type)
element.classList.add(shape.type);
@ -95,7 +107,7 @@ export default class Controller extends Component {
clearTimeout(shape.hideTimeout);
shape.hideTimeout = setTimeout(
() => this.hide(shape), shape.timeout || 3000);
() => this.hide(shape), shape.timeout || 5000);
this.lastShape = shape;
}

View File

@ -20,11 +20,15 @@ vn-snackbar .shape {
margin-bottom: 15px;
color: white;
padding: 12px 25px 12px 12px;
display: flex ;
flex-direction: row;
justify-content: center;
align-items: center;
& > .text {
text-align: center;
vn-chip {
vn-chip {
position: absolute;
left: -16px;
top: -16px;
@ -64,4 +68,12 @@ vn-snackbar .shape {
top: 0;
right: 0
}
}
.clickable{
background-color: $color-main;
padding: 6px;
border-radius: 50%;
cursor: pointer;
margin-right: 7px;
}
}

View File

@ -0,0 +1,22 @@
<tpl-body>
<section>
<h5 class="vn-py-sm" translate>Send cau</h5>
<vn-horizontal>
<vn-textarea vn-one
label="ExplainReason"
ng-model="$ctrl.reason"
rows="2"
required="true">
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<span>
{{'By sending this ticket, all the data related to the error, the section, the user, etc., are already sent.' | translate}}
</span>
</vn-horizontal>
</section>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Send</button>
</tpl-buttons>

View File

@ -0,0 +1,30 @@
import ngModule from '../../module';
import Dialog from '../dialog';
export default class Controller extends Dialog {
constructor($element, $, $transclude) {
super($element, $, $transclude);
}
responseHandler(response) {
if (response !== 'accept')
return super.responseHandler(response);
this.$http.post('Ostickets/send-to-support', {
reason: this.reason,
additionalData: this.additionalData
})
.then(() => super.responseHandler(response))
.then(() => this.vnApp.showSuccess(this.$t('Email sended!')));
}
}
Controller.$inject = ['$element', '$scope', '$transclude'];
ngModule.vnComponent('vnSupportDialog', {
slotTemplate: require('./index.html'),
controller: Controller,
bindings: {
additionalData: '<?'
}
});

View File

@ -0,0 +1,8 @@
<tpl-item>
<div>
{{name}}
</div>
<div class="text-caption text-secondary">
{{nickname}}, {{code}}
</div>
</tpl-item>

View File

@ -0,0 +1,40 @@
import ngModule from '../../module';
import Autocomplete from '../autocomplete';
export default class WorkerAutocomplete extends Autocomplete {
constructor(...args) {
super(...args);
}
$onInit() {
super.$onInit();
let url = 'Workers/search';
if (this.departments) {
const parameter = encodeURIComponent(JSON.stringify(this.departments));
url = `Workers/search?departmentCodes=${parameter}`;
}
Object.assign(this, {
label: 'Worker',
url,
searchFunction: function({$search}) {
return {and: [
{'active': {neq: false}},
{or: [
{'name': $search},
{'nickname': {like: '%' + $search + '%'}},
{'code': {like: $search + '%'}}
]}
]};
},
});
}
}
ngModule.vnComponent('vnWorkerAutocomplete', {
slotTemplate: require('./index.html'),
controller: WorkerAutocomplete,
bindings: {
departments: '<?'
},
});

View File

@ -13,4 +13,5 @@ Finalize: Finalize
Previous: Back
Load more: Load more
Auto-scroll interrupted, please adjust the search: Auto-scroll interrupted, please adjust the search
General search: General search
General search: General search
ExplainReason: Explain the reason why this error should not occur

View File

@ -64,3 +64,6 @@ No results found: Sin resultados
No data: Sin datos
Undo changes: Deshacer cambios
Load more results: Cargar más resultados
Send cau: Enviar cau
By sending this ticket, all the data related to the error, the section, the user, etc., are already sent.: Al enviar este cau ya se envían todos los datos relacionados con el error, la sección, el usuario, etc
ExplainReason: Explique el motivo por el que no deberia aparecer este fallo

View File

@ -23,9 +23,9 @@ export default class App {
this.logger.showSuccess(message);
}
showError(message) {
showError(message, additionalData) {
if (this.logger)
this.logger.showError(message);
this.logger.showError(message, additionalData);
}
pushLoader() {

View File

@ -60,7 +60,7 @@ export default class Token {
if (!this.token) return;
const created = storage.getItem('vnTokenCreated');
this.created = created && new Date(created);
this.renewPeriod = storage.getItem('vnTokenRenewPeriod');
this.ttl = storage.getItem('vnTokenTtl');
}
setStorage(storage, token, created, ttl) {

View File

@ -25,15 +25,15 @@ export default class App extends Component {
}
showMessage(message) {
this.$.snackbar.show({message: message});
this.$.snackbar.show({message});
}
showSuccess(message) {
this.$.snackbar.showSuccess({message: message});
this.$.snackbar.showSuccess({message});
}
showError(message) {
this.$.snackbar.showError({message: message});
showError(message, additionalData) {
this.$.snackbar.showError({message, additionalData});
}
}

View File

@ -15,9 +15,6 @@ export default class Controller {
}
$onInit() {
if (!this.$state.params.id)
this.$state.go('login');
this.$http.get('UserPasswords/findOne')
.then(res => {
this.passRequirements = res.data;
@ -25,7 +22,7 @@ export default class Controller {
}
submit() {
const userId = this.$state.params.userId;
const userId = parseInt(this.$state.params.userId);
const oldPassword = this.oldPassword;
const newPassword = this.newPassword;
const repeatPassword = this.repeatPassword;
@ -36,18 +33,13 @@ export default class Controller {
if (newPassword != this.repeatPassword)
throw new UserError(`Passwords don't match`);
const headers = {
Authorization: this.$state.params.id
};
this.$http.patch('Accounts/change-password',
{
id: userId,
userId,
oldPassword,
newPassword,
code
},
{headers}
}
).then(() => {
this.vnApp.showSuccess(this.$translate.instant('Password updated!'));
this.$state.go('login');

View File

@ -36,7 +36,7 @@ export default class Controller {
const err = req.data?.error;
if (err?.code == 'passExpired')
this.$state.go('change-password', err.details.token);
this.$state.go('change-password', err.details);
this.loading = false;
this.password = '';

View File

@ -148,7 +148,13 @@ function $exceptionHandler(vnApp, $window, $state, $injector) {
if (messageT)
message = $translate.instant(messageT);
vnApp.showError(message);
const additonalData = {
frontPath: $state.current.name,
httpRequest: cause?.replace('Possibly unhandled rejection: ', ''),
backError: exception
};
vnApp.showError(message, additonalData);
};
}
ngModule.factory('$exceptionHandler', $exceptionHandler);

View File

@ -45,7 +45,7 @@ function config($stateProvider, $urlRouterProvider) {
})
.state('change-password', {
parent: 'outLayout',
url: '/change-password?id&userId&twoFactor',
url: '/change-password?userId&twoFactor',
description: 'Change password',
template: '<vn-change-password></vn-change-password>'
})

View File

@ -178,5 +178,12 @@
"The renew period has not been exceeded": "The renew period has not been exceeded",
"You can not use the same password": "You can not use the same password",
"Valid priorities": "Valid priorities: %d",
"Negative basis of tickets": "Negative basis of tickets: {{ticketsIds}}"
"Negative basis of tickets": "Negative basis of tickets: {{ticketsIds}}",
"This ticket cannot be left empty.": "This ticket cannot be left empty. %s",
"Social name should be uppercase": "Social name should be uppercase",
"Street should be uppercase": "Street should be uppercase",
"You don't have enough privileges.": "You don't have enough privileges.",
"This ticket is locked.": "This ticket is locked.",
"This ticket is not editable.": "This ticket is not editable.",
"The ticket doesn't exist.": "The ticket doesn't exist."
}

View File

@ -305,7 +305,15 @@
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
"Valid priorities": "Prioridades válidas: %d",
"Negative basis of tickets": "Base negativa para los tickets: {{ticketsIds}}",
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
"You cannot assign an alias that you are not assigned to": "No puede asignar un alias que no tenga asignado",
"This ticket cannot be left empty.": "Este ticket no se puede dejar vacío. %s",
"The company has not informed the supplier account for bank transfers": "La empresa no tiene informado la cuenta de proveedor para transferencias bancarias",
"You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado",
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado"
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado",
"You don't have enough privileges.": "No tienes suficientes permisos.",
"This ticket is locked.": "Este ticket está bloqueado.",
"This ticket is not editable.": "Este ticket no es editable.",
"The ticket doesn't exist.": "No existe el ticket.",
"Social name should be uppercase": "La razón social debe ir en mayúscula",
"Street should be uppercase": "La dirección fiscal debe ir en mayúscula"
}

View File

@ -1,21 +1,45 @@
const validateIban = require('../validateIban');
describe('IBAN validation', () => {
it('should return false for non-IBAN input', () => {
let isValid = validateIban('Pepinillos');
it('should return false for invalid Spanish IBAN format', () => {
let isValid = validateIban('ES00 9999 0000 9999 0000 9999', 'ES');
expect(isValid).toBeFalsy();
});
it('should return false for invalid spanish IBAN input', () => {
let isValid = validateIban('ES00 9999 0000 9999 0000 9999');
expect(isValid).toBeFalsy();
});
it('should return true for valid spanish IBAN', () => {
let isValid = validateIban('ES91 2100 0418 4502 0005 1332');
it('should return true for valid Spanish IBAN', () => {
let isValid = validateIban('ES91 2100 0418 4502 0005 1332', 'ES');
expect(isValid).toBeTruthy();
});
it('should return false for invalid Spanish IBAN with incorrect length', () => {
let isValid = validateIban('ES91210004184502000513', 'ES');
expect(isValid).toBeFalsy();
});
it('should return false for invalid Spanish IBAN with incorrect module97 result', () => {
let isValid = validateIban('ES9121000418450200051331', 'ES');
expect(isValid).toBeFalsy();
});
it('should return true for a non-Spanish countryCode', () => {
let isValid = validateIban('DE89370400440532013000', 'AT');
expect(isValid).toBeTruthy();
});
it('should return true for null IBAN', () => {
let isValid = validateIban(null, 'ES');
expect(isValid).toBeTruthy();
});
it('should return false for non-string IBAN', () => {
let isValid = validateIban(12345, 'ES');
expect(isValid).toBeFalsy();
});
});

View File

@ -1,6 +1,7 @@
module.exports = function(iban) {
module.exports = function(iban, countryCode) {
if (iban == null) return true;
if (typeof iban != 'string') return false;
if (countryCode?.toLowerCase() != 'es') return true;
iban = iban.toUpperCase();
iban = trim(iban);

View File

@ -1,12 +1,15 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('changePassword', {
Self.remoteMethod('changePassword', {
description: 'Changes the user password',
accessType: 'WRITE',
accessScopes: ['changePassword'],
accepts: [
{
arg: 'userId',
type: 'integer',
description: 'The user id',
required: true
}, {
arg: 'oldPassword',
type: 'string',
description: 'The old password',
@ -28,9 +31,7 @@ module.exports = Self => {
}
});
Self.changePassword = async function(ctx, oldPassword, newPassword, code, options) {
const userId = ctx.req.accessToken.userId;
Self.changePassword = async function(userId, oldPassword, newPassword, code, options) {
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);

View File

@ -1,7 +1,7 @@
const {models} = require('vn-loopback/server/server');
describe('account changePassword()', () => {
const ctx = {req: {accessToken: {userId: 70}}};
const userId = 70;
const unauthCtx = {
req: {
headers: {},
@ -20,7 +20,7 @@ describe('account changePassword()', () => {
try {
const options = {transaction: tx};
await models.Account.changePassword(ctx, 'wrongPassword', 'nightmare.9999', null, options);
await models.Account.changePassword(userId, 'wrongPassword', 'nightmare.9999', null, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
@ -37,8 +37,8 @@ describe('account changePassword()', () => {
try {
const options = {transaction: tx};
await models.Account.changePassword(ctx, 'nightmare', 'nightmare.9999', null, options);
await models.Account.changePassword(ctx, 'nightmare.9999', 'nightmare.9999', null, options);
await models.Account.changePassword(userId, 'nightmare', 'nightmare.9999', null, options);
await models.Account.changePassword(userId, 'nightmare.9999', 'nightmare.9999', null, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
@ -54,7 +54,7 @@ describe('account changePassword()', () => {
try {
const options = {transaction: tx};
await models.Account.changePassword(ctx, 'nightmare', 'nightmare.9999', null, options);
await models.Account.changePassword(userId, 'nightmare', 'nightmare.9999', null, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
@ -86,8 +86,8 @@ describe('account changePassword()', () => {
}
try {
const authCode = await models.AuthCode.findOne({where: {userFk: 70}}, options);
await models.Account.changePassword(ctx, 'nightmare', 'nightmare.9999', authCode.code, options);
const authCode = await models.AuthCode.findOne({where: {userFk: userId}}, options);
await models.Account.changePassword(userId, 'nightmare', 'nightmare.9999', authCode.code, options);
await tx.rollback();
} catch (e) {
await tx.rollback();

View File

@ -25,15 +25,13 @@
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
<vn-worker-autocomplete
disabled="false"
ng-model="$ctrl.claim.workerFk"
url="Workers/activeWithRole"
show-field="nickname"
search-function="{firstName: $search}"
where="{role: 'salesPerson'}"
ng-model="$ctrl.claim.workerFk"
departments="['VT']"
label="Attended by">
</vn-autocomplete>
</vn-worker-autocomplete>
<vn-autocomplete
ng-model="$ctrl.claim.claimStateFk"
data="claimStates"

View File

@ -36,7 +36,7 @@
data="claimDevelopments"
form="form">
</vn-watcher>
<vn-vertical class="vn-w-md">
<vn-vertical class="vn-w-lg">
<vn-card class="vn-pa-lg">
<vn-vertical>
<form name="form">
@ -66,16 +66,11 @@
show-field="description"
rule>
</vn-autocomplete>
<vn-autocomplete
<vn-worker-autocomplete
ng-model="claimDevelopment.workerFk"
url="Workers/activeWithInheritedRole"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
label="Worker"
rule>
</vn-autocomplete>
</vn-worker-autocomplete>
<vn-autocomplete
label="Redelivery"
ng-model="claimDevelopment.claimRedeliveryFk"
@ -118,4 +113,4 @@
ng-click="watcher.loadOriginalData()">
</vn-button> -->
</vn-button-bar>
</vn-vertical>
</vn-vertical>

View File

@ -22,26 +22,18 @@
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
<vn-worker-autocomplete
vn-one
ng-model="filter.salesPersonFk"
url="Workers/activeWithRole"
search-function="{firstName: $search}"
value-field="id"
where="{role: {inq: ['salesTeamBoss', 'salesPerson', 'officeBoss']}}"
departments="['VT']"
label="Salesperson">
<tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete
</vn-worker-autocomplete>
<vn-worker-autocomplete
vn-one
ng-model="filter.attenderFk"
url="Workers/activeWithRole"
search-function="{firstName: $search}"
value-field="id"
where="{role: {inq: ['salesTeamBoss', 'salesPerson']}}"
departments="['VT']"
label="Attended by">
<tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete>
</vn-worker-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one

View File

@ -7,8 +7,8 @@ describe('Client Create', () => {
email: 'Deadpool@marvel.com',
fi: '16195279J',
name: 'Wade',
socialName: 'Deadpool Marvel',
street: 'Wall Street',
socialName: 'DEADPOOL MARVEL',
street: 'WALL STREET',
city: 'New York',
businessTypeFk: 'florist',
provinceFk: 1

View File

@ -1,7 +1,7 @@
const models = require('vn-loopback/server/server').models;
describe('Client last active tickets', () => {
it('should receive an array of last active tickets of Bruce Wayne', async() => {
it('should receive an array of last active tickets of BRUCE WAYNE', async() => {
const tx = await models.Client.beginTransaction({});
try {

View File

@ -1,7 +1,7 @@
const models = require('vn-loopback/server/server').models;
describe('Client transactions', () => {
it('should call transactions() method to receive a list of Web Payments from Bruce Wayne', async() => {
it('should call transactions() method to receive a list of Web Payments from BRUCE WAYNE', async() => {
const tx = await models.Client.beginTransaction({});
try {

View File

@ -36,6 +36,20 @@ module.exports = Self => {
min: 3, max: 10
});
Self.validatesFormatOf('street', {
message: 'Street should be uppercase',
allowNull: false,
allowBlank: false,
with: /^[^a-z]*$/
});
Self.validatesFormatOf('socialName', {
message: 'Social name should be uppercase',
allowNull: false,
allowBlank: false,
with: /^[^a-z]*$/
});
Self.validateAsync('socialName', socialNameIsUnique, {
message: 'The company name must be unique'
});
@ -76,16 +90,17 @@ module.exports = Self => {
});
async function ibanNeedsValidation(err, done) {
const filter = {
fields: ['code'],
where: {id: this.countryFk}
};
const country = await Self.app.models.Country.findOne(filter);
const code = country ? country.code.toLowerCase() : null;
if (code != 'es')
if (!this.bankEntityFk)
return done();
if (!validateIban(this.iban))
const bankEntity = await Self.app.models.BankEntity.findById(this.bankEntityFk);
const filter = {
fields: ['code'],
where: {id: bankEntity.countryFk}
};
const country = await Self.app.models.Country.findOne(filter);
if (!validateIban(this.iban, country?.code))
err();
done();
}

View File

@ -14,13 +14,13 @@
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
vn-one
label="Comercial Name"
vn-one
label="Comercial Name"
ng-model="$ctrl.client.name"
rule
vn-focus>
</vn-textfield>
<vn-autocomplete
<vn-autocomplete
vn-one
ng-model="$ctrl.client.businessTypeFk"
url="BusinessTypes"
@ -37,8 +37,8 @@
rule>
</vn-textfield>
<vn-textfield
vn-one
label="Email"
vn-one
label="Email"
ng-model="$ctrl.client.email"
rule
info="You can save multiple emails">
@ -59,18 +59,15 @@
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
<vn-worker-autocomplete
vn-one
ng-model="$ctrl.client.salesPersonFk"
url="Workers/activeWithInheritedRole"
departments="['VT']"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'salesPerson'}"
label="Salesperson"
vn-acl="salesAssistant">
</vn-autocomplete>
<vn-autocomplete
</vn-worker-autocomplete>
<vn-autocomplete
vn-one
ng-model="$ctrl.client.contactChannelFk"
data="contactChannels"

View File

@ -17,12 +17,11 @@
<vn-autocomplete
vn-one
ng-model="filter.buyerId"
url="Workers/activeWithRole"
url="TicketRequests/getItemTypeWorker"
search-function="{firstName: $search}"
show-field="nickname"
value-field="id"
where="{role: {inq: ['logistic', 'buyer']}}"
label="Buyer">
<tpl-item>{{nickname}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>

View File

@ -15,18 +15,15 @@
rule
vn-focus>
</vn-textfield>
<vn-autocomplete
<vn-worker-autocomplete
label="Salesperson"
ng-model="$ctrl.client.salesPersonFk"
url="Workers/activeWithInheritedRole"
search-function="{firstName: $search}"
show-field="firstName"
where="{role: 'salesPerson'}">
<tpl-item>{{firstName}} {{lastName}}</tpl-item>
</vn-autocomplete>
departments="['VT']"
show-field="nickname">
</vn-worker-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
<vn-autocomplete
vn-id="businessTypeFk"
ng-model="$ctrl.client.businessTypeFk"
url="BusinessTypes"
@ -68,7 +65,7 @@
show-field="code"
rule>
<tpl-item>
{{code}} - {{town.name}} ({{town.province.name}},
{{code}} - {{town.name}} ({{town.province.name}},
{{town.province.country.country}})
</tpl-item>
<append>
@ -82,7 +79,7 @@
</append>
</vn-datalist>
<vn-datalist
vn-id="town"
vn-id="town"
label="City"
ng-model="$ctrl.client.city"
selection="$ctrl.town"
@ -90,7 +87,7 @@
fields="['id', 'name', 'provinceFk']"
value-field="name">
<tpl-item>
{{name}}, {{province.name}}
{{name}}, {{province.name}}
({{province.country.country}})
</tpl-item>
</vn-datalist>
@ -150,4 +147,4 @@
<vn-geo-postcode
vn-id="postcode"
on-response="$ctrl.onResponse($response)">
</vn-geo-postcode>
</vn-geo-postcode>

View File

@ -33,6 +33,7 @@
ng-model="$ctrl.client.socialName"
info="Only letters, numbers and spaces can be used"
required="true"
ng-keyup="$ctrl.client.socialName = $ctrl.client.socialName.toUpperCase()"
rule>
</vn-textfield>
<vn-textfield
@ -46,6 +47,7 @@
vn-two
label="Street"
ng-model="$ctrl.client.street"
ng-keyup="$ctrl.client.street = $ctrl.client.street.toUpperCase()"
rule>
</vn-textfield>
</vn-horizontal>

View File

@ -14,17 +14,12 @@
vn-one label="Name"
ng-model="filter.name">
</vn-textfield>
<vn-autocomplete
<vn-worker-autocomplete
vn-one
ng-model="filter.salesPersonFk"
url="Workers/activeWithInheritedRole"
search-function="{firstName: $search}"
show-field="firstName"
value-field="id"
where="{role: 'salesPerson'}"
departments="['VT']"
label="Salesperson">
<tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete>
</vn-worker-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield

View File

@ -8,7 +8,7 @@
auto-load="true">
</vn-crud-model>
<vn-data-viewer model="model">
<vn-card class="vn-w-md">
<vn-card class="vn-w-lg">
<vn-table model="model" auto-load="false">
<vn-thead>
<vn-tr>
@ -27,7 +27,7 @@
</span>
</vn-td>
<vn-td number expand>{{::clientSms.sms.destination}}</vn-td>
<vn-td>{{::clientSms.sms.message}}</vn-td>
<vn-td expand vn-tooltip="{{::clientSms.sms.message}}">{{::clientSms.sms.message}}</vn-td>
<vn-td>{{::clientSms.sms.status}}</vn-td>
<vn-td shrink-datetime>{{::clientSms.sms.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr>

View File

@ -39,7 +39,7 @@
label="Recovery email"
ng-model="$ctrl.account.email"
info="This email is used for user to regain access their account."
rule="VnUser.name">
rule="VnUser.email">
</vn-textfield>
</vn-horizontal>
</vn-card>

View File

@ -54,11 +54,10 @@
vn-id="salesPerson"
disabled="false"
ng-model="$ctrl.filter.salesPersonFk"
url="Workers/activeWithRole"
url="TicketRequests/getItemTypeWorker"
show-field="nickname"
search-function="{firstName: $search}"
value-field="id"
where="{role: {inq: ['logistic', 'buyer']}}"
label="Buyer"
on-change="$ctrl.addFilters()">
</vn-autocomplete>

View File

@ -0,0 +1,20 @@
name: invoice in
columns:
id: id
serialNumber: serial number
serial: serial
supplierFk: supplier
issued: issued
supplierRef: supplierRef
isBooked: is booked
currencyFk: currency
created: created
companyFk: company
docFk: document
booked: booked
operated: operated
bookEntried: book entried
isVatDeductible: is VAT deductible
withholdingSageFk: withholding
expenceFkDeductible: expence deductible
editorFk: editor

View File

@ -0,0 +1,20 @@
name: factura recibida
columns:
id: id
serialNumber: número de serie
serial: serie
supplierFk: proveedor
issued: fecha emisión
supplierRef: referéncia proveedor
isBooked: facturado
currencyFk: moneda
created: creado
companyFk: empresa
docFk: documento
booked: fecha contabilización
operated: fecha entrega
bookEntried: fecha asiento
isVatDeductible: impuesto deducible
withholdingSageFk: código de retención
expenceFkDeductible: gasto deducible
editorFk: editor

View File

@ -0,0 +1,9 @@
name: invoice in due day
columns:
id: id
invoiceInFk: invoice in
dueDated: due date
bankFk: bank
amount: amount
foreignValue : foreign amount
created: created

View File

@ -0,0 +1,9 @@
name: vencimientos factura recibida
columns:
id: id
invoiceInFk: factura
dueDated: fecha vto.
bankFk: banco
amount: importe
foreignValue : importe divisa
created: creado

View File

@ -0,0 +1,12 @@
name: invoice in tax
columns:
id: id
invoiceInFk: invoice in
taxCodeFk: tax
taxableBase: taxable base
expenceFk: expence
foreignValue: foreign amount
taxTypeSageFk: tax type
transactionTypeSageFk: transaction type
created: created
editorFk: editor

View File

@ -0,0 +1,12 @@
name: factura recibida impuesto
columns:
id: id
invoiceInFk: factura recibida
taxCodeFk: código IVA
taxableBase: base imponible
expenceFk: código gasto
foreignValue: importe divisa
taxTypeSageFk: código impuesto
transactionTypeSageFk: código transacción
created: creado
editorFk: editor

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