Merge branch 'dev' into 3811-ticket.sale_updatePrice
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
3cc89c170c
|
@ -1,12 +1,13 @@
|
||||||
const jsdom = require('jsdom');
|
const jsdom = require('jsdom');
|
||||||
const mysql = require('mysql');
|
const mysql = require('mysql');
|
||||||
|
const FormData = require('form-data');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('closeTicket', {
|
Self.remoteMethodCtx('closeTicket', {
|
||||||
description: 'Close tickets without response from the user',
|
description: 'Close tickets without response from the user',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
returns: {
|
returns: {
|
||||||
type: 'Object',
|
type: 'object',
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
|
@ -54,9 +55,9 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await requestToken();
|
await getRequestToken();
|
||||||
|
|
||||||
async function requestToken() {
|
async function getRequestToken() {
|
||||||
const response = await fetch(ostUri);
|
const response = await fetch(ostUri);
|
||||||
|
|
||||||
const result = response.headers.get('set-cookie');
|
const result = response.headers.get('set-cookie');
|
||||||
|
@ -93,24 +94,45 @@ module.exports = Self => {
|
||||||
await close(token, secondCookie);
|
await close(token, secondCookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getLockCode(token, secondCookie, ticketId) {
|
||||||
|
const ostUri = `${config.host}/ajax.php/lock/ticket/${ticketId}`;
|
||||||
|
const params = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': token,
|
||||||
|
'Cookie': secondCookie
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const response = await fetch(ostUri, params);
|
||||||
|
const body = await response.text();
|
||||||
|
const json = JSON.parse(body);
|
||||||
|
|
||||||
|
return json.code;
|
||||||
|
}
|
||||||
|
|
||||||
async function close(token, secondCookie) {
|
async function close(token, secondCookie) {
|
||||||
for (const ticketId of ticketsId) {
|
for (const ticketId of ticketsId) {
|
||||||
const ostUri = `${config.host}/ajax.php/tickets/${ticketId}/status`;
|
const lockCode = await getLockCode(token, secondCookie, ticketId);
|
||||||
const data = {
|
let form = new FormData();
|
||||||
status_id: config.newStatusId,
|
form.append('__CSRFToken__', token);
|
||||||
comments: config.comment,
|
form.append('id', ticketId);
|
||||||
undefined: config.action
|
form.append('a', config.responseType);
|
||||||
};
|
form.append('lockCode', lockCode);
|
||||||
|
form.append('from_email_id', config.fromEmailId);
|
||||||
|
form.append('reply-to', config.replyTo);
|
||||||
|
form.append('cannedResp', 0);
|
||||||
|
form.append('response', config.comment);
|
||||||
|
form.append('signature', 'none');
|
||||||
|
form.append('reply_status_id', config.newStatusId);
|
||||||
|
|
||||||
|
const ostUri = `${config.host}/tickets.php?id=${ticketId}`;
|
||||||
const params = {
|
const params = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: new URLSearchParams(data),
|
body: form,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
||||||
'X-CSRFToken': token,
|
|
||||||
'Cookie': secondCookie
|
'Cookie': secondCookie
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return fetch(ostUri, params);
|
return fetch(ostUri, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,6 @@
|
||||||
"newStatusId": {
|
"newStatusId": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"action": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"day": {
|
"day": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
@ -47,6 +44,15 @@
|
||||||
},
|
},
|
||||||
"portDb": {
|
"portDb": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
|
},
|
||||||
|
"responseType": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"fromEmailId": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"replyTo": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,3 @@ CREATE TABLE `vn`.`osTicketConfig` (
|
||||||
`portDb` int(11) DEFAULT NULL,
|
`portDb` int(11) DEFAULT NULL,
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||||
|
|
||||||
INSERT INTO `vn`.`osTicketConfig`(`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `action`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`)
|
|
||||||
VALUES
|
|
||||||
(0, 'https://cau.verdnatura.es/scp', NULL, NULL, 'open', 3, 'Cerrar', 60, 'Este CAU se ha cerrado automáticamente', NULL, NULL, NULL, NULL);
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
ALTER TABLE `vn`.`osTicketConfig` DROP COLUMN `action`;
|
||||||
|
ALTER TABLE `vn`.`osTicketConfig` ADD responseType varchar(100) NULL;
|
||||||
|
ALTER TABLE `vn`.`osTicketConfig` ADD fromEmailId INT NULL;
|
||||||
|
ALTER TABLE `vn`.`osTicketConfig` ADD replyTo varchar(100) NULL;
|
||||||
|
|
||||||
|
UPDATE `vn`.`osTicketConfig`
|
||||||
|
SET responseType='reply', fromEmailId=5, replyTo='all'
|
||||||
|
WHERE id=0;
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE `vn`.`claimConfig` DROP COLUMN `pickupContact`;
|
|
@ -1778,10 +1778,10 @@ INSERT INTO `vn`.`claimEnd`(`id`, `saleFk`, `claimFk`, `workerFk`, `claimDestina
|
||||||
(1, 31, 4, 21, 2),
|
(1, 31, 4, 21, 2),
|
||||||
(2, 32, 3, 21, 3);
|
(2, 32, 3, 21, 3);
|
||||||
|
|
||||||
INSERT INTO `vn`.`claimConfig`(`id`, `pickupContact`, `maxResponsibility`)
|
INSERT INTO `vn`.`claimConfig`(`id`, `maxResponsibility`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Contact description', 50),
|
(1, 50),
|
||||||
(2, 'Contact description', 30);
|
(2, 30);
|
||||||
|
|
||||||
INSERT INTO `vn`.`claimRatio`(`clientFk`, `yearSale`, `claimAmount`, `claimingRate`, `priceIncreasing`, `packingRate`)
|
INSERT INTO `vn`.`claimRatio`(`clientFk`, `yearSale`, `claimAmount`, `claimingRate`, `priceIncreasing`, `packingRate`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -1791,7 +1791,7 @@ INSERT INTO `vn`.`claimRatio`(`clientFk`, `yearSale`, `claimAmount`, `claimingRa
|
||||||
(1104, 2500, 150.00, 0.02, 0.10, 1.00);
|
(1104, 2500, 150.00, 0.02, 0.10, 1.00);
|
||||||
|
|
||||||
INSERT INTO vn.claimRma (`id`, `code`, `created`, `workerFk`)
|
INSERT INTO vn.claimRma (`id`, `code`, `created`, `workerFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, '02676A049183', DEFAULT, 1106),
|
(1, '02676A049183', DEFAULT, 1106),
|
||||||
(2, '02676A049183', DEFAULT, 1106),
|
(2, '02676A049183', DEFAULT, 1106),
|
||||||
(3, '02676A049183', DEFAULT, 1107),
|
(3, '02676A049183', DEFAULT, 1107),
|
||||||
|
@ -2710,3 +2710,7 @@ INSERT INTO `vn`.`ticketCollection` (`ticketFk`, `collectionFk`, `created`, `lev
|
||||||
UPDATE `account`.`user`
|
UPDATE `account`.`user`
|
||||||
SET `hasGrant` = 1
|
SET `hasGrant` = 1
|
||||||
WHERE `id` = 66;
|
WHERE `id` = 66;
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`, `responseType`, `fromEmailId`, `replyTo`)
|
||||||
|
VALUES
|
||||||
|
(0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', 'open', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all');
|
|
@ -596,7 +596,14 @@ export default {
|
||||||
submitNotesButton: 'button[type=submit]'
|
submitNotesButton: 'button[type=submit]'
|
||||||
},
|
},
|
||||||
ticketExpedition: {
|
ticketExpedition: {
|
||||||
thirdExpeditionRemoveButton: 'vn-ticket-expedition vn-table div > vn-tbody > vn-tr:nth-child(3) > vn-td:nth-child(1) > vn-icon-button[icon="delete"]',
|
firstSaleCheckbox: 'vn-ticket-expedition vn-tr:nth-child(1) vn-check[ng-model="expedition.checked"]',
|
||||||
|
thirdSaleCheckbox: 'vn-ticket-expedition vn-tr:nth-child(3) vn-check[ng-model="expedition.checked"]',
|
||||||
|
deleteExpeditionButton: 'vn-ticket-expedition vn-tool-bar > vn-button[icon="delete"]',
|
||||||
|
moveExpeditionButton: 'vn-ticket-expedition vn-tool-bar > vn-button[icon="keyboard_arrow_down"]',
|
||||||
|
moreMenuWithoutRoute: 'vn-item[name="withoutRoute"]',
|
||||||
|
moreMenuWithRoute: 'vn-item[name="withRoute"]',
|
||||||
|
newRouteId: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newRoute"]',
|
||||||
|
saveButton: '.vn-dialog.shown [response="accept"]',
|
||||||
expeditionRow: 'vn-ticket-expedition vn-table vn-tbody > vn-tr'
|
expeditionRow: 'vn-ticket-expedition vn-table vn-tbody > vn-tr'
|
||||||
},
|
},
|
||||||
ticketPackages: {
|
ticketPackages: {
|
||||||
|
|
|
@ -18,7 +18,8 @@ describe('Ticket expeditions and log path', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should delete a former expedition and confirm the remaining expedition are the expected ones`, async() => {
|
it(`should delete a former expedition and confirm the remaining expedition are the expected ones`, async() => {
|
||||||
await page.waitToClick(selectors.ticketExpedition.thirdExpeditionRemoveButton);
|
await page.waitToClick(selectors.ticketExpedition.thirdSaleCheckbox);
|
||||||
|
await page.waitToClick(selectors.ticketExpedition.deleteExpeditionButton);
|
||||||
await page.waitToClick(selectors.globalItems.acceptButton);
|
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||||
await page.reloadSection('ticket.card.expedition');
|
await page.reloadSection('ticket.card.expedition');
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import selectors from '../../helpers/selectors.js';
|
||||||
|
import getBrowser from '../../helpers/puppeteer';
|
||||||
|
|
||||||
|
describe('Ticket expeditions', () => {
|
||||||
|
let browser;
|
||||||
|
let page;
|
||||||
|
|
||||||
|
beforeAll(async() => {
|
||||||
|
browser = await getBrowser();
|
||||||
|
page = browser.page;
|
||||||
|
await page.loginAndModule('production', 'ticket');
|
||||||
|
await page.accessToSearchResult('1');
|
||||||
|
await page.accessToSection('ticket.card.expedition');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async() => {
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should move one expedition to new ticket withoute route`, async() => {
|
||||||
|
await page.waitToClick(selectors.ticketExpedition.thirdSaleCheckbox);
|
||||||
|
await page.waitToClick(selectors.ticketExpedition.moveExpeditionButton);
|
||||||
|
await page.waitToClick(selectors.ticketExpedition.moreMenuWithoutRoute);
|
||||||
|
await page.waitToClick(selectors.ticketExpedition.saveButton);
|
||||||
|
await page.waitForState('ticket.card.summary');
|
||||||
|
await page.accessToSection('ticket.card.expedition');
|
||||||
|
|
||||||
|
await page.waitForSelector(selectors.ticketExpedition.expeditionRow, {});
|
||||||
|
const result = await page
|
||||||
|
.countElement(selectors.ticketExpedition.expeditionRow);
|
||||||
|
|
||||||
|
expect(result).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should move one expedition to new ticket with route`, async() => {
|
||||||
|
await page.waitToClick(selectors.ticketExpedition.firstSaleCheckbox);
|
||||||
|
await page.waitToClick(selectors.ticketExpedition.moveExpeditionButton);
|
||||||
|
await page.waitToClick(selectors.ticketExpedition.moreMenuWithRoute);
|
||||||
|
await page.write(selectors.ticketExpedition.newRouteId, '1');
|
||||||
|
await page.waitToClick(selectors.ticketExpedition.saveButton);
|
||||||
|
await page.waitForState('ticket.card.summary');
|
||||||
|
await page.accessToSection('ticket.card.expedition');
|
||||||
|
|
||||||
|
await page.waitForSelector(selectors.ticketExpedition.expeditionRow, {});
|
||||||
|
const result = await page
|
||||||
|
.countElement(selectors.ticketExpedition.expeditionRow);
|
||||||
|
|
||||||
|
expect(result).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -134,6 +134,7 @@
|
||||||
"Password does not meet requirements": "Password does not meet requirements",
|
"Password does not meet requirements": "Password does not meet requirements",
|
||||||
"You don't have privileges to change the zone": "You don't have privileges to change the zone or for these parameters there are more than one shipping options, talk to agencies",
|
"You don't have privileges to change the zone": "You don't have privileges to change the zone or for these parameters there are more than one shipping options, talk to agencies",
|
||||||
"Not enough privileges to edit a client": "Not enough privileges to edit a client",
|
"Not enough privileges to edit a client": "Not enough privileges to edit a client",
|
||||||
|
"Claim pickup order sent": "Claim pickup order sent [({{claimId}})]({{{claimUrl}}}) to client *{{clientName}}*",
|
||||||
"You don't have grant privilege": "You don't have grant privilege",
|
"You don't have grant privilege": "You don't have grant privilege",
|
||||||
"You don't own the role and you can't assign it to another user": "You don't own the role and you can't assign it to another user"
|
"You don't own the role and you can't assign it to another user": "You don't own the role and you can't assign it to another user"
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,6 +236,8 @@
|
||||||
"Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador",
|
"Modifiable user details only by an administrator": "Detalles de usuario modificables solo por un administrador",
|
||||||
"Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador",
|
"Modifiable password only via recovery or by an administrator": "Contraseña modificable solo a través de la recuperación o por un administrador",
|
||||||
"Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente",
|
"Not enough privileges to edit a client": "No tienes suficientes privilegios para editar un cliente",
|
||||||
|
"This route does not exists": "Esta ruta no existe",
|
||||||
|
"Claim pickup order sent": "Reclamación Orden de recogida enviada [({{claimId}})]({{{claimUrl}}}) al cliente *{{clientName}}*",
|
||||||
"You don't have grant privilege": "No tienes privilegios para dar privilegios",
|
"You don't have grant privilege": "No tienes privilegios para dar privilegios",
|
||||||
"You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario"
|
"You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario"
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ module.exports = Self => {
|
||||||
arg: 'id',
|
arg: 'id',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
required: true,
|
required: true,
|
||||||
description: 'The client id',
|
description: 'The claim id',
|
||||||
http: {source: 'path'}
|
http: {source: 'path'}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -42,6 +42,11 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.claimPickupEmail = async ctx => {
|
Self.claimPickupEmail = async ctx => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const userId = ctx.req.accessToken.userId;
|
||||||
|
const $t = ctx.req.__; // $translate
|
||||||
|
const origin = ctx.req.headers.origin;
|
||||||
|
|
||||||
const args = Object.assign({}, ctx.args);
|
const args = Object.assign({}, ctx.args);
|
||||||
const params = {
|
const params = {
|
||||||
recipient: args.recipient,
|
recipient: args.recipient,
|
||||||
|
@ -52,6 +57,34 @@ module.exports = Self => {
|
||||||
for (const param in args)
|
for (const param in args)
|
||||||
params[param] = args[param];
|
params[param] = args[param];
|
||||||
|
|
||||||
|
const claim = await models.Claim.findById(args.id, {
|
||||||
|
fields: ['id', 'clientFk'],
|
||||||
|
include: {
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['name', 'salesPersonFk']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = $t('Claim pickup order sent', {
|
||||||
|
claimId: args.id,
|
||||||
|
clientName: claim.client().name,
|
||||||
|
claimUrl: `${origin}/#!/claim/${args.id}/summary`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const salesPersonId = claim.client().salesPersonFk;
|
||||||
|
if (salesPersonId)
|
||||||
|
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
||||||
|
|
||||||
|
await models.ClaimLog.create({
|
||||||
|
originFk: args.id,
|
||||||
|
userFk: userId,
|
||||||
|
action: 'insert',
|
||||||
|
description: 'Claim-pickup-order sent',
|
||||||
|
changedModel: 'Mail'
|
||||||
|
});
|
||||||
|
|
||||||
const email = new Email('claim-pickup-order', params);
|
const email = new Email('claim-pickup-order', params);
|
||||||
|
|
||||||
return email.send();
|
return email.send();
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
label="Pick up"
|
label="Pick up"
|
||||||
ng-model="$ctrl.claim.hasToPickUp"
|
ng-model="$ctrl.claim.hasToPickUp"
|
||||||
vn-acl="claimManager"
|
vn-acl="claimManager"
|
||||||
info="When checked will notify to the salesPerson">
|
title="{{'When checked will notify to the salesPerson' | translate}}">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
|
@ -5,5 +5,5 @@ Responsability: Responsabilidad
|
||||||
Company: Empresa
|
Company: Empresa
|
||||||
Sales/Client: Comercial/Cliente
|
Sales/Client: Comercial/Cliente
|
||||||
Pick up: Recoger
|
Pick up: Recoger
|
||||||
When checked will notify a pickup to the salesPerson: Cuando se marque enviará una notificación de recogida al comercial
|
When checked will notify to the salesPerson: Cuando se marque enviará una notificación de recogida al comercial
|
||||||
Packages received: Bultos recibidos
|
Packages received: Bultos recibidos
|
|
@ -25,16 +25,23 @@
|
||||||
</vn-button-menu>
|
</vn-button-menu>
|
||||||
</h5>
|
</h5>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-one>
|
<vn-auto>
|
||||||
<vn-label-value
|
<h4>
|
||||||
label="Created"
|
<a
|
||||||
value="{{$ctrl.summary.claim.created | date: 'dd/MM/yyyy'}}">
|
ui-sref="claim.card.basicData({id:$ctrl.claim.id})"
|
||||||
</vn-label-value>
|
target="_self">
|
||||||
<vn-label-value
|
<span translate vn-tooltip="Go to">Basic data</span>
|
||||||
label="State"
|
</a>
|
||||||
value="{{$ctrl.summary.claim.claimState.description}}">
|
</h4>
|
||||||
</vn-label-value>
|
<vn-label-value
|
||||||
<vn-label-value
|
label="Created"
|
||||||
|
value="{{$ctrl.summary.claim.created | date: 'dd/MM/yyyy'}}">
|
||||||
|
</vn-label-value>
|
||||||
|
<vn-label-value
|
||||||
|
label="State"
|
||||||
|
value="{{$ctrl.summary.claim.claimState.description}}">
|
||||||
|
</vn-label-value>
|
||||||
|
<vn-label-value
|
||||||
label="Salesperson"
|
label="Salesperson"
|
||||||
value="{{$ctrl.summary.claim.client.salesPersonUser.name}}">
|
value="{{$ctrl.summary.claim.client.salesPersonUser.name}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
|
@ -42,7 +49,14 @@
|
||||||
label="Attended by"
|
label="Attended by"
|
||||||
value="{{$ctrl.summary.claim.worker.user.nickname}}">
|
value="{{$ctrl.summary.claim.worker.user.nickname}}">
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</vn-one>
|
<vn-check
|
||||||
|
class="vn-mr-md"
|
||||||
|
label="Pick up"
|
||||||
|
ng-model="$ctrl.summary.claim.hasToPickUp"
|
||||||
|
title="{{'When checked will notify to the salesPerson' | translate}}"
|
||||||
|
disabled="true">
|
||||||
|
</vn-check>
|
||||||
|
</vn-auto>
|
||||||
<vn-auto>
|
<vn-auto>
|
||||||
<h4 ng-show="$ctrl.isSalesPerson && $ctrl.summary.observations.length">
|
<h4 ng-show="$ctrl.isSalesPerson && $ctrl.summary.observations.length">
|
||||||
<a
|
<a
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('deleteExpeditions', {
|
||||||
|
description: 'Delete the selected expeditions',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'expeditionIds',
|
||||||
|
type: ['number'],
|
||||||
|
required: true,
|
||||||
|
description: 'The expeditions ids to delete'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/deleteExpeditions`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deleteExpeditions = async(expeditionIds, 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 {
|
||||||
|
const promises = [];
|
||||||
|
for (let expeditionId of expeditionIds) {
|
||||||
|
const deletedExpedition = models.Expedition.destroyById(expeditionId, myOptions);
|
||||||
|
promises.push(deletedExpedition);
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletedExpeditions = await Promise.all(promises);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return deletedExpeditions;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,93 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('moveExpeditions', {
|
||||||
|
description: 'Move the selected expeditions to another ticket',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'clientId',
|
||||||
|
type: 'number',
|
||||||
|
description: `The client id`,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'landed',
|
||||||
|
type: 'date',
|
||||||
|
description: `The landing date`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'warehouseId',
|
||||||
|
type: 'number',
|
||||||
|
description: `The warehouse id`,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'addressId',
|
||||||
|
type: 'number',
|
||||||
|
description: `The address id`,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'agencyModeId',
|
||||||
|
type: 'any',
|
||||||
|
description: `The agencyMode id`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'routeId',
|
||||||
|
type: 'any',
|
||||||
|
description: `The route id`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'expeditionIds',
|
||||||
|
type: ['number'],
|
||||||
|
required: true,
|
||||||
|
description: 'The expeditions ids to move'
|
||||||
|
}],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/moveExpeditions`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.moveExpeditions = async(ctx, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const args = ctx.args;
|
||||||
|
const myOptions = {};
|
||||||
|
let tx;
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (args.routeId) {
|
||||||
|
const route = await models.Route.findById(args.routeId, null, myOptions);
|
||||||
|
if (!route) throw new UserError('This route does not exists');
|
||||||
|
}
|
||||||
|
const ticket = await models.Ticket.new(ctx, myOptions);
|
||||||
|
const promises = [];
|
||||||
|
for (let expeditionsId of args.expeditionIds) {
|
||||||
|
const expeditionToUpdate = await models.Expedition.findById(expeditionsId, null, myOptions);
|
||||||
|
const expeditionUpdated = expeditionToUpdate.updateAttribute('ticketFk', ticket.id, myOptions);
|
||||||
|
promises.push(expeditionUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return ticket;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,22 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('ticket deleteExpeditions()', () => {
|
||||||
|
it('should delete the selected expeditions', async() => {
|
||||||
|
const tx = await models.Expedition.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const expeditionIds = [12, 13];
|
||||||
|
const result = await models.Expedition.deleteExpeditions(expeditionIds, options);
|
||||||
|
|
||||||
|
expect(result.length).toEqual(2);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('ticket moveExpeditions()', () => {
|
||||||
|
it('should move the selected expeditions to new ticket', async() => {
|
||||||
|
const tx = await models.Expedition.beginTransaction({});
|
||||||
|
const ctx = {
|
||||||
|
req: {accessToken: {userId: 9}},
|
||||||
|
args: {},
|
||||||
|
params: {}
|
||||||
|
};
|
||||||
|
const myCtx = Object.assign({}, ctx);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
myCtx.args = {
|
||||||
|
clientId: 1101,
|
||||||
|
landed: new Date(),
|
||||||
|
warehouseId: 1,
|
||||||
|
addressId: 121,
|
||||||
|
agencyModeId: 1,
|
||||||
|
routeId: null,
|
||||||
|
expeditionIds: [1, 2]
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const ticket = await models.Expedition.moveExpeditions(myCtx, options);
|
||||||
|
|
||||||
|
const newestTicketIdInFixtures = 27;
|
||||||
|
|
||||||
|
expect(ticket.id).toBeGreaterThan(newestTicketIdInFixtures);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -18,9 +18,12 @@ describe('ticket componentUpdate()', () => {
|
||||||
beforeAll(async() => {
|
beforeAll(async() => {
|
||||||
const deliveryComponenet = await models.Component.findOne({where: {code: 'delivery'}});
|
const deliveryComponenet = await models.Component.findOne({where: {code: 'delivery'}});
|
||||||
deliveryComponentId = deliveryComponenet.id;
|
deliveryComponentId = deliveryComponenet.id;
|
||||||
componentOfSaleSeven = `SELECT value FROM vn.saleComponent WHERE saleFk = 7 AND componentFk = ${deliveryComponentId}`;
|
componentOfSaleSeven = `SELECT value
|
||||||
componentOfSaleEight = `SELECT value FROM vn.saleComponent WHERE saleFk = 8 AND componentFk = ${deliveryComponentId}`;
|
FROM vn.saleComponent
|
||||||
|
WHERE saleFk = 7 AND componentFk = ${deliveryComponentId}`;
|
||||||
|
componentOfSaleEight = `SELECT value
|
||||||
|
FROM vn.saleComponent
|
||||||
|
WHERE saleFk = 8 AND componentFk = ${deliveryComponentId}`;
|
||||||
[componentValue] = await models.SaleComponent.rawSql(componentOfSaleSeven);
|
[componentValue] = await models.SaleComponent.rawSql(componentOfSaleSeven);
|
||||||
firstvalueBeforeChange = componentValue.value;
|
firstvalueBeforeChange = componentValue.value;
|
||||||
|
|
||||||
|
|
|
@ -110,14 +110,38 @@ module.exports = Self => {
|
||||||
for (let sale of sales) {
|
for (let sale of sales) {
|
||||||
const oldDiscount = sale.discount;
|
const oldDiscount = sale.discount;
|
||||||
const value = ((-sale.price * newDiscount) / 100);
|
const value = ((-sale.price * newDiscount) / 100);
|
||||||
const newComponent = models.SaleComponent.upsert({
|
|
||||||
saleFk: sale.id,
|
const manaComponent = await models.Component.findOne({
|
||||||
value: value,
|
where: {code: 'mana'}
|
||||||
componentFk: componentId}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
|
const manaClaimComponent = await models.Component.findOne({
|
||||||
|
where: {code: 'manaClaim'}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const [oldComponent] = await models.SaleComponent.find({
|
||||||
|
where: {
|
||||||
|
and: [
|
||||||
|
{saleFk: sale.id},
|
||||||
|
{componentFk: {inq: [manaComponent.id, manaClaimComponent.id]}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
let deletedComponent;
|
||||||
|
if (oldComponent) {
|
||||||
|
const filter = {
|
||||||
|
saleFk: sale.id,
|
||||||
|
componentFk: oldComponent.componentFk
|
||||||
|
};
|
||||||
|
deletedComponent = await models.SaleComponent.destroyAll(filter, myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newComponent = await createSaleComponent(sale.id, value, componentId, myOptions);
|
||||||
|
|
||||||
const updatedSale = sale.updateAttribute('discount', newDiscount, myOptions);
|
const updatedSale = sale.updateAttribute('discount', newDiscount, myOptions);
|
||||||
|
|
||||||
promises.push(newComponent, updatedSale);
|
promises.push(newComponent, updatedSale, deletedComponent);
|
||||||
|
|
||||||
const change = `${oldDiscount}% ➔ *${newDiscount}%*`;
|
const change = `${oldDiscount}% ➔ *${newDiscount}%*`;
|
||||||
changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${change}`;
|
changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${change}`;
|
||||||
|
@ -160,4 +184,14 @@ module.exports = Self => {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function createSaleComponent(saleId, value, componentId, myOptions) {
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
return models.SaleComponent.create({
|
||||||
|
saleFk: saleId,
|
||||||
|
value: value,
|
||||||
|
componentFk: componentId
|
||||||
|
}, myOptions);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
module.exports = function(Self) {
|
module.exports = function(Self) {
|
||||||
require('../methods/expedition/filter')(Self);
|
require('../methods/expedition/filter')(Self);
|
||||||
|
require('../methods/expedition/deleteExpeditions')(Self);
|
||||||
|
require('../methods/expedition/moveExpeditions')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,54 +8,77 @@
|
||||||
auto-load="true">
|
auto-load="true">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-data-viewer model="model">
|
<vn-data-viewer model="model">
|
||||||
<vn-card class="vn-w-xl">
|
<vn-card class="vn-pa-lg">
|
||||||
<vn-table model="model">
|
<vn-horizontal class="header">
|
||||||
<vn-thead>
|
<vn-tool-bar class="vn-mb-md">
|
||||||
<vn-tr>
|
<vn-button icon="keyboard_arrow_down"
|
||||||
<vn-th></vn-th>
|
label="Move"
|
||||||
<vn-th field="itemFk" number>Expedition</vn-th>
|
ng-click="moreOptions.show($event)"
|
||||||
<vn-th field="itemFk" number>Item</vn-th>
|
disabled="!$ctrl.totalChecked">
|
||||||
<vn-th field="packageItemName">Name</vn-th>
|
</vn-button>
|
||||||
<vn-th field="freightItemName">Package type</vn-th>
|
<vn-button
|
||||||
<vn-th field="counter" number>Counter</vn-th>
|
disabled="!$ctrl.checked.length"
|
||||||
<vn-th field="externalId" number>externalId</vn-th>
|
ng-click="removeConfirm.show()"
|
||||||
<vn-th field="created" expand>Created</vn-th>
|
icon="delete"
|
||||||
<vn-th field="state" expand>State</vn-th>
|
vn-tooltip="Delete expedition">
|
||||||
<vn-th></vn-th>
|
</vn-button>
|
||||||
</vn-tr>
|
</vn-tool-bar>
|
||||||
</vn-thead>
|
<vn-one class="taxes" ng-if="$ctrl.sales.length > 0">
|
||||||
<vn-tbody>
|
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
|
||||||
<vn-tr ng-repeat="expedition in expeditions">
|
<p><vn-label translate>VAT</vn-label> {{$ctrl.ticket.totalWithVat - $ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
|
||||||
<vn-td class="vn-px-md" style="width:30px; color:#FFA410;">
|
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.ticket.totalWithVat | currency: 'EUR':2}}</strong></p>
|
||||||
<vn-icon-button icon="delete"
|
</vn-one>
|
||||||
ng-click="deleteExpedition.show(expedition.id)"
|
</vn-horizontal>
|
||||||
vn-tooltip="Delete expedition">
|
<vn-table model="model">
|
||||||
</vn-icon-button>
|
<vn-thead>
|
||||||
</vn-td>
|
<vn-tr>
|
||||||
<vn-td number expand>{{expedition.id | zeroFill:6}}</vn-td>
|
<vn-th shrink>
|
||||||
<vn-td number>
|
<vn-multi-check
|
||||||
<span
|
model="model">
|
||||||
ng-class="{link: expedition.packagingItemFk}"
|
</vn-multi-check>
|
||||||
ng-click="itemDescriptor.show($event, expedition.packagingItemFk)">
|
</vn-th>
|
||||||
{{expedition.packagingFk}}
|
<vn-th field="itemFk" number>Expedition</vn-th>
|
||||||
</span>
|
<vn-th field="itemFk" number>Item</vn-th>
|
||||||
</vn-td>
|
<vn-th field="packageItemName">Name</vn-th>
|
||||||
<vn-td>{{::expedition.packageItemName}}</vn-td>
|
<vn-th field="freightItemName">Package type</vn-th>
|
||||||
<vn-td>{{::expedition.freightItemName}}</vn-td>
|
<vn-th field="counter" number>Counter</vn-th>
|
||||||
<vn-td number>{{::expedition.counter}}</vn-td>
|
<vn-th field="externalId" number>externalId</vn-th>
|
||||||
<vn-td expand>{{::expedition.externalId}}</vn-td>
|
<vn-th field="created" expand>Created</vn-th>
|
||||||
<vn-td shrink-datetime>{{::expedition.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
|
<vn-th field="state" expand>State</vn-th>
|
||||||
<vn-td>{{::expedition.state}}</vn-td>
|
<vn-th></vn-th>
|
||||||
<vn-td>
|
</vn-tr>
|
||||||
<vn-icon-button
|
</vn-thead>
|
||||||
vn-click-stop="$ctrl.showLog(expedition)"
|
<vn-tbody>
|
||||||
vn-tooltip="Status log"
|
<vn-tr ng-repeat="expedition in expeditions">
|
||||||
icon="history">
|
<vn-td shrink>
|
||||||
</vn-icon-button>
|
<vn-check tabindex="-1"
|
||||||
</vn-td>
|
ng-model="expedition.checked">
|
||||||
</vn-tr>
|
</vn-check>
|
||||||
</vn-tbody>
|
</vn-td>
|
||||||
</vn-table>
|
<vn-td number expand>{{expedition.id | zeroFill:6}}</vn-td>
|
||||||
|
<vn-td number>
|
||||||
|
<span
|
||||||
|
ng-class="{link: expedition.packagingItemFk}"
|
||||||
|
ng-click="itemDescriptor.show($event, expedition.packagingItemFk)">
|
||||||
|
{{expedition.packagingFk}}
|
||||||
|
</span>
|
||||||
|
</vn-td>
|
||||||
|
<vn-td>{{::expedition.packageItemName}}</vn-td>
|
||||||
|
<vn-td>{{::expedition.freightItemName}}</vn-td>
|
||||||
|
<vn-td number>{{::expedition.counter}}</vn-td>
|
||||||
|
<vn-td expand>{{::expedition.externalId}}</vn-td>
|
||||||
|
<vn-td shrink-datetime>{{::expedition.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
|
||||||
|
<vn-td>{{::expedition.state}}</vn-td>
|
||||||
|
<vn-td>
|
||||||
|
<vn-icon-button
|
||||||
|
vn-click-stop="$ctrl.showLog(expedition)"
|
||||||
|
vn-tooltip="Status log"
|
||||||
|
icon="history">
|
||||||
|
</vn-icon-button>
|
||||||
|
</vn-td>
|
||||||
|
</vn-tr>
|
||||||
|
</vn-tbody>
|
||||||
|
</vn-table>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
</vn-data-viewer>
|
</vn-data-viewer>
|
||||||
<vn-item-descriptor-popover
|
<vn-item-descriptor-popover
|
||||||
|
@ -67,24 +90,24 @@
|
||||||
vn-id="worker-descriptor">
|
vn-id="worker-descriptor">
|
||||||
</vn-worker-descriptor-popover>
|
</vn-worker-descriptor-popover>
|
||||||
<vn-confirm
|
<vn-confirm
|
||||||
vn-id="delete-expedition"
|
vn-id="removeConfirm"
|
||||||
on-accept="$ctrl.onDialogAccept($data)"
|
message="Are you sure you want to delete this expedition?"
|
||||||
question="Delete expedition"
|
question="Delete expedition"
|
||||||
message="Are you sure you want to delete this expedition?">
|
on-accept="$ctrl.onRemove()">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
|
|
||||||
<vn-popup vn-id="statusLog">
|
<vn-popup vn-id="statusLog">
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="model"
|
vn-id="modelExpeditionStates"
|
||||||
url="ExpeditionStates/filter"
|
url="ExpeditionStates/filter"
|
||||||
link="{expeditionFk: $ctrl.expedition.id}"
|
link="{expeditionFk: $ctrl.expedition.id}"
|
||||||
data="expeditionStates"
|
data="expeditionStates"
|
||||||
order="created DESC"
|
order="created DESC"
|
||||||
auto-load="true">
|
auto-load="true">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-data-viewer model="model">
|
<vn-data-viewer model="modelExpeditionStates">
|
||||||
<vn-card class="vn-w-md">
|
<vn-card class="vn-w-md">
|
||||||
<vn-table model="model">
|
<vn-table model="modelExpeditionStates">
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th field="state">State</vn-th>
|
<vn-th field="state">State</vn-th>
|
||||||
|
@ -112,3 +135,36 @@
|
||||||
vn-id="workerDescriptor">
|
vn-id="workerDescriptor">
|
||||||
</vn-worker-descriptor-popover>
|
</vn-worker-descriptor-popover>
|
||||||
</vn-popup>
|
</vn-popup>
|
||||||
|
|
||||||
|
<vn-menu vn-id="moreOptions">
|
||||||
|
<vn-item translate
|
||||||
|
name="withoutRoute"
|
||||||
|
ng-click="selectLanded.show('withoutRoute')">
|
||||||
|
New ticket without route
|
||||||
|
</vn-item>
|
||||||
|
<vn-item translate
|
||||||
|
name="withRoute"
|
||||||
|
ng-click="selectLanded.show('withRoute')">
|
||||||
|
New ticket with route
|
||||||
|
</vn-item>
|
||||||
|
</vn-menu>
|
||||||
|
|
||||||
|
<vn-dialog
|
||||||
|
vn-id="selectLanded"
|
||||||
|
on-accept="$ctrl.createTicket($ctrl.landed, $ctrl.newRoute)">
|
||||||
|
<tpl-body>
|
||||||
|
<vn-date-picker
|
||||||
|
label="Landed"
|
||||||
|
ng-model="$ctrl.landed">
|
||||||
|
</vn-date-picker>
|
||||||
|
<vn-textfield
|
||||||
|
ng-show="selectLanded.data == 'withRoute'"
|
||||||
|
label="Route id"
|
||||||
|
ng-model="$ctrl.newRoute">
|
||||||
|
</vn-textfield>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
|
<button response="accept" translate>Accept</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
|
@ -2,6 +2,27 @@ import ngModule from '../module';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
|
|
||||||
class Controller extends Section {
|
class Controller extends Section {
|
||||||
|
constructor($element, $scope) {
|
||||||
|
super($element, $scope);
|
||||||
|
this.landed = new Date();
|
||||||
|
this.newRoute = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get checked() {
|
||||||
|
const rows = this.$.model.data || [];
|
||||||
|
const checkedRows = [];
|
||||||
|
for (let row of rows) {
|
||||||
|
if (row.checked)
|
||||||
|
checkedRows.push(row.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkedRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
get totalChecked() {
|
||||||
|
return this.checked.length;
|
||||||
|
}
|
||||||
|
|
||||||
onDialogAccept(id) {
|
onDialogAccept(id) {
|
||||||
return this.$http.delete(`Expeditions/${id}`)
|
return this.$http.delete(`Expeditions/${id}`)
|
||||||
.then(() => this.$.model.refresh());
|
.then(() => this.$.model.refresh());
|
||||||
|
@ -11,6 +32,33 @@ class Controller extends Section {
|
||||||
this.expedition = expedition;
|
this.expedition = expedition;
|
||||||
this.$.statusLog.show();
|
this.$.statusLog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onRemove() {
|
||||||
|
const params = {expeditionIds: this.checked};
|
||||||
|
const query = `Expeditions/deleteExpeditions`;
|
||||||
|
this.$http.post(query, params)
|
||||||
|
.then(() => {
|
||||||
|
this.vnApp.showSuccess(this.$t('Expedition removed'));
|
||||||
|
this.$.model.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createTicket(landed, routeFk) {
|
||||||
|
const params = {
|
||||||
|
clientId: this.ticket.clientFk,
|
||||||
|
landed: landed,
|
||||||
|
warehouseId: this.ticket.warehouseFk,
|
||||||
|
addressId: this.ticket.addressFk,
|
||||||
|
agencyModeId: this.ticket.agencyModeFk,
|
||||||
|
routeId: routeFk,
|
||||||
|
expeditionIds: this.checked
|
||||||
|
};
|
||||||
|
const query = `Expeditions/moveExpeditions`;
|
||||||
|
this.$http.post(query, params).then(res => {
|
||||||
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
|
this.$state.go('ticket.card.summary', {id: res.data.id});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnTicketExpedition', {
|
ngModule.vnComponent('vnTicketExpedition', {
|
||||||
|
|
|
@ -17,6 +17,14 @@ describe('Ticket', () => {
|
||||||
refresh: () => {}
|
refresh: () => {}
|
||||||
};
|
};
|
||||||
controller = $componentController('vnTicketExpedition', {$element: null, $scope});
|
controller = $componentController('vnTicketExpedition', {$element: null, $scope});
|
||||||
|
controller.$.model.data = [
|
||||||
|
{id: 1},
|
||||||
|
{id: 2},
|
||||||
|
{id: 3}
|
||||||
|
];
|
||||||
|
const modelData = controller.$.model.data;
|
||||||
|
modelData[0].checked = true;
|
||||||
|
modelData[1].checked = true;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('onDialogAccept()', () => {
|
describe('onDialogAccept()', () => {
|
||||||
|
@ -50,5 +58,51 @@ describe('Ticket', () => {
|
||||||
expect(controller.$.statusLog.show).toHaveBeenCalledWith();
|
expect(controller.$.statusLog.show).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('onRemove()', () => {
|
||||||
|
it('should make a query and then call to the model refresh() method', () => {
|
||||||
|
jest.spyOn($scope.model, 'refresh');
|
||||||
|
|
||||||
|
const expectedParams = {expeditionIds: [1, 2]};
|
||||||
|
$httpBackend.expect('POST', 'Expeditions/deleteExpeditions', expectedParams).respond(200);
|
||||||
|
controller.onRemove();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect($scope.model.refresh).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createTicket()', () => {
|
||||||
|
it('should make a query and then call to the $state go() method', () => {
|
||||||
|
jest.spyOn(controller.$state, 'go').mockReturnThis();
|
||||||
|
|
||||||
|
const ticket = {
|
||||||
|
clientFk: 1101,
|
||||||
|
landed: new Date(),
|
||||||
|
addressFk: 121,
|
||||||
|
agencyModeFk: 1,
|
||||||
|
warehouseFk: 1
|
||||||
|
};
|
||||||
|
const routeId = null;
|
||||||
|
controller.ticket = ticket;
|
||||||
|
|
||||||
|
const ticketToTransfer = {id: 28};
|
||||||
|
|
||||||
|
const expectedParams = {
|
||||||
|
clientId: 1101,
|
||||||
|
landed: new Date(),
|
||||||
|
warehouseId: 1,
|
||||||
|
addressId: 121,
|
||||||
|
agencyModeId: 1,
|
||||||
|
routeId: null,
|
||||||
|
expeditionIds: [1, 2]
|
||||||
|
};
|
||||||
|
$httpBackend.expect('POST', 'Expeditions/moveExpeditions', expectedParams).respond(ticketToTransfer);
|
||||||
|
controller.createTicket(ticket.landed, routeId);
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.$state.go).toHaveBeenCalledWith('ticket.card.summary', {id: ticketToTransfer.id});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1 +1,6 @@
|
||||||
Status log: Hitorial de estados
|
Status log: Hitorial de estados
|
||||||
|
Expedition removed: Expedición eliminada
|
||||||
|
Move: Mover
|
||||||
|
New ticket without route: Nuevo ticket sin ruta
|
||||||
|
New ticket with route: Nuevo ticket con ruta
|
||||||
|
Route id: Id ruta
|
|
@ -310,7 +310,7 @@
|
||||||
ng-model="$ctrl.manaCode">
|
ng-model="$ctrl.manaCode">
|
||||||
</vn-radio>
|
</vn-radio>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
<div class="simulator">
|
<div class="simulator" ng-show="$ctrl.edit.sale">
|
||||||
<p class="simulatorTitle" translate>New price</p>
|
<p class="simulatorTitle" translate>New price</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>{{$ctrl.getNewPrice() | currency: 'EUR': 2}}</strong>
|
<strong>{{$ctrl.getNewPrice() | currency: 'EUR': 2}}</strong>
|
||||||
|
@ -331,32 +331,6 @@
|
||||||
</div>
|
</div>
|
||||||
</vn-popover>
|
</vn-popover>
|
||||||
|
|
||||||
<!-- Multiple discount dialog -->
|
|
||||||
<vn-dialog vn-id="editDiscountDialog"
|
|
||||||
on-open="$ctrl.getMana()"
|
|
||||||
message="Edit discount">
|
|
||||||
<tpl-body>
|
|
||||||
<vn-spinner class="vn-pa-xs"
|
|
||||||
ng-if="$ctrl.edit.mana == null"
|
|
||||||
enable="true">
|
|
||||||
</vn-spinner>
|
|
||||||
<div ng-if="$ctrl.edit.mana != null">
|
|
||||||
<div class="vn-pa-md">
|
|
||||||
<vn-input-number vn-focus
|
|
||||||
label="Discount"
|
|
||||||
ng-model="$ctrl.edit.discount"
|
|
||||||
on-change="$ctrl.changeMultipleDiscount()"
|
|
||||||
clear-disabled="true"
|
|
||||||
suffix="%">
|
|
||||||
</vn-input-number>
|
|
||||||
</div>
|
|
||||||
<section class="header vn-pa-md">
|
|
||||||
<span>Mana: <strong>{{::$ctrl.edit.mana | currency: 'EUR': 0}}</strong></span>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</tpl-body>
|
|
||||||
</vn-dialog>
|
|
||||||
|
|
||||||
<!-- Transfer Popover -->
|
<!-- Transfer Popover -->
|
||||||
<vn-popover vn-id="transfer">
|
<vn-popover vn-id="transfer">
|
||||||
<div class="vn-pa-lg transfer">
|
<div class="vn-pa-lg transfer">
|
||||||
|
@ -500,7 +474,7 @@
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item translate
|
<vn-item translate
|
||||||
name="discount"
|
name="discount"
|
||||||
ng-click="$ctrl.showEditDiscountDialog($event)">
|
ng-click="$ctrl.showEditDiscountPopover($event, sale)">
|
||||||
Update discount
|
Update discount
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item translate
|
<vn-item translate
|
||||||
|
|
|
@ -251,26 +251,21 @@ class Controller extends Section {
|
||||||
|
|
||||||
showEditDiscountPopover(event, sale) {
|
showEditDiscountPopover(event, sale) {
|
||||||
if (this.isLocked) return;
|
if (this.isLocked) return;
|
||||||
|
if (sale) {
|
||||||
this.edit = {
|
this.edit = {
|
||||||
discount: sale.discount,
|
discount: sale.discount,
|
||||||
sale: sale
|
sale: sale
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
this.edit = {
|
||||||
|
discount: null,
|
||||||
|
sales: this.selectedValidSales()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
this.$.editDiscount.show(event);
|
this.$.editDiscount.show(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
showEditDiscountDialog(event) {
|
|
||||||
if (this.isLocked) return;
|
|
||||||
|
|
||||||
this.edit = {
|
|
||||||
discount: null,
|
|
||||||
sales: this.selectedValidSales()
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$.editDiscountDialog.show(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
changeDiscount() {
|
changeDiscount() {
|
||||||
const sale = this.edit.sale;
|
const sale = this.edit.sale;
|
||||||
const newDiscount = this.edit.discount;
|
const newDiscount = this.edit.discount;
|
||||||
|
@ -286,11 +281,10 @@ class Controller extends Section {
|
||||||
const hasChanges = sales.some(sale => {
|
const hasChanges = sales.some(sale => {
|
||||||
return sale.discount != newDiscount;
|
return sale.discount != newDiscount;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (newDiscount != null && hasChanges)
|
if (newDiscount != null && hasChanges)
|
||||||
this.updateDiscount(sales);
|
this.updateDiscount(sales);
|
||||||
|
|
||||||
this.$.editDiscountDialog.hide();
|
this.$.editDiscount.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDiscount(sales) {
|
updateDiscount(sales) {
|
||||||
|
@ -311,7 +305,7 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
getNewPrice() {
|
getNewPrice() {
|
||||||
if (this.edit) {
|
if (this.edit.sale) {
|
||||||
const sale = this.edit.sale;
|
const sale = this.edit.sale;
|
||||||
let newDiscount = sale.discount;
|
let newDiscount = sale.discount;
|
||||||
let newPrice = this.edit.price || sale.price;
|
let newPrice = this.edit.price || sale.price;
|
||||||
|
@ -513,7 +507,8 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
this.changeDiscount();
|
if (this.edit.sale) this.changeDiscount();
|
||||||
|
if (this.edit.sales) this.changeMultipleDiscount();
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
|
|
|
@ -394,7 +394,7 @@ describe('Ticket', () => {
|
||||||
secondSelectedSale.checked = true;
|
secondSelectedSale.checked = true;
|
||||||
|
|
||||||
const expectedSales = [firstSelectedSale, secondSelectedSale];
|
const expectedSales = [firstSelectedSale, secondSelectedSale];
|
||||||
controller.$.editDiscountDialog = {hide: jest.fn()};
|
controller.$.editDiscount = {hide: jest.fn()};
|
||||||
controller.edit = {
|
controller.edit = {
|
||||||
discount: 10,
|
discount: 10,
|
||||||
sales: expectedSales
|
sales: expectedSales
|
||||||
|
@ -403,7 +403,7 @@ describe('Ticket', () => {
|
||||||
controller.changeMultipleDiscount();
|
controller.changeMultipleDiscount();
|
||||||
|
|
||||||
expect(controller.updateDiscount).toHaveBeenCalledWith(expectedSales);
|
expect(controller.updateDiscount).toHaveBeenCalledWith(expectedSales);
|
||||||
expect(controller.$.editDiscountDialog.hide).toHaveBeenCalledWith();
|
expect(controller.$.editDiscount.hide).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not call to the updateDiscount() method and then to the editDiscountDialog hide() method', () => {
|
it('should not call to the updateDiscount() method and then to the editDiscountDialog hide() method', () => {
|
||||||
|
@ -418,7 +418,7 @@ describe('Ticket', () => {
|
||||||
secondSelectedSale.discount = 10;
|
secondSelectedSale.discount = 10;
|
||||||
|
|
||||||
const expectedSales = [firstSelectedSale, secondSelectedSale];
|
const expectedSales = [firstSelectedSale, secondSelectedSale];
|
||||||
controller.$.editDiscountDialog = {hide: jest.fn()};
|
controller.$.editDiscount = {hide: jest.fn()};
|
||||||
controller.edit = {
|
controller.edit = {
|
||||||
discount: 10,
|
discount: 10,
|
||||||
sales: expectedSales
|
sales: expectedSales
|
||||||
|
@ -427,7 +427,7 @@ describe('Ticket', () => {
|
||||||
controller.changeMultipleDiscount();
|
controller.changeMultipleDiscount();
|
||||||
|
|
||||||
expect(controller.updateDiscount).not.toHaveBeenCalledWith(expectedSales);
|
expect(controller.updateDiscount).not.toHaveBeenCalledWith(expectedSales);
|
||||||
expect(controller.$.editDiscountDialog.hide).toHaveBeenCalledWith();
|
expect(controller.$.editDiscount.hide).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"bmp-js": "^0.1.0",
|
"bmp-js": "^0.1.0",
|
||||||
"compression": "^1.7.3",
|
"compression": "^1.7.3",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
"fs-extra": "^5.0.0",
|
"fs-extra": "^5.0.0",
|
||||||
"ftps": "^1.2.0",
|
"ftps": "^1.2.0",
|
||||||
"got": "^10.7.0",
|
"got": "^10.7.0",
|
||||||
|
|
|
@ -23,9 +23,10 @@
|
||||||
<!-- Block -->
|
<!-- Block -->
|
||||||
<div class="grid-row">
|
<div class="grid-row">
|
||||||
<div class="grid-block vn-pa-ml">
|
<div class="grid-block vn-pa-ml">
|
||||||
<h1>{{ $t('title') }}</h1>
|
<h1>{{ $t('title', [id]) }}</h1>
|
||||||
<p>{{$t('description.dear')}},</p>
|
<p>{{ $t('description.dear') }},</p>
|
||||||
<p>{{$t('description.instructions')}}</p>
|
<p v-html="instructions"></p>
|
||||||
|
<p>{{ $t('description.conclusion') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Footer block -->
|
<!-- Footer block -->
|
||||||
|
|
|
@ -8,6 +8,19 @@ module.exports = {
|
||||||
'email-header': emailHeader.build(),
|
'email-header': emailHeader.build(),
|
||||||
'email-footer': emailFooter.build()
|
'email-footer': emailFooter.build()
|
||||||
},
|
},
|
||||||
|
async serverPrefetch() {
|
||||||
|
this.ticket = await this.fetchTicket(this.id);
|
||||||
|
|
||||||
|
if (!this.ticket)
|
||||||
|
throw new Error('Something went wrong');
|
||||||
|
|
||||||
|
this.instructions = this.$t('description.instructions', [this.id, this.ticket.id]);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchTicket(id) {
|
||||||
|
return this.findOneFromDef('ticket', [id]);
|
||||||
|
}
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
id: {
|
id: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
subject: Orden de recogida
|
subject: Reclamación Verdnatura
|
||||||
title: Orden de recogida
|
title: Reclamación Verdnatura {0}
|
||||||
description:
|
description:
|
||||||
dear: Estimado cliente
|
dear: Estimado cliente
|
||||||
instructions: Aqui tienes tu orden de recogida.
|
instructions: 'Le informamos que se ha aceptado su solicitud de reclamación <strong>nº {0}</strong> correspondiente al pedido <strong>{1}</strong>.
|
||||||
|
Para tramitar la recogida, rellene el <a href="https://form.jotform.com/verdnatura/recogida-verdnatura"
|
||||||
|
title="Formulario Recogida" target="_blank" style="color: #8dba25">SIGUIENTE FORMULARIO</a> en un <strong>plazo máximo de 24h.</strong>
|
||||||
|
<br/><br/>Cuando recibamos el género en nuestras instalaciones emitiremos el abono correspondiente.
|
||||||
|
Debe imprimir el archivo adjunto e incluirlo en la caja. En el caso de no poder imprimirlo, <strong>identifique la caja con el número de reclamación CLARAMENTE LEGIBLE.</strong>'
|
||||||
|
conclusion: Un saludo
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
SELECT
|
||||||
|
c.ticketFk as id
|
||||||
|
FROM claim c
|
||||||
|
WHERE c.id = ?
|
|
@ -78,9 +78,6 @@
|
||||||
<h3>{{client.name}}</h3>
|
<h3>{{client.name}}</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p v-html="$t('sections.agency.description')"></p>
|
|
||||||
<p>{{claimConfig.pickupContact}}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Footer block -->
|
<!-- Footer block -->
|
||||||
|
|
|
@ -7,7 +7,6 @@ module.exports = {
|
||||||
async serverPrefetch() {
|
async serverPrefetch() {
|
||||||
this.client = await this.fetchClient(this.id);
|
this.client = await this.fetchClient(this.id);
|
||||||
this.sales = await this.fetchSales(this.id);
|
this.sales = await this.fetchSales(this.id);
|
||||||
this.claimConfig = await this.fetchClaimConfig();
|
|
||||||
|
|
||||||
if (!this.client)
|
if (!this.client)
|
||||||
throw new Error('Something went wrong');
|
throw new Error('Something went wrong');
|
||||||
|
@ -25,10 +24,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
fetchSales(id) {
|
fetchSales(id) {
|
||||||
return this.rawSqlFromDef('sales', [id]);
|
return this.rawSqlFromDef('sales', [id]);
|
||||||
},
|
}
|
||||||
fetchClaimConfig() {
|
|
||||||
return this.findOneFromDef('claimConfig');
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
'report-header': reportHeader.build(),
|
'report-header': reportHeader.build(),
|
||||||
|
|
|
@ -11,7 +11,3 @@ concept: Concepto
|
||||||
clientSignature: Firma del cliente
|
clientSignature: Firma del cliente
|
||||||
claim: Reclamación {0}
|
claim: Reclamación {0}
|
||||||
phone: Teléfono
|
phone: Teléfono
|
||||||
sections:
|
|
||||||
agency:
|
|
||||||
description: 'Para agilizar su recogida, por favor, póngase en contacto con la oficina
|
|
||||||
de Logista Parcel.'
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
SELECT pickupContact
|
|
||||||
FROM claimConfig;
|
|
Loading…
Reference in New Issue