diff --git a/db/changes/10400-christmas/00-ACL.sql b/db/changes/10400-christmas/00-ACL.sql
new file mode 100644
index 000000000..4f87cb708
--- /dev/null
+++ b/db/changes/10400-christmas/00-ACL.sql
@@ -0,0 +1,2 @@
+INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
+ VALUES ('Sale','payBack','WRITE','ALLOW','ROLE','employee');
diff --git a/db/changes/10400-christmas/00-ticket_doRefund.sql b/db/changes/10400-christmas/00-ticket_doRefund.sql
new file mode 100644
index 000000000..1c1faf315
--- /dev/null
+++ b/db/changes/10400-christmas/00-ticket_doRefund.sql
@@ -0,0 +1,90 @@
+DROP PROCEDURE IF EXISTS `vn`.`ticket_doRefund`;
+
+DELIMITER $$
+$$
+CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_doRefund`(IN vOriginTicket INT, OUT vNewTicket INT)
+BEGIN
+
+ DECLARE vDone BIT DEFAULT 0;
+ DECLARE vCustomer MEDIUMINT;
+ DECLARE vWarehouse TINYINT;
+ DECLARE vCompany MEDIUMINT;
+ DECLARE vAddress MEDIUMINT;
+ DECLARE vRefundAgencyMode INT;
+ DECLARE vItemFk INT;
+ DECLARE vQuantity DECIMAL (10,2);
+ DECLARE vConcept VARCHAR(50);
+ DECLARE vPrice DECIMAL (10,2);
+ DECLARE vDiscount TINYINT;
+ DECLARE vSaleNew INT;
+ DECLARE vSaleMain INT;
+ DECLARE vZoneFk INT;
+
+ DECLARE vRsMainTicket CURSOR FOR
+ SELECT *
+ FROM tmp.sale;
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = 1;
+
+ SELECT id INTO vRefundAgencyMode
+ FROM agencyMode WHERE `name` = 'ABONO';
+
+ SELECT clientFk, warehouseFk, companyFk, addressFk
+ INTO vCustomer, vWarehouse, vCompany, vAddress
+ FROM ticket
+ WHERE id = vOriginTicket;
+
+ SELECT id INTO vZoneFk
+ FROM zone WHERE agencyModeFk = vRefundAgencyMode
+ LIMIT 1;
+
+ INSERT INTO vn.ticket (
+ clientFk,
+ shipped,
+ addressFk,
+ agencyModeFk,
+ nickname,
+ warehouseFk,
+ companyFk,
+ landed,
+ zoneFk
+ )
+ SELECT
+ vCustomer,
+ CURDATE(),
+ vAddress,
+ vRefundAgencyMode,
+ a.nickname,
+ vWarehouse,
+ vCompany,
+ CURDATE(),
+ vZoneFk
+ FROM address a
+ WHERE a.id = vAddress;
+
+ SET vNewTicket = LAST_INSERT_ID();
+
+ SET vDone := 0;
+ OPEN vRsMainTicket ;
+ FETCH vRsMainTicket INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount;
+
+ WHILE NOT vDone DO
+
+ INSERT INTO vn.sale(ticketFk, itemFk, quantity, concept, price, discount)
+ VALUES( vNewTicket, vItemFk, vQuantity, vConcept, vPrice, vDiscount );
+
+ SET vSaleNew = LAST_INSERT_ID();
+
+ INSERT INTO vn.saleComponent(saleFk,componentFk,`value`)
+ SELECT vSaleNew,componentFk,`value`
+ FROM vn.saleComponent
+ WHERE saleFk = vSaleMain;
+
+ FETCH vRsMainTicket INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount;
+
+ END WHILE;
+ CLOSE vRsMainTicket;
+
+END;
+$$
+DELIMITER ;
\ No newline at end of file
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index 8675797e7..155ffdd22 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -558,6 +558,7 @@ export default {
moreMenuUnmarkReseved: 'vn-item[name="unreserve"]',
moreMenuUpdateDiscount: 'vn-item[name="discount"]',
moreMenuRecalculatePrice: 'vn-item[name="calculatePrice"]',
+ moreMenuPayBack: 'vn-item[name="payBack"]',
moreMenuUpdateDiscountInput: 'vn-input-number[ng-model="$ctrl.edit.discount"] input',
transferQuantityInput: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable > span > text',
transferQuantityCell: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable',
diff --git a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
index dfda4dcfb..1f3aedadf 100644
--- a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
+++ b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
@@ -206,7 +206,16 @@ describe('Ticket Edit sale path', () => {
expect(message.text).toContain('Data saved!');
});
+ it('should select the third sale and create a pay back', async() => {
+ await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
+ await page.waitToClick(selectors.ticketSales.moreMenu);
+ await page.waitToClick(selectors.ticketSales.moreMenuPayBack);
+ await page.waitForState('ticket.card.sale');
+ });
+
it('should select the third sale and create a claim of it', async() => {
+ await page.accessToSearchResult('16');
+ await page.accessToSection('ticket.card.sale');
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
diff --git a/modules/client/front/balance/create/index.js b/modules/client/front/balance/create/index.js
index d306c6f84..591af2839 100644
--- a/modules/client/front/balance/create/index.js
+++ b/modules/client/front/balance/create/index.js
@@ -82,7 +82,7 @@ class Controller extends Dialog {
}
set amountToReturn(value) {
- if (!value) return;
+ if (isNaN(value)) return;
value = value.toFixed(2);
diff --git a/modules/item/back/methods/item/filter.js b/modules/item/back/methods/item/filter.js
index a7ca38173..98e78c7aa 100644
--- a/modules/item/back/methods/item/filter.js
+++ b/modules/item/back/methods/item/filter.js
@@ -112,7 +112,10 @@ module.exports = Self => {
case 'search':
return /^\d+$/.test(value)
? {or: [{'i.id': value}, codeWhere]}
- : {or: [{'i.name': {like: `%${value}%`}}, codeWhere]};
+ : {or: [
+ {'i.name': {like: `%${value}%`}},
+ {'i.longName': {like: `%${value}%`}},
+ codeWhere]};
case 'id':
case 'isActive':
case 'typeFk':
diff --git a/modules/ticket/back/methods/sale/payBack.js b/modules/ticket/back/methods/sale/payBack.js
new file mode 100644
index 000000000..41150a1e0
--- /dev/null
+++ b/modules/ticket/back/methods/sale/payBack.js
@@ -0,0 +1,70 @@
+module.exports = Self => {
+ Self.remoteMethodCtx('payBack', {
+ description: 'Create ticket with the selected lines changing the sign to the quantites',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'sales',
+ description: 'The sales',
+ type: ['object'],
+ required: true
+ },
+ {
+ arg: 'ticketId',
+ type: 'number',
+ required: true,
+ description: 'The ticket id'
+ }],
+ returns: {
+ type: 'number',
+ root: true
+ },
+ http: {
+ path: `/payBack`,
+ verb: 'post'
+ }
+ });
+
+ Self.payBack = async(ctx, sales, ticketId, options) => {
+ const myOptions = {};
+ let tx;
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
+ myOptions.transaction = tx;
+ }
+
+ try {
+ const salesIds = [];
+ const params = [];
+ sales.forEach(sale => {
+ salesIds.push(sale.id);
+ params.push('?');
+ });
+
+ const paramsString = params.join();
+
+ const query = `
+ DROP TEMPORARY TABLE IF EXISTS tmp.sale;
+ CREATE TEMPORARY TABLE tmp.sale
+ SELECT s.id, s.itemFk, - s.quantity, s.concept, s.price, s.discount
+ FROM sale s
+ WHERE s.id IN (${paramsString});
+ CALL vn.ticket_doRefund(${ticketId}, @newTicket);
+ DROP TEMPORARY TABLE tmp.sale;`;
+
+ await Self.rawSql(query, salesIds, myOptions);
+ const [newTicket] = await Self.rawSql('SELECT @newTicket id', null, myOptions);
+ ticketId = newTicket.id;
+
+ if (tx) await tx.commit();
+
+ return ticketId;
+ } catch (e) {
+ if (tx) await tx.rollback();
+ throw e;
+ }
+ };
+};
diff --git a/modules/ticket/back/methods/sale/specs/payment.spec.js b/modules/ticket/back/methods/sale/specs/payment.spec.js
new file mode 100644
index 000000000..9f0bcc98a
--- /dev/null
+++ b/modules/ticket/back/methods/sale/specs/payment.spec.js
@@ -0,0 +1,26 @@
+const models = require('vn-loopback/server/server').models;
+
+describe('sale payBack()', () => {
+ it('should create ticket with the selected lines changing the sign to the quantites', async() => {
+ const tx = await models.Sale.beginTransaction({});
+ const ticketId = 11;
+ const sales = [
+ {id: 7, ticketFk: 11},
+ {id: 8, ticketFk: 11}
+ ];
+ try {
+ const options = {transaction: tx};
+
+ const ctx = {req: {accessToken: {userId: 9}}};
+ const response = await models.Sale.payBack(ctx, sales, ticketId, options);
+ const [newTicketId] = await models.Sale.rawSql('SELECT MAX(t.id) id FROM vn.ticket t;', null, options);
+
+ expect(response).toEqual(newTicketId.id);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+});
diff --git a/modules/ticket/back/models/sale.js b/modules/ticket/back/models/sale.js
index 545e054dc..9efd66057 100644
--- a/modules/ticket/back/models/sale.js
+++ b/modules/ticket/back/models/sale.js
@@ -6,6 +6,7 @@ module.exports = Self => {
require('../methods/sale/updateQuantity')(Self);
require('../methods/sale/updateConcept')(Self);
require('../methods/sale/recalculatePrice')(Self);
+ require('../methods/sale/payBack')(Self);
require('../methods/sale/canEdit')(Self);
Self.validatesPresenceOf('concept', {
diff --git a/modules/ticket/front/sale/index.html b/modules/ticket/front/sale/index.html
index f7a279d9a..fe1f5684d 100644
--- a/modules/ticket/front/sale/index.html
+++ b/modules/ticket/front/sale/index.html
@@ -490,4 +490,9 @@
ng-if="$ctrl.isEditable && $ctrl.hasReserves()">
Unmark as reserved
+