diff --git a/modules/order/front/line/index.html b/modules/order/front/line/index.html
index 91c05657b..bdda0739d 100644
--- a/modules/order/front/line/index.html
+++ b/modules/order/front/line/index.html
@@ -43,6 +43,7 @@
diff --git a/modules/order/front/summary/index.html b/modules/order/front/summary/index.html
index ba769a616..e3022493d 100644
--- a/modules/order/front/summary/index.html
+++ b/modules/order/front/summary/index.html
@@ -72,6 +72,7 @@
diff --git a/modules/order/front/volume/index.html b/modules/order/front/volume/index.html
index 4772ccd43..e368e9c1b 100644
--- a/modules/order/front/volume/index.html
+++ b/modules/order/front/volume/index.html
@@ -42,6 +42,7 @@
diff --git a/modules/ticket/back/methods/sale/updatePrice.js b/modules/ticket/back/methods/sale/updatePrice.js
index e315d6504..e5907aa75 100644
--- a/modules/ticket/back/methods/sale/updatePrice.js
+++ b/modules/ticket/back/methods/sale/updatePrice.js
@@ -19,7 +19,7 @@ module.exports = Self => {
}
],
returns: {
- type: 'string',
+ type: 'Number',
root: true
},
http: {
@@ -36,7 +36,6 @@ module.exports = Self => {
let options = {transaction: tx};
let filter = {
- fields: ['id', 'ticketFk', 'price'],
include: {
relation: 'ticket',
scope: {
@@ -88,6 +87,8 @@ module.exports = Self => {
await Self.rawSql(query, [salesPerson], options);
await tx.commit();
+
+ return sale;
} catch (error) {
await tx.rollback();
throw error;
diff --git a/modules/ticket/back/methods/ticket-request/confirm.js b/modules/ticket/back/methods/ticket-request/confirm.js
index c08ecba9b..79551796d 100644
--- a/modules/ticket/back/methods/ticket-request/confirm.js
+++ b/modules/ticket/back/methods/ticket-request/confirm.js
@@ -45,20 +45,15 @@ module.exports = Self => {
include: {relation: 'ticket'}
});
- let query = `CALL vn.getItemVisibleAvailable(?,?,?,?)`;
-
- let params = [
+ let [[stock]] = await Self.rawSql(`CALL vn.getItemVisibleAvailable(?,?,?,?)`, [
ctx.args.itemFk,
request.ticket().shipped,
request.ticket().warehouseFk,
false
- ];
-
- let [res] = await Self.rawSql(query, params);
- let available = res[0].available;
- if (!available)
- throw new UserError(`That item is not available on that day`);
+ ]);
+ if (stock.available < quantity)
+ throw new UserError(`This item is not available`);
if (request.saleFk) {
let sale = await models.Sale.findById(request.saleFk);
diff --git a/modules/ticket/back/methods/ticket/addSale.js b/modules/ticket/back/methods/ticket/addSale.js
new file mode 100644
index 000000000..595d036c5
--- /dev/null
+++ b/modules/ticket/back/methods/ticket/addSale.js
@@ -0,0 +1,71 @@
+
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethod('addSale', {
+ description: 'Inserts a new sale for the current ticket',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'id',
+ type: 'Number',
+ required: true,
+ description: 'The ticket id',
+ http: {source: 'path'}
+ },
+ {
+ arg: 'itemId',
+ type: 'Number',
+ required: true
+ },
+ {
+ arg: 'quantity',
+ type: 'Number',
+ required: true
+ }],
+ returns: {
+ type: 'Object',
+ root: true
+ },
+ http: {
+ path: `/:id/addSale`,
+ verb: 'POST'
+ }
+ });
+
+ Self.addSale = async(id, itemId, quantity) => {
+ const models = Self.app.models;
+
+ const isEditable = await models.Ticket.isEditable(id);
+ if (!isEditable)
+ throw new UserError(`The sales of this ticket can't be modified`);
+
+ const item = await models.Item.findById(itemId);
+ const ticket = await models.Ticket.findById(id);
+
+ const shouldRefresh = false;
+ const [[stock]] = await Self.rawSql(`CALL vn.getItemVisibleAvailable(?, ?, ?, ?)`, [
+ itemId,
+ ticket.shipped,
+ ticket.warehouseFk,
+ shouldRefresh
+ ]);
+
+ if (stock.available < quantity)
+ throw new UserError(`This item is not available`);
+
+ const newSale = await models.Sale.create({
+ ticketFk: id,
+ itemFk: item.id,
+ concept: item.name,
+ quantity: quantity
+ });
+
+ await Self.rawSql('CALL vn.ticketCalculateSale(?)', [newSale.id]);
+
+ return models.Sale.findById(newSale.id, {
+ include: {
+ relation: 'item'
+ }
+ });
+ };
+};
diff --git a/modules/ticket/back/methods/ticket/getSales.js b/modules/ticket/back/methods/ticket/getSales.js
index 25b8aaff3..0c33d9db9 100644
--- a/modules/ticket/back/methods/ticket/getSales.js
+++ b/modules/ticket/back/methods/ticket/getSales.js
@@ -51,7 +51,7 @@ module.exports = Self => {
claimMap[claim.saleFk] = claim;
for (line of lines) {
- line.tags = map[line.itemFk];
+ line.item = map[line.itemFk];
line.claim = claimMap[line.id];
}
diff --git a/modules/ticket/back/methods/ticket/specs/addSale.spec.js b/modules/ticket/back/methods/ticket/specs/addSale.spec.js
new file mode 100644
index 000000000..b035a74db
--- /dev/null
+++ b/modules/ticket/back/methods/ticket/specs/addSale.spec.js
@@ -0,0 +1,49 @@
+const app = require('vn-loopback/server/server');
+
+describe('ticket addSale()', () => {
+ const ticketId = 13;
+ let newSale;
+
+ afterAll(async done => {
+ const sale = await app.models.Sale.findById(newSale.id);
+ await sale.destroy();
+
+ done();
+ });
+
+ it('should create a new sale for the ticket with id 13', async() => {
+ const itemId = 4;
+ const quantity = 10;
+ newSale = await app.models.Ticket.addSale(ticketId, itemId, quantity);
+
+ expect(newSale.itemFk).toEqual(4);
+ });
+
+ it('should not be able to add a sale if the item quantity is not available', async() => {
+ const itemId = 11;
+ const quantity = 10;
+
+ let error;
+ await app.models.Ticket.addSale(ticketId, itemId, quantity).catch(e => {
+ error = e;
+ }).finally(() => {
+ expect(error.message).toEqual(`This item is not available`);
+ });
+
+ expect(error).toBeDefined();
+ });
+
+ it('should not be able to add a sale if the ticket is not editable', async() => {
+ const notEditableTicketId = 1;
+ const itemId = 4;
+ const quantity = 10;
+ let error;
+ await app.models.Ticket.addSale(notEditableTicketId, itemId, quantity).catch(e => {
+ error = e;
+ }).finally(() => {
+ expect(error.message).toEqual(`The sales of this ticket can't be modified`);
+ });
+
+ expect(error).toBeDefined();
+ });
+});
diff --git a/modules/ticket/back/methods/ticket/specs/getSales.spec.js b/modules/ticket/back/methods/ticket/specs/getSales.spec.js
index cefa9ff4d..4e57f570f 100644
--- a/modules/ticket/back/methods/ticket/specs/getSales.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/getSales.spec.js
@@ -5,10 +5,10 @@ describe('ticket getSales()', () => {
let sales = await app.models.Ticket.getSales(16);
expect(sales.length).toEqual(4);
- expect(sales[0].tags).toBeDefined();
- expect(sales[1].tags).toBeDefined();
- expect(sales[2].tags).toBeDefined();
- expect(sales[3].tags).toBeDefined();
+ expect(sales[0].item).toBeDefined();
+ expect(sales[1].item).toBeDefined();
+ expect(sales[2].item).toBeDefined();
+ expect(sales[3].item).toBeDefined();
expect(sales[0].claim).toBeDefined();
});
});
diff --git a/modules/ticket/back/models/sale.js b/modules/ticket/back/models/sale.js
index bc042ed8a..df5b95839 100644
--- a/modules/ticket/back/models/sale.js
+++ b/modules/ticket/back/models/sale.js
@@ -6,4 +6,8 @@ module.exports = Self => {
require('../methods/sale/removes')(Self);
require('../methods/sale/updatePrice')(Self);
require('../methods/sale/updateQuantity')(Self);
+
+ Self.validatesPresenceOf('concept', {
+ message: `Concept cannot be blank`
+ });
};
diff --git a/modules/ticket/back/models/sale.json b/modules/ticket/back/models/sale.json
index 17a8c604e..894a2608f 100644
--- a/modules/ticket/back/models/sale.json
+++ b/modules/ticket/back/models/sale.json
@@ -13,7 +13,8 @@
"description": "Identifier"
},
"concept": {
- "type": "String"
+ "type": "String",
+ "required": true
},
"quantity": {
"type": "Number"
diff --git a/modules/ticket/back/models/ticket.js b/modules/ticket/back/models/ticket.js
index 8ee2b4580..608a0b4b4 100644
--- a/modules/ticket/back/models/ticket.js
+++ b/modules/ticket/back/models/ticket.js
@@ -22,4 +22,5 @@ module.exports = Self => {
require('../methods/ticket/checkEmptiness')(Self);
require('../methods/ticket/updateDiscount')(Self);
require('../methods/ticket/uploadFile')(Self);
+ require('../methods/ticket/addSale')(Self);
};
diff --git a/modules/ticket/front/basic-data/step-two/index.html b/modules/ticket/front/basic-data/step-two/index.html
index a8c3859e5..1ecd061e2 100644
--- a/modules/ticket/front/basic-data/step-two/index.html
+++ b/modules/ticket/front/basic-data/step-two/index.html
@@ -18,7 +18,8 @@
+ item="::sale.item"
+ name="::sale.concept">
|
{{::sale.quantity}} |
diff --git a/modules/ticket/front/component/index.html b/modules/ticket/front/component/index.html
index 27ba166d0..228a0270c 100644
--- a/modules/ticket/front/component/index.html
+++ b/modules/ticket/front/component/index.html
@@ -41,6 +41,7 @@
diff --git a/modules/ticket/front/sale-checked/index.html b/modules/ticket/front/sale-checked/index.html
index 4126e3477..cc4db0aaf 100644
--- a/modules/ticket/front/sale-checked/index.html
+++ b/modules/ticket/front/sale-checked/index.html
@@ -37,6 +37,7 @@
diff --git a/modules/ticket/front/sale-tracking/index.html b/modules/ticket/front/sale-tracking/index.html
index 020052e01..c3c918c09 100644
--- a/modules/ticket/front/sale-tracking/index.html
+++ b/modules/ticket/front/sale-tracking/index.html
@@ -41,6 +41,7 @@
diff --git a/modules/ticket/front/sale/index.html b/modules/ticket/front/sale/index.html
index 9b370c40c..3e2900101 100644
--- a/modules/ticket/front/sale/index.html
+++ b/modules/ticket/front/sale/index.html
@@ -93,66 +93,86 @@
+ vn-tooltip="{{::$ctrl.$translate.instant('Reserved')}}">
+
-
-
-
-
-
- {{::sale.itemFk | zeroFill:6}}
+
+
+
+
+
+
+ {{sale.itemFk | zeroFill:6}}
-
-
- {{sale.quantity}}
-
-
-
-
-
-
+
+
+
+
+
+
+
+ {{sale.quantity}}
+
+
+
+
+
+
+
+ item="sale.item"
+ name="sale.concept"
+ sub-name="sale.subName">
-
-
-
- {{sale.price | currency: 'EUR':2}}
-
-
-
+
+
+
+
+
+
+
+
{{sale.price | currency: 'EUR':2}}
-
-
-
- {{sale.discount}} %
-
-
-
- {{sale.discount}} %
-
-
- {{$ctrl.getSaleTotal(sale) | currency: 'EUR':2}}
-
-
-
-
-
+
+
+
+
+ {{sale.discount | percentage}}
+
+
+
+ {{$ctrl.getSaleTotal(sale) | currency: 'EUR':2}}
+
+
+
+
+
+
+
+
@@ -248,7 +268,9 @@
- No results |
+
+ No results |
+
\ No newline at end of file
diff --git a/modules/ticket/front/sale/index.js b/modules/ticket/front/sale/index.js
index bca462cb8..909f6068b 100644
--- a/modules/ticket/front/sale/index.js
+++ b/modules/ticket/front/sale/index.js
@@ -17,9 +17,10 @@ class Controller {
{callback: this.createClaim, name: 'Add claim'},
{callback: this.showSMSDialog, name: 'Send SMS'}
];
-
+ this._sales = [];
this.imagesPath = '//verdnatura.es/vn-image-data/catalog';
}
+
get sales() {
return this._sales;
}
@@ -38,7 +39,6 @@ class Controller {
this.updateNewPrice();
}
-
refreshTotal() {
this.loadSubTotal();
this.loadVAT();
@@ -52,6 +52,9 @@ class Controller {
}
getSaleTotal(sale) {
+ if (!sale.quantity || !sale.price)
+ return;
+
return sale.quantity * sale.price * ((100 - sale.discount) / 100);
}
@@ -99,16 +102,45 @@ class Controller {
return false;
}
+ /**
+ * Returns checked instances
+ *
+ * @return {Array} Checked instances
+ */
getCheckedLines() {
- let lines = [];
- let data = this.sales;
- if (data) {
- for (let i = 0; i < data.length; i++) {
- if (data[i].checked)
- lines.push({id: data[i].id, instance: i});
- }
- }
- return lines;
+ if (!this.sales) return;
+
+ return this.sales.filter(sale => {
+ return sale.checked;
+ });
+ }
+
+ /**
+ * Returns an array of indexes
+ * from checked instances
+ *
+ * @return {Array} Indexes of checked instances
+ */
+ getCheckedLinesIndex() {
+ if (!this.sales) return;
+
+ let indexes = [];
+ this.sales.forEach((sale, index) => {
+ if (sale.checked) indexes.push(index);
+ });
+
+ return indexes;
+ }
+
+ removeCheckedLines() {
+ const sales = this.getCheckedLines();
+
+ sales.forEach(sale => {
+ const index = this.sales.indexOf(sale);
+ this.sales.splice(index, 1);
+ });
+
+ this.refreshTotal();
}
onStateOkClick() {
@@ -130,21 +162,21 @@ class Controller {
onRemoveLinesClick(response) {
if (response === 'ACCEPT') {
let sales = this.getCheckedLines();
+
+ // Remove unsaved instances
+ sales.forEach((sale, index) => {
+ if (!sale.id) sales.splice(index);
+ });
+
let params = {sales: sales, actualTicketFk: this.ticket.id};
let query = `/api/Sales/removes`;
this.$http.post(query, params).then(() => {
- this.removeInstances(sales);
+ this.removeCheckedLines();
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
});
}
}
- removeInstances(instances) {
- for (let i of instances)
- this.sales.splice(i.instance, 1);
- this.refreshTotal();
- }
-
showRemoveLinesDialog() {
this.$scope.deleteLines.show();
}
@@ -208,14 +240,12 @@ class Controller {
}
createClaim() {
- let claim = {
+ const claim = {
ticketFk: this.ticket.id,
clientFk: this.ticket.clientFk,
ticketCreated: this.ticket.shipped
};
- let sales = this.getCheckedLines();
- for (let i = 0; i < sales.length; i++)
- sales[i].quantity = this.sales[sales[i].instance].quantity;
+ const sales = this.getCheckedLines();
this.$http.post(`/api/Claims/createFromSales`, {claim: claim, sales: sales}).then(res => {
this.$state.go('claim.card.basicData', {id: res.data.id});
});
@@ -255,6 +285,7 @@ class Controller {
}
showEditPricePopover(event, sale) {
+ if (!this.isEditable) return;
this.sale = sale;
this.editedPrice = this.sale.price;
this.edit = {
@@ -268,10 +299,11 @@ class Controller {
updatePrice() {
if (this.editedPrice != this.sale.price) {
- this.$http.post(`/api/Sales/${this.edit.id}/updatePrice`, {newPrice: this.editedPrice}).then(() => {
- this.sale.price = this.edit.price;
+ this.$http.post(`/api/Sales/${this.edit.id}/updatePrice`, {newPrice: this.editedPrice}).then(res => {
+ if (res.data)
+ this.sale.price = res.data.price;
+
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
- this.$scope.model.refresh();
});
}
@@ -282,7 +314,9 @@ class Controller {
this.newPrice = this.sale.quantity * this.editedPrice - ((this.sale.discount * (this.sale.quantity * this.editedPrice)) / 100);
}
- showEditPopover(event, sale) {
+ showEditDiscountPopover(event, sale) {
+ if (!this.isEditable) return;
+
this.sale = sale;
this.edit = [{
ticketFk: this.ticket.id,
@@ -310,24 +344,14 @@ class Controller {
this.$scope.editPopover.hide();
}
- updateQuantity(id, quantity) {
- this.$http.post(`/api/Sales/${id}/updateQuantity`, {quantity: parseInt(quantity)}).then(() => {
- this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
- }).catch(e => {
- this.vnApp.showError(e.data.error.message);
- }).finally(() => {
- this.$scope.model.refresh();
- });
- }
-
- /**
+ /*
* Unmark sale as reserved
*/
unmarkAsReserved() {
this.setReserved(false);
}
- /**
+ /*
* Mark sale as reserved
*/
markAsReserved() {
@@ -363,10 +387,9 @@ class Controller {
showSMSDialog() {
const address = this.ticket.address;
- const lines = this.getCheckedLines();
- const items = lines.map(line => {
- const instance = this.sales[line.instance];
- return `${instance.quantity} ${instance.concept}`;
+ const sales = this.getCheckedLines();
+ const items = sales.map(sale => {
+ return `${sale.quantity} ${sale.concept}`;
});
const notAvailables = items.join(', ');
const params = {
@@ -390,6 +413,79 @@ class Controller {
hasInvoice() {
return this.ticket.refFk !== null;
}
+
+ /**
+ * Inserts a new instance
+ */
+ add() {
+ this.$scope.model.insert({});
+ }
+
+ /*
+ * Creates a new sale if it's a new instance
+ * Updates the sale quantity for existing instance
+ */
+ onChangeQuantity(sale) {
+ if (!sale.id)
+ this.addSale(sale);
+ else
+ this.updateQuantity(sale);
+ }
+
+ /*
+ * Updates a sale quantity
+ */
+ updateQuantity(sale) {
+ const data = {quantity: parseInt(sale.quantity)};
+ const query = `/api/Sales/${sale.id}/updateQuantity`;
+ this.$http.post(query, data).then(() => {
+ this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
+ }).catch(e => {
+ this.$scope.model.refresh();
+ throw e;
+ });
+ }
+
+ /*
+ * Updates a sale concept
+ */
+ updateConcept(sale) {
+ const data = {concept: sale.concept};
+ const query = `/api/Sales/${sale.id}`;
+ this.$http.patch(query, data).then(() => {
+ this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
+ }).catch(e => {
+ this.$scope.model.refresh();
+ throw e;
+ });
+ }
+
+ /*
+ * Adds a new sale
+ */
+ addSale(sale) {
+ const data = {
+ itemId: sale.itemFk,
+ quantity: sale.quantity
+ };
+ const query = `/api/tickets/${this.ticket.id}/addSale`;
+ this.$http.post(query, data).then(res => {
+ if (!res.data) return;
+
+ const newSale = res.data;
+
+ sale.id = newSale.id;
+ sale.image = newSale.item.image;
+ sale.subName = newSale.item.subName;
+ sale.concept = newSale.concept;
+ sale.quantity = newSale.quantity;
+ sale.discount = newSale.discount;
+ sale.price = newSale.price;
+ sale.item = newSale.item;
+
+ this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
+ });
+ }
}
Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate'];
diff --git a/modules/ticket/front/sale/locale/es.yml b/modules/ticket/front/sale/locale/es.yml
index 235ca9e41..b8966232f 100644
--- a/modules/ticket/front/sale/locale/es.yml
+++ b/modules/ticket/front/sale/locale/es.yml
@@ -1,4 +1,5 @@
New price: Nuevo precio
+Add item: Añadir artículo
Add turn: Añadir a turno
Delete ticket: Borrar ticket
Mark as reserved: Marcar como reservado
diff --git a/modules/ticket/front/sale/specs/index.spec.js b/modules/ticket/front/sale/specs/index.spec.js
index b1cadd0fc..938b4c252 100644
--- a/modules/ticket/front/sale/specs/index.spec.js
+++ b/modules/ticket/front/sale/specs/index.spec.js
@@ -64,8 +64,10 @@ describe('Ticket', () => {
it('should perform a query and call windows open', () => {
spyOn(controller.$state, 'go');
- let res = {id: 1};
- $httpBackend.expectPOST(`/api/Claims/createFromSales`).respond(res);
+ const claim = {id: 1};
+ const sales = [{id: 1}, {id: 2}];
+ $httpBackend.when('POST', `/api/Claims/createFromSales`, {claim, sales}).respond(claim);
+ $httpBackend.expect('POST', `/api/Claims/createFromSales`).respond(claim);
controller.createClaim();
$httpBackend.flush();
@@ -93,7 +95,7 @@ describe('Ticket', () => {
it('should make an array of the instances with the property checked true()', () => {
let sale = controller.sales[1];
sale.checked = true;
- let expectedResult = [{id: sale.id, instance: 1}];
+ let expectedResult = [sale];
expect(controller.getCheckedLines()).toEqual(expectedResult);
});
@@ -156,12 +158,10 @@ describe('Ticket', () => {
describe('setReserved()', () => {
it('should call getCheckedLines, $.index.accept and make a query ', () => {
- controller.sales[1].checked = true;
+ const sale = controller.sales[1];
+ sale.checked = true;
let expectedRequest = {
- sales: [{
- id: sales[1].id,
- instance: 1
- }],
+ sales: [sale],
ticketFk: ticket.id,
reserved: false
};
@@ -184,5 +184,55 @@ describe('Ticket', () => {
expect(controller.newSMS.message).not.toEqual('');
});
});
+
+ describe('updateQuantity()', () => {
+ it('should make a POST query saving sale quantity', () => {
+ const data = {quantity: 10};
+ const sale = sales[0];
+ sale.quantity = 10;
+
+ $httpBackend.when('POST', `/api/Sales/1/updateQuantity`, data).respond();
+ $httpBackend.expect('POST', `/api/Sales/1/updateQuantity`, data).respond();
+ controller.updateQuantity(sale);
+ $httpBackend.flush();
+ });
+ });
+
+ describe('updateConcept()', () => {
+ it('should make a POST query saving sale concept', () => {
+ const data = {concept: 'My new weapon'};
+ const sale = sales[0];
+ sale.concept = 'My new weapon';
+
+ $httpBackend.when('PATCH', `/api/Sales/1`, data).respond();
+ $httpBackend.expect('PATCH', `/api/Sales/1`, data).respond();
+ controller.updateConcept(sale);
+ $httpBackend.flush();
+ });
+ });
+
+ describe('addSale()', () => {
+ it('should make a POST query adding a new sale', () => {
+ const newSale = {itemFk: 4, quantity: 10};
+ const params = {itemId: 4, quantity: 10};
+
+ const expectedResult = {
+ id: 30,
+ quantity: 10,
+ discount: 0,
+ price: 0,
+ itemFk: 4,
+ item: {
+ subName: 'Item subName',
+ image: '30.png'
+ }
+ };
+
+ $httpBackend.when('POST', `/api/tickets/1/addSale`, params).respond(expectedResult);
+ $httpBackend.expect('POST', `/api/tickets/1/addSale`, params).respond(expectedResult);
+ controller.addSale(newSale);
+ $httpBackend.flush();
+ });
+ });
});
});
diff --git a/modules/ticket/front/summary/index.html b/modules/ticket/front/summary/index.html
index c718e38f0..7105e0515 100644
--- a/modules/ticket/front/summary/index.html
+++ b/modules/ticket/front/summary/index.html
@@ -101,6 +101,7 @@
{{::sale.price | currency: 'EUR':2}}
diff --git a/modules/ticket/front/volume/index.html b/modules/ticket/front/volume/index.html
index cfb095bd5..ef21d8f50 100644
--- a/modules/ticket/front/volume/index.html
+++ b/modules/ticket/front/volume/index.html
@@ -46,6 +46,7 @@
{{::sale.quantity}}