refs #4815 feat:DeviceLog Model #1705
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -6,7 +6,7 @@ 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
|
## [2338.01] - 2023-09-21
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
@ -14,6 +14,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
### Fixed
|
### 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
|
## [2332.01] - 2023-08-10
|
||||||
|
|
||||||
|
@ -44,9 +58,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- (Clientes -> Razón social) Permite crear clientes con la misma razón social según el país
|
- (Clientes -> Razón social) Permite crear clientes con la misma razón social según el país
|
||||||
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
|
|
||||||
## [2328.01] - 2023-07-13
|
## [2328.01] - 2023-07-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -71,11 +71,10 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await Self.get(fileCabinet, filter);
|
const [response] = await Self.get(fileCabinet, filter);
|
||||||
const [documents] = response.Items;
|
if (!response) return false;
|
||||||
if (!documents) return false;
|
|
||||||
|
|
||||||
return {id: documents.Id};
|
return {id: response['Document ID']};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
const email = new Email('delivery-note', params);
|
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({
|
return email.send({
|
||||||
overrideAttachments: true,
|
overrideAttachments: true,
|
||||||
|
|
|
@ -16,19 +16,9 @@ describe('docuware download()', () => {
|
||||||
|
|
||||||
it('should return the document data', async() => {
|
it('should return the document data', async() => {
|
||||||
const docuwareId = 1;
|
const docuwareId = 1;
|
||||||
const response = {
|
const response = [{
|
||||||
Items: [
|
'Document ID': docuwareId
|
||||||
{
|
}];
|
||||||
Id: docuwareId,
|
|
||||||
Fields: [
|
|
||||||
{
|
|
||||||
FieldName: 'ESTADO',
|
|
||||||
Item: 'Firmado'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
spyOn(docuwareModel, 'get').and.returnValue((new Promise(resolve => resolve(response))));
|
spyOn(docuwareModel, 'get').and.returnValue((new Promise(resolve => resolve(response))));
|
||||||
|
|
||||||
const result = await models.Docuware.checkFile(ticketId, fileCabinetName, null, true);
|
const result = await models.Docuware.checkFile(ticketId, fileCabinetName, null, true);
|
||||||
|
|
|
@ -111,7 +111,7 @@ module.exports = Self => {
|
||||||
throw new UserError('Action not allowed on the test environment');
|
throw new UserError('Action not allowed on the test environment');
|
||||||
|
|
||||||
// delete old
|
// delete old
|
||||||
const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, false);
|
const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false);
|
||||||
if (docuwareFile) {
|
if (docuwareFile) {
|
||||||
const deleteJson = {
|
const deleteJson = {
|
||||||
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
|
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
|
||||||
|
|
|
@ -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, ' ').split('\n').join('<br>');
|
||||||
|
} catch {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,4 +1,5 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/osticket/osTicketReportEmail')(Self);
|
require('../methods/osticket/osTicketReportEmail')(Self);
|
||||||
require('../methods/osticket/closeTicket')(Self);
|
require('../methods/osticket/closeTicket')(Self);
|
||||||
|
require('../methods/osticket/sendToSupport')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,7 +31,6 @@ RUN sed -i -e 's/@mockDate/'"$MOCKDATE"'/g' mockDate.sql \
|
||||||
&& gosu mysql docker-structure.sh
|
&& gosu mysql docker-structure.sh
|
||||||
COPY changes ./changes
|
COPY changes ./changes
|
||||||
COPY dump/fixtures.sql ./
|
COPY dump/fixtures.sql ./
|
||||||
ARG STAMP=unknown
|
|
||||||
RUN gosu mysql docker-fixtures.sh
|
RUN gosu mysql docker-fixtures.sh
|
||||||
|
|
||||||
RUN echo "[INFO] -> Import finished" \
|
RUN echo "[INFO] -> Import finished" \
|
||||||
|
|
|
@ -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.
|
|
||||||
*/
|
|
|
@ -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');
|
|
@ -0,0 +1,3 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||||
|
VALUES
|
||||||
|
('Worker', 'search', 'READ', 'ALLOW', 'ROLE', 'employee');
|
|
@ -0,0 +1,2 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
|
||||||
|
VALUES ('ExpeditionState','addExpeditionState','WRITE','ALLOW','ROLE','delivery');
|
|
@ -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');
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
UPDATE `vn`.`department`
|
||||||
|
SET code='VN'
|
||||||
|
WHERE name='VERDNATURA';
|
|
@ -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');
|
||||||
|
|
|
@ -22,12 +22,8 @@ module.exports = class Docker {
|
||||||
* @param {String} networkName Name of the container network
|
* @param {String} networkName Name of the container network
|
||||||
*/
|
*/
|
||||||
async run(ci, networkName = 'jenkins') {
|
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...');
|
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.');
|
log('Image built.');
|
||||||
|
|
||||||
let dockerArgs;
|
let dockerArgs;
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -65,7 +65,6 @@ TABLES=(
|
||||||
sample
|
sample
|
||||||
state
|
state
|
||||||
ticketUpdateAction
|
ticketUpdateAction
|
||||||
time
|
|
||||||
volumeConfig
|
volumeConfig
|
||||||
workCenter
|
workCenter
|
||||||
companyI18n
|
companyI18n
|
||||||
|
@ -98,13 +97,6 @@ TABLES=(
|
||||||
)
|
)
|
||||||
dump_tables ${TABLES[@]}
|
dump_tables ${TABLES[@]}
|
||||||
|
|
||||||
TABLES=(
|
|
||||||
postgresql
|
|
||||||
labour_agreement
|
|
||||||
media_type
|
|
||||||
)
|
|
||||||
dump_tables ${TABLES[@]}
|
|
||||||
|
|
||||||
TABLES=(
|
TABLES=(
|
||||||
sage
|
sage
|
||||||
TiposIva
|
TiposIva
|
||||||
|
|
|
@ -7,7 +7,6 @@ SCHEMAS=(
|
||||||
edi
|
edi
|
||||||
hedera
|
hedera
|
||||||
pbx
|
pbx
|
||||||
postgresql
|
|
||||||
sage
|
sage
|
||||||
salix
|
salix
|
||||||
stock
|
stock
|
||||||
|
@ -23,7 +22,6 @@ IGNORETABLES=(
|
||||||
--ignore-table=bs.productionIndicators
|
--ignore-table=bs.productionIndicators
|
||||||
--ignore-table=bs.VentasPorCliente
|
--ignore-table=bs.VentasPorCliente
|
||||||
--ignore-table=bs.v_ventas
|
--ignore-table=bs.v_ventas
|
||||||
--ignore-table=postgresql.currentWorkersStats
|
|
||||||
--ignore-table=vn.accounting__
|
--ignore-table=vn.accounting__
|
||||||
--ignore-table=vn.agencyModeZone
|
--ignore-table=vn.agencyModeZone
|
||||||
--ignore-table=vn.agencyProvince
|
--ignore-table=vn.agencyProvince
|
||||||
|
|
|
@ -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() => {
|
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 workerId = 1110;
|
||||||
const tabletId = 1;
|
const tabletId = 1;
|
||||||
let stmts = [];
|
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() => {
|
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 workerId = 1110;
|
||||||
const tabletId = 1;
|
const tabletId = 1;
|
||||||
let stmts = [];
|
let stmts = [];
|
||||||
|
@ -239,12 +237,6 @@ xdescribe('worker workerTimeControl_check()', () => {
|
||||||
|
|
||||||
stmts.push('START TRANSACTION');
|
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)
|
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
||||||
VALUES
|
VALUES
|
||||||
(?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
|
(?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
|
||||||
|
|
|
@ -632,6 +632,7 @@ let actions = {
|
||||||
await this.write(selector, value.toString());
|
await this.write(selector, value.toString());
|
||||||
break;
|
break;
|
||||||
case 'vn-autocomplete':
|
case 'vn-autocomplete':
|
||||||
|
case 'vn-worker-autocomplete':
|
||||||
if (value)
|
if (value)
|
||||||
await this.autocompleteSearch(selector, value.toString());
|
await this.autocompleteSearch(selector, value.toString());
|
||||||
else
|
else
|
||||||
|
@ -667,6 +668,7 @@ let actions = {
|
||||||
switch (tagName) {
|
switch (tagName) {
|
||||||
case 'vn-textfield':
|
case 'vn-textfield':
|
||||||
case 'vn-autocomplete':
|
case 'vn-autocomplete':
|
||||||
|
case 'vn-worker-autocomplete':
|
||||||
case 'vn-input-time':
|
case 'vn-input-time':
|
||||||
case 'vn-datalist':
|
case 'vn-datalist':
|
||||||
el = await input.$('input');
|
el = await input.$('input');
|
||||||
|
|
|
@ -187,7 +187,7 @@ export default {
|
||||||
country: 'vn-client-create vn-autocomplete[ng-model="$ctrl.client.countryFk"]',
|
country: 'vn-client-create vn-autocomplete[ng-model="$ctrl.client.countryFk"]',
|
||||||
userName: 'vn-client-create vn-textfield[ng-model="$ctrl.client.userName"]',
|
userName: 'vn-client-create vn-textfield[ng-model="$ctrl.client.userName"]',
|
||||||
email: 'vn-client-create vn-textfield[ng-model="$ctrl.client.email"]',
|
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',
|
saveNewProvicenButton: '#saveProvince',
|
||||||
saveNewCityButton: '#saveCity',
|
saveNewCityButton: '#saveCity',
|
||||||
saveNewPoscode: '#savePostcode',
|
saveNewPoscode: '#savePostcode',
|
||||||
|
@ -199,7 +199,7 @@ export default {
|
||||||
email: 'vn-client-basic-data vn-textfield[ng-model="$ctrl.client.email"]',
|
email: 'vn-client-basic-data vn-textfield[ng-model="$ctrl.client.email"]',
|
||||||
phone: 'vn-client-basic-data vn-textfield[ng-model="$ctrl.client.phone"]',
|
phone: 'vn-client-basic-data vn-textfield[ng-model="$ctrl.client.phone"]',
|
||||||
mobile: 'vn-client-basic-data vn-textfield[ng-model="$ctrl.client.mobile"]',
|
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"]',
|
channel: 'vn-client-basic-data vn-autocomplete[ng-model="$ctrl.client.contactChannelFk"]',
|
||||||
transferor: 'vn-client-basic-data vn-autocomplete[ng-model="$ctrl.client.transferorFk"]',
|
transferor: 'vn-client-basic-data vn-autocomplete[ng-model="$ctrl.client.transferorFk"]',
|
||||||
businessType: 'vn-client-basic-data vn-autocomplete[ng-model="$ctrl.client.businessTypeFk"]',
|
businessType: 'vn-client-basic-data vn-autocomplete[ng-model="$ctrl.client.businessTypeFk"]',
|
||||||
|
@ -735,7 +735,7 @@ export default {
|
||||||
},
|
},
|
||||||
createStateView: {
|
createStateView: {
|
||||||
state: 'vn-autocomplete[ng-model="$ctrl.stateFk"]',
|
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]`
|
saveStateButton: `button[type=submit]`
|
||||||
},
|
},
|
||||||
claimsIndex: {
|
claimsIndex: {
|
||||||
|
@ -781,12 +781,12 @@ export default {
|
||||||
firstClaimReason: 'vn-claim-development vn-horizontal:nth-child(1) vn-autocomplete[ng-model="claimDevelopment.claimReasonFk"]',
|
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"]',
|
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"]',
|
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"]',
|
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"]',
|
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"]',
|
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"]',
|
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"]',
|
secondClaimRedelivery: 'vn-claim-development vn-horizontal:nth-child(2) vn-autocomplete[ng-model="claimDevelopment.claimRedeliveryFk"]',
|
||||||
saveDevelopmentButton: 'button[type=submit]'
|
saveDevelopmentButton: 'button[type=submit]'
|
||||||
},
|
},
|
||||||
|
@ -854,7 +854,7 @@ export default {
|
||||||
|
|
||||||
},
|
},
|
||||||
createRouteView: {
|
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"]',
|
createdDatePicker: 'vn-route-create vn-date-picker[ng-model="$ctrl.route.created"]',
|
||||||
vehicleAuto: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.vehicleFk"]',
|
vehicleAuto: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.vehicleFk"]',
|
||||||
agency: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.agencyModeFk"]',
|
agency: 'vn-route-create vn-autocomplete[ng-model="$ctrl.route.agencyModeFk"]',
|
||||||
|
@ -976,7 +976,7 @@ export default {
|
||||||
street: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.street"]',
|
street: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.street"]',
|
||||||
user: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.name"]',
|
user: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.name"]',
|
||||||
email: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.email"]',
|
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"]',
|
role: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.roleFk"]',
|
||||||
iban: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.iban"]',
|
iban: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.iban"]',
|
||||||
createButton: 'vn-worker-create vn-submit[label="Create"]',
|
createButton: 'vn-worker-create vn-submit[label="Create"]',
|
||||||
|
|
|
@ -59,7 +59,7 @@ describe('Ticket Create new tracking state path', () => {
|
||||||
const result = await page
|
const result = await page
|
||||||
.waitToGetProperty(selectors.createStateView.worker, 'value');
|
.waitToGetProperty(selectors.createStateView.worker, 'value');
|
||||||
|
|
||||||
expect(result).toEqual('salesPersonNick');
|
expect(result).toEqual('salesPerson');
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should succesfully create a valid state`, async() => {
|
it(`should succesfully create a valid state`, async() => {
|
||||||
|
|
|
@ -17,10 +17,9 @@ import './style.scss';
|
||||||
* @event change Thrown when value is changed
|
* @event change Thrown when value is changed
|
||||||
*/
|
*/
|
||||||
export default class Autocomplete extends Field {
|
export default class Autocomplete extends Field {
|
||||||
constructor($element, $, $compile, $transclude) {
|
constructor($element, $, $transclude) {
|
||||||
super($element, $, $compile);
|
super($element, $, $transclude);
|
||||||
this.$transclude = $transclude;
|
this.$transclude = $transclude;
|
||||||
this.$compile = $compile;
|
|
||||||
this._selection = null;
|
this._selection = null;
|
||||||
this.input = this.element.querySelector('input');
|
this.input = this.element.querySelector('input');
|
||||||
}
|
}
|
||||||
|
@ -153,7 +152,14 @@ export default class Autocomplete extends Field {
|
||||||
filter.include = this.include;
|
filter.include = this.include;
|
||||||
|
|
||||||
let json = encodeURIComponent(JSON.stringify(filter));
|
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),
|
json => this.onSelectionRequest(json.data),
|
||||||
() => this.onSelectionRequest()
|
() => this.onSelectionRequest()
|
||||||
);
|
);
|
||||||
|
@ -282,7 +288,7 @@ export default class Autocomplete extends Field {
|
||||||
this.refreshSelection();
|
this.refreshSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Autocomplete.$inject = ['$element', '$scope', '$compile', '$transclude'];
|
Autocomplete.$inject = ['$element', '$scope', '$transclude'];
|
||||||
|
|
||||||
ngModule.vnComponent('vnAutocomplete', {
|
ngModule.vnComponent('vnAutocomplete', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -3,8 +3,8 @@ import FormInput from '../form-input';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
export default class Field extends FormInput {
|
export default class Field extends FormInput {
|
||||||
constructor($element, $scope) {
|
constructor($element, $scope, $transclude) {
|
||||||
super($element, $scope);
|
super($element, $scope, $transclude);
|
||||||
|
|
||||||
this.prefix = null;
|
this.prefix = null;
|
||||||
this.suffix = 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', {
|
ngModule.vnComponent('vnField', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -51,7 +51,9 @@ import './textarea';
|
||||||
import './th';
|
import './th';
|
||||||
import './treeview';
|
import './treeview';
|
||||||
import './wday-picker';
|
import './wday-picker';
|
||||||
|
import './worker-autocomplete';
|
||||||
import './datalist';
|
import './datalist';
|
||||||
import './contextmenu';
|
import './contextmenu';
|
||||||
import './rating';
|
import './rating';
|
||||||
import './smart-table';
|
import './smart-table';
|
||||||
|
import './support-dialog';
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -27,6 +27,18 @@ export default class Controller extends Component {
|
||||||
setTimeout(() => element.classList.add('shown'), 30);
|
setTimeout(() => element.classList.add('shown'), 30);
|
||||||
shape.element = element;
|
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)
|
if (shape.type)
|
||||||
element.classList.add(shape.type);
|
element.classList.add(shape.type);
|
||||||
|
|
||||||
|
@ -95,7 +107,7 @@ export default class Controller extends Component {
|
||||||
|
|
||||||
clearTimeout(shape.hideTimeout);
|
clearTimeout(shape.hideTimeout);
|
||||||
shape.hideTimeout = setTimeout(
|
shape.hideTimeout = setTimeout(
|
||||||
() => this.hide(shape), shape.timeout || 3000);
|
() => this.hide(shape), shape.timeout || 5000);
|
||||||
|
|
||||||
this.lastShape = shape;
|
this.lastShape = shape;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,10 @@ vn-snackbar .shape {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 12px 25px 12px 12px;
|
padding: 12px 25px 12px 12px;
|
||||||
|
display: flex ;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
& > .text {
|
& > .text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -64,4 +68,12 @@ vn-snackbar .shape {
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0
|
right: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clickable{
|
||||||
|
background-color: $color-main;
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 7px;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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>
|
|
@ -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: '<?'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,8 @@
|
||||||
|
<tpl-item>
|
||||||
|
<div>
|
||||||
|
{{name}}
|
||||||
|
</div>
|
||||||
|
<div class="text-caption text-secondary">
|
||||||
|
{{nickname}}, {{code}}
|
||||||
|
</div>
|
||||||
|
</tpl-item>
|
|
@ -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: '<?'
|
||||||
|
},
|
||||||
|
});
|
|
@ -14,3 +14,4 @@ Previous: Back
|
||||||
Load more: Load more
|
Load more: Load more
|
||||||
Auto-scroll interrupted, please adjust the search: Auto-scroll interrupted, please adjust the search
|
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
|
||||||
|
|
|
@ -64,3 +64,6 @@ No results found: Sin resultados
|
||||||
No data: Sin datos
|
No data: Sin datos
|
||||||
Undo changes: Deshacer cambios
|
Undo changes: Deshacer cambios
|
||||||
Load more results: Cargar más resultados
|
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
|
||||||
|
|
|
@ -23,9 +23,9 @@ export default class App {
|
||||||
this.logger.showSuccess(message);
|
this.logger.showSuccess(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
showError(message) {
|
showError(message, additionalData) {
|
||||||
if (this.logger)
|
if (this.logger)
|
||||||
this.logger.showError(message);
|
this.logger.showError(message, additionalData);
|
||||||
}
|
}
|
||||||
|
|
||||||
pushLoader() {
|
pushLoader() {
|
||||||
|
|
|
@ -60,7 +60,7 @@ export default class Token {
|
||||||
if (!this.token) return;
|
if (!this.token) return;
|
||||||
const created = storage.getItem('vnTokenCreated');
|
const created = storage.getItem('vnTokenCreated');
|
||||||
this.created = created && new Date(created);
|
this.created = created && new Date(created);
|
||||||
this.renewPeriod = storage.getItem('vnTokenRenewPeriod');
|
this.ttl = storage.getItem('vnTokenTtl');
|
||||||
}
|
}
|
||||||
|
|
||||||
setStorage(storage, token, created, ttl) {
|
setStorage(storage, token, created, ttl) {
|
||||||
|
|
|
@ -25,15 +25,15 @@ export default class App extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
showMessage(message) {
|
showMessage(message) {
|
||||||
this.$.snackbar.show({message: message});
|
this.$.snackbar.show({message});
|
||||||
}
|
}
|
||||||
|
|
||||||
showSuccess(message) {
|
showSuccess(message) {
|
||||||
this.$.snackbar.showSuccess({message: message});
|
this.$.snackbar.showSuccess({message});
|
||||||
}
|
}
|
||||||
|
|
||||||
showError(message) {
|
showError(message, additionalData) {
|
||||||
this.$.snackbar.showError({message: message});
|
this.$.snackbar.showError({message, additionalData});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,13 @@ function $exceptionHandler(vnApp, $window, $state, $injector) {
|
||||||
|
|
||||||
if (messageT)
|
if (messageT)
|
||||||
message = $translate.instant(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);
|
ngModule.factory('$exceptionHandler', $exceptionHandler);
|
||||||
|
|
|
@ -1,21 +1,45 @@
|
||||||
const validateIban = require('../validateIban');
|
const validateIban = require('../validateIban');
|
||||||
|
|
||||||
describe('IBAN validation', () => {
|
describe('IBAN validation', () => {
|
||||||
it('should return false for non-IBAN input', () => {
|
it('should return false for invalid Spanish IBAN format', () => {
|
||||||
let isValid = validateIban('Pepinillos');
|
let isValid = validateIban('ES00 9999 0000 9999 0000 9999', 'ES');
|
||||||
|
|
||||||
expect(isValid).toBeFalsy();
|
expect(isValid).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false for invalid spanish IBAN input', () => {
|
it('should return true for valid Spanish IBAN', () => {
|
||||||
let isValid = validateIban('ES00 9999 0000 9999 0000 9999');
|
let isValid = validateIban('ES91 2100 0418 4502 0005 1332', 'ES');
|
||||||
|
|
||||||
expect(isValid).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return true for valid spanish IBAN', () => {
|
|
||||||
let isValid = validateIban('ES91 2100 0418 4502 0005 1332');
|
|
||||||
|
|
||||||
expect(isValid).toBeTruthy();
|
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();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
module.exports = function(iban) {
|
module.exports = function(iban, countryCode) {
|
||||||
if (iban == null) return true;
|
if (iban == null) return true;
|
||||||
if (typeof iban != 'string') return false;
|
if (typeof iban != 'string') return false;
|
||||||
|
if (countryCode?.toLowerCase() != 'es') return true;
|
||||||
|
|
||||||
iban = iban.toUpperCase();
|
iban = iban.toUpperCase();
|
||||||
iban = trim(iban);
|
iban = trim(iban);
|
||||||
|
|
|
@ -25,15 +25,13 @@
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
disabled="false"
|
disabled="false"
|
||||||
ng-model="$ctrl.claim.workerFk"
|
|
||||||
url="Workers/activeWithRole"
|
|
||||||
show-field="nickname"
|
show-field="nickname"
|
||||||
search-function="{firstName: $search}"
|
ng-model="$ctrl.claim.workerFk"
|
||||||
where="{role: 'salesPerson'}"
|
departments="['VT']"
|
||||||
label="Attended by">
|
label="Attended by">
|
||||||
</vn-autocomplete>
|
</vn-worker-autocomplete>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
ng-model="$ctrl.claim.claimStateFk"
|
ng-model="$ctrl.claim.claimStateFk"
|
||||||
data="claimStates"
|
data="claimStates"
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
data="claimDevelopments"
|
data="claimDevelopments"
|
||||||
form="form">
|
form="form">
|
||||||
</vn-watcher>
|
</vn-watcher>
|
||||||
<vn-vertical class="vn-w-md">
|
<vn-vertical class="vn-w-lg">
|
||||||
<vn-card class="vn-pa-lg">
|
<vn-card class="vn-pa-lg">
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
<form name="form">
|
<form name="form">
|
||||||
|
@ -66,16 +66,11 @@
|
||||||
show-field="description"
|
show-field="description"
|
||||||
rule>
|
rule>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
ng-model="claimDevelopment.workerFk"
|
ng-model="claimDevelopment.workerFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
|
||||||
show-field="nickname"
|
show-field="nickname"
|
||||||
search-function="{firstName: $search}"
|
|
||||||
value-field="id"
|
|
||||||
where="{role: 'employee'}"
|
|
||||||
label="Worker"
|
|
||||||
rule>
|
rule>
|
||||||
</vn-autocomplete>
|
</vn-worker-autocomplete>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
label="Redelivery"
|
label="Redelivery"
|
||||||
ng-model="claimDevelopment.claimRedeliveryFk"
|
ng-model="claimDevelopment.claimRedeliveryFk"
|
||||||
|
|
|
@ -22,26 +22,18 @@
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.salesPersonFk"
|
ng-model="filter.salesPersonFk"
|
||||||
url="Workers/activeWithRole"
|
departments="['VT']"
|
||||||
search-function="{firstName: $search}"
|
|
||||||
value-field="id"
|
|
||||||
where="{role: {inq: ['salesTeamBoss', 'salesPerson', 'officeBoss']}}"
|
|
||||||
label="Salesperson">
|
label="Salesperson">
|
||||||
<tpl-item>{{firstName}} {{name}}</tpl-item>
|
</vn-worker-autocomplete>
|
||||||
</vn-autocomplete>
|
<vn-worker-autocomplete
|
||||||
<vn-autocomplete
|
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.attenderFk"
|
ng-model="filter.attenderFk"
|
||||||
url="Workers/activeWithRole"
|
departments="['VT']"
|
||||||
search-function="{firstName: $search}"
|
|
||||||
value-field="id"
|
|
||||||
where="{role: {inq: ['salesTeamBoss', 'salesPerson']}}"
|
|
||||||
label="Attended by">
|
label="Attended by">
|
||||||
<tpl-item>{{firstName}} {{name}}</tpl-item>
|
</vn-worker-autocomplete>
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete vn-one
|
<vn-autocomplete vn-one
|
||||||
|
|
|
@ -90,16 +90,17 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function ibanNeedsValidation(err, done) {
|
async function ibanNeedsValidation(err, done) {
|
||||||
const filter = {
|
if (!this.bankEntityFk)
|
||||||
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')
|
|
||||||
return done();
|
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();
|
err();
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,17 +59,14 @@
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="$ctrl.client.salesPersonFk"
|
ng-model="$ctrl.client.salesPersonFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
departments="['VT']"
|
||||||
show-field="nickname"
|
show-field="nickname"
|
||||||
search-function="{firstName: $search}"
|
|
||||||
value-field="id"
|
|
||||||
where="{role: 'salesPerson'}"
|
|
||||||
label="Salesperson"
|
label="Salesperson"
|
||||||
vn-acl="salesAssistant">
|
vn-acl="salesAssistant">
|
||||||
</vn-autocomplete>
|
</vn-worker-autocomplete>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="$ctrl.client.contactChannelFk"
|
ng-model="$ctrl.client.contactChannelFk"
|
||||||
|
|
|
@ -17,12 +17,11 @@
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.buyerId"
|
ng-model="filter.buyerId"
|
||||||
url="Workers/activeWithRole"
|
url="TicketRequests/getItemTypeWorker"
|
||||||
search-function="{firstName: $search}"
|
search-function="{firstName: $search}"
|
||||||
|
show-field="nickname"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
where="{role: {inq: ['logistic', 'buyer']}}"
|
|
||||||
label="Buyer">
|
label="Buyer">
|
||||||
<tpl-item>{{nickname}}</tpl-item>
|
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
|
|
|
@ -15,15 +15,12 @@
|
||||||
rule
|
rule
|
||||||
vn-focus>
|
vn-focus>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
label="Salesperson"
|
label="Salesperson"
|
||||||
ng-model="$ctrl.client.salesPersonFk"
|
ng-model="$ctrl.client.salesPersonFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
departments="['VT']"
|
||||||
search-function="{firstName: $search}"
|
show-field="nickname">
|
||||||
show-field="firstName"
|
</vn-worker-autocomplete>
|
||||||
where="{role: 'salesPerson'}">
|
|
||||||
<tpl-item>{{firstName}} {{lastName}}</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
|
|
|
@ -14,17 +14,12 @@
|
||||||
vn-one label="Name"
|
vn-one label="Name"
|
||||||
ng-model="filter.name">
|
ng-model="filter.name">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.salesPersonFk"
|
ng-model="filter.salesPersonFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
departments="['VT']"
|
||||||
search-function="{firstName: $search}"
|
|
||||||
show-field="firstName"
|
|
||||||
value-field="id"
|
|
||||||
where="{role: 'salesPerson'}"
|
|
||||||
label="Salesperson">
|
label="Salesperson">
|
||||||
<tpl-item>{{firstName}} {{name}}</tpl-item>
|
</vn-worker-autocomplete>
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
|
|
|
@ -54,11 +54,10 @@
|
||||||
vn-id="salesPerson"
|
vn-id="salesPerson"
|
||||||
disabled="false"
|
disabled="false"
|
||||||
ng-model="$ctrl.filter.salesPersonFk"
|
ng-model="$ctrl.filter.salesPersonFk"
|
||||||
url="Workers/activeWithRole"
|
url="TicketRequests/getItemTypeWorker"
|
||||||
show-field="nickname"
|
show-field="nickname"
|
||||||
search-function="{firstName: $search}"
|
search-function="{firstName: $search}"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
where="{role: {inq: ['logistic', 'buyer']}}"
|
|
||||||
label="Buyer"
|
label="Buyer"
|
||||||
on-change="$ctrl.addFilters()">
|
on-change="$ctrl.addFilters()">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
|
|
|
@ -28,11 +28,15 @@ module.exports = Self => {
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
const stmt = new ParameterizedSQL(`
|
const stmt = new ParameterizedSQL(`
|
||||||
SELECT iss.created,
|
SELECT
|
||||||
|
iss.id,
|
||||||
|
iss.created,
|
||||||
iss.saleFk,
|
iss.saleFk,
|
||||||
iss.quantity,
|
iss.quantity,
|
||||||
iss.userFk,
|
iss.userFk,
|
||||||
|
ish.id itemShelvingFk,
|
||||||
ish.shelvingFk,
|
ish.shelvingFk,
|
||||||
|
s.parkingFk,
|
||||||
p.code,
|
p.code,
|
||||||
u.name
|
u.name
|
||||||
FROM itemShelvingSale iss
|
FROM itemShelvingSale iss
|
||||||
|
|
|
@ -41,11 +41,6 @@
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "VnUser",
|
"model": "VnUser",
|
||||||
"foreignKey": "userFk"
|
"foreignKey": "userFk"
|
||||||
},
|
|
||||||
"shelving": {
|
|
||||||
"type": "belongsTo",
|
|
||||||
"model": "Shelving",
|
|
||||||
"foreignKey": "shelvingFk"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,11 +54,10 @@
|
||||||
vn-id="buyer"
|
vn-id="buyer"
|
||||||
disabled="false"
|
disabled="false"
|
||||||
ng-model="$ctrl.filter.buyerFk"
|
ng-model="$ctrl.filter.buyerFk"
|
||||||
url="Workers/activeWithRole"
|
url="TicketRequests/getItemTypeWorker"
|
||||||
show-field="nickname"
|
show-field="nickname"
|
||||||
search-function="{firstName: $search}"
|
search-function="{firstName: $search}"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
where="{role: {inq: ['logistic', 'buyer']}}"
|
|
||||||
label="Buyer"
|
label="Buyer"
|
||||||
on-change="$ctrl.addFilters()">
|
on-change="$ctrl.addFilters()">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
|
|
|
@ -44,8 +44,7 @@ class Controller extends Section {
|
||||||
{
|
{
|
||||||
field: 'buyerFk',
|
field: 'buyerFk',
|
||||||
autocomplete: {
|
autocomplete: {
|
||||||
url: 'Workers/activeWithRole',
|
url: 'TicketRequests/getItemTypeWorker',
|
||||||
where: `{role: {inq: ['logistic', 'buyer']}}`,
|
|
||||||
searchFunction: '{firstName: $search}',
|
searchFunction: '{firstName: $search}',
|
||||||
showField: 'nickname',
|
showField: 'nickname',
|
||||||
valueField: 'id',
|
valueField: 'id',
|
||||||
|
|
|
@ -22,12 +22,11 @@
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.attenderFk"
|
ng-model="filter.attenderFk"
|
||||||
url="Workers/activeWithRole"
|
url="TicketRequests/getItemTypeWorker"
|
||||||
search-function="{firstName: $search}"
|
search-function="{firstName: $search}"
|
||||||
|
show-field="nickname"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
where="{role: {inq: ['logistic', 'buyer']}}"
|
label="Atender">
|
||||||
label="Buyer">
|
|
||||||
<tpl-item>{{nickname}}</tpl-item>
|
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal class="vn-px-lg">
|
<vn-horizontal class="vn-px-lg">
|
||||||
|
@ -46,18 +45,13 @@
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal class="vn-px-lg">
|
<vn-horizontal class="vn-px-lg">
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.requesterFk"
|
ng-model="filter.requesterFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
departments="['VT']"
|
||||||
search-function="{firstName: $search}"
|
label="Salesperson">
|
||||||
value-field="id"
|
</vn-worker-autocomplete>
|
||||||
where="{role: 'salesPerson'}"
|
|
||||||
label="Comercial">
|
|
||||||
<tpl-item>{{firstName}} {{lastName}}</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
|
||||||
<section class="vn-px-md">
|
<section class="vn-px-md">
|
||||||
<vn-horizontal class="manifold-panel vn-pa-md">
|
<vn-horizontal class="manifold-panel vn-pa-md">
|
||||||
<vn-date-picker
|
<vn-date-picker
|
||||||
|
|
|
@ -43,16 +43,12 @@
|
||||||
label="Nickname"
|
label="Nickname"
|
||||||
ng-model="filter.nickname">
|
ng-model="filter.nickname">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.salesPersonFk"
|
ng-model="filter.salesPersonFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
departments="['VT']"
|
||||||
search-function="{firstName: $search}"
|
|
||||||
value-field="id"
|
|
||||||
where="{role: 'employee'}"
|
|
||||||
label="Sales person">
|
label="Sales person">
|
||||||
<tpl-item>{{firstName}} {{name}}</tpl-item>
|
</vn-worker-autocomplete>
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
label="Invoice"
|
label="Invoice"
|
||||||
|
|
|
@ -25,10 +25,6 @@ class Controller extends SearchPanel {
|
||||||
this.filter.values.push({});
|
this.filter.values.push({});
|
||||||
setTimeout(() => this.parentPopover.relocate());
|
setTimeout(() => this.parentPopover.relocate());
|
||||||
}
|
}
|
||||||
|
|
||||||
changeTag() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnOrderCatalogSearchPanel', {
|
ngModule.vnComponent('vnOrderCatalogSearchPanel', {
|
||||||
|
|
|
@ -25,16 +25,13 @@
|
||||||
show-field="name"
|
show-field="name"
|
||||||
value-field="id">
|
value-field="id">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.workerFk"
|
ng-model="filter.workerFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
departments="['VT']"
|
||||||
search-function="{firstName: $search}"
|
|
||||||
show-field="nickname"
|
show-field="nickname"
|
||||||
value-field="id"
|
|
||||||
where="{role: 'salesPerson'}"
|
|
||||||
label="Sales person">
|
label="Sales person">
|
||||||
</vn-autocomplete>
|
</vn-worker-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-date-picker
|
<vn-date-picker
|
||||||
|
|
|
@ -95,7 +95,7 @@ module.exports = Self => {
|
||||||
t.id ticketFk,
|
t.id ticketFk,
|
||||||
co.country,
|
co.country,
|
||||||
t.clientFk,
|
t.clientFk,
|
||||||
sub.id hasCmrDms,
|
IF(sub.id, TRUE, FALSE) hasCmrDms,
|
||||||
DATE(t.shipped) shipped
|
DATE(t.shipped) shipped
|
||||||
FROM ticket t
|
FROM ticket t
|
||||||
JOIN ticketState ts ON ts.ticketFk = t.id
|
JOIN ticketState ts ON ts.ticketFk = t.id
|
||||||
|
|
|
@ -24,7 +24,14 @@ module.exports = Self => {
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
const route = await Self.app.models.Route.findById(id, null, myOptions);
|
const route = await Self.app.models.Route.findById(id, {
|
||||||
|
include: {
|
||||||
|
relation: 'agencyMode',
|
||||||
|
scope: {
|
||||||
|
fields: ['name']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
const zoneAgencyModes = await Self.app.models.ZoneAgencyMode.find({
|
const zoneAgencyModes = await Self.app.models.ZoneAgencyMode.find({
|
||||||
where: {
|
where: {
|
||||||
|
@ -35,11 +42,13 @@ module.exports = Self => {
|
||||||
const zoneIds = [];
|
const zoneIds = [];
|
||||||
for (let zoneAgencyMode of zoneAgencyModes)
|
for (let zoneAgencyMode of zoneAgencyModes)
|
||||||
zoneIds.push(zoneAgencyMode.zoneFk);
|
zoneIds.push(zoneAgencyMode.zoneFk);
|
||||||
|
|
||||||
const minDate = new Date(route.created);
|
const minDate = new Date(route.created);
|
||||||
minDate.setHours(0, 0, 0, 0);
|
minDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
const maxDate = new Date(route.created);
|
const maxDate = new Date(route.created);
|
||||||
maxDate.setHours(23, 59, 59, 59);
|
maxDate.setHours(23, 59, 59, 59);
|
||||||
|
|
||||||
let tickets = await Self.app.models.Ticket.find({
|
let tickets = await Self.app.models.Ticket.find({
|
||||||
where: {
|
where: {
|
||||||
zoneFk: {inq: zoneIds},
|
zoneFk: {inq: zoneIds},
|
||||||
|
@ -80,6 +89,12 @@ module.exports = Self => {
|
||||||
]
|
]
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
return tickets;
|
return tickets.map(ticket => {
|
||||||
|
const simpleTicket = ticket.toJSON();
|
||||||
|
return {
|
||||||
|
...simpleTicket,
|
||||||
|
agencyName: route.agencyMode().name
|
||||||
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,20 +8,11 @@
|
||||||
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
|
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
|
||||||
<vn-card class="vn-pa-lg">
|
<vn-card class="vn-pa-lg">
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
ng-model="$ctrl.route.workerFk"
|
ng-model="$ctrl.route.workerFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
|
||||||
show-field="nickname"
|
show-field="nickname"
|
||||||
search-function="{firstName: $search}"
|
|
||||||
value-field="id"
|
|
||||||
where="{role: 'employee'}"
|
|
||||||
label="Worker"
|
|
||||||
vn-name="worker">
|
vn-name="worker">
|
||||||
<tpl-item>
|
</vn-worker-autocomplete>
|
||||||
<div>{{::nickname}}</div>
|
|
||||||
<div class="text-secondary text-caption">{{::name}}</div>
|
|
||||||
</tpl-item>
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
label="Vehicle"
|
label="Vehicle"
|
||||||
ng-model="$ctrl.route.vehicleFk"
|
ng-model="$ctrl.route.vehicleFk"
|
||||||
|
|
|
@ -8,14 +8,10 @@
|
||||||
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
|
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
|
||||||
<vn-card class="vn-pa-lg">
|
<vn-card class="vn-pa-lg">
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
label="Worker"
|
|
||||||
ng-model="$ctrl.route.workerFk"
|
ng-model="$ctrl.route.workerFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
show-field="nickname">
|
||||||
show-field="nickname"
|
</vn-worker-autocomplete>
|
||||||
search-function="{firstName: $search}"
|
|
||||||
where="{role: 'employee'}">
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-date-picker
|
<vn-date-picker
|
||||||
label="Created"
|
label="Created"
|
||||||
ng-model="$ctrl.route.created">
|
ng-model="$ctrl.route.created">
|
||||||
|
|
|
@ -15,16 +15,11 @@
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal class="vn-px-lg">
|
<vn-horizontal class="vn-px-lg">
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.workerFk"
|
ng-model="filter.workerFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
show-field="nickname">
|
||||||
show-field="nickname"
|
</vn-worker-autocomplete>
|
||||||
search-function="{firstName: $search}"
|
|
||||||
value-field="id"
|
|
||||||
where="{role: 'employee'}"
|
|
||||||
label="Worker">
|
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
label="Agency"
|
label="Agency"
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
icon="link_off"
|
icon="link_off"
|
||||||
class="pointer"
|
class="pointer"
|
||||||
title="{{'Unlink zone' | translate: {zoneName: ticket.zone.name, agencyName: ticket.agencyMode.name} }}"
|
title="{{'Unlink zone' | translate: {zoneName: ticket.zone.name, agencyName: ticket.agencyName} }}"
|
||||||
ng-click="unlinkZoneConfirmation.show(ticket)">
|
ng-click="unlinkZoneConfirmation.show(ticket)">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
<vn-th field="street" expand>Street</vn-th>
|
<vn-th field="street" expand>Street</vn-th>
|
||||||
<vn-th field="city">City</vn-th>
|
<vn-th field="city">City</vn-th>
|
||||||
<vn-th field="postalCode" translate-attr="{title: 'Postcode'}" shrink>PC</vn-th>
|
<vn-th field="postalCode" translate-attr="{title: 'Postcode'}" shrink>PC</vn-th>
|
||||||
<vn-th field="clientFk" expand>Client</vn-th>
|
<vn-th field="nickname" expand>Client</vn-th>
|
||||||
<vn-th field="warehouse" expand>Warehouse</vn-th>
|
<vn-th field="warehouse" expand>Warehouse</vn-th>
|
||||||
<vn-th field="packages" shrink>Packages</vn-th>
|
<vn-th field="packages" shrink>Packages</vn-th>
|
||||||
<vn-th field="volume" shrink>m³</vn-th>
|
<vn-th field="volume" shrink>m³</vn-th>
|
||||||
|
|
|
@ -7,18 +7,18 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function ibanValidation(err, done) {
|
async function ibanValidation(err, done) {
|
||||||
const supplier = await Self.app.models.Supplier.findById(this.supplierFk);
|
if (!this.bankEntityFk)
|
||||||
|
return done();
|
||||||
|
|
||||||
|
const bankEntity = await Self.app.models.BankEntity.findById(this.bankEntityFk);
|
||||||
const filter = {
|
const filter = {
|
||||||
fields: ['code'],
|
fields: ['code'],
|
||||||
where: {id: supplier.countryFk}
|
where: {id: bankEntity.countryFk}
|
||||||
};
|
};
|
||||||
|
|
||||||
const country = await Self.app.models.Country.findOne(filter);
|
const country = await Self.app.models.Country.findOne(filter);
|
||||||
const code = country ? country.code.toLowerCase() : null;
|
|
||||||
if (code != 'es')
|
|
||||||
return done();
|
|
||||||
|
|
||||||
if (!validateIban(this.iban))
|
if (!validateIban(this.iban, country?.code))
|
||||||
err();
|
err();
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,12 +142,12 @@ module.exports = Self => {
|
||||||
const changes = ctx.data || ctx.instance;
|
const changes = ctx.data || ctx.instance;
|
||||||
const orgData = ctx.currentInstance;
|
const orgData = ctx.currentInstance;
|
||||||
|
|
||||||
const socialName = changes.name || orgData.name;
|
const name = changes.name || orgData.name;
|
||||||
const hasChanges = orgData && changes;
|
const hasChanges = orgData && changes;
|
||||||
const socialNameChanged = hasChanges
|
const nameChanged = hasChanges
|
||||||
&& orgData.socialName != socialName;
|
&& orgData.name != name;
|
||||||
|
|
||||||
if ((socialNameChanged) && !isAlpha(socialName))
|
if ((nameChanged) && !isAlpha(name))
|
||||||
throw new UserError('The social name has an invalid format');
|
throw new UserError('The social name has an invalid format');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,17 +15,13 @@
|
||||||
rule
|
rule
|
||||||
vn-focus>
|
vn-focus>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="$ctrl.supplier.workerFk"
|
ng-model="$ctrl.supplier.workerFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
|
||||||
search-function="{firstName: $search}"
|
|
||||||
show-field="nickname"
|
show-field="nickname"
|
||||||
value-field="id"
|
|
||||||
where="{role: 'employee'}"
|
|
||||||
label="Responsible"
|
label="Responsible"
|
||||||
info="Responsible for approving invoices">
|
info="Responsible for approving invoices">
|
||||||
</vn-autocomplete>
|
</vn-worker-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-check
|
<vn-check
|
||||||
|
|
|
@ -17,12 +17,11 @@
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.buyerId"
|
ng-model="filter.buyerId"
|
||||||
url="Workers/activeWithRole"
|
url="TicketRequests/getItemTypeWorker"
|
||||||
search-function="{firstName: $search}"
|
search-function="{firstName: $search}"
|
||||||
|
show-field="nickname"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
where="{role: {inq: ['logistic', 'buyer']}}"
|
|
||||||
label="Buyer">
|
label="Buyer">
|
||||||
<tpl-item>{{nickname}}</tpl-item>
|
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('addExpeditionState', {
|
||||||
|
description: 'Update an expedition state',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'expeditions',
|
||||||
|
type: ['object'],
|
||||||
|
required: true,
|
||||||
|
description: 'Array of objects containing expeditionFk and stateCode'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'boolean',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/addExpeditionState`,
|
||||||
|
verb: 'post'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.addExpeditionState = async(expeditions, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
let tx;
|
||||||
|
const myOptions = {};
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
let expeditionId;
|
||||||
|
try {
|
||||||
|
for (const expedition of expeditions) {
|
||||||
|
const expeditionStateType = await models.ExpeditionStateType.findOne(
|
||||||
|
{
|
||||||
|
fields: ['id'],
|
||||||
|
where: {code: expedition.stateCode}
|
||||||
|
}, myOptions
|
||||||
|
);
|
||||||
|
if (!expeditionStateType)
|
||||||
|
throw new UserError(`Invalid state code: ${expedition.stateCode}.`);
|
||||||
|
|
||||||
|
const typeFk = expeditionStateType.id;
|
||||||
|
expeditionId = expedition.expeditionFk;
|
||||||
|
|
||||||
|
await models.ExpeditionState.create({
|
||||||
|
expeditionFk: expedition.expeditionFk,
|
||||||
|
typeFk,
|
||||||
|
}, myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
if (e instanceof UserError)
|
||||||
|
throw e;
|
||||||
|
|
||||||
|
throw new UserError(`Invalid expedition id: ${expeditionId}.`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,75 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('expeditionState addExpeditionState()', () => {
|
||||||
|
it('should update the expedition states', async() => {
|
||||||
|
const tx = await models.ExpeditionState.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const payload = [
|
||||||
|
{
|
||||||
|
expeditionFk: 8,
|
||||||
|
stateCode: 'ON DELIVERY'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
await models.ExpeditionState.addExpeditionState(payload, options);
|
||||||
|
|
||||||
|
const expeditionState = await models.ExpeditionState.findOne({
|
||||||
|
where: {id: 5}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(expeditionState.typeFk).toEqual(1);
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error an error when an stateCode does not exist', async() => {
|
||||||
|
const tx = await models.ExpeditionState.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const payload = [
|
||||||
|
{
|
||||||
|
expeditionFk: 2,
|
||||||
|
stateCode: 'DUMMY'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
await models.ExpeditionState.addExpeditionState(payload, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toContain('Invalid state code: DUMMY.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when expeditionFk does not exist', async() => {
|
||||||
|
const tx = await models.ExpeditionState.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const payload = [
|
||||||
|
{
|
||||||
|
expeditionFk: 50,
|
||||||
|
stateCode: 'LOST'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
await models.ExpeditionState.addExpeditionState(payload, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toContain('Invalid expedition id: 50.');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,69 @@
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('delete', {
|
||||||
|
description: 'Delete sale trackings and item shelving sales',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'saleFk',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The sale id'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'stateCode',
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/delete`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.delete = async(saleFk, stateCode, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const myOptions = {};
|
||||||
|
let tx;
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (stateCode === 'PREPARED') {
|
||||||
|
const itemShelvingSales = await models.ItemShelvingSale.find({where: {saleFk: saleFk}}, myOptions);
|
||||||
|
for (let itemShelvingSale of itemShelvingSales)
|
||||||
|
await itemShelvingSale.destroy(myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = await models.State.findOne({
|
||||||
|
where: {code: stateCode}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
where: {
|
||||||
|
saleFk: saleFk,
|
||||||
|
stateFk: state.id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const saleTrackings = await models.SaleTracking.find(filter, myOptions);
|
||||||
|
for (let saleTracking of saleTrackings)
|
||||||
|
await saleTracking.destroy(myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,94 @@
|
||||||
|
|
||||||
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('filter', {
|
||||||
|
description: 'Returns a list with the lines of a ticket and its different states of preparation',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The ticket id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
description: 'Filter defining where and paginated data'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/filter`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.filter = async(id, filter, options) => {
|
||||||
|
const conn = Self.dataSource.connector;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const stmts = [];
|
||||||
|
let stmt;
|
||||||
|
|
||||||
|
stmts.push('CALL cache.last_buy_refresh(FALSE)');
|
||||||
|
|
||||||
|
stmt = new ParameterizedSQL(
|
||||||
|
`SELECT t.clientFk,
|
||||||
|
t.shipped,
|
||||||
|
s.ticketFk,
|
||||||
|
s.itemFk,
|
||||||
|
s.quantity,
|
||||||
|
s.concept,
|
||||||
|
s.id saleFk,
|
||||||
|
i.image,
|
||||||
|
i.subName,
|
||||||
|
IF(stPrevious.saleFk,TRUE,FALSE) as isPreviousSelected,
|
||||||
|
stPrevious.isChecked as isPrevious,
|
||||||
|
stPrepared.isChecked as isPrepared,
|
||||||
|
stControled.isChecked as isControled,
|
||||||
|
sgd.id saleGroupDetailFk,
|
||||||
|
(MAX(sgd.id) IS NOT NULL) AS hasSaleGroupDetail,
|
||||||
|
p.code AS parkingCode,
|
||||||
|
i.value5,
|
||||||
|
i.value6,
|
||||||
|
i.value7,
|
||||||
|
i.value8,
|
||||||
|
i.value9,
|
||||||
|
i.value10
|
||||||
|
FROM vn.ticket t
|
||||||
|
JOIN vn.sale s ON s.ticketFk = t.id
|
||||||
|
JOIN vn.item i ON i.id = s.itemFk
|
||||||
|
LEFT JOIN cache.last_buy lb ON lb.item_id = i.id AND lb.warehouse_id = t.warehouseFk
|
||||||
|
LEFT JOIN vn.state st ON TRUE
|
||||||
|
LEFT JOIN vn.saleTracking stPrevious ON stPrevious.saleFk = s.id
|
||||||
|
AND stPrevious.stateFk = (SELECT id FROM vn.state WHERE code = 'PREVIOUS_PREPARATION')
|
||||||
|
LEFT JOIN vn.saleTracking stPrepared ON stPrepared.saleFk = s.id
|
||||||
|
AND stPrepared.stateFk = (SELECT id FROM vn.state WHERE code = 'PREPARED')
|
||||||
|
LEFT JOIN vn.saleTracking stControled ON stControled.saleFk = s.id
|
||||||
|
AND stControled.stateFk = (SELECT id FROM vn.state s2 WHERE code = 'CHECKED')
|
||||||
|
LEFT JOIN vn.saleGroupDetail sgd ON sgd.saleFk = s.id
|
||||||
|
LEFT JOIN vn.saleGroup sg ON sg.id = sgd.saleGroupFk
|
||||||
|
LEFT JOIN vn.parking p ON p.id = sg.parkingFk
|
||||||
|
WHERE t.id = ?
|
||||||
|
GROUP BY s.id`, [id]);
|
||||||
|
|
||||||
|
stmts.push(stmt);
|
||||||
|
|
||||||
|
stmt.merge(Self.makeSuffix(filter));
|
||||||
|
|
||||||
|
const index = stmts.push(stmt) - 1;
|
||||||
|
const sql = ParameterizedSQL.join(stmts, ';');
|
||||||
|
const result = await conn.executeStmt(sql, myOptions);
|
||||||
|
|
||||||
|
return result[index];
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,90 @@
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('new', {
|
||||||
|
description: `Replaces the record or creates it if it doesn't exist`,
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'saleFk',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The sale id'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'isChecked',
|
||||||
|
type: 'boolean'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'quantity',
|
||||||
|
type: 'number'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'stateCode',
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/new`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.new = async(ctx, saleFk, isChecked, quantity, stateCode, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const userId = ctx.req.accessToken.userId;
|
||||||
|
const myOptions = {};
|
||||||
|
let tx;
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const state = await models.State.findOne({
|
||||||
|
where: {code: stateCode}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const saleTracking = await models.SaleTracking.findOne({
|
||||||
|
where: {
|
||||||
|
saleFk: saleFk,
|
||||||
|
stateFk: state.id,
|
||||||
|
workerFk: userId
|
||||||
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
let newSaleTracking;
|
||||||
|
if (saleTracking) {
|
||||||
|
newSaleTracking = await saleTracking.updateAttributes({
|
||||||
|
saleFk: saleFk,
|
||||||
|
stateFk: state.id,
|
||||||
|
workerFk: userId,
|
||||||
|
isChecked: isChecked,
|
||||||
|
originalQuantity: quantity,
|
||||||
|
isScanned: null
|
||||||
|
}, myOptions);
|
||||||
|
} else {
|
||||||
|
newSaleTracking = await models.SaleTracking.create({
|
||||||
|
saleFk: saleFk,
|
||||||
|
stateFk: state.id,
|
||||||
|
workerFk: userId,
|
||||||
|
isChecked: isChecked,
|
||||||
|
originalQuantity: quantity,
|
||||||
|
isScanned: null
|
||||||
|
}, myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return newSaleTracking;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('sale-tracking delete()', () => {
|
||||||
|
it('should delete a row of saleTracking and itemShelvingSale', async() => {
|
||||||
|
const tx = await models.SaleTracking.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const itemShelvingsBefore = await models.ItemShelvingSale.find(null, options);
|
||||||
|
const saleTrackingsBefore = await models.SaleTracking.find(null, options);
|
||||||
|
|
||||||
|
const saleFk = 1;
|
||||||
|
const stateCode = 'PREPARED';
|
||||||
|
const result = await models.SaleTracking.delete(saleFk, stateCode, options);
|
||||||
|
|
||||||
|
const itemShelvingsAfter = await models.ItemShelvingSale.find(null, options);
|
||||||
|
const saleTrackingsAfter = await models.SaleTracking.find(null, options);
|
||||||
|
|
||||||
|
expect(result).toEqual(true);
|
||||||
|
expect(saleTrackingsAfter.length).toBeLessThan(saleTrackingsBefore.length);
|
||||||
|
expect(itemShelvingsAfter.length).toBeLessThan(itemShelvingsBefore.length);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
|
describe('sale-tracking filter()', () => {
|
||||||
|
it('should return 1 result filtering by ticket id', async() => {
|
||||||
|
const tx = await app.models.Claim.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const id = 1;
|
||||||
|
const filter = {order: ['concept ASC', 'quantity DESC']};
|
||||||
|
const result = await app.models.SaleTracking.filter(id, filter, options);
|
||||||
|
|
||||||
|
expect(result.length).toEqual(4);
|
||||||
|
expect(result[0].ticketFk).toEqual(1);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('ticket listSaleTracking()', () => {
|
describe('sale-tracking listSaleTracking()', () => {
|
||||||
it('should call the listSaleTracking method and return the response', async() => {
|
it('should call the listSaleTracking method and return the response', async() => {
|
||||||
const tx = await models.SaleTracking.beginTransaction({});
|
const tx = await models.SaleTracking.beginTransaction({});
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('sale-tracking new()', () => {
|
||||||
|
it('should update a saleTracking', async() => {
|
||||||
|
const tx = await models.SaleTracking.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const ctx = {req: {accessToken: {userId: 55}}};
|
||||||
|
|
||||||
|
const saleFk = 1;
|
||||||
|
const isChecked = true;
|
||||||
|
const quantity = 20;
|
||||||
|
const stateCode = 'PREPARED';
|
||||||
|
const result = await models.SaleTracking.new(ctx, saleFk, isChecked, quantity, stateCode, options);
|
||||||
|
|
||||||
|
expect(result.isChecked).toBe(true);
|
||||||
|
expect(result.originalQuantity).toBe(20);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a saleTracking', async() => {
|
||||||
|
const tx = await models.SaleTracking.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const ctx = {req: {accessToken: {userId: 1}}};
|
||||||
|
|
||||||
|
const saleFk = 1;
|
||||||
|
const isChecked = true;
|
||||||
|
const quantity = 20;
|
||||||
|
const stateCode = 'PREPARED';
|
||||||
|
const result = await models.SaleTracking.new(ctx, saleFk, isChecked, quantity, stateCode, options);
|
||||||
|
|
||||||
|
expect(result.isChecked).toBe(true);
|
||||||
|
expect(result.originalQuantity).toBe(20);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -67,8 +67,12 @@ module.exports = Self => {
|
||||||
const sales = await models.Sale.find(salesFilter, myOptions);
|
const sales = await models.Sale.find(salesFilter, myOptions);
|
||||||
const ticketsIds = [...new Set(sales.map(sale => sale.ticketFk))];
|
const ticketsIds = [...new Set(sales.map(sale => sale.ticketFk))];
|
||||||
|
|
||||||
|
let [firstTicketId] = ticketsIds;
|
||||||
|
if (!firstTicketId) {
|
||||||
|
[ticketServices] = await models.TicketService.find({where: {id: {inq: servicesIds}}}, myOptions);
|
||||||
|
firstTicketId = ticketServices.ticketFk;
|
||||||
|
}
|
||||||
const now = Date.vnNew();
|
const now = Date.vnNew();
|
||||||
const [firstTicketId] = ticketsIds;
|
|
||||||
|
|
||||||
const refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions);
|
const refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions);
|
||||||
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethodCtx('salePreparingList', {
|
|
||||||
description: 'Returns a list with the lines of a ticket and its different states of preparation',
|
|
||||||
accessType: 'READ',
|
|
||||||
accepts: [{
|
|
||||||
arg: 'id',
|
|
||||||
type: 'number',
|
|
||||||
required: true,
|
|
||||||
description: 'The ticket id',
|
|
||||||
http: {source: 'path'}
|
|
||||||
}],
|
|
||||||
returns: {
|
|
||||||
type: ['object'],
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
path: `/:id/salePreparingList`,
|
|
||||||
verb: 'GET'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.salePreparingList = async(ctx, id, options) => {
|
|
||||||
const myOptions = {};
|
|
||||||
|
|
||||||
if (typeof options == 'object')
|
|
||||||
Object.assign(myOptions, options);
|
|
||||||
|
|
||||||
query = `CALL vn.salePreparingList(?)`;
|
|
||||||
const [sales] = await Self.rawSql(query, [id], myOptions);
|
|
||||||
|
|
||||||
return sales;
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -22,9 +22,13 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.getItemTypeWorker = async filter => {
|
Self.getItemTypeWorker = async(filter, options) => {
|
||||||
|
const myOptions = {};
|
||||||
const conn = Self.dataSource.connector;
|
const conn = Self.dataSource.connector;
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
const query =
|
const query =
|
||||||
`SELECT DISTINCT u.id, u.nickname
|
`SELECT DISTINCT u.id, u.nickname
|
||||||
FROM itemType it
|
FROM itemType it
|
||||||
|
|
|
@ -32,6 +32,14 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.docuwareDownload = async id => {
|
Self.docuwareDownload = async id => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const docuwareInfo = await models.Docuware.findOne({
|
||||||
|
where: {
|
||||||
|
code: 'deliveryNote',
|
||||||
|
action: 'find'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
condition: [
|
condition: [
|
||||||
{
|
{
|
||||||
|
@ -50,6 +58,6 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
return Self.app.models.Docuware.download(id, 'deliveryNote', filter);
|
return models.Docuware.download(id, 'deliveryNote', filter);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('priceDifference', {
|
Self.remoteMethodCtx('priceDifference', {
|
||||||
description: 'Returns sales with price difference if the ticket is editable',
|
description: 'Returns sales with price difference if the ticket is editable',
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
"ExpeditionState": {
|
"ExpeditionState": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"ExpeditionStateType": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"Packaging": {
|
"Packaging": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "ExpeditionStateType",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "expeditionStateType"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number",
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"code": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
module.exports = function(Self) {
|
module.exports = function(Self) {
|
||||||
require('../methods/expedition-state/filter')(Self);
|
require('../methods/expedition-state/filter')(Self);
|
||||||
|
require('../methods/expedition-state/addExpeditionState')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
|
require('../methods/sale-tracking/filter')(Self);
|
||||||
require('../methods/sale-tracking/listSaleTracking')(Self);
|
require('../methods/sale-tracking/listSaleTracking')(Self);
|
||||||
|
require('../methods/sale-tracking/new')(Self);
|
||||||
|
require('../methods/sale-tracking/delete')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/sale/getClaimableFromTicket')(Self);
|
require('../methods/sale/getClaimableFromTicket')(Self);
|
||||||
require('../methods/sale/salePreparingList')(Self);
|
|
||||||
require('../methods/sale/reserve')(Self);
|
require('../methods/sale/reserve')(Self);
|
||||||
require('../methods/sale/deleteSales')(Self);
|
require('../methods/sale/deleteSales')(Self);
|
||||||
require('../methods/sale/updatePrice')(Self);
|
require('../methods/sale/updatePrice')(Self);
|
||||||
|
|
|
@ -42,4 +42,5 @@ module.exports = function(Self) {
|
||||||
require('../methods/ticket/expeditionPalletLabel')(Self);
|
require('../methods/ticket/expeditionPalletLabel')(Self);
|
||||||
require('../methods/ticket/saveSign')(Self);
|
require('../methods/ticket/saveSign')(Self);
|
||||||
require('../methods/ticket/invoiceTickets')(Self);
|
require('../methods/ticket/invoiceTickets')(Self);
|
||||||
|
require('../methods/ticket/docuwareDownload')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,10 +3,11 @@ import Section from 'salix/components/section';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
class Controller extends Section {
|
class Controller extends Section {
|
||||||
constructor($element, $, vnReport, vnEmail) {
|
constructor($element, $, vnReport, vnEmail, vnFile) {
|
||||||
super($element, $);
|
super($element, $);
|
||||||
this.vnReport = vnReport;
|
this.vnReport = vnReport;
|
||||||
this.vnEmail = vnEmail;
|
this.vnEmail = vnEmail;
|
||||||
|
this.vnFile = vnFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
get ticketId() {
|
get ticketId() {
|
||||||
|
@ -322,7 +323,7 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
docuwareDownload() {
|
docuwareDownload() {
|
||||||
this.vnFile.download(`api/Ticket/${this.ticket.id}/docuwareDownload`);
|
this.vnFile.download(`api/Tickets/${this.ticket.id}/docuwareDownload`);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTicketWeight(weight) {
|
setTicketWeight(weight) {
|
||||||
|
@ -335,7 +336,7 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail'];
|
Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail', 'vnFile'];
|
||||||
|
|
||||||
ngModule.vnComponent('vnTicketDescriptorMenu', {
|
ngModule.vnComponent('vnTicketDescriptorMenu', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
url="sales"
|
url="SaleTrackings/{{$ctrl.$params.id}}/filter"
|
||||||
filter="::$ctrl.filter"
|
|
||||||
link="{ticketFk: $ctrl.$params.id}"
|
|
||||||
limit="20"
|
limit="20"
|
||||||
data="$ctrl.sales"
|
data="$ctrl.sales"
|
||||||
order="concept ASC"
|
order="concept ASC, quantity DESC"
|
||||||
|
auto-load="true">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-crud-model
|
||||||
|
url="Shelvings"
|
||||||
|
data="shelvings"
|
||||||
|
auto-load="true">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-crud-model
|
||||||
|
url="Parkings"
|
||||||
|
data="parkings"
|
||||||
auto-load="true">
|
auto-load="true">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-data-viewer model="model">
|
<vn-data-viewer model="model">
|
||||||
|
@ -13,7 +21,7 @@
|
||||||
<vn-table model="model">
|
<vn-table model="model">
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th field="isChecked" center>Is checked</vn-th>
|
<vn-th field="isChecked" center expand>Is checked</vn-th>
|
||||||
<vn-th field="itemFk" number>Item</vn-th>
|
<vn-th field="itemFk" number>Item</vn-th>
|
||||||
<vn-th field="concept">Description</vn-th>
|
<vn-th field="concept">Description</vn-th>
|
||||||
<vn-th field="quantity" number>Quantity</vn-th>
|
<vn-th field="quantity" number>Quantity</vn-th>
|
||||||
|
@ -23,76 +31,84 @@
|
||||||
</vn-thead>
|
</vn-thead>
|
||||||
<vn-tbody>
|
<vn-tbody>
|
||||||
<vn-tr ng-repeat="sale in $ctrl.sales">
|
<vn-tr ng-repeat="sale in $ctrl.sales">
|
||||||
<vn-td center>
|
<vn-td center expand>
|
||||||
<vn-chip
|
<vn-chip
|
||||||
ng-class="::{
|
ng-class="{
|
||||||
'pink': sale.preparingList.hasSaleGroupDetail,
|
'pink': sale.hasSaleGroupDetail,
|
||||||
'none': !sale.preparingList.hasSaleGroupDetail,
|
'none': !sale.hasSaleGroupDetail,
|
||||||
}"
|
}"
|
||||||
class="circleState"
|
class="circleState"
|
||||||
vn-tooltip="has saleGroupDetail"
|
vn-tooltip="sale group detail"
|
||||||
>
|
vn-click-stop="$ctrl.clickSaleGroupDetail($index)">
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
<vn-chip ng-class="::{
|
<vn-chip
|
||||||
'notice': sale.preparingList.isPreviousSelected,
|
ng-class="{
|
||||||
'none': !sale.preparingList.isPreviousSelected,
|
'notice': sale.isPreviousSelected,
|
||||||
|
'none': !sale.isPreviousSelected,
|
||||||
}"
|
}"
|
||||||
class="circleState"
|
class="circleState"
|
||||||
vn-tooltip="is previousSelected">
|
vn-tooltip="previous selected"
|
||||||
|
vn-click-stop="$ctrl.clickPreviousSelected($index)">
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
<vn-chip ng-class="::{
|
<vn-chip
|
||||||
'dark-notice': sale.preparingList.isPrevious,
|
ng-class="{
|
||||||
'none': !sale.preparingList.isPrevious,
|
'dark-notice': sale.isPrevious,
|
||||||
|
'none': !sale.isPrevious,
|
||||||
}"
|
}"
|
||||||
class="circleState"
|
class="circleState"
|
||||||
vn-tooltip="is previous">
|
vn-tooltip="previous"
|
||||||
|
vn-click-stop="$ctrl.clickPrevious($index)">
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
<vn-chip ng-class="::{
|
<vn-chip
|
||||||
'warning': sale.preparingList.isPrepared,
|
ng-class="{
|
||||||
'none': !sale.preparingList.isPrepared,
|
'warning': sale.isPrepared,
|
||||||
|
'none': !sale.isPrepared,
|
||||||
}"
|
}"
|
||||||
class="circleState"
|
class="circleState"
|
||||||
vn-tooltip="is prepared">
|
vn-tooltip="prepared"
|
||||||
|
vn-click-stop="$ctrl.clickPrepared($index)">
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
<vn-chip ng-class="::{
|
<vn-chip
|
||||||
'yellow': sale.preparingList.isControled,
|
ng-class="{
|
||||||
'none': !sale.preparingList.isControled,
|
'yellow': sale.isControled,
|
||||||
|
'none': !sale.isControled,
|
||||||
}"
|
}"
|
||||||
class="circleState"
|
class="circleState"
|
||||||
vn-tooltip="is controled">
|
vn-tooltip="checked"
|
||||||
|
vn-click-stop="$ctrl.clickControled($index)">
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
<span
|
<span
|
||||||
ng-click="itemDescriptor.show($event, sale.item.id)"
|
ng-click="itemDescriptor.show($event, sale.itemFk)"
|
||||||
class="link">
|
class="link">
|
||||||
{{::sale.item.id}}
|
{{::sale.itemFk}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td vn-fetched-tags>
|
<vn-td vn-fetched-tags>
|
||||||
<div>
|
<div>
|
||||||
<vn-one title="{{::sale.item.name}}">{{::sale.item.name}}</vn-one>
|
<vn-one title="{{::sale.concept}}">{{::sale.concept}}</vn-one>
|
||||||
<vn-one ng-if="::sale.item.subName">
|
<vn-one ng-if="::sale.subName">
|
||||||
<h3 title="{{::sale.item.subName}}">{{::sale.item.subName}}</h3>
|
<h3 title="{{::sale.subName}}">{{::sale.subName}}</h3>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
</div>
|
</div>
|
||||||
<vn-fetched-tags
|
<vn-fetched-tags
|
||||||
max-length="6"
|
max-length="6"
|
||||||
item="::sale.item"
|
item="::sale"
|
||||||
tabindex="-1">
|
tabindex="-1">
|
||||||
</vn-fetched-tags>
|
</vn-fetched-tags>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>{{::sale.quantity}}</vn-td>
|
<vn-td number>{{::sale.quantity}}</vn-td>
|
||||||
<vn-td center>{{::sale.saleGroupDetail.saleGroup.parking.code | dashIfEmpty}}</vn-td>
|
<vn-td center>{{::sale.parkingCode | dashIfEmpty}}</vn-td>
|
||||||
<vn-td actions>
|
<vn-td actions>
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
vn-click-stop="$ctrl.showSaleTracking(sale)"
|
vn-click-stop="$ctrl.showSaleTracking(sale)"
|
||||||
vn-tooltip="Sale tracking"
|
vn-tooltip="Log states"
|
||||||
icon="history">
|
icon="history">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
vn-click-stop="$ctrl.showItemShelvingSale(sale)"
|
vn-click-stop="$ctrl.showItemShelvingSale(sale)"
|
||||||
vn-tooltip="ItemShelvings sale"
|
vn-tooltip="Shelvings sale"
|
||||||
icon="icon-inventory">
|
icon="icon-inventory">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
|
@ -154,28 +170,35 @@
|
||||||
|
|
||||||
<vn-popup vn-id="itemShelvingSale">
|
<vn-popup vn-id="itemShelvingSale">
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="modelSaleTracking"
|
vn-id="modelItemShelvingSale"
|
||||||
url="ItemShelvingSales/filter"
|
url="ItemShelvingSales/filter"
|
||||||
link="{saleFk: $ctrl.saleId}"
|
link="{saleFk: $ctrl.saleId}"
|
||||||
limit="20"
|
limit="20"
|
||||||
data="$ctrl.itemShelvingSales"
|
data="$ctrl.itemShelvingSales"
|
||||||
auto-load="true">
|
auto-load="true">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-data-viewer model="modelSaleTracking">
|
<vn-data-viewer model="modelItemShelvingSale" class="vn-w-lg">
|
||||||
<vn-card class="vn-w-lg">
|
<vn-table>
|
||||||
<vn-table model="modelSaleTracking">
|
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th field="quantity" number>Quantity</vn-th>
|
<vn-th field="quantity" number>Quantity</vn-th>
|
||||||
<vn-th field="workerFk">Worker</vn-th>
|
<vn-th field="workerFk">Worker</vn-th>
|
||||||
<vn-th field="shelving" shrink>Shelving</vn-th>
|
<vn-th field="shelving" expand>Shelving</vn-th>
|
||||||
<vn-th field="parking" shrink>Parking</vn-th>
|
<vn-th field="parking" expand>Parking</vn-th>
|
||||||
<vn-th field="created" expand>Created</vn-th>
|
<vn-th field="created" expand>Created</vn-th>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
</vn-thead>
|
</vn-thead>
|
||||||
<vn-tbody>
|
<vn-tbody>
|
||||||
<vn-tr ng-repeat="itemShelvingSale in $ctrl.itemShelvingSales">
|
<vn-tr ng-repeat="itemShelvingSale in $ctrl.itemShelvingSales">
|
||||||
<vn-td number>{{::itemShelvingSale.quantity}}</vn-td>
|
<vn-td-editable number shrink>
|
||||||
|
<text>{{itemShelvingSale.quantity}}</text>
|
||||||
|
<field>
|
||||||
|
<vn-input-number class="dense" vn-focus
|
||||||
|
ng-model="itemShelvingSale.quantity"
|
||||||
|
on-change="$ctrl.updateQuantity(itemShelvingSale)">
|
||||||
|
</vn-input-number>
|
||||||
|
</field>
|
||||||
|
</vn-td-editable>
|
||||||
<vn-td expand>
|
<vn-td expand>
|
||||||
<span
|
<span
|
||||||
class="link"
|
class="link"
|
||||||
|
@ -183,8 +206,24 @@
|
||||||
{{::itemShelvingSale.name | dashIfEmpty}}
|
{{::itemShelvingSale.name | dashIfEmpty}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>{{::itemShelvingSale.shelvingFk}}</vn-td>
|
<vn-td expand>
|
||||||
<vn-td shrink>{{::itemShelvingSale.code}}</vn-td>
|
<vn-autocomplete
|
||||||
|
data="shelvings"
|
||||||
|
show-field="code"
|
||||||
|
value-field="code"
|
||||||
|
ng-model="itemShelvingSale.shelvingFk"
|
||||||
|
on-change="$ctrl.updateShelving(itemShelvingSale)">
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-td>
|
||||||
|
<vn-td expand>
|
||||||
|
<vn-autocomplete
|
||||||
|
data="parkings"
|
||||||
|
show-field="code"
|
||||||
|
value-field="id"
|
||||||
|
ng-model="itemShelvingSale.parkingFk"
|
||||||
|
on-change="$ctrl.updateParking(itemShelvingSale)">
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-td>
|
||||||
<vn-td expand>{{::itemShelvingSale.created | date: 'dd/MM/yyyy HH:mm'}}</vn-td>
|
<vn-td expand>{{::itemShelvingSale.created | date: 'dd/MM/yyyy HH:mm'}}</vn-td>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
</vn-tbody>
|
</vn-tbody>
|
||||||
|
|
|
@ -3,62 +3,6 @@ import Section from 'salix/components/section';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
class Controller extends Section {
|
class Controller extends Section {
|
||||||
constructor($element, $) {
|
|
||||||
super($element, $);
|
|
||||||
this.filter = {
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
relation: 'item'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
relation: 'saleTracking',
|
|
||||||
scope: {
|
|
||||||
fields: ['isChecked']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
relation: 'saleGroupDetail',
|
|
||||||
scope: {
|
|
||||||
fields: ['saleGroupFk'],
|
|
||||||
include: {
|
|
||||||
relation: 'saleGroup',
|
|
||||||
scope: {
|
|
||||||
fields: ['parkingFk'],
|
|
||||||
include: {
|
|
||||||
relation: 'parking',
|
|
||||||
scope: {
|
|
||||||
fields: ['code']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get sales() {
|
|
||||||
return this._sales;
|
|
||||||
}
|
|
||||||
|
|
||||||
set sales(value) {
|
|
||||||
this._sales = value;
|
|
||||||
if (value) {
|
|
||||||
const query = `Sales/${this.$params.id}/salePreparingList`;
|
|
||||||
this.$http.get(query)
|
|
||||||
.then(res => {
|
|
||||||
this.salePreparingList = res.data;
|
|
||||||
for (const salePreparing of this.salePreparingList) {
|
|
||||||
for (const sale of this.sales) {
|
|
||||||
if (salePreparing.saleFk == sale.id)
|
|
||||||
sale.preparingList = salePreparing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showItemDescriptor(event, sale) {
|
showItemDescriptor(event, sale) {
|
||||||
this.quicklinks = {
|
this.quicklinks = {
|
||||||
btnThree: {
|
btnThree: {
|
||||||
|
@ -75,20 +19,145 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
showSaleTracking(sale) {
|
showSaleTracking(sale) {
|
||||||
this.saleId = sale.id;
|
this.saleId = sale.saleFk;
|
||||||
this.$.saleTracking.show();
|
this.$.saleTracking.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
showItemShelvingSale(sale) {
|
showItemShelvingSale(sale) {
|
||||||
this.saleId = sale.id;
|
this.saleId = sale.saleFk;
|
||||||
this.$.itemShelvingSale.show();
|
this.$.itemShelvingSale.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clickSaleGroupDetail(index) {
|
||||||
|
const sale = this.sales[index];
|
||||||
|
if (!sale.saleGroupDetailFk) return;
|
||||||
|
|
||||||
|
return this.$http.delete(`SaleGroupDetails/${sale.saleGroupDetailFk}`)
|
||||||
|
.then(() => {
|
||||||
|
sale.hasSaleGroupDetail = false;
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clickPreviousSelected(index) {
|
||||||
|
const sale = this.sales[index];
|
||||||
|
if (!sale.isPreviousSelected) {
|
||||||
|
this.saleTrackingNew(sale, 'PREVIOUS_PREPARATION', false);
|
||||||
|
sale.isPreviousSelected = true;
|
||||||
|
} else {
|
||||||
|
this.saleTrackingDel(sale, 'PREVIOUS_PREPARATION');
|
||||||
|
sale.isPreviousSelected = false;
|
||||||
|
sale.isPrevious = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clickPrevious(index) {
|
||||||
|
const sale = this.sales[index];
|
||||||
|
if (!sale.isPrevious) {
|
||||||
|
this.saleTrackingNew(sale, 'PREVIOUS_PREPARATION', true);
|
||||||
|
sale.isPrevious = true;
|
||||||
|
sale.isPreviousSelected = true;
|
||||||
|
} else {
|
||||||
|
this.saleTrackingNew(sale, 'PREVIOUS_PREPARATION', false);
|
||||||
|
sale.isPrevious = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clickPrepared(index) {
|
||||||
|
const sale = this.sales[index];
|
||||||
|
if (!sale.isPrepared) {
|
||||||
|
this.saleTrackingNew(sale, 'PREPARED', true);
|
||||||
|
sale.isPrepared = true;
|
||||||
|
} else {
|
||||||
|
this.saleTrackingDel(sale, 'PREPARED');
|
||||||
|
sale.isPrepared = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clickControled(index) {
|
||||||
|
const sale = this.sales[index];
|
||||||
|
if (!sale.isControled) {
|
||||||
|
this.saleTrackingNew(sale, 'CHECKED', true);
|
||||||
|
sale.isControled = true;
|
||||||
|
} else {
|
||||||
|
this.saleTrackingDel(sale, 'CHECKED');
|
||||||
|
sale.isControled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saleTrackingNew(sale, stateCode, isChecked) {
|
||||||
|
const params = {
|
||||||
|
saleFk: sale.saleFk,
|
||||||
|
isChecked: isChecked,
|
||||||
|
quantity: sale.quantity,
|
||||||
|
stateCode: stateCode
|
||||||
|
};
|
||||||
|
this.$http.post(`SaleTrackings/new`, params).then(() => {
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
saleTrackingDel(sale, stateCode) {
|
||||||
|
const params = {
|
||||||
|
saleFk: sale.saleFk,
|
||||||
|
stateCode: stateCode
|
||||||
|
};
|
||||||
|
this.$http.post(`SaleTrackings/delete`, params).then(() => {
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateQuantity(itemShelvingSale) {
|
||||||
|
const params = {
|
||||||
|
quantity: itemShelvingSale.quantity
|
||||||
|
};
|
||||||
|
this.$http.patch(`ItemShelvingSales/${itemShelvingSale.id}`, params)
|
||||||
|
.then(() => {
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateShelving(itemShelvingSale) {
|
||||||
|
const params = {
|
||||||
|
shelvingFk: itemShelvingSale.shelvingFk
|
||||||
|
};
|
||||||
|
const res = await this.$http.patch(`ItemShelvings/${itemShelvingSale.itemShelvingFk}`, params);
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
fields: ['parkingFk'],
|
||||||
|
where: {
|
||||||
|
code: res.data.shelvingFk
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.$http.get(`Shelvings/findOne`, {filter})
|
||||||
|
.then(res => {
|
||||||
|
itemShelvingSale.parkingFk = res.data.parkingFk;
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateParking(itemShelvingSale) {
|
||||||
|
const filter = {
|
||||||
|
fields: ['id'],
|
||||||
|
where: {
|
||||||
|
code: itemShelvingSale.shelvingFk
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const res = await this.$http.get(`Shelvings/findOne`, {filter});
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
parkingFk: itemShelvingSale.parkingFk
|
||||||
|
};
|
||||||
|
this.$http.patch(`Shelvings/${res.data.id}`, params)
|
||||||
|
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnTicketSaleTracking', {
|
ngModule.vnComponent('vnTicketSaleTracking', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
bindings: {
|
bindings: {
|
||||||
ticket: '<'
|
ticket: '<',
|
||||||
|
model: '<?'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
ItemShelvings sale: Carros línea
|
Shelvings sale: Carros línea
|
||||||
has saleGroupDetail: tiene detalle grupo lineas
|
Log states: Historial estados
|
||||||
is previousSelected: es previa seleccionada
|
sale group detail: detalle grupo lineas
|
||||||
is previous: es previa
|
previous selected: previa seleccionado
|
||||||
is prepared: esta preparado
|
previous: previa
|
||||||
is controled: esta controlado
|
prepared: preparado
|
||||||
|
checked: revisado
|
||||||
|
|
|
@ -1,14 +1,5 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
vn-sale-tracking {
|
|
||||||
.chip {
|
|
||||||
display: inline-block;
|
|
||||||
min-width: 15px;
|
|
||||||
min-height: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.circleState {
|
.circleState {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
@ -62,16 +62,12 @@
|
||||||
label="Nickname"
|
label="Nickname"
|
||||||
ng-model="filter.nickname">
|
ng-model="filter.nickname">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
ng-model="filter.salesPersonFk"
|
ng-model="filter.salesPersonFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
departments="['VT']"
|
||||||
search-function="{firstName: $search}"
|
|
||||||
value-field="id"
|
|
||||||
where="{role: 'employee'}"
|
|
||||||
label="Sales person">
|
label="Sales person">
|
||||||
<tpl-item>{{firstName}} {{name}}</tpl-item>
|
</vn-worker-autocomplete>
|
||||||
</vn-autocomplete>
|
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
label="Invoice"
|
label="Invoice"
|
||||||
|
|
|
@ -19,17 +19,10 @@
|
||||||
label="State"
|
label="State"
|
||||||
vn-focus>
|
vn-focus>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-autocomplete
|
<vn-worker-autocomplete
|
||||||
vn-one
|
vn-one
|
||||||
url="Workers/activeWithInheritedRole"
|
ng-model="$ctrl.workerFk">
|
||||||
ng-if="$ctrl.isPickerDesignedState"
|
</vn-worker-autocomplete>
|
||||||
ng-model="$ctrl.workerFk"
|
|
||||||
show-field="nickname"
|
|
||||||
search-function="{firstName: $search}"
|
|
||||||
value-field="id"
|
|
||||||
where="{role: 'employee'}"
|
|
||||||
label="Worker">
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-button-bar>
|
<vn-button-bar>
|
||||||
|
|
|
@ -121,15 +121,18 @@ module.exports = Self => {
|
||||||
`, [started, ended]);
|
`, [started, ended]);
|
||||||
stmts.push(stmt);
|
stmts.push(stmt);
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO mail (receiver, subject, body)
|
stmt = new ParameterizedSQL(`
|
||||||
|
INSERT INTO mail (receiver, subject, body)
|
||||||
SELECT CONCAT(u.name, '@verdnatura.es'),
|
SELECT CONCAT(u.name, '@verdnatura.es'),
|
||||||
CONCAT('Error registro de horas semana ', ?, ' año ', ?) ,
|
CONCAT('Error registro de horas semana ', ?, ' año ', ?) ,
|
||||||
CONCAT('No se ha podido enviar el registro de horas al empleado/s: ', GROUP_CONCAT(DISTINCT CONCAT('<br>', w.id, ' ', w.firstName, ' ', w.lastName)))
|
CONCAT('No se ha podido enviar el registro de horas al empleado/s: ',
|
||||||
|
GROUP_CONCAT(DISTINCT CONCAT('<br>', w.id, ' ', w.firstName, ' ', w.lastName)))
|
||||||
FROM tmp.timeControlError tce
|
FROM tmp.timeControlError tce
|
||||||
JOIN vn.workerTimeControl wtc ON wtc.id = tce.id
|
JOIN vn.workerTimeControl wtc ON wtc.id = tce.id
|
||||||
JOIN worker w ON w.id = wtc.userFK
|
JOIN worker w ON w.id = wtc.userFK
|
||||||
JOIN account.user u ON u.id = w.bossFk
|
JOIN account.user u ON u.id = w.bossFk
|
||||||
GROUP BY w.bossFk`, [args.week, args.year]);
|
GROUP BY w.bossFk
|
||||||
|
`, [args.week, args.year]);
|
||||||
stmts.push(stmt);
|
stmts.push(stmt);
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`
|
stmt = new ParameterizedSQL(`
|
||||||
|
@ -177,10 +180,8 @@ module.exports = Self => {
|
||||||
const workerTimeControlConfig = await models.WorkerTimeControlConfig.findOne(null, myOptions);
|
const workerTimeControlConfig = await models.WorkerTimeControlConfig.findOne(null, myOptions);
|
||||||
|
|
||||||
for (let day of days[index]) {
|
for (let day of days[index]) {
|
||||||
if (!myOptions.transaction) {
|
|
||||||
tx = await Self.beginTransaction({});
|
tx = await Self.beginTransaction({});
|
||||||
myOptions.transaction = tx;
|
myOptions.transaction = tx;
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
workerFk = day.workerFk;
|
workerFk = day.workerFk;
|
||||||
if (day.timeWorkDecimal > 0 && day.timeWorkedDecimal == null
|
if (day.timeWorkDecimal > 0 && day.timeWorkedDecimal == null
|
||||||
|
@ -365,13 +366,31 @@ module.exports = Self => {
|
||||||
previousReceiver = day.receiver;
|
previousReceiver = day.receiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tx) {
|
if (tx) await tx.commit();
|
||||||
await tx.commit();
|
|
||||||
delete myOptions.transaction;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
const stmts = [];
|
||||||
|
let stmt;
|
||||||
|
stmt = new ParameterizedSQL(`
|
||||||
|
INSERT INTO mail (receiver, subject, body)
|
||||||
|
SELECT CONCAT(u.name, '@verdnatura.es'),
|
||||||
|
CONCAT('Error registro de horas semana ', ?, ' año ', ?) ,
|
||||||
|
CONCAT('No se ha podido enviar el registro de horas al empleado: ',
|
||||||
|
w.id, ' ', w.firstName, ' ', w.lastName, ' por el motivo: ', ?)
|
||||||
|
FROM worker w
|
||||||
|
JOIN account.user u ON u.id = w.bossFk
|
||||||
|
WHERE w.id = ?
|
||||||
|
`, [args.week, args.year, e.message, day.workerFk]);
|
||||||
|
stmts.push(stmt);
|
||||||
|
|
||||||
|
const sql = ParameterizedSQL.join(stmts, ';');
|
||||||
|
await conn.executeStmt(sql);
|
||||||
|
|
||||||
|
previousWorkerFk = day.workerFk;
|
||||||
|
previousReceiver = day.receiver;
|
||||||
|
|
||||||
if (tx) await tx.rollback();
|
if (tx) await tx.rollback();
|
||||||
throw e;
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ module.exports = Self => {
|
||||||
const salix = await models.Url.findOne({
|
const salix = await models.Url.findOne({
|
||||||
where: {
|
where: {
|
||||||
appName: 'salix',
|
appName: 'salix',
|
||||||
environment: process.env.NODE_ENV || 'dev'
|
environment: process.env.NODE_ENV || 'development'
|
||||||
}
|
}
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ module.exports = Self => {
|
||||||
const url = `${salix.url}worker/${args.workerId}/time-control?timestamp=${timestamp}`;
|
const url = `${salix.url}worker/${args.workerId}/time-control?timestamp=${timestamp}`;
|
||||||
ctx.args.url = url;
|
ctx.args.url = url;
|
||||||
|
|
||||||
Self.sendTemplate(ctx, 'weekly-hour-record');
|
await Self.sendTemplate(ctx, 'weekly-hour-record');
|
||||||
|
|
||||||
return models.WorkerTimeControl.updateWorkerTimeControlMail(ctx, myOptions);
|
return models.WorkerTimeControl.updateWorkerTimeControlMail(ctx, myOptions);
|
||||||
};
|
};
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue