diff --git a/back/methods/collection/setSaleQuantity.js b/back/methods/collection/setSaleQuantity.js
index 644c44a60..b6c56ddc4 100644
--- a/back/methods/collection/setSaleQuantity.js
+++ b/back/methods/collection/setSaleQuantity.js
@@ -26,11 +26,30 @@ module.exports = Self => {
Self.setSaleQuantity = async(saleId, quantity) => {
const models = Self.app.models;
+ const myOptions = {};
+ let tx;
- const sale = await models.Sale.findById(saleId);
- return await sale.updateAttributes({
- originalQuantity: sale.quantity,
- quantity: quantity
- });
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
+ myOptions.transaction = tx;
+ }
+
+ try {
+ const sale = await models.Sale.findById(saleId, null, myOptions);
+ const saleUpdated = await sale.updateAttributes({
+ originalQuantity: sale.quantity,
+ quantity: quantity
+ }, myOptions);
+
+ if (tx) await tx.commit();
+
+ return saleUpdated;
+ } catch (e) {
+ if (tx) await tx.rollback();
+ throw e;
+ }
};
};
diff --git a/back/methods/collection/spec/setSaleQuantity.spec.js b/back/methods/collection/spec/setSaleQuantity.spec.js
index 5d06a4383..63dc3bd2d 100644
--- a/back/methods/collection/spec/setSaleQuantity.spec.js
+++ b/back/methods/collection/spec/setSaleQuantity.spec.js
@@ -2,15 +2,26 @@ const models = require('vn-loopback/server/server').models;
describe('setSaleQuantity()', () => {
it('should change quantity sale', async() => {
- const saleId = 30;
- const newQuantity = 10;
+ const tx = await models.Ticket.beginTransaction({});
- const originalSale = await models.Sale.findById(saleId);
+ try {
+ const options = {transaction: tx};
- await models.Collection.setSaleQuantity(saleId, newQuantity);
- const updateSale = await models.Sale.findById(saleId);
+ const saleId = 30;
+ const newQuantity = 10;
- expect(updateSale.originalQuantity).toEqual(originalSale.quantity);
- expect(updateSale.quantity).toEqual(newQuantity);
+ const originalSale = await models.Sale.findById(saleId, null, options);
+
+ await models.Collection.setSaleQuantity(saleId, newQuantity, options);
+ const updateSale = await models.Sale.findById(saleId, null, options);
+
+ expect(updateSale.originalQuantity).toEqual(originalSale.quantity);
+ expect(updateSale.quantity).toEqual(newQuantity);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
});
});
diff --git a/db/changes/225201/00-aclTicketLog.sql b/db/changes/225201/00-aclTicketLog.sql
new file mode 100644
index 000000000..edba17ab4
--- /dev/null
+++ b/db/changes/225201/00-aclTicketLog.sql
@@ -0,0 +1,3 @@
+INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
+ VALUES
+ ('TicketLog', 'getChanges', 'READ', 'ALLOW', 'ROLE', 'employee');
diff --git a/db/changes/225201/00-ticketSms.sql b/db/changes/225201/00-ticketSms.sql
new file mode 100644
index 000000000..f454f99b1
--- /dev/null
+++ b/db/changes/225201/00-ticketSms.sql
@@ -0,0 +1,8 @@
+CREATE TABLE `vn`.`ticketSms` (
+ `smsFk` mediumint(8) unsigned NOT NULL,
+ `ticketFk` int(11) DEFAULT NULL,
+ PRIMARY KEY (`smsFk`),
+ KEY `ticketSms_FK_1` (`ticketFk`),
+ CONSTRAINT `ticketSms_FK` FOREIGN KEY (`smsFk`) REFERENCES `sms` (`id`) ON UPDATE CASCADE,
+ CONSTRAINT `ticketSms_FK_1` FOREIGN KEY (`ticketFk`) REFERENCES `ticket` (`id`) ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index 114a12bff..c4ce78658 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -2733,6 +2733,14 @@ UPDATE `account`.`user`
SET `hasGrant` = 1
WHERE `id` = 66;
+INSERT INTO `vn`.`ticketLog` (`originFk`, userFk, `action`, changedModel, oldInstance, newInstance, changedModelId, `description`)
+ VALUES
+ (7, 18, 'update', 'Sale', '{"quantity":1}', '{"quantity":10}', 1, NULL),
+ (7, 18, 'update', 'Ticket', '{"quantity":1,"concept":"Chest ammo box"}', '{"quantity":10,"concept":"Chest ammo box"}', 1, NULL),
+ (7, 18, 'update', 'Sale', '{"price":3}', '{"price":5}', 1, NULL),
+ (7, 18, 'update', NULL, NULL, NULL, NULL, "Cambio cantidad Melee weapon heavy shield 1x0.5m de '5' a '10'");
+
+
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');
diff --git a/loopback/locale/en.json b/loopback/locale/en.json
index 3c6f6188f..4885e34ec 100644
--- a/loopback/locale/en.json
+++ b/loopback/locale/en.json
@@ -66,9 +66,10 @@
"MESSAGE_INSURANCE_CHANGE": "I have changed the insurence credit of client [{{clientName}} ({{clientId}})]({{{url}}}) to *{{credit}} €*",
"Changed client paymethod": "I have changed the pay method for client [{{clientName}} ({{clientId}})]({{{url}}})",
"Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})",
- "Claim will be picked": "The product from the claim [{{claimId}}]({{{claimUrl}}}) from the client *{{clientName}}* will be picked",
- "Claim state has changed to incomplete": "The state of the claim [{{claimId}}]({{{claimUrl}}}) from client *{{clientName}}* has changed to *incomplete*",
- "Claim state has changed to canceled": "The state of the claim [{{claimId}}]({{{claimUrl}}}) from client *{{clientName}}* has changed to *canceled*",
+ "Change quantity": "{{concept}} change of {{oldQuantity}} to {{newQuantity}}",
+ "Claim will be picked": "The product from the claim [({{claimId}})]({{{claimUrl}}}) from the client *{{clientName}}* will be picked",
+ "Claim state has changed to incomplete": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *incomplete*",
+ "Claim state has changed to canceled": "The state of the claim [({{claimId}})]({{{claimUrl}}}) from client *{{clientName}}* has changed to *canceled*",
"Customs agent is required for a non UEE member": "Customs agent is required for a non UEE member",
"Incoterms is required for a non UEE member": "Incoterms is required for a non UEE member",
"Client checked as validated despite of duplication": "Client checked as validated despite of duplication from client id {{clientId}}",
diff --git a/loopback/locale/es.json b/loopback/locale/es.json
index c12d6bbe5..a500ff550 100644
--- a/loopback/locale/es.json
+++ b/loopback/locale/es.json
@@ -134,9 +134,10 @@
"MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*",
"Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})",
"Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})",
- "Claim will be picked": "Se recogerá el género de la reclamación [{{claimId}}]({{{claimUrl}}}) del cliente *{{clientName}}*",
- "Claim state has changed to incomplete": "Se ha cambiado el estado de la reclamación [{{claimId}}]({{{claimUrl}}}) del cliente *{{clientName}}* a *incompleta*",
- "Claim state has changed to canceled": "Se ha cambiado el estado de la reclamación [{{claimId}}]({{{claimUrl}}}) del cliente *{{clientName}}* a *anulado*",
+ "Change quantity": "{{concept}} cambia de {{oldQuantity}} a {{newQuantity}}",
+ "Claim will be picked": "Se recogerá el género de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}*",
+ "Claim state has changed to incomplete": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *incompleta*",
+ "Claim state has changed to canceled": "Se ha cambiado el estado de la reclamación [({{claimId}})]({{{claimUrl}}}) del cliente *{{clientName}}* a *anulado*",
"Client checked as validated despite of duplication": "Cliente comprobado a pesar de que existe el cliente id {{clientId}}",
"ORDER_ROW_UNAVAILABLE": "No hay disponibilidad de este producto",
"Distance must be lesser than 1000": "La distancia debe ser inferior a 1000",
diff --git a/modules/client/back/model-config.json b/modules/client/back/model-config.json
index 4ef34ca3a..b466aa5a1 100644
--- a/modules/client/back/model-config.json
+++ b/modules/client/back/model-config.json
@@ -104,6 +104,9 @@
"SageTransactionType": {
"dataSource": "vn"
},
+ "TicketSms": {
+ "dataSource": "vn"
+ },
"TpvError": {
"dataSource": "vn"
},
diff --git a/modules/client/back/models/ticket-sms.json b/modules/client/back/models/ticket-sms.json
new file mode 100644
index 000000000..03f592f51
--- /dev/null
+++ b/modules/client/back/models/ticket-sms.json
@@ -0,0 +1,28 @@
+{
+ "name": "TicketSms",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "ticketSms"
+ }
+ },
+ "properties": {
+ "smsFk": {
+ "type": "number",
+ "id": true,
+ "description": "Identifier"
+ }
+ },
+ "relations": {
+ "ticket": {
+ "type": "belongsTo",
+ "model": "Ticket",
+ "foreignKey": "ticketFk"
+ },
+ "sms": {
+ "type": "belongsTo",
+ "model": "Sms",
+ "foreignKey": "smsFk"
+ }
+ }
+}
diff --git a/modules/ticket/back/methods/ticket-log/getChanges.js b/modules/ticket/back/methods/ticket-log/getChanges.js
new file mode 100644
index 000000000..d020a0957
--- /dev/null
+++ b/modules/ticket/back/methods/ticket-log/getChanges.js
@@ -0,0 +1,64 @@
+module.exports = Self => {
+ Self.remoteMethodCtx('getChanges', {
+ description: 'Get changues in the sales of a ticket',
+ accessType: 'READ',
+ accepts: [{
+ arg: 'id',
+ type: 'number',
+ required: true,
+ description: 'the ticket id',
+ http: {source: 'path'}
+ }],
+ returns: {
+ type: 'object',
+ root: true
+ },
+ http: {
+ path: `/:id/getChanges`,
+ verb: 'get'
+ }
+ });
+
+ Self.getChanges = async(ctx, id, options) => {
+ const models = Self.app.models;
+ const myOptions = {};
+ const $t = ctx.req.__; // $translate
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const ticketLogs = await models.TicketLog.find(
+ {
+ where: {
+ and: [
+ {originFk: id},
+ {action: 'update'},
+ {changedModel: 'Sale'}
+ ]
+ },
+ fields: [
+ 'oldInstance',
+ 'newInstance',
+ 'changedModelId'
+ ],
+ }, myOptions);
+
+ const changes = [];
+ for (const ticketLog of ticketLogs) {
+ const oldQuantity = ticketLog.oldInstance.quantity;
+ const newQuantity = ticketLog.newInstance.quantity;
+
+ if (oldQuantity || newQuantity) {
+ const sale = await models.Sale.findById(ticketLog.changedModelId, null, myOptions);
+ const message = $t('Change quantity', {
+ concept: sale.concept,
+ oldQuantity: oldQuantity || 0,
+ newQuantity: newQuantity || 0,
+ });
+
+ changes.push(message);
+ }
+ }
+ return changes.join('\n');
+ };
+};
diff --git a/modules/ticket/back/methods/ticket-log/specs/getChanges.spec.js b/modules/ticket/back/methods/ticket-log/specs/getChanges.spec.js
new file mode 100644
index 000000000..c0f7dde0e
--- /dev/null
+++ b/modules/ticket/back/methods/ticket-log/specs/getChanges.spec.js
@@ -0,0 +1,16 @@
+const models = require('vn-loopback/server/server').models;
+
+describe('ticketLog getChanges()', () => {
+ const ctx = {req: {}};
+
+ ctx.req.__ = value => {
+ return value;
+ };
+ it('should return the changes in the sales of a ticket', async() => {
+ const ticketId = 7;
+
+ const changues = await models.TicketLog.getChanges(ctx, ticketId);
+
+ expect(changues).toContain(`Change quantity`);
+ });
+});
diff --git a/modules/ticket/back/methods/ticket/sendSms.js b/modules/ticket/back/methods/ticket/sendSms.js
index a0adcae07..2336ae859 100644
--- a/modules/ticket/back/methods/ticket/sendSms.js
+++ b/modules/ticket/back/methods/ticket/sendSms.js
@@ -31,6 +31,7 @@ module.exports = Self => {
});
Self.sendSms = async(ctx, id, destination, message, options) => {
+ const models = Self.app.models;
const myOptions = {};
let tx;
@@ -45,7 +46,14 @@ module.exports = Self => {
const userId = ctx.req.accessToken.userId;
try {
- const sms = await Self.app.models.Sms.send(ctx, destination, message);
+ const sms = await models.Sms.send(ctx, destination, message);
+
+ const newTicketSms = {
+ ticketFk: id,
+ smsFk: sms.id
+ };
+ await models.TicketSms.create(newTicketSms);
+
const logRecord = {
originFk: id,
userFk: userId,
@@ -60,7 +68,7 @@ module.exports = Self => {
}
};
- const ticketLog = await Self.app.models.TicketLog.create(logRecord, myOptions);
+ const ticketLog = await models.TicketLog.create(logRecord, myOptions);
sms.logId = ticketLog.id;
diff --git a/modules/ticket/back/methods/ticket/specs/sendSms.spec.js b/modules/ticket/back/methods/ticket/specs/sendSms.spec.js
index f50253b10..f94b8be2a 100644
--- a/modules/ticket/back/methods/ticket/specs/sendSms.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/sendSms.spec.js
@@ -15,9 +15,16 @@ describe('ticket sendSms()', () => {
const sms = await models.Ticket.sendSms(ctx, id, destination, message, options);
const createdLog = await models.TicketLog.findById(sms.logId, null, options);
+
+ const filter = {
+ ticketFk: createdLog.originFk
+ };
+ const ticketSms = await models.TicketSms.findOne(filter, options);
+
const json = JSON.parse(JSON.stringify(createdLog.newInstance));
expect(json.message).toEqual(message);
+ expect(ticketSms.ticketFk).toEqual(createdLog.originFk);
await tx.rollback();
} catch (e) {
diff --git a/modules/ticket/back/models/ticket-log.js b/modules/ticket/back/models/ticket-log.js
new file mode 100644
index 000000000..81855ada7
--- /dev/null
+++ b/modules/ticket/back/models/ticket-log.js
@@ -0,0 +1,3 @@
+module.exports = function(Self) {
+ require('../methods/ticket-log/getChanges')(Self);
+};
diff --git a/modules/ticket/front/descriptor-menu/index.html b/modules/ticket/front/descriptor-menu/index.html
index 1f0ae8362..805e0b391 100644
--- a/modules/ticket/front/descriptor-menu/index.html
+++ b/modules/ticket/front/descriptor-menu/index.html
@@ -43,7 +43,7 @@
ng-if="!$ctrl.hasDocuwareFile"
ng-click="$ctrl.showPdfDeliveryNote('withoutPrices')"
translate>
- as PDF without prices
+ as PDF without prices
SMS Minimum import
+
+ SMS Notify changes
+
@@ -251,13 +257,13 @@
-
-
-
\ No newline at end of file
+
diff --git a/modules/ticket/front/descriptor-menu/index.js b/modules/ticket/front/descriptor-menu/index.js
index 8ed30e9da..168002d07 100644
--- a/modules/ticket/front/descriptor-menu/index.js
+++ b/modules/ticket/front/descriptor-menu/index.js
@@ -225,6 +225,18 @@ class Controller extends Section {
});
}
+ sendChangesSms() {
+ return this.$http.get(`TicketLogs/${this.id}/getChanges`)
+ .then(res => {
+ const params = {
+ ticketId: this.id,
+ created: this.ticket.updated,
+ changes: res.data
+ };
+ this.showSMSDialog({message: this.$t('Send changes', params)});
+ });
+ }
+
showSMSDialog(params) {
const address = this.ticket.address;
const client = this.ticket.client;
diff --git a/modules/ticket/front/descriptor-menu/index.spec.js b/modules/ticket/front/descriptor-menu/index.spec.js
index f3d8ea3b3..48b64f4a0 100644
--- a/modules/ticket/front/descriptor-menu/index.spec.js
+++ b/modules/ticket/front/descriptor-menu/index.spec.js
@@ -258,6 +258,19 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
});
});
+ describe('sendChangesSms()', () => {
+ it('should make a query and open the sms dialog', () => {
+ controller.$.sms = {open: () => {}};
+ jest.spyOn(controller.$.sms, 'open');
+
+ $httpBackend.expectGET(`TicketLogs/${ticket.id}/getChanges`).respond();
+ controller.sendChangesSms();
+ $httpBackend.flush();
+
+ expect(controller.$.sms.open).toHaveBeenCalledWith();
+ });
+ });
+
describe('showSMSDialog()', () => {
it('should set the destionationFk and destination properties and then call the sms open() method', () => {
controller.$.sms = {open: () => {}};
diff --git a/modules/ticket/front/descriptor-menu/locale/es.yml b/modules/ticket/front/descriptor-menu/locale/es.yml
index a09a32131..a2725f485 100644
--- a/modules/ticket/front/descriptor-menu/locale/es.yml
+++ b/modules/ticket/front/descriptor-menu/locale/es.yml
@@ -11,4 +11,5 @@ Show Proforma: Ver proforma
Refund all: Abonar todo
Invoice sent: Factura enviada
The following refund ticket have been created: "Se ha creado siguiente ticket de abono: {{ticketId}}"
-Transfer client: Transferir cliente
\ No newline at end of file
+Transfer client: Transferir cliente
+SMS Notify changes: SMS Notificar cambios
diff --git a/modules/ticket/front/descriptor/locale/en.yml b/modules/ticket/front/descriptor/locale/en.yml
index 64075c7ef..8eed2265d 100644
--- a/modules/ticket/front/descriptor/locale/en.yml
+++ b/modules/ticket/front/descriptor/locale/en.yml
@@ -1,2 +1,3 @@
Make a payment: "Verdnatura communicates:\rYour order is pending of payment.\rPlease, enter the web page and make the payment with card.\rThank you."
Minimum is needed: "Verdnatura communicates:\rA minimum import of 50€ (Without BAT) is needed for your order {{ticketId}} from date {{created | date: 'dd/MM/yyyy'}} to receive it with no extra fees."
+Send changes: "Verdnatura communicates:\rOrder {{ticketId}} date {{created | date: 'dd/MM/yyyy'}}\r{{changes}}"
diff --git a/modules/ticket/front/descriptor/locale/es.yml b/modules/ticket/front/descriptor/locale/es.yml
index bce9e62d7..d921b5dc2 100644
--- a/modules/ticket/front/descriptor/locale/es.yml
+++ b/modules/ticket/front/descriptor/locale/es.yml
@@ -23,3 +23,4 @@ Restore ticket: Restaurar ticket
You are going to restore this ticket: Vas a restaurar este ticket
Are you sure you want to restore this ticket?: ¿Seguro que quieres restaurar el ticket?
Are you sure you want to refund all?: ¿Seguro que quieres abonar todo?
+Send changes: "Verdnatura le recuerda:\rPedido {{ticketId}} día {{created | date: 'dd/MM/yyyy'}}\r{{changes}}"