5660-changeRolDeliveryAssistant #1607
|
@ -5,6 +5,7 @@ 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/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
|
||||||
## [2334.01] - 2023-08-24
|
## [2334.01] - 2023-08-24
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -14,17 +15,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
||||||
## [2332.01] - 2023-08-09
|
## [2332.01] - 2023-08-10
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- (Trabajadores -> Gestión documental) Soporte para Docuware
|
- (Trabajadores -> Gestión documental) Soporte para Docuware
|
||||||
- (General -> Agencia) Soporte para Viaexpress
|
- (General -> Agencia) Soporte para Viaexpress
|
||||||
|
- (Tickets -> SMS) Nueva sección en Lilium
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- (General -> Tickets) Devuelve el motivo por el cual no es editable
|
- (General -> Tickets) Devuelve el motivo por el cual no es editable
|
||||||
- (Desplegables -> Trabajadores) Mejorados
|
- (Desplegables -> Trabajadores) Mejorados
|
||||||
|
- (General -> Clientes) Razón social y dirección en mayúsculas
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- (Clientes -> SMS) Al pasar el ratón por encima muestra el mensaje completo
|
||||||
|
|
||||||
|
|
||||||
## [2330.01] - 2023-07-27
|
## [2330.01] - 2023-07-27
|
||||||
|
|
|
@ -7,6 +7,11 @@ module.exports = Self => {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The user name or email',
|
description: 'The user name or email',
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'app',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The directory for mail'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
http: {
|
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 models = Self.app.models;
|
||||||
|
|
||||||
const usesEmail = user.indexOf('@') !== -1;
|
const usesEmail = user.indexOf('@') !== -1;
|
||||||
|
@ -29,7 +34,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Self.resetPassword({email: user, emailTemplate: 'recover-password'});
|
await Self.resetPassword({email: user, emailTemplate: 'recover-password', app});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === 'EMAIL_NOT_FOUND')
|
if (err.code === 'EMAIL_NOT_FOUND')
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -53,19 +53,13 @@ module.exports = Self => {
|
||||||
return Self.validateLogin(user, password);
|
return Self.validateLogin(user, password);
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.passExpired = async(vnUser, myOptions) => {
|
Self.passExpired = async vnUser => {
|
||||||
const today = Date.vnNew();
|
const today = Date.vnNew();
|
||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
if (vnUser.passExpired && vnUser.passExpired.getTime() <= today.getTime()) {
|
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');
|
const err = new UserError('Pass expired', 'passExpired');
|
||||||
changePasswordToken.twoFactor = vnUser.twoFactor ? true : false;
|
err.details = {userId: vnUser.id, twoFactor: vnUser.twoFactor ? true : false};
|
||||||
err.details = {token: changePasswordToken};
|
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -96,11 +96,21 @@ module.exports = function(Self) {
|
||||||
const headers = httpRequest.headers;
|
const headers = httpRequest.headers;
|
||||||
const origin = headers.origin;
|
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 user = await Self.app.models.VnUser.findById(info.user.id);
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
recipient: info.email,
|
recipient: info.email,
|
||||||
lang: user.lang,
|
lang: user.lang,
|
||||||
url: `${origin}/#!/reset-password?access_token=${info.accessToken.id}`
|
url: origin + '/#!' + recoverHash
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = Object.assign({}, info.options);
|
const options = Object.assign({}, info.options);
|
||||||
|
|
|
@ -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 ;
|
|
@ -0,0 +1,3 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||||
|
VALUES
|
||||||
|
('TicketSms', 'find', 'READ', 'ALLOW', 'ROLE', 'salesPerson');
|
|
@ -0,0 +1,6 @@
|
||||||
|
UPDATE `salix`.`ACL`
|
||||||
|
SET principalId='salesPerson'
|
||||||
|
WHERE
|
||||||
|
model='Ticket'
|
||||||
|
AND property='setDeleted'
|
||||||
|
AND accessType='WRITE';
|
|
@ -360,13 +360,13 @@ 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`)
|
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
|
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'),
|
(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'),
|
(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'),
|
(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'),
|
(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'),
|
(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'),
|
(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'),
|
(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'),
|
(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'),
|
(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'),
|
(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'),
|
||||||
|
|
|
@ -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() => {
|
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.name, 'Carol Danvers');
|
||||||
await page.write(selectors.createClientView.socialName, 'AVG tax');
|
await page.write(selectors.createClientView.socialName, 'AVG TAX');
|
||||||
await page.write(selectors.createClientView.street, 'Many places');
|
await page.write(selectors.createClientView.street, 'MANY PLACES');
|
||||||
await page.clearInput(selectors.createClientView.email);
|
await page.clearInput(selectors.createClientView.email);
|
||||||
await page.write(selectors.createClientView.email, 'incorrect email format');
|
await page.write(selectors.createClientView.email, 'incorrect email format');
|
||||||
await page.waitToClick(selectors.createClientView.createButton);
|
await page.waitToClick(selectors.createClientView.createButton);
|
||||||
|
|
|
@ -61,7 +61,7 @@ describe('Client Edit fiscalData path', () => {
|
||||||
await page.clearInput(selectors.clientFiscalData.fiscalId);
|
await page.clearInput(selectors.clientFiscalData.fiscalId);
|
||||||
await page.write(selectors.clientFiscalData.fiscalId, 'INVALID!');
|
await page.write(selectors.clientFiscalData.fiscalId, 'INVALID!');
|
||||||
await page.clearInput(selectors.clientFiscalData.address);
|
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.country, 'España');
|
||||||
await page.autocompleteSearch(selectors.clientFiscalData.province, 'Province one');
|
await page.autocompleteSearch(selectors.clientFiscalData.province, 'Province one');
|
||||||
await page.clearInput(selectors.clientFiscalData.city);
|
await page.clearInput(selectors.clientFiscalData.city);
|
||||||
|
@ -190,7 +190,7 @@ describe('Client Edit fiscalData path', () => {
|
||||||
const verifiedData = await page.checkboxState(selectors.clientFiscalData.verifiedDataCheckbox);
|
const verifiedData = await page.checkboxState(selectors.clientFiscalData.verifiedDataCheckbox);
|
||||||
|
|
||||||
expect(fiscalId).toEqual('94980061C');
|
expect(fiscalId).toEqual('94980061C');
|
||||||
expect(address).toEqual('Somewhere edited');
|
expect(address).toEqual('SOMEWHERE EDITED');
|
||||||
expect(postcode).toContain('46000');
|
expect(postcode).toContain('46000');
|
||||||
expect(sageTax).toEqual('Operaciones no sujetas');
|
expect(sageTax).toEqual('Operaciones no sujetas');
|
||||||
expect(sageTransaction).toEqual('Regularización de inversiones');
|
expect(sageTransaction).toEqual('Regularización de inversiones');
|
||||||
|
|
|
@ -28,7 +28,7 @@ describe('Client lock verified data path', () => {
|
||||||
it('should edit the social name', async() => {
|
it('should edit the social name', async() => {
|
||||||
await page.waitForSelector(selectors.clientFiscalData.socialName);
|
await page.waitForSelector(selectors.clientFiscalData.socialName);
|
||||||
await page.clearInput(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);
|
await page.waitToClick(selectors.clientFiscalData.saveButton);
|
||||||
const message = await page.waitForSnackbar();
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ describe('Client lock verified data path', () => {
|
||||||
await page.reloadSection('client.card.fiscalData');
|
await page.reloadSection('client.card.fiscalData');
|
||||||
const result = await page.waitToGetProperty(selectors.clientFiscalData.socialName, 'value');
|
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');
|
await page.reloadSection('client.card.fiscalData');
|
||||||
const result = await page.waitToGetProperty(selectors.clientFiscalData.socialName, 'value');
|
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');
|
await page.reloadSection('client.card.fiscalData');
|
||||||
const result = await page.waitToGetProperty(selectors.clientFiscalData.socialName, 'value');
|
const result = await page.waitToGetProperty(selectors.clientFiscalData.socialName, 'value');
|
||||||
|
|
||||||
expect(result).toEqual('new social name edition');
|
expect(result).toEqual('NEW SOCIAL NAME EDITION');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ describe('Client summary path', () => {
|
||||||
it('should display fiscal address details', async() => {
|
it('should display fiscal address details', async() => {
|
||||||
const result = await page.waitToGetProperty(selectors.clientSummary.street, 'innerText');
|
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() => {
|
it('should display some fiscal data', async() => {
|
||||||
|
|
|
@ -23,7 +23,7 @@ describe('Worker create path', () => {
|
||||||
await page.write(selectors.workerCreate.fi, '78457139E');
|
await page.write(selectors.workerCreate.fi, '78457139E');
|
||||||
await page.write(selectors.workerCreate.phone, '12356789');
|
await page.write(selectors.workerCreate.phone, '12356789');
|
||||||
await page.write(selectors.workerCreate.postcode, '46680');
|
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.email, 'doctorDoom@marvel.com');
|
||||||
await page.write(selectors.workerCreate.iban, 'ES9121000418450200051332');
|
await page.write(selectors.workerCreate.iban, 'ES9121000418450200051332');
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe('Ticket create path', () => {
|
||||||
beforeAll(async() => {
|
beforeAll(async() => {
|
||||||
browser = await getBrowser();
|
browser = await getBrowser();
|
||||||
page = browser.page;
|
page = browser.page;
|
||||||
await page.loginAndModule('employee', 'ticket');
|
await page.loginAndModule('salesPerson', 'ticket');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async() => {
|
afterAll(async() => {
|
||||||
|
|
|
@ -15,9 +15,6 @@ export default class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
if (!this.$state.params.id)
|
|
||||||
this.$state.go('login');
|
|
||||||
|
|
||||||
this.$http.get('UserPasswords/findOne')
|
this.$http.get('UserPasswords/findOne')
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.passRequirements = res.data;
|
this.passRequirements = res.data;
|
||||||
|
@ -25,7 +22,7 @@ export default class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
const userId = this.$state.params.userId;
|
const userId = parseInt(this.$state.params.userId);
|
||||||
const oldPassword = this.oldPassword;
|
const oldPassword = this.oldPassword;
|
||||||
const newPassword = this.newPassword;
|
const newPassword = this.newPassword;
|
||||||
const repeatPassword = this.repeatPassword;
|
const repeatPassword = this.repeatPassword;
|
||||||
|
@ -36,18 +33,13 @@ export default class Controller {
|
||||||
if (newPassword != this.repeatPassword)
|
if (newPassword != this.repeatPassword)
|
||||||
throw new UserError(`Passwords don't match`);
|
throw new UserError(`Passwords don't match`);
|
||||||
|
|
||||||
const headers = {
|
|
||||||
Authorization: this.$state.params.id
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$http.patch('Accounts/change-password',
|
this.$http.patch('Accounts/change-password',
|
||||||
{
|
{
|
||||||
id: userId,
|
userId,
|
||||||
oldPassword,
|
oldPassword,
|
||||||
newPassword,
|
newPassword,
|
||||||
code
|
code
|
||||||
},
|
}
|
||||||
{headers}
|
|
||||||
).then(() => {
|
).then(() => {
|
||||||
this.vnApp.showSuccess(this.$translate.instant('Password updated!'));
|
this.vnApp.showSuccess(this.$translate.instant('Password updated!'));
|
||||||
this.$state.go('login');
|
this.$state.go('login');
|
||||||
|
|
|
@ -36,7 +36,7 @@ export default class Controller {
|
||||||
|
|
||||||
const err = req.data?.error;
|
const err = req.data?.error;
|
||||||
if (err?.code == 'passExpired')
|
if (err?.code == 'passExpired')
|
||||||
this.$state.go('change-password', err.details.token);
|
this.$state.go('change-password', err.details);
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.password = '';
|
this.password = '';
|
||||||
|
|
|
@ -45,7 +45,7 @@ function config($stateProvider, $urlRouterProvider) {
|
||||||
})
|
})
|
||||||
.state('change-password', {
|
.state('change-password', {
|
||||||
parent: 'outLayout',
|
parent: 'outLayout',
|
||||||
url: '/change-password?id&userId&twoFactor',
|
url: '/change-password?userId&twoFactor',
|
||||||
description: 'Change password',
|
description: 'Change password',
|
||||||
template: '<vn-change-password></vn-change-password>'
|
template: '<vn-change-password></vn-change-password>'
|
||||||
})
|
})
|
||||||
|
|
|
@ -179,6 +179,9 @@
|
||||||
"You can not use the same password": "You can not use the same password",
|
"You can not use the same password": "You can not use the same password",
|
||||||
"Valid priorities": "Valid priorities: %d",
|
"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.",
|
"You don't have enough privileges.": "You don't have enough privileges.",
|
||||||
"This ticket is locked.": "This ticket is locked.",
|
"This ticket is locked.": "This ticket is locked.",
|
||||||
"This ticket is not editable.": "This ticket is not editable.",
|
"This ticket is not editable.": "This ticket is not editable.",
|
||||||
|
|
|
@ -305,11 +305,15 @@
|
||||||
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
|
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado",
|
||||||
"Valid priorities": "Prioridades válidas: %d",
|
"Valid priorities": "Prioridades válidas: %d",
|
||||||
"Negative basis of tickets": "Base negativa para los tickets: {{ticketsIds}}",
|
"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",
|
"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.",
|
"You don't have enough privileges.": "No tienes suficientes permisos.",
|
||||||
"This ticket is locked.": "Este ticket está bloqueado.",
|
"This ticket is locked.": "Este ticket está bloqueado.",
|
||||||
"This ticket is not editable.": "Este ticket no es editable.",
|
"This ticket is not editable.": "Este ticket no es editable.",
|
||||||
"The ticket doesn't exist.": "No existe el ticket."
|
"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"
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('changePassword', {
|
Self.remoteMethod('changePassword', {
|
||||||
description: 'Changes the user password',
|
description: 'Changes the user password',
|
||||||
accessType: 'WRITE',
|
|
||||||
accessScopes: ['changePassword'],
|
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
|
arg: 'userId',
|
||||||
|
type: 'integer',
|
||||||
|
description: 'The user id',
|
||||||
|
required: true
|
||||||
|
}, {
|
||||||
arg: 'oldPassword',
|
arg: 'oldPassword',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The old password',
|
description: 'The old password',
|
||||||
|
@ -28,9 +31,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.changePassword = async function(ctx, oldPassword, newPassword, code, options) {
|
Self.changePassword = async function(userId, oldPassword, newPassword, code, options) {
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
|
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const {models} = require('vn-loopback/server/server');
|
const {models} = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('account changePassword()', () => {
|
describe('account changePassword()', () => {
|
||||||
const ctx = {req: {accessToken: {userId: 70}}};
|
const userId = 70;
|
||||||
const unauthCtx = {
|
const unauthCtx = {
|
||||||
req: {
|
req: {
|
||||||
headers: {},
|
headers: {},
|
||||||
|
@ -20,7 +20,7 @@ describe('account changePassword()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
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();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
@ -37,8 +37,8 @@ describe('account changePassword()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
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 models.Account.changePassword(ctx, 'nightmare.9999', 'nightmare.9999', null, options);
|
await models.Account.changePassword(userId, 'nightmare.9999', 'nightmare.9999', null, options);
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
@ -54,7 +54,7 @@ describe('account changePassword()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
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();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
@ -86,8 +86,8 @@ describe('account changePassword()', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const authCode = await models.AuthCode.findOne({where: {userFk: 70}}, options);
|
const authCode = await models.AuthCode.findOne({where: {userFk: userId}}, options);
|
||||||
await models.Account.changePassword(ctx, 'nightmare', 'nightmare.9999', authCode.code, options);
|
await models.Account.changePassword(userId, 'nightmare', 'nightmare.9999', authCode.code, options);
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
|
|
@ -7,8 +7,8 @@ describe('Client Create', () => {
|
||||||
email: 'Deadpool@marvel.com',
|
email: 'Deadpool@marvel.com',
|
||||||
fi: '16195279J',
|
fi: '16195279J',
|
||||||
name: 'Wade',
|
name: 'Wade',
|
||||||
socialName: 'Deadpool Marvel',
|
socialName: 'DEADPOOL MARVEL',
|
||||||
street: 'Wall Street',
|
street: 'WALL STREET',
|
||||||
city: 'New York',
|
city: 'New York',
|
||||||
businessTypeFk: 'florist',
|
businessTypeFk: 'florist',
|
||||||
provinceFk: 1
|
provinceFk: 1
|
||||||
|
|
|
@ -36,6 +36,20 @@ module.exports = Self => {
|
||||||
min: 3, max: 10
|
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, {
|
Self.validateAsync('socialName', socialNameIsUnique, {
|
||||||
message: 'The company name must be unique'
|
message: 'The company name must be unique'
|
||||||
});
|
});
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
ng-model="$ctrl.client.socialName"
|
ng-model="$ctrl.client.socialName"
|
||||||
info="Only letters, numbers and spaces can be used"
|
info="Only letters, numbers and spaces can be used"
|
||||||
required="true"
|
required="true"
|
||||||
|
ng-keyup="$ctrl.client.socialName = $ctrl.client.socialName.toUpperCase()"
|
||||||
rule>
|
rule>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
|
@ -46,6 +47,7 @@
|
||||||
vn-two
|
vn-two
|
||||||
label="Street"
|
label="Street"
|
||||||
ng-model="$ctrl.client.street"
|
ng-model="$ctrl.client.street"
|
||||||
|
ng-keyup="$ctrl.client.street = $ctrl.client.street.toUpperCase()"
|
||||||
rule>
|
rule>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
auto-load="true">
|
auto-load="true">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-data-viewer model="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-table model="model" auto-load="false">
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number expand>{{::clientSms.sms.destination}}</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>{{::clientSms.sms.status}}</vn-td>
|
||||||
<vn-td shrink-datetime>{{::clientSms.sms.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
|
<vn-td shrink-datetime>{{::clientSms.sms.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-auto>
|
<vn-auto>
|
||||||
<section
|
<section
|
||||||
class="inline-tag ellipsize"
|
class="inline-tag ellipsize"
|
||||||
ng-class="::{empty: !$ctrl.item.value5}"
|
ng-class="::{empty: !$ctrl.item.tag5}"
|
||||||
title="{{::$ctrl.item.tag5}}: {{::$ctrl.item.value5}}">
|
title="{{::$ctrl.item.tag5}}: {{::$ctrl.item.value5}}">
|
||||||
{{::$ctrl.item.value5}}
|
{{::$ctrl.item.value5}}
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
class="inline-tag ellipsize"
|
class="inline-tag ellipsize"
|
||||||
ng-class="::{empty: !$ctrl.item.value6}"
|
ng-class="::{empty: !$ctrl.item.tag6}"
|
||||||
title="{{::$ctrl.item.tag6}}: {{::$ctrl.item.value6}}">
|
title="{{::$ctrl.item.tag6}}: {{::$ctrl.item.value6}}">
|
||||||
{{::$ctrl.item.value6}}
|
{{::$ctrl.item.value6}}
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
class="inline-tag ellipsize"
|
class="inline-tag ellipsize"
|
||||||
ng-class="::{empty: !$ctrl.item.value7}"
|
ng-class="::{empty: !$ctrl.item.tag7}"
|
||||||
title="{{::$ctrl.item.tag7}}: {{::$ctrl.item.value7}}">
|
title="{{::$ctrl.item.tag7}}: {{::$ctrl.item.value7}}">
|
||||||
{{::$ctrl.item.value7}}
|
{{::$ctrl.item.value7}}
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
class="inline-tag ellipsize"
|
class="inline-tag ellipsize"
|
||||||
ng-class="::{empty: !$ctrl.item.value8}"
|
ng-class="::{empty: !$ctrl.item.tag8}"
|
||||||
title="{{::$ctrl.item.tag8}}: {{::$ctrl.item.value8}}">
|
title="{{::$ctrl.item.tag8}}: {{::$ctrl.item.value8}}">
|
||||||
{{::$ctrl.item.value8}}
|
{{::$ctrl.item.value8}}
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
class="inline-tag ellipsize"
|
class="inline-tag ellipsize"
|
||||||
ng-class="::{empty: !$ctrl.item.value9}"
|
ng-class="::{empty: !$ctrl.item.tag9}"
|
||||||
title="{{::$ctrl.item.tag9}}: {{::$ctrl.item.value9}}">
|
title="{{::$ctrl.item.tag9}}: {{::$ctrl.item.value9}}">
|
||||||
{{::$ctrl.item.value9}}
|
{{::$ctrl.item.value9}}
|
||||||
</section>
|
</section>
|
||||||
<section
|
<section
|
||||||
class="inline-tag ellipsize"
|
class="inline-tag ellipsize"
|
||||||
ng-class="::{empty: !$ctrl.item.value10}"
|
ng-class="::{empty: !$ctrl.item.tag10}"
|
||||||
title="{{::$ctrl.item.tag10}}: {{::$ctrl.item.value10}}">
|
title="{{::$ctrl.item.tag10}}: {{::$ctrl.item.value10}}">
|
||||||
{{::$ctrl.item.value10}}
|
{{::$ctrl.item.value10}}
|
||||||
</section>
|
</section>
|
||||||
</vn-auto>
|
</vn-auto>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
vn-fetched-tags {
|
vn-fetched-tags {
|
||||||
& > vn-horizontal {
|
& > vn-horizontal {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
max-width: 210px;
|
||||||
& > vn-auto {
|
& > vn-auto {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
@ -43,19 +43,19 @@ vn-fetched-tags {
|
||||||
& > .inline-tag {
|
& > .inline-tag {
|
||||||
color: $color-font-secondary;
|
color: $color-font-secondary;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: .75rem;
|
font-size: .8rem;
|
||||||
height: 12px;
|
height: 13px;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
width: 64px;
|
width: 64px;
|
||||||
min-width: 64px;
|
min-width: 64px;
|
||||||
max-width: 64px;
|
max-width: 64px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border: 1px solid $color-spacer;
|
border: 1px solid $color-font-secondary;
|
||||||
|
|
||||||
&.empty {
|
&.empty {
|
||||||
border: 1px solid $color-spacer-light;
|
border: 1px solid darken($color-font-secondary, 30%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('cmr', {
|
||||||
|
description: 'Returns the cmr',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The cmr id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
arg: 'body',
|
||||||
|
type: 'file',
|
||||||
|
root: true
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Type',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Disposition',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: '/:id/cmr',
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.cmr = (ctx, id) => Self.printReport(ctx, id, 'cmr');
|
||||||
|
};
|
|
@ -0,0 +1,133 @@
|
||||||
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
const buildFilter = require('vn-loopback/util/filter').buildFilter;
|
||||||
|
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('getExternalCmrs', {
|
||||||
|
description: 'Returns an array of external cmrs',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'cmrFk',
|
||||||
|
type: 'integer',
|
||||||
|
description: 'Searchs the route by id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'ticketFk',
|
||||||
|
type: 'integer',
|
||||||
|
description: 'The worker id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'country',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The agencyMode id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'clientFk',
|
||||||
|
type: 'integer',
|
||||||
|
description: 'The vehicle id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'hasCmrDms',
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'The vehicle id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'shipped',
|
||||||
|
type: 'date',
|
||||||
|
description: 'The to date filter',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/getExternalCmrs`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.getExternalCmrs = async(
|
||||||
|
filter,
|
||||||
|
cmrFk,
|
||||||
|
ticketFk,
|
||||||
|
country,
|
||||||
|
clientFk,
|
||||||
|
hasCmrDms,
|
||||||
|
shipped,
|
||||||
|
options
|
||||||
|
) => {
|
||||||
|
const params = {
|
||||||
|
cmrFk,
|
||||||
|
ticketFk,
|
||||||
|
country,
|
||||||
|
clientFk,
|
||||||
|
hasCmrDms,
|
||||||
|
shipped,
|
||||||
|
};
|
||||||
|
const conn = Self.dataSource.connector;
|
||||||
|
|
||||||
|
let where = buildFilter(params, (param, value) => {return {[param]: value}});
|
||||||
|
filter = mergeFilters(filter, {where});
|
||||||
|
|
||||||
|
if (!filter.where) {
|
||||||
|
const yesterday = new Date();
|
||||||
|
yesterday.setDate(yesterday.getDate() - 1);
|
||||||
|
filter.where = {'shipped': yesterday.toISOString().split('T')[0]}
|
||||||
|
}
|
||||||
|
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
let stmts = [];
|
||||||
|
const stmt = new ParameterizedSQL(`
|
||||||
|
SELECT *
|
||||||
|
FROM (
|
||||||
|
SELECT t.cmrFk,
|
||||||
|
t.id ticketFk,
|
||||||
|
co.country,
|
||||||
|
t.clientFk,
|
||||||
|
sub.id hasCmrDms,
|
||||||
|
DATE(t.shipped) shipped
|
||||||
|
FROM ticket t
|
||||||
|
JOIN ticketState ts ON ts.ticketFk = t.id
|
||||||
|
JOIN state s ON s.id = ts.stateFk
|
||||||
|
JOIN alertLevel al ON al.id = s.alertLevel
|
||||||
|
JOIN client c ON c.id = t.clientFk
|
||||||
|
JOIN address a ON a.id = t.addressFk
|
||||||
|
JOIN province p ON p.id = a.provinceFk
|
||||||
|
JOIN country co ON co.id = p.countryFk
|
||||||
|
JOIN agencyMode am ON am.id = t.agencyModeFk
|
||||||
|
JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
|
||||||
|
JOIN warehouse w ON w.id = t.warehouseFk
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT td.ticketFk, d.id
|
||||||
|
FROM ticketDms td
|
||||||
|
JOIN dms d ON d.id = td.dmsFk
|
||||||
|
JOIN dmsType dt ON dt.id = d.dmsTypeFk
|
||||||
|
WHERE dt.name = 'cmr'
|
||||||
|
) sub ON sub.ticketFk = t.id
|
||||||
|
WHERE co.code <> 'ES'
|
||||||
|
AND am.name <> 'ABONO'
|
||||||
|
AND w.code = 'ALG'
|
||||||
|
AND dm.code = 'DELIVERY'
|
||||||
|
AND t.cmrFk
|
||||||
|
) sub
|
||||||
|
`);
|
||||||
|
|
||||||
|
stmt.merge(conn.makeSuffix(filter));
|
||||||
|
const itemsIndex = stmts.push(stmt) - 1;
|
||||||
|
|
||||||
|
const sql = ParameterizedSQL.join(stmts, ';');
|
||||||
|
const result = await conn.executeStmt(sql);
|
||||||
|
return itemsIndex === 0 ? result : result[itemsIndex];
|
||||||
|
};
|
||||||
|
};
|
|
@ -14,6 +14,8 @@ module.exports = Self => {
|
||||||
require('../methods/route/driverRouteEmail')(Self);
|
require('../methods/route/driverRouteEmail')(Self);
|
||||||
require('../methods/route/sendSms')(Self);
|
require('../methods/route/sendSms')(Self);
|
||||||
require('../methods/route/downloadZip')(Self);
|
require('../methods/route/downloadZip')(Self);
|
||||||
|
require('../methods/route/cmr')(Self);
|
||||||
|
require('../methods/route/getExternalCmrs')(Self);
|
||||||
|
|
||||||
Self.validate('kmStart', validateDistance, {
|
Self.validate('kmStart', validateDistance, {
|
||||||
message: 'Distance must be lesser than 1000'
|
message: 'Distance must be lesser than 1000'
|
||||||
|
@ -28,5 +30,5 @@ module.exports = Self => {
|
||||||
const routeMaxKm = 1000;
|
const routeMaxKm = 1000;
|
||||||
if (routeTotalKm > routeMaxKm || this.kmStart > this.kmEnd)
|
if (routeTotalKm > routeMaxKm || this.kmStart > this.kmEnd)
|
||||||
err();
|
err();
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -46,8 +46,6 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteRoadmaps() {
|
deleteRoadmaps() {
|
||||||
console.log(this.checked);
|
|
||||||
|
|
||||||
for (const roadmap of this.checked) {
|
for (const roadmap of this.checked) {
|
||||||
this.$http.delete(`Roadmaps/${roadmap.id}`)
|
this.$http.delete(`Roadmaps/${roadmap.id}`)
|
||||||
.then(() => this.$.model.refresh())
|
.then(() => this.$.model.refresh())
|
||||||
|
|
|
@ -5,177 +5,177 @@ const config = require('vn-print/core/config');
|
||||||
const storage = require('vn-print/core/storage');
|
const storage = require('vn-print/core/storage');
|
||||||
|
|
||||||
module.exports = async function(ctx, Self, tickets, reqArgs = {}) {
|
module.exports = async function(ctx, Self, tickets, reqArgs = {}) {
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
if (tickets.length == 0) return;
|
if (tickets.length == 0) return;
|
||||||
|
|
||||||
const failedtickets = [];
|
const failedtickets = [];
|
||||||
for (const ticket of tickets) {
|
for (const ticket of tickets) {
|
||||||
try {
|
try {
|
||||||
await Self.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id], {userId});
|
await Self.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id], {userId});
|
||||||
|
|
||||||
const [invoiceOut] = await Self.rawSql(`
|
const [invoiceOut] = await Self.rawSql(`
|
||||||
SELECT io.id, io.ref, io.serial, cny.code companyCode, io.issued
|
SELECT io.id, io.ref, io.serial, cny.code companyCode, io.issued
|
||||||
FROM ticket t
|
FROM ticket t
|
||||||
JOIN invoiceOut io ON io.ref = t.refFk
|
JOIN invoiceOut io ON io.ref = t.refFk
|
||||||
JOIN company cny ON cny.id = io.companyFk
|
JOIN company cny ON cny.id = io.companyFk
|
||||||
WHERE t.id = ?
|
WHERE t.id = ?
|
||||||
`, [ticket.id]);
|
`, [ticket.id]);
|
||||||
|
|
||||||
const mailOptions = {
|
const mailOptions = {
|
||||||
overrideAttachments: true,
|
overrideAttachments: true,
|
||||||
attachments: []
|
attachments: []
|
||||||
};
|
};
|
||||||
|
|
||||||
const isToBeMailed = ticket.recipient && ticket.salesPersonFk && ticket.isToBeMailed;
|
const isToBeMailed = ticket.recipient && ticket.salesPersonFk && ticket.isToBeMailed;
|
||||||
|
|
||||||
if (invoiceOut) {
|
if (invoiceOut) {
|
||||||
const args = {
|
const args = {
|
||||||
reference: invoiceOut.ref,
|
reference: invoiceOut.ref,
|
||||||
recipientId: ticket.clientFk,
|
recipientId: ticket.clientFk,
|
||||||
recipient: ticket.recipient,
|
recipient: ticket.recipient,
|
||||||
replyTo: ticket.salesPersonEmail
|
replyTo: ticket.salesPersonEmail
|
||||||
};
|
};
|
||||||
|
|
||||||
const invoiceReport = new Report('invoice', args);
|
const invoiceReport = new Report('invoice', args);
|
||||||
const stream = await invoiceReport.toPdfStream();
|
const stream = await invoiceReport.toPdfStream();
|
||||||
|
|
||||||
const issued = invoiceOut.issued;
|
const issued = invoiceOut.issued;
|
||||||
const year = issued.getFullYear().toString();
|
const year = issued.getFullYear().toString();
|
||||||
const month = (issued.getMonth() + 1).toString();
|
const month = (issued.getMonth() + 1).toString();
|
||||||
const day = issued.getDate().toString();
|
const day = issued.getDate().toString();
|
||||||
|
|
||||||
const fileName = `${year}${invoiceOut.ref}.pdf`;
|
const fileName = `${year}${invoiceOut.ref}.pdf`;
|
||||||
|
|
||||||
// Store invoice
|
// Store invoice
|
||||||
await storage.write(stream, {
|
await storage.write(stream, {
|
||||||
type: 'invoice',
|
type: 'invoice',
|
||||||
path: `${year}/${month}/${day}`,
|
path: `${year}/${month}/${day}`,
|
||||||
fileName: fileName
|
fileName: fileName
|
||||||
});
|
});
|
||||||
|
|
||||||
await Self.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id], {userId});
|
await Self.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id], {userId});
|
||||||
|
|
||||||
if (isToBeMailed) {
|
if (isToBeMailed) {
|
||||||
const invoiceAttachment = {
|
const invoiceAttachment = {
|
||||||
filename: fileName,
|
filename: fileName,
|
||||||
content: stream
|
content: stream
|
||||||
};
|
};
|
||||||
|
|
||||||
if (invoiceOut.serial == 'E' && invoiceOut.companyCode == 'VNL') {
|
if (invoiceOut.serial == 'E' && invoiceOut.companyCode == 'VNL') {
|
||||||
const exportation = new Report('exportation', args);
|
const exportation = new Report('exportation', args);
|
||||||
const stream = await exportation.toPdfStream();
|
const stream = await exportation.toPdfStream();
|
||||||
const fileName = `CITES-${invoiceOut.ref}.pdf`;
|
const fileName = `CITES-${invoiceOut.ref}.pdf`;
|
||||||
|
|
||||||
mailOptions.attachments.push({
|
mailOptions.attachments.push({
|
||||||
filename: fileName,
|
filename: fileName,
|
||||||
content: stream
|
content: stream
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mailOptions.attachments.push(invoiceAttachment);
|
mailOptions.attachments.push(invoiceAttachment);
|
||||||
|
|
||||||
const email = new Email('invoice', args);
|
const email = new Email('invoice', args);
|
||||||
await email.send(mailOptions);
|
await email.send(mailOptions);
|
||||||
}
|
}
|
||||||
} else if (isToBeMailed) {
|
} else if (isToBeMailed) {
|
||||||
const args = {
|
const args = {
|
||||||
id: ticket.id,
|
id: ticket.id,
|
||||||
recipientId: ticket.clientFk,
|
recipientId: ticket.clientFk,
|
||||||
recipient: ticket.recipient,
|
recipient: ticket.recipient,
|
||||||
replyTo: ticket.salesPersonEmail
|
replyTo: ticket.salesPersonEmail
|
||||||
};
|
};
|
||||||
|
|
||||||
const email = new Email('delivery-note-link', args);
|
const email = new Email('delivery-note-link', args);
|
||||||
await email.send();
|
await email.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Incoterms authorization
|
// Incoterms authorization
|
||||||
const [{firstOrder}] = await Self.rawSql(`
|
const [{firstOrder}] = await Self.rawSql(`
|
||||||
SELECT COUNT(*) as firstOrder
|
SELECT COUNT(*) as firstOrder
|
||||||
FROM ticket t
|
FROM ticket t
|
||||||
JOIN client c ON c.id = t.clientFk
|
JOIN client c ON c.id = t.clientFk
|
||||||
WHERE t.clientFk = ?
|
WHERE t.clientFk = ?
|
||||||
AND NOT t.isDeleted
|
AND NOT t.isDeleted
|
||||||
AND c.isVies
|
AND c.isVies
|
||||||
`, [ticket.clientFk]);
|
`, [ticket.clientFk]);
|
||||||
|
|
||||||
if (firstOrder == 1) {
|
if (firstOrder == 1) {
|
||||||
const args = {
|
const args = {
|
||||||
id: ticket.clientFk,
|
id: ticket.clientFk,
|
||||||
companyId: ticket.companyFk,
|
companyId: ticket.companyFk,
|
||||||
recipientId: ticket.clientFk,
|
recipientId: ticket.clientFk,
|
||||||
recipient: ticket.recipient,
|
recipient: ticket.recipient,
|
||||||
replyTo: ticket.salesPersonEmail
|
replyTo: ticket.salesPersonEmail
|
||||||
};
|
};
|
||||||
|
|
||||||
const email = new Email('incoterms-authorization', args);
|
const email = new Email('incoterms-authorization', args);
|
||||||
await email.send();
|
await email.send();
|
||||||
|
|
||||||
const [sample] = await Self.rawSql(
|
const [sample] = await Self.rawSql(
|
||||||
`SELECT id
|
`SELECT id
|
||||||
FROM sample
|
FROM sample
|
||||||
WHERE code = 'incoterms-authorization'
|
WHERE code = 'incoterms-authorization'
|
||||||
`);
|
`);
|
||||||
|
|
||||||
await Self.rawSql(`
|
await Self.rawSql(`
|
||||||
INSERT INTO clientSample (clientFk, typeFk, companyFk) VALUES(?, ?, ?)
|
INSERT INTO clientSample (clientFk, typeFk, companyFk) VALUES(?, ?, ?)
|
||||||
`, [ticket.clientFk, sample.id, ticket.companyFk], {userId});
|
`, [ticket.clientFk, sample.id, ticket.companyFk], {userId});
|
||||||
}
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Domain not found
|
// Domain not found
|
||||||
if (error.responseCode == 450)
|
if (error.responseCode == 450)
|
||||||
return invalidEmail(ticket);
|
return invalidEmail(ticket);
|
||||||
|
|
||||||
// Save tickets on a list of failed ids
|
// Save tickets on a list of failed ids
|
||||||
failedtickets.push({
|
failedtickets.push({
|
||||||
id: ticket.id,
|
id: ticket.id,
|
||||||
stacktrace: error
|
stacktrace: error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send email with failed tickets
|
// Send email with failed tickets
|
||||||
if (failedtickets.length > 0) {
|
if (failedtickets.length > 0) {
|
||||||
let body = 'This following tickets have failed:<br/><br/>';
|
let body = 'This following tickets have failed:<br/><br/>';
|
||||||
|
|
||||||
for (const ticket of failedtickets) {
|
for (const ticket of failedtickets) {
|
||||||
body += `Ticket: <strong>${ticket.id}</strong>
|
body += `Ticket: <strong>${ticket.id}</strong>
|
||||||
<br/> <strong>${ticket.stacktrace}</strong><br/><br/>`;
|
<br/> <strong>${ticket.stacktrace}</strong><br/><br/>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
smtp.send({
|
smtp.send({
|
||||||
to: config.app.reportEmail,
|
to: config.app.reportEmail,
|
||||||
subject: '[API] Nightly ticket closure report',
|
subject: '[API] Nightly ticket closure report',
|
||||||
html: body
|
html: body
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function invalidEmail(ticket) {
|
async function invalidEmail(ticket) {
|
||||||
await Self.rawSql(`UPDATE client SET email = NULL WHERE id = ?`, [
|
await Self.rawSql(`UPDATE client SET email = NULL WHERE id = ?`, [
|
||||||
ticket.clientFk
|
ticket.clientFk
|
||||||
], {userId});
|
], {userId});
|
||||||
|
|
||||||
const oldInstance = `{"email": "${ticket.recipient}"}`;
|
const oldInstance = `{"email": "${ticket.recipient}"}`;
|
||||||
const newInstance = `{"email": ""}`;
|
const newInstance = `{"email": ""}`;
|
||||||
await Self.rawSql(`
|
await Self.rawSql(`
|
||||||
INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance)
|
INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance)
|
||||||
VALUES (?, NULL, 'UPDATE', 'Client', ?, ?)`, [
|
VALUES (?, NULL, 'UPDATE', 'Client', ?, ?)`, [
|
||||||
ticket.clientFk,
|
ticket.clientFk,
|
||||||
oldInstance,
|
oldInstance,
|
||||||
newInstance
|
newInstance
|
||||||
], {userId});
|
], {userId});
|
||||||
|
|
||||||
const body = `No se ha podido enviar el albarán <strong>${ticket.id}</strong>
|
const body = `No se ha podido enviar el albarán <strong>${ticket.id}</strong>
|
||||||
al cliente <strong>${ticket.clientFk} - ${ticket.clientName}</strong>
|
al cliente <strong>${ticket.clientFk} - ${ticket.clientName}</strong>
|
||||||
porque la dirección de email <strong>"${ticket.recipient}"</strong> no es correcta
|
porque la dirección de email <strong>"${ticket.recipient}"</strong> no es correcta
|
||||||
o no está disponible.<br/><br/>
|
o no está disponible.<br/><br/>
|
||||||
Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente.
|
Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente.
|
||||||
Actualiza la dirección de email con una correcta.`;
|
Actualiza la dirección de email con una correcta.`;
|
||||||
|
|
||||||
smtp.send({
|
smtp.send({
|
||||||
to: ticket.salesPersonEmail,
|
to: ticket.salesPersonEmail,
|
||||||
subject: 'No se ha podido enviar el albarán',
|
subject: 'No se ha podido enviar el albarán',
|
||||||
html: body
|
html: body
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const loggable = require('vn-loopback/util/log');
|
const loggable = require('vn-loopback/util/log');
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('componentUpdate', {
|
Self.remoteMethodCtx('componentUpdate', {
|
||||||
|
@ -112,7 +113,6 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const $t = ctx.req.__; // $translate
|
const $t = ctx.req.__; // $translate
|
||||||
await models.Ticket.isEditableOrThrow(ctx, args.id, myOptions);
|
await models.Ticket.isEditableOrThrow(ctx, args.id, myOptions);
|
||||||
|
@ -127,11 +127,8 @@ module.exports = Self => {
|
||||||
args.warehouseFk,
|
args.warehouseFk,
|
||||||
myOptions);
|
myOptions);
|
||||||
|
|
||||||
if (!zoneShipped || zoneShipped.zoneFk != args.zoneFk) {
|
if (!zoneShipped || zoneShipped.zoneFk != args.zoneFk)
|
||||||
const error = `You don't have privileges to change the zone`;
|
throw new UserError(`You don't have privileges to change the zone`);
|
||||||
|
|
||||||
throw new UserError(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.isWithoutNegatives) {
|
if (args.isWithoutNegatives) {
|
||||||
|
|
|
@ -248,6 +248,7 @@ module.exports = Self => {
|
||||||
am.name AS agencyMode,
|
am.name AS agencyMode,
|
||||||
am.id AS agencyModeFk,
|
am.id AS agencyModeFk,
|
||||||
st.name AS state,
|
st.name AS state,
|
||||||
|
st.classColor,
|
||||||
wk.lastName AS salesPerson,
|
wk.lastName AS salesPerson,
|
||||||
ts.stateFk AS stateFk,
|
ts.stateFk AS stateFk,
|
||||||
ts.alertLevel AS alertLevel,
|
ts.alertLevel AS alertLevel,
|
||||||
|
@ -339,7 +340,8 @@ module.exports = Self => {
|
||||||
{'tp.isFreezed': hasProblem},
|
{'tp.isFreezed': hasProblem},
|
||||||
{'tp.risk': hasProblem},
|
{'tp.risk': hasProblem},
|
||||||
{'tp.hasTicketRequest': hasProblem},
|
{'tp.hasTicketRequest': hasProblem},
|
||||||
{'tp.itemShortage': range}
|
{'tp.itemShortage': range},
|
||||||
|
{'tp.hasRounding': hasProblem}
|
||||||
]};
|
]};
|
||||||
|
|
||||||
if (hasWhere)
|
if (hasWhere)
|
||||||
|
|
|
@ -194,7 +194,8 @@ module.exports = Self => {
|
||||||
{'tp.hasTicketRequest': hasProblem},
|
{'tp.hasTicketRequest': hasProblem},
|
||||||
{'tp.itemShortage': range},
|
{'tp.itemShortage': range},
|
||||||
{'tp.hasComponentLack': hasProblem},
|
{'tp.hasComponentLack': hasProblem},
|
||||||
{'tp.isTooLittle': hasProblem}
|
{'tp.isTooLittle': hasProblem},
|
||||||
|
{'tp.hasRounding': hasProblem}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ describe('sale transferSales()', () => {
|
||||||
const userId = 1101;
|
const userId = 1101;
|
||||||
const activeCtx = {
|
const activeCtx = {
|
||||||
accessToken: {userId: userId},
|
accessToken: {userId: userId},
|
||||||
|
headers: {origin: ''},
|
||||||
|
__: value => value
|
||||||
};
|
};
|
||||||
const ctx = {req: activeCtx};
|
const ctx = {req: activeCtx};
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ module.exports = Self => {
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {userId};
|
const myOptions = {userId};
|
||||||
|
const $t = ctx.req.__; // $translate
|
||||||
let tx;
|
let tx;
|
||||||
|
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
|
@ -95,9 +96,18 @@ module.exports = Self => {
|
||||||
|
|
||||||
const isTicketEmpty = await models.Ticket.isEmpty(id, myOptions);
|
const isTicketEmpty = await models.Ticket.isEmpty(id, myOptions);
|
||||||
if (isTicketEmpty) {
|
if (isTicketEmpty) {
|
||||||
await originalTicket.updateAttributes({
|
try {
|
||||||
isDeleted: true
|
await models.Ticket.setDeleted(ctx, id, myOptions);
|
||||||
}, myOptions);
|
} catch (e) {
|
||||||
|
if (e.statusCode === 400) {
|
||||||
|
throw new UserError(
|
||||||
|
`This ticket cannot be left empty.`,
|
||||||
|
'TRANSFER_SET_DELETED',
|
||||||
|
$t(e.message, ...e.translateArgs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
|
@ -27,6 +27,9 @@
|
||||||
"code": {
|
"code": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"required": false
|
"required": false
|
||||||
|
},
|
||||||
|
"classColor": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,12 @@
|
||||||
class="bright"
|
class="bright"
|
||||||
icon="icon-components">
|
icon="icon-components">
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
|
<vn-icon
|
||||||
|
ng-show="::ticket.hasRounding"
|
||||||
|
translate-attr="{title: 'Rounding'}"
|
||||||
|
class="bright"
|
||||||
|
icon="sync_problem">
|
||||||
|
</vn-icon>
|
||||||
</td>
|
</td>
|
||||||
<td><span
|
<td><span
|
||||||
ng-click="ticketDescriptor.show($event, ticket.id)"
|
ng-click="ticketDescriptor.show($event, ticket.id)"
|
||||||
|
|
|
@ -36,3 +36,4 @@ import './future';
|
||||||
import './future-search-panel';
|
import './future-search-panel';
|
||||||
import './advance';
|
import './advance';
|
||||||
import './advance-search-panel';
|
import './advance-search-panel';
|
||||||
|
import './sms';
|
||||||
|
|
|
@ -75,6 +75,12 @@
|
||||||
class="bright"
|
class="bright"
|
||||||
icon="icon-components">
|
icon="icon-components">
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
|
<vn-icon
|
||||||
|
ng-show="::ticket.hasRounding"
|
||||||
|
translate-attr="{title: 'Rounding'}"
|
||||||
|
class="bright"
|
||||||
|
icon="sync_problem">
|
||||||
|
</vn-icon>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>{{::ticket.id}}</vn-td>
|
<vn-td shrink>{{::ticket.id}}</vn-td>
|
||||||
<vn-td class="expendable">
|
<vn-td class="expendable">
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
{"state": "ticket.card.components", "icon": "icon-components"},
|
{"state": "ticket.card.components", "icon": "icon-components"},
|
||||||
{"state": "ticket.card.saleTracking", "icon": "assignment"},
|
{"state": "ticket.card.saleTracking", "icon": "assignment"},
|
||||||
{"state": "ticket.card.dms.index", "icon": "cloud_download"},
|
{"state": "ticket.card.dms.index", "icon": "cloud_download"},
|
||||||
{"state": "ticket.card.boxing", "icon": "science"}
|
{"state": "ticket.card.boxing", "icon": "science"},
|
||||||
|
{"state": "ticket.card.sms", "icon": "sms"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"keybindings": [
|
"keybindings": [
|
||||||
|
@ -287,6 +288,15 @@
|
||||||
"state": "ticket.advance",
|
"state": "ticket.advance",
|
||||||
"component": "vn-ticket-advance",
|
"component": "vn-ticket-advance",
|
||||||
"description": "Advance tickets"
|
"description": "Advance tickets"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/sms",
|
||||||
|
"state": "ticket.card.sms",
|
||||||
|
"component": "vn-ticket-sms",
|
||||||
|
"description": "Sms",
|
||||||
|
"params": {
|
||||||
|
"ticket": "$ctrl.ticket"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
<vn-card>
|
||||||
|
</vn-card>
|
|
@ -0,0 +1,21 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import Section from 'salix/components/section';
|
||||||
|
|
||||||
|
class Controller extends Section {
|
||||||
|
constructor($element, $) {
|
||||||
|
super($element, $);
|
||||||
|
}
|
||||||
|
|
||||||
|
async $onInit() {
|
||||||
|
this.$state.go('ticket.card.summary', {id: this.$params.id});
|
||||||
|
window.location.href = await this.vnApp.getUrl(`ticket/${this.$params.id}/sms`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.vnComponent('vnTicketSms', {
|
||||||
|
template: require('./index.html'),
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
ticket: '<'
|
||||||
|
}
|
||||||
|
});
|
|
@ -71,8 +71,9 @@ module.exports = Self => {
|
||||||
'Stored on': 'created',
|
'Stored on': 'created',
|
||||||
'Document ID': 'id'
|
'Document ID': 'id'
|
||||||
};
|
};
|
||||||
|
|
||||||
workerDocuware =
|
workerDocuware =
|
||||||
await models.Docuware.getById('hr', worker.lastName + worker.firstName, docuwareParse) ?? [];
|
await models.Docuware.getById('hr', worker.lastName + ' ' + worker.firstName, docuwareParse) ?? [];
|
||||||
for (document of workerDocuware) {
|
for (document of workerDocuware) {
|
||||||
const defaultData = {
|
const defaultData = {
|
||||||
file: 'dw' + document.id + '.png',
|
file: 'dw' + document.id + '.png',
|
||||||
|
|
|
@ -20,11 +20,11 @@ describe('Worker new', () => {
|
||||||
const employeeId = 1;
|
const employeeId = 1;
|
||||||
const defaultWorker = {
|
const defaultWorker = {
|
||||||
fi: '78457139E',
|
fi: '78457139E',
|
||||||
name: 'defaultWorker',
|
name: 'DEFAULTERWORKER',
|
||||||
firstName: 'default',
|
firstName: 'DEFAULT',
|
||||||
lastNames: 'worker',
|
lastNames: 'WORKER',
|
||||||
email: 'defaultWorker@mydomain.com',
|
email: 'defaultWorker@mydomain.com',
|
||||||
street: 'S/ defaultWorkerStreet',
|
street: 'S/ DEFAULTWORKERSTREET',
|
||||||
city: 'defaultWorkerCity',
|
city: 'defaultWorkerCity',
|
||||||
provinceFk: 1,
|
provinceFk: 1,
|
||||||
companyFk: 442,
|
companyFk: 442,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "salix-back",
|
"name": "salix-back",
|
||||||
"version": "23.34.01",
|
"version": "23.32.02",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
const Stylesheet = require(`vn-print/core/stylesheet`);
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const vnPrintPath = path.resolve('print');
|
||||||
|
|
||||||
|
module.exports = new Stylesheet([
|
||||||
|
`${vnPrintPath}/common/css/spacing.css`,
|
||||||
|
`${vnPrintPath}/common/css/misc.css`,
|
||||||
|
`${vnPrintPath}/common/css/layout.css`,
|
||||||
|
`${vnPrintPath}/common/css/report.css`,
|
||||||
|
`${__dirname}/style.css`])
|
||||||
|
.mergeStyles();
|
|
@ -0,0 +1,101 @@
|
||||||
|
html {
|
||||||
|
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
|
||||||
|
margin: 10px;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
.mainTable, .specialTable, .categoryTable {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.mainTable td {
|
||||||
|
width: 50%;
|
||||||
|
border: 1px solid black;
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 15px;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
.signTable {
|
||||||
|
height: 12%;
|
||||||
|
}
|
||||||
|
.signTable td {
|
||||||
|
width: calc(100% / 3);
|
||||||
|
border: 1px solid black;
|
||||||
|
vertical-align: top;
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 15px;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
#title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 85px;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
border: 1px solid #cccccc;
|
||||||
|
height: 0px;
|
||||||
|
border-radius: 25px;
|
||||||
|
}
|
||||||
|
#cellHeader {
|
||||||
|
border: 0px;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
#label, #merchandiseLabels {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
#merchandiseLabels {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.imgSection {
|
||||||
|
text-align: center;
|
||||||
|
height: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
object-fit: contain;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
#lineBreak {
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
.specialTable td {
|
||||||
|
border: 1px solid black;
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 15px;
|
||||||
|
font-size: inherit;
|
||||||
|
border-top: none;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.specialTable #itemCategoryList {
|
||||||
|
width: 70%;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.categoryTable {
|
||||||
|
padding-bottom: none;
|
||||||
|
}
|
||||||
|
.categoryTable td {
|
||||||
|
vertical-align: top;
|
||||||
|
font-size: inherit;
|
||||||
|
border: none;
|
||||||
|
padding: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.categoryTable #merchandiseLabels {
|
||||||
|
border-bottom: 4px solid #cccccc;
|
||||||
|
padding: none;
|
||||||
|
}
|
||||||
|
#merchandiseDetail {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
#merchandiseData {
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
#merchandiseLabels td {
|
||||||
|
padding-bottom: 11px;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<table class="mainTable">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">1. Remitente / Expediteur / Sender</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.senderName}}</b><br>
|
||||||
|
{{data.senderStreet}}<br>
|
||||||
|
{{data.senderPostCode}} {{data.senderCity}} {{(data.senderCountry) ? `(${data.senderCountry})` : null}}
|
||||||
|
</td>
|
||||||
|
<td id="cellHeader">
|
||||||
|
<span id="title">CMR</span><br>
|
||||||
|
{{data.cmrFk}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">2. Consignatario / Destinataire / Consignee</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.deliveryAddressFk}}<br>
|
||||||
|
{{data.deliveryName}}<br>
|
||||||
|
{{data.deliveryPhone || data.clientPhone}}
|
||||||
|
{{((data.deliveryPhone || data.clientPhone) && data.deliveryMobile) ? '/' : null}}
|
||||||
|
{{data.deliveryMobile}}</b>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">16. Transportista / Transporteur / Carrier</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.carrierName}}</b><br>
|
||||||
|
{{data.carrierStreet}}<br>
|
||||||
|
{{data.carrierPostalCode}} {{data.carrierCity}} {{(data.carrierCountry) ? `(${data.carrierCountry})` : null}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
3. Lugar y fecha de entrega /
|
||||||
|
Lieu et date de livraison /
|
||||||
|
Place and date of delivery
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.deliveryStreet}}<br>
|
||||||
|
{{data.deliveryPostalCode}} {{data.deliveryCity}} {{(data.deliveryCountry) ? `(${data.deliveryCountry})` : null}}<br>
|
||||||
|
{{(data.ead) ? formatDate(data.ead, '%d/%m/%Y') : null}}</b>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">17. Porteadores sucesivos / Transporteurs succesifs / Succesive Carriers</span>
|
||||||
|
<hr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
4. Lugar y fecha de carga /
|
||||||
|
Lieu et date del prise en charge de la merchandise /
|
||||||
|
Place and date of taking over the goods
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.loadStreet}}<br>
|
||||||
|
{{data.loadPostalCode}} {{data.loadCity}} {{(data.loadCountry) ? `(${data.loadCountry})` : null}}<br>
|
||||||
|
{{formatDate(data.created, '%d/%m/%Y')}}</b>
|
||||||
|
</td>
|
||||||
|
<td rowspan="2">
|
||||||
|
<span id="label">
|
||||||
|
18. Obervaciones del transportista /
|
||||||
|
Reserves et observations du transporteur /
|
||||||
|
Carrier's reservations and observations
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.truckPlate}}</b><br>
|
||||||
|
{{data.observations}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">5. Documentos anexos / Documents annexes / Documents attached</span>
|
||||||
|
<hr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table class="specialTable">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
7 & 8. Número de bultos y clase de embalage /
|
||||||
|
Number of packages and packaging class /
|
||||||
|
Nombre de colis et classe d'emballage
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<div id="lineBreak">
|
||||||
|
<b>{{data.packagesList}}</b>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td id="itemCategoryList">
|
||||||
|
<table class="categoryTable">
|
||||||
|
<tr id="merchandiseLabels">
|
||||||
|
<td>6. Marcas y números / Brands and numbers / Marques et numéros</td>
|
||||||
|
<td>9. Naturaleza de la merc. / Nature of goods / Nature des marchandises</td>
|
||||||
|
<td>10. nº Estadístico / Statistical no. / n° statistique</td>
|
||||||
|
<td>11. Peso bruto / Gross weight / Poids brut (kg)</td>
|
||||||
|
<td>12. Volumen / Volume (m3)</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="merchandise in merchandises" id="merchandiseData">
|
||||||
|
<td>{{merchandise.ticketFk}}</td>
|
||||||
|
<td>{{merchandise.name}}</td>
|
||||||
|
<td>N/A</td>
|
||||||
|
<td>{{merchandise.weight}}</td>
|
||||||
|
<td>{{merchandise.volume}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div v-if="!merchandises" id="merchandiseDetail">
|
||||||
|
{{data.merchandiseDetail}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table class="mainTable">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
13. Instrucciones del remitente /
|
||||||
|
Instrunstions de l'expèditeur / Sender
|
||||||
|
instruccions
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.senderInstruccions}}</b>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
19. Estipulaciones particulares /
|
||||||
|
Conventions particulieres /
|
||||||
|
Special agreements
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.specialAgreements}}</b>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
14. Forma de pago /
|
||||||
|
Prescriptions d'affranchissement /
|
||||||
|
Instruction as to payment for carriage
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.paymentInstruccions}}</b>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">20. A pagar por / Être payé pour / To be paid by</span>
|
||||||
|
<hr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">21. Formalizado en / Etabile a / Estabilshed in</span>
|
||||||
|
<hr>
|
||||||
|
<b>{{data.loadStreet}}</b><br>
|
||||||
|
{{data.loadPostalCode}} {{data.loadCity}} {{(data.loadCountry) ? `(${data.loadCountry})` : null}} <br>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">15. Reembolso / Remboursement / Cash on delivery</span>
|
||||||
|
<hr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<table class="signTable">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
22. Firma y sello del remitente /
|
||||||
|
Signature et timbre de l'expèditeur /
|
||||||
|
Signature and stamp of the sender
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<div class="imgSection">
|
||||||
|
<img :src="senderStamp"/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
23. Firma y sello del transportista /
|
||||||
|
Signature et timbre du transporteur /
|
||||||
|
Signature and stamp of the carrier
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<div class="imgSection">
|
||||||
|
<img :src="deliveryStamp"/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span id="label">
|
||||||
|
24. Firma y sello del consignatario /
|
||||||
|
Signature et timbre du destinataire /
|
||||||
|
Signature and stamp of the consignee
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
<div class="imgSection">
|
||||||
|
<img :src="signPath"/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,45 @@
|
||||||
|
const config = require(`vn-print/core/config`);
|
||||||
|
const vnReport = require('../../../core/mixins/vn-report.js');
|
||||||
|
const md5 = require('md5');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
|
||||||
|
const prefixBase64 = 'data:image/png;base64,';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'cmr',
|
||||||
|
mixins: [vnReport],
|
||||||
|
async serverPrefetch() {
|
||||||
|
this.data = await this.findOneFromDef('data', [this.id]);
|
||||||
|
if (this.data.ticketFk) {
|
||||||
|
this.merchandises = await this.rawSqlFromDef('merchandise', [this.data.ticketFk]);
|
||||||
|
this.signature = await this.findOneFromDef('signature', [this.data.ticketFk]);
|
||||||
|
} else
|
||||||
|
this.merchandises = null;
|
||||||
|
|
||||||
|
this.senderStamp = (this.data.senderStamp)
|
||||||
|
? `${prefixBase64} ${this.data.senderStamp.toString('base64')}`
|
||||||
|
: null;
|
||||||
|
this.deliveryStamp = (this.data.deliveryStamp)
|
||||||
|
? `${prefixBase64} ${this.data.deliveryStamp.toString('base64')}`
|
||||||
|
: null;
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
description: 'The cmr id'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
signPath() {
|
||||||
|
if (!this.signature) return;
|
||||||
|
|
||||||
|
const signatureName = this.signature.signature
|
||||||
|
const hash = md5(signatureName.toString()).substring(0, 3);
|
||||||
|
const file = `${config.storage.root}/${hash}/${signatureName}.png`;
|
||||||
|
if (!fs.existsSync(file)) return null;
|
||||||
|
|
||||||
|
return `${prefixBase64} ${Buffer.from(fs.readFileSync(file), 'utf8').toString('base64')}`;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1 @@
|
||||||
|
reportName: cmr
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"format": "A4"
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
SELECT c.id cmrFk,
|
||||||
|
t.id ticketFk,
|
||||||
|
c.truckPlate,
|
||||||
|
c.observations,
|
||||||
|
c.senderInstruccions,
|
||||||
|
c.paymentInstruccions,
|
||||||
|
c.specialAgreements,
|
||||||
|
c.created,
|
||||||
|
c.packagesList,
|
||||||
|
c.merchandiseDetail,
|
||||||
|
c.ead,
|
||||||
|
s.name carrierName,
|
||||||
|
s.street carrierStreet,
|
||||||
|
s.postCode carrierPostCode,
|
||||||
|
s.city carrierCity,
|
||||||
|
cou.country carrierCountry,
|
||||||
|
s2.name senderName,
|
||||||
|
s2.street senderStreet,
|
||||||
|
s2.postCode senderPostCode,
|
||||||
|
s2.city senderCity,
|
||||||
|
cou2.country senderCountry,
|
||||||
|
a.street deliveryStreet,
|
||||||
|
a.id deliveryAddressFk,
|
||||||
|
a.postalCode deliveryPostalCode,
|
||||||
|
a.city deliveryCity,
|
||||||
|
a.nickname deliveryName,
|
||||||
|
a.phone deliveryPhone,
|
||||||
|
a.mobile deliveryMobile,
|
||||||
|
cou3.country deliveryCountry,
|
||||||
|
cl.phone clientPhone,
|
||||||
|
a2.street loadStreet,
|
||||||
|
a2.postalCode loadPostalCode,
|
||||||
|
a2.city loadCity,
|
||||||
|
cou4.country loadCountry,
|
||||||
|
co.stamp senderStamp,
|
||||||
|
s.stamp deliveryStamp
|
||||||
|
FROM cmr c
|
||||||
|
LEFT JOIN supplier s ON s.id = c.supplierFk
|
||||||
|
LEFT JOIN country cou ON cou.id = s.countryFk
|
||||||
|
LEFT JOIN company co ON co.id = c.companyFk
|
||||||
|
LEFT JOIN supplierAccount sa ON sa.id = co.supplierAccountFk
|
||||||
|
LEFT JOIN supplier s2 ON s2.id = sa.supplierFk
|
||||||
|
LEFT JOIN country cou2 ON cou2.id = s2.countryFk
|
||||||
|
LEFT JOIN `address` a ON a.id = c.addressToFk
|
||||||
|
LEFT JOIN province p ON p.id = a.provinceFk
|
||||||
|
LEFT JOIN country cou3 ON cou3.id = p.countryFk
|
||||||
|
LEFT JOIN client cl ON cl.id = a.clientFk
|
||||||
|
LEFT JOIN `address` a2 ON a2.id = c.addressFromFk
|
||||||
|
LEFT JOIN province p2 ON p2.id = a2.provinceFk
|
||||||
|
LEFT JOIN country cou4 ON cou4.id = p2.countryFk
|
||||||
|
LEFT JOIN ticket t ON t.cmrFk = c.id
|
||||||
|
WHERE c.id = ?
|
|
@ -0,0 +1,11 @@
|
||||||
|
SELECT s.ticketFk,
|
||||||
|
ic.name,
|
||||||
|
CAST(SUM(sv.weight) AS DECIMAL(10,2)) `weight`,
|
||||||
|
CAST(SUM(sv.volume) AS DECIMAL(10,3)) volume
|
||||||
|
FROM sale s
|
||||||
|
JOIN saleVolume sv ON sv.saleFk = s.id
|
||||||
|
JOIN item i ON i.id = s.itemFk
|
||||||
|
JOIN itemType it ON it.id = i.typeFk
|
||||||
|
JOIN itemCategory ic ON ic.id = it.categoryFk
|
||||||
|
WHERE sv.ticketFk = ?
|
||||||
|
GROUP BY ic.id
|
|
@ -0,0 +1,5 @@
|
||||||
|
SELECT dc.id `signature`
|
||||||
|
FROM ticket t
|
||||||
|
JOIN ticketDms dt ON dt.ticketFk = t.id
|
||||||
|
LEFT JOIN dms dc ON dc.id = dt.dmsFk
|
||||||
|
WHERE t.id = ?
|
Loading…
Reference in New Issue