3430-ticket_step-two ticket without negatives #823

Merged
joan merged 31 commits from 3430-ticket_step-two into dev 2022-02-01 08:34:41 +00:00
58 changed files with 10965 additions and 26434 deletions
Showing only changes of commit c87e3e1210 - Show all commits

View File

@ -120,7 +120,7 @@ module.exports = Self => {
} }
const defaultOptions = { const defaultOptions = {
body: params form: params
}; };
if (options) Object.assign(defaultOptions, options); if (options) Object.assign(defaultOptions, options);

View File

@ -0,0 +1,2 @@
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
VALUES ('Sale','payBack','WRITE','ALLOW','ROLE','employee');

View File

@ -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 ;

View File

@ -0,0 +1,5 @@
ALTER TABLE `vn`.`smsConfig` ADD apiKey varchar(50) NULL;
ALTER TABLE `vn`.`smsConfig` CHANGE `user` user__ varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL;
ALTER TABLE `vn`.`smsConfig` CHANGE password password__ varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL;
ALTER TABLE `vn`.`sms` MODIFY COLUMN statusCode smallint(9) DEFAULT 0 NULL;
ALTER TABLE `vn`.`sms` MODIFY COLUMN status varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT 'OK' NULL;

View File

@ -0,0 +1,7 @@
UPDATE `vn`.`smsConfig`
SET `uri` = 'https://api.gateway360.com/api/3.0/sms/send'
WHERE `id` = 1;
UPDATE `vn`.`smsConfig`
SET `apiKey` = '5715476da95b46d686a5a255e6459523'
WHERE `id` = 1;

File diff suppressed because one or more lines are too long

View File

@ -606,7 +606,7 @@ INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeF
(9 , NULL, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, CURDATE()), (9 , NULL, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, CURDATE()),
(10, 1, 1, 5, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1102, 'Ingram Street', 2, NULL, 0, 1, 5, 1, CURDATE()), (10, 1, 1, 5, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1102, 'Ingram Street', 2, NULL, 0, 1, 5, 1, CURDATE()),
(11, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1102, 'NY roofs', 122, NULL, 0, 3, 5, 1, CURDATE()), (11, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1102, 'NY roofs', 122, NULL, 0, 3, 5, 1, CURDATE()),
(12, 1, 1, 1, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, CURDATE()), (12, 1, 8, 1, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, CURDATE()),
(13, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 3, 5, 1, CURDATE()), (13, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1103, 'Phone Box', 123, NULL, 0, 3, 5, 1, CURDATE()),
(14, 1, 2, 1, NULL, CURDATE(), CURDATE(), 1104, 'Malibu Point', 4, NULL, 0, 9, 5, 1, CURDATE()), (14, 1, 2, 1, NULL, CURDATE(), CURDATE(), 1104, 'Malibu Point', 4, NULL, 0, 9, 5, 1, CURDATE()),
(15, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1105, 'An incredibly long alias for testing purposes', 125, NULL, 0, 3, 5, 1, CURDATE()), (15, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 1105, 'An incredibly long alias for testing purposes', 125, NULL, 0, 3, 5, 1, CURDATE()),
@ -1078,11 +1078,15 @@ INSERT INTO `vn`.`itemPlacement`(`id`, `itemFk`, `warehouseFk`, `code`)
(3, 1, 3, 'A33'), (3, 1, 3, 'A33'),
(4, 2, 1, 'A44'); (4, 2, 1, 'A44');
INSERT INTO `vn`.`train`(`id`, `name`)
INSERT INTO `vn`.`collection`(`id`, `workerFk`, `stateFk`, `created`)
VALUES VALUES
(1, 1106, 5, DATE_ADD(CURDATE(),INTERVAL +1 DAY)), (1, 'Train1'),
(2, 1106, 14, CURDATE()); (2, 'Train2');
INSERT INTO `vn`.`collection`(`id`, `workerFk`, `stateFk`, `created`, `trainFk`)
VALUES
(1, 1106, 5, DATE_ADD(CURDATE(),INTERVAL +1 DAY), 1),
(2, 1106, 14, CURDATE(), 1);
INSERT INTO `vn`.`ticketCollection`(`id`, `ticketFk`, `collectionFk`) INSERT INTO `vn`.`ticketCollection`(`id`, `ticketFk`, `collectionFk`)
VALUES VALUES
@ -1290,11 +1294,11 @@ INSERT INTO `vn`.`supplierAddress`(`id`, `supplierFk`, `nickname`, `street`, `pr
(5, 442, 'GCR building', 'Bristol district', 1, '46000', 'Gotham', '111111111', '222222222'), (5, 442, 'GCR building', 'Bristol district', 1, '46000', 'Gotham', '111111111', '222222222'),
(6, 442, 'The Gotham Tonight building', 'Bristol district', 1, '46000', 'Gotham', '111111111', '222222222'); (6, 442, 'The Gotham Tonight building', 'Bristol district', 1, '46000', 'Gotham', '111111111', '222222222');
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`,`isFarmer`,`commission`, `created`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`, `taxTypeSageFk`, `withholdingSageFk`, `transactionTypeSageFk`, `workerFk`, `supplierActivityFk`, `isPayMethodChecked`) INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`, `commission`, `created`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`, `taxTypeSageFk`, `withholdingSageFk`, `transactionTypeSageFk`, `workerFk`, `supplierActivityFk`, `isPayMethodChecked`)
VALUES VALUES
(1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, 0, CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1), (1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1),
(2, 'Farmer King', 'The farmer', 4000020002, 1, '87945234L', 1, 0, CURDATE(), 1, 'supplier address 2', 'SILLA', 2, 43022, 1, 2, 10, 93, 2, 8, 18, 'animals', 1), (2, 'Farmer King', 'The farmer', 4000020002, 1, '87945234L', 0, CURDATE(), 1, 'supplier address 2', 'SILLA', 2, 43022, 1, 2, 10, 93, 2, 8, 18, 'animals', 1),
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, 0, CURDATE(), 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'flowerPlants', 1); (442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, CURDATE(), 1, 'supplier address 3', 'SILLA', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'flowerPlants', 1);
INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`) INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`)
VALUES VALUES
@ -1904,9 +1908,9 @@ INSERT INTO `postgresql`.`calendar_employee` (`business_id`, `calendar_state_id`
(1107, 1, IF(MONTH(CURDATE()) >= 1 AND DAY(CURDATE()) > 20, DATE_ADD(CURDATE(), INTERVAL -14 DAY), DATE_ADD(CURDATE(), INTERVAL 9 DAY))), (1107, 1, IF(MONTH(CURDATE()) >= 1 AND DAY(CURDATE()) > 20, DATE_ADD(CURDATE(), INTERVAL -14 DAY), DATE_ADD(CURDATE(), INTERVAL 9 DAY))),
(1107, 2, IF(MONTH(CURDATE()) >= 1 AND DAY(CURDATE()) > 20, DATE_ADD(CURDATE(), INTERVAL -15 DAY), DATE_ADD(CURDATE(), INTERVAL 7 DAY))); (1107, 2, IF(MONTH(CURDATE()) >= 1 AND DAY(CURDATE()) > 20, DATE_ADD(CURDATE(), INTERVAL -15 DAY), DATE_ADD(CURDATE(), INTERVAL 7 DAY)));
INSERT INTO `vn`.`smsConfig` (`id`, `uri`, `title`) INSERT INTO `vn`.`smsConfig` (`id`, `uri`, `title`, `apiKey`)
VALUES VALUES
('1', 'https://websms.xtratelecom.es/api_php/server.wsdl', 'Verdnatura'); ('1', 'https://api.gateway360.com/api/3.0/sms/send', 'Verdnatura', '5715476da95b46d686a5a255e6459523');
INSERT INTO `vn`.`sharingClient`(`id`, `workerFk`, `started`, `ended`, `clientFk`) INSERT INTO `vn`.`sharingClient`(`id`, `workerFk`, `started`, `ended`, `clientFk`)
VALUES VALUES

File diff suppressed because it is too large Load Diff

View File

@ -458,7 +458,8 @@ export default {
firstSaleQuantity: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5)', firstSaleQuantity: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5)',
firstSaleDiscount: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(8)', firstSaleDiscount: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(8)',
invoiceOutRef: 'vn-ticket-summary > vn-card > vn-horizontal > vn-one:nth-child(1) > vn-label-value:nth-child(7) > section > span', invoiceOutRef: 'vn-ticket-summary > vn-card > vn-horizontal > vn-one:nth-child(1) > vn-label-value:nth-child(7) > section > span',
setOk: 'vn-ticket-summary vn-button[label="SET OK"] > button', stateButton: 'vn-ticket-summary vn-button-menu > button ',
stateAutocomplete: 'div.filter.ng-scope > vn-textfield > div.container > div.infix > div.control',
descriptorTicketId: 'vn-ticket-descriptor > vn-descriptor-content > div > div.body > div.top > div' descriptorTicketId: 'vn-ticket-descriptor > vn-descriptor-content > div > div.body > div.top > div'
}, },
ticketsIndex: { ticketsIndex: {
@ -559,6 +560,7 @@ export default {
moreMenuUnmarkReseved: 'vn-item[name="unreserve"]', moreMenuUnmarkReseved: 'vn-item[name="unreserve"]',
moreMenuUpdateDiscount: 'vn-item[name="discount"]', moreMenuUpdateDiscount: 'vn-item[name="discount"]',
moreMenuRecalculatePrice: 'vn-item[name="calculatePrice"]', moreMenuRecalculatePrice: 'vn-item[name="calculatePrice"]',
moreMenuPayBack: 'vn-item[name="payBack"]',
moreMenuUpdateDiscountInput: 'vn-input-number[ng-model="$ctrl.edit.discount"] input', 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', 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', transferQuantityCell: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable',

View File

@ -206,7 +206,16 @@ describe('Ticket Edit sale path', () => {
expect(message.text).toContain('Data saved!'); 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() => { 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.thirdSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu); await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim); await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);

View File

@ -76,8 +76,24 @@ describe('Ticket Summary path', () => {
await page.waitForState('ticket.card.summary'); await page.waitForState('ticket.card.summary');
}); });
it('should click on the SET OK button', async() => { it('should set the ticket state to OK using the top right button', async() => {
await page.waitToClick(selectors.ticketSummary.setOk); const searchValue = 'OK';
await page.waitToClick(selectors.ticketSummary.stateButton);
await page.write(selectors.ticketSummary.stateAutocomplete, searchValue);
try {
await page.waitForFunction(text => {
const element = document.querySelector('li.active');
if (element)
return element.innerText.toLowerCase().includes(text.toLowerCase());
}, {}, searchValue);
} catch (error) {
const state = await page.evaluate(() => {
const stateSelector = 'vn-ticket-summary vn-label-value:nth-child(1) > section > span';
return document.querySelector(stateSelector).value;
});
throw new Error(`${stateSelector} innerText is ${state}! ${error}`);
}
await page.keyboard.press('Enter');
const message = await page.waitForSnackbar(); const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!'); expect(message.text).toContain('Data saved!');

View File

@ -23,371 +23,408 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-100:before { .icon-isTooLittle:before {
content: "\e976"; content: "\e91b";
} }
.icon-account:before { .icon-frozen:before {
content: "\e900"; content: "\e900";
} }
.icon-actions:before { .icon-Person:before {
content: "\e901"; content: "\e901";
} }
.icon-addperson:before { .icon-handmadeArtificial:before {
content: "\e902"; content: "\e902";
} }
.icon-agency:before { .icon-fruit:before {
content: "\e903"; content: "\e903";
} }
.icon-albaran:before { .icon-funeral:before {
content: "\e904"; content: "\e904";
} }
.icon-anonymous:before { .icon-treatments:before {
content: "\e905"; content: "\e905";
} }
.icon-apps:before { .icon-preserved:before {
content: "\e906"; content: "\e906";
} }
.icon-artificial:before { .icon-greenery:before {
content: "\e907"; content: "\e907";
} }
.icon-attach:before { .icon-plant:before {
content: "\e908"; content: "\e908";
} }
.icon-barcode:before { .icon-handmade:before {
content: "\e909"; content: "\e909";
} }
.icon-basket:before { .icon-accessory:before {
content: "\e90a"; content: "\e90a";
} }
.icon-basketadd:before { .icon-artificial:before {
content: "\e90b"; content: "\e90b";
} }
.icon-bin:before { .icon-flower:before {
content: "\e90c"; content: "\e90c";
} }
.icon-botanical:before { .icon-fixedPrice:before {
content: "\e90d"; content: "\e90d";
} }
.icon-bucket:before { .icon-addperson:before {
content: "\e90e"; content: "\e90e";
} }
.icon-buscaman:before { .icon-supplierfalse:before {
content: "\e90f"; content: "\e90f";
} }
.icon-buyrequest:before { .icon-invoice-out:before {
content: "\e910"; content: "\e910";
} }
.icon-calc_volum .path1:before { .icon-invoice-in:before {
content: "\e911"; content: "\e911";
}
.icon-invoice-in-create:before {
content: "\e912";
}
.icon-basketadd:before {
content: "\e913";
}
.icon-basket:before {
content: "\e914";
}
.icon-calc_volum .path1:before {
content: "\e915";
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
} }
.icon-calc_volum .path2:before { .icon-calc_volum .path2:before {
content: "\e912"; content: "\e916";
margin-left: -1em; margin-left: -1em;
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
} }
.icon-calc_volum .path3:before { .icon-calc_volum .path3:before {
content: "\e913"; content: "\e917";
margin-left: -1em; margin-left: -1em;
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
} }
.icon-calc_volum .path4:before { .icon-calc_volum .path4:before {
content: "\e914"; content: "\e918";
margin-left: -1em; margin-left: -1em;
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
} }
.icon-calc_volum .path5:before { .icon-calc_volum .path5:before {
content: "\e915"; content: "\e919";
margin-left: -1em; margin-left: -1em;
color: rgb(0, 0, 0); color: rgb(0, 0, 0);
} }
.icon-calc_volum .path6:before { .icon-calc_volum .path6:before {
content: "\e916"; content: "\e91a";
margin-left: -1em; margin-left: -1em;
color: rgb(255, 255, 255); color: rgb(255, 255, 255);
} }
.icon-calendar:before { .icon-deliveryprices:before {
content: "\e917";
}
.icon-catalog:before {
content: "\e918";
}
.icon-claims:before {
content: "\e919";
}
.icon-client:before {
content: "\e91a";
}
.icon-clone:before {
content: "\e91b";
}
.icon-columnadd:before {
content: "\e91c"; content: "\e91c";
} }
.icon-columndelete:before { .icon-onlinepayment:before {
content: "\e91d"; content: "\e91d";
} }
.icon-accessory:before { .icon-risk:before {
content: "\e91e"; content: "\e91e";
} }
.icon-components:before { .icon-noweb:before {
content: "\e91f"; content: "\e91f";
} }
.icon-handmade:before { .icon-no036:before {
content: "\e920"; content: "\e920";
} }
.icon-consignatarios:before { .icon-inactive:before {
content: "\e921"; content: "\e921";
} }
.icon-control:before { .icon-unavailable:before {
content: "\e922"; content: "\e922";
} }
.icon-credit:before { .icon-invoice-01:before {
content: "\e923"; content: "\e923";
} }
.icon-deletedTicketCross:before {
content: "\e924";
}
.icon-deleteline:before {
content: "\e925";
}
.icon-delivery:before {
content: "\e926";
}
.icon-deliveryprices:before {
content: "\e927";
}
.icon-details:before {
content: "\e928";
}
.icon-dfiscales:before {
content: "\e929";
}
.icon-doc:before {
content: "\e92a";
}
.icon-entry:before {
content: "\e92b";
}
.icon-exit:before {
content: "\e92c";
}
.icon-eye:before {
content: "\e92d";
}
.icon-fixedPrice:before {
content: "\e92e";
}
.icon-flower:before {
content: "\e92f";
}
.icon-frozen:before {
content: "\e930";
}
.icon-fruit:before {
content: "\e931";
}
.icon-funeral:before {
content: "\e932";
}
.icon-greuge:before {
content: "\e933";
}
.icon-grid:before {
content: "\e934";
}
.icon-handmadeArtificial:before {
content: "\e935";
}
.icon-headercol:before {
content: "\e936";
}
.icon-history:before {
content: "\e937";
}
.icon-disabled:before {
content: "\e938";
}
.icon-info:before {
content: "\e939";
}
.icon-inventory:before {
content: "\e93a";
}
.icon-invoice:before { .icon-invoice:before {
content: "\e93b"; content: "\e924";
} color: #5f5f5f;
.icon-invoice-in:before {
content: "\e93c";
}
.icon-invoice-in-create:before {
content: "\e93d";
}
.icon-invoice-out:before {
content: "\e93e";
}
.icon-item:before {
content: "\e93f";
}
.icon-languaje:before {
content: "\e940";
}
.icon-lines:before {
content: "\e941";
}
.icon-linesprepaired:before {
content: "\e942";
}
.icon-logout:before {
content: "\e943";
}
.icon-mana:before {
content: "\e944";
}
.icon-mandatory:before {
content: "\e945";
}
.icon-net:before {
content: "\e946";
}
.icon-niche:before {
content: "\e947";
}
.icon-no036:before {
content: "\e948";
}
.icon-notes:before {
content: "\e949";
}
.icon-noweb:before {
content: "\e94a";
}
.icon-onlinepayment:before {
content: "\e94b";
}
.icon-package:before {
content: "\e94c";
}
.icon-payment:before {
content: "\e94d";
}
.icon-pbx:before {
content: "\e94e";
}
.icon-Person:before {
content: "\e94f";
}
.icon-pets:before {
content: "\e950";
}
.icon-photo:before {
content: "\e951";
}
.icon-plant:before {
content: "\e952";
}
.icon-stowaway:before {
content: "\e953";
}
.icon-preserved:before {
content: "\e954";
}
.icon-recovery:before {
content: "\e955";
}
.icon-regentry:before {
content: "\e956";
}
.icon-reserve:before {
content: "\e957";
}
.icon-revision:before {
content: "\e958";
}
.icon-risk:before {
content: "\e959";
}
.icon-services:before {
content: "\e95a";
}
.icon-settings:before {
content: "\e95b";
}
.icon-shipment-01 .path1:before {
content: "\e95c";
color: rgb(225, 225, 225);
}
.icon-shipment-01 .path2:before {
content: "\e95d";
margin-left: -1em;
color: rgb(0, 0, 0);
}
.icon-sign:before {
content: "\e95e";
}
.icon-sms:before {
content: "\e95f";
}
.icon-solclaim:before {
content: "\e960";
}
.icon-solunion:before {
content: "\e961";
}
.icon-splitline:before {
content: "\e962";
}
.icon-splur:before {
content: "\e963";
} }
.icon-supplier:before { .icon-supplier:before {
content: "\e965"; content: "\e925";
} }
.icon-supplierfalse:before { .icon-client2:before {
content: "\e966"; content: "\e926";
} }
.icon-tags:before { .icon-supplier2:before {
content: "\e967"; content: "\e927";
} }
.icon-tax:before { .icon-client:before {
content: "\e968"; content: "\e928";
} }
.icon-thermometer:before { .icon-shipment-01:before {
content: "\e969"; content: "\e929";
color: #000;
} }
.icon-ticket:before { .icon-inventory:before {
content: "\e96a"; content: "\e92b";
}
.icon-traceability:before {
content: "\e96b";
}
.icon-transaction:before {
content: "\e96c";
}
.icon-treatments:before {
content: "\e96d";
}
.icon-unavailable:before {
content: "\e96e";
}
.icon-greenery:before {
content: "\e96f";
}
.icon-volume:before {
content: "\e970";
}
.icon-wand:before {
content: "\e971";
}
.icon-web:before {
content: "\e972";
}
.icon-wiki:before {
content: "\e973";
}
.icon-worker:before {
content: "\e974";
} }
.icon-zone:before { .icon-zone:before {
content: "\e92c";
}
.icon-wiki:before {
content: "\e92d";
}
.icon-attach:before {
content: "\e92e";
}
.icon-zone2:before {
content: "\e92f";
}
.icon-anonymous:before {
content: "\e930";
}
.icon-net:before {
content: "\e931";
}
.icon-buyrequest:before {
content: "\e932";
}
.icon-thermometer:before {
content: "\e933";
}
.icon-entry:before {
content: "\e934";
}
.icon-deletedTicket:before {
content: "\e935";
}
.icon-deliveryprices-01:before {
content: "\e936";
}
.icon-catalog:before {
content: "\e937";
}
.icon-agency:before {
content: "\e938";
}
.icon-delivery:before {
content: "\e939";
}
.icon-wand:before {
content: "\e93a";
}
.icon-buscaman:before {
content: "\e93b";
}
.icon-pbx:before {
content: "\e93c";
}
.icon-calendar:before {
content: "\e93d";
}
.icon-splitline:before {
content: "\e93e";
}
.icon-consignatarios:before {
content: "\e93f";
}
.icon-tax:before {
content: "\e940";
}
.icon-notes:before {
content: "\e941";
}
.icon-lines:before {
content: "\e942";
}
.icon-languaje:before {
content: "\e943";
}
.icon-greuge:before {
content: "\e944";
}
.icon-credit:before {
content: "\e945";
}
.icon-components:before {
content: "\e946";
}
.icon-pets:before {
content: "\e947";
}
.icon-linesprepaired:before {
content: "\e948";
}
.icon-control:before {
content: "\e949";
}
.icon-revision:before {
content: "\e94a";
}
.icon-newinvoices:before {
content: "\e94b";
}
.icon-services:before {
content: "\e94c";
}
.icon-newalbaran:before {
content: "\e94d";
}
.icon-solunion:before {
content: "\e94e";
}
.icon-stowaway:before {
content: "\e94f";
}
.icon-exit:before {
content: "\e950";
}
.icon-apps:before {
content: "\e951";
}
.icon-info:before {
content: "\e952";
}
.icon-columndelete:before {
content: "\e953";
}
.icon-columnadd:before {
content: "\e954";
}
.icon-deleteline:before {
content: "\e955";
}
.icon-item:before {
content: "\e956";
}
.icon-worker:before {
content: "\e957";
}
.icon-headercol:before {
content: "\e958";
}
.icon-reserva:before {
content: "\e959";
}
.icon-100:before {
content: "\e95a";
}
.icon-noweb1:before {
content: "\e95b";
}
.icon-settings1:before {
content: "\e95c";
}
.icon-sign:before {
content: "\e95d";
}
.icon-polizon:before {
content: "\e95e";
}
.icon-solclaim:before {
content: "\e95f";
}
.icon-actions:before {
content: "\e960";
}
.icon-details:before {
content: "\e961";
}
.icon-traceability:before {
content: "\e962";
}
.icon-claims:before {
content: "\e963";
}
.icon-regentry:before {
content: "\e964";
}
.icon-regentry-1:before {
content: "\e965";
}
.icon-transaction:before {
content: "\e966";
}
.icon-history:before {
content: "\e968";
}
.icon-entry:before {
content: "\e969";
}
.icon-mana:before {
content: "\e96a";
}
.icon-ticket:before {
content: "\e96b";
}
.icon-niche:before {
content: "\e96c";
}
.icon-tags:before {
content: "\e96d";
}
.icon-volume:before {
content: "\e96e";
}
.icon-bin:before {
content: "\e96f";
}
.icon-splur:before {
content: "\e970";
}
.icon-barcode:before {
content: "\e971";
}
.icon-botanical:before {
content: "\e972";
}
.icon-clone:before {
content: "\e973";
}
.icon-photo:before {
content: "\e974";
}
.icon-sms:before {
content: "\e975"; content: "\e975";
} }
.icon-eye:before {
content: "\e976";
}
.icon-doc:before {
content: "\e977";
}
.icon-package:before {
content: "\e978";
}
.icon-settings:before {
content: "\e979";
}
.icon-bucket:before {
content: "\e97a";
color: #000;
}
.icon-mandatory:before {
content: "\e97b";
}
.icon-recovery:before {
content: "\e97c";
}
.icon-payment:before {
content: "\e97e";
}
.icon-invoices:before {
content: "\e97f";
color: #000;
}
.icon-grid:before {
content: "\e980";
}
.icon-logout:before {
content: "\e981";
}
.icon-web:before {
content: "\e982";
}
.icon-albaran:before {
content: "\e983";
}
.icon-dfiscales:before {
content: "\e984";
}

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -96,13 +96,13 @@
"This postcode already exists": "Este código postal ya existe", "This postcode already exists": "Este código postal ya existe",
"Concept cannot be blank": "El concepto no puede quedar en blanco", "Concept cannot be blank": "El concepto no puede quedar en blanco",
"File doesn't exists": "El archivo no existe", "File doesn't exists": "El archivo no existe",
"You don't have privileges to change the zone": "No tienes permisos para cambiar la zona", "You don't have privileges to change the zone or for these parameters there are more than one shipping options, talk to agencies": "No tienes permisos para cambiar la zona o para esos parámetros hay más de una opción de envío, hable con las agencias",
"This ticket is already on weekly tickets": "Este ticket ya está en tickets programados", "This ticket is already on weekly tickets": "Este ticket ya está en tickets programados",
"Ticket id cannot be blank": "El id de ticket no puede quedar en blanco", "Ticket id cannot be blank": "El id de ticket no puede quedar en blanco",
"Weekday cannot be blank": "El día de la semana no puede quedar en blanco", "Weekday cannot be blank": "El día de la semana no puede quedar en blanco",
"You can't delete a confirmed order": "No puedes borrar un pedido confirmado", "You can't delete a confirmed order": "No puedes borrar un pedido confirmado",
"Can't create stowaway for this ticket": "No se puede crear un polizon para este ticket", "Can't create stowaway for this ticket": "No se puede crear un polizon para este ticket",
"The socialName has an invalid format": "El nombre fiscal tiene un formato incorrecto", "The social name has an invalid format": "El nombre fiscal tiene un formato incorrecto",
"Invalid quantity": "Cantidad invalida", "Invalid quantity": "Cantidad invalida",
"This postal code is not valid": "This postal code is not valid", "This postal code is not valid": "This postal code is not valid",
"is invalid": "is invalid", "is invalid": "is invalid",
@ -215,6 +215,6 @@
"You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días", "You can't create a claim from a ticket delivered more than seven days ago": "No puedes crear una reclamación de un ticket entregado hace más de siete días",
"The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día", "The worker has hours recorded that day": "El trabajador tiene horas fichadas ese día",
"The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día", "The worker has a marked absence that day": "El trabajador tiene marcada una ausencia ese día",
"isWithoutNegatives": "Tiene Negativos", "You can not modify is pay method checked": "No se puede modificar el campo método de pago validado",
"You can not modify is pay method checked": "No se puede modificar el campo método de pago validado" "Can't transfer claimed sales": "No puedes transferir lineas reclamadas",
} "isWithoutNegatives": "Tiene Negativos"

View File

@ -59,11 +59,12 @@ module.exports = Self => {
const landedPlusWeek = new Date(ticket.landed); const landedPlusWeek = new Date(ticket.landed);
landedPlusWeek.setDate(landedPlusWeek.getDate() + 7); landedPlusWeek.setDate(landedPlusWeek.getDate() + 7);
const hasClaimManagerRole = await models.Account.hasRole(userId, 'claimManager', myOptions);
const isClaimable = landedPlusWeek >= new Date(); const isClaimable = landedPlusWeek >= new Date();
if (ticket.isDeleted) if (ticket.isDeleted)
throw new UserError(`You can't create a claim for a removed ticket`); throw new UserError(`You can't create a claim for a removed ticket`);
if (!isClaimable) if (!isClaimable && !hasClaimManagerRole)
throw new UserError(`You can't create a claim from a ticket delivered more than seven days ago`); throw new UserError(`You can't create a claim from a ticket delivered more than seven days ago`);
const newClaim = await Self.create({ const newClaim = await Self.create({

View File

@ -46,9 +46,40 @@ describe('Claim createFromSales()', () => {
} }
}); });
it('should be able to create a claim for a ticket delivered more than seven days ago as claimManager', async() => {
const tx = await models.Claim.beginTransaction({});
const claimManagerId = 72;
activeCtx.accessToken.userId = claimManagerId;
try {
const options = {transaction: tx};
const todayMinusEightDays = new Date();
todayMinusEightDays.setDate(todayMinusEightDays.getDate() - 8);
const ticket = await models.Ticket.findById(ticketId, options);
await ticket.updateAttribute('landed', todayMinusEightDays, options);
const claim = await models.Claim.createFromSales(ctx, ticketId, newSale, options);
expect(claim.ticketFk).toEqual(ticketId);
const claimBeginning = await models.ClaimBeginning.findOne({where: {claimFk: claim.id}}, options);
expect(claimBeginning.saleFk).toEqual(newSale[0].id);
expect(claimBeginning.quantity).toEqual(newSale[0].quantity);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should not be able to create a claim for a ticket delivered more than seven days ago', async() => { it('should not be able to create a claim for a ticket delivered more than seven days ago', async() => {
const tx = await models.Claim.beginTransaction({}); const tx = await models.Claim.beginTransaction({});
activeCtx.accessToken.userId = 1;
let error; let error;
try { try {

View File

@ -1,5 +1,4 @@
const soap = require('soap'); const got = require('got');
const xmlParser = require('xml2js').parseString;
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
@ -35,57 +34,48 @@ module.exports = Self => {
Self.send = async(ctx, destinationFk, destination, message) => { Self.send = async(ctx, destinationFk, destination, message) => {
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const smsConfig = await Self.app.models.SmsConfig.findOne(); const smsConfig = await Self.app.models.SmsConfig.findOne();
const soapClient = await soap.createClientAsync(smsConfig.uri);
if (destination.length == 9) {
const spainPrefix = '0034';
destination = spainPrefix + destination;
}
const params = { const params = {
user: smsConfig.user, api_key: smsConfig.apiKey,
pass: smsConfig.password, messages: [{
src: smsConfig.title, from: smsConfig.title,
dst: destination, to: destination,
msg: message text: message
}]
}; };
let xmlResponse; let response;
let xmlResult;
let xmlParsed;
let status;
try { try {
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production')
status = { params.fake = 1;
codigo: [200],
descripcion: ['Fake response'] const jsonTest = {
}; json: params
} else { };
[xmlResponse] = await soapClient.sendSMSAsync(params); response = await got.post(smsConfig.uri, jsonTest).json();
xmlResult = xmlResponse.result.$value;
xmlParsed = await new Promise((resolve, reject) => {
xmlParser(xmlResult, (err, result) => {
if (err)
reject(err);
resolve(result);
});
});
[status] = xmlParsed['xtratelecom-sms-response'].sms;
}
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
const statusCode = status.codigo[0]; const [result] = response.result;
const statusDescription = status.descripcion[0]; const error = result.error_id;
const newSms = { const newSms = {
senderFk: userId, senderFk: userId,
destinationFk: destinationFk || null, destinationFk: destinationFk || null,
destination: destination, destination: destination,
message: message, message: message,
statusCode: statusCode, status: error
status: statusDescription
}; };
const sms = await Self.create(newSms); const sms = await Self.create(newSms);
if (statusCode != 200) if (error)
throw new UserError(`We weren't able to send this SMS`); throw new UserError(`We weren't able to send this SMS`);
return sms; return sms;

View File

@ -1,14 +1,10 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
const soap = require('soap');
describe('sms send()', () => { describe('sms send()', () => {
it('should return the expected message and status code', async() => { it('should not return status error', async() => {
const code = 200; const ctx = {req: {accessToken: {userId: 1}}};
spyOn(soap, 'createClientAsync').and.returnValue('a so fake client'); const result = await app.models.Sms.send(ctx, 1105, '123456789', 'My SMS Body');
let ctx = {req: {accessToken: {userId: 1}}};
let result = await app.models.Sms.send(ctx, 1105, 'destination', 'My SMS Body');
expect(result.statusCode).toEqual(code); expect(result.status).toBeUndefined();
expect(result.status).toContain('Fake response');
}); });
}); });

View File

@ -55,15 +55,6 @@ module.exports = Self => {
with: /^[\w|.|-]+@[\w|-]+(\.[\w|-]+)*(,[\w|.|-]+@[\w|-]+(\.[\w|-]+)*)*$/ with: /^[\w|.|-]+@[\w|-]+(\.[\w|-]+)*(,[\w|.|-]+@[\w|-]+(\.[\w|-]+)*)*$/
}); });
Self.validate('businessTypeFk', hasBusinessType, {
message: `The type of business must be filled in basic data`
});
function hasBusinessType(err) {
if (!this.businessTypeFk)
err();
}
Self.validatesLengthOf('postcode', { Self.validatesLengthOf('postcode', {
allowNull: true, allowNull: true,
allowBlank: true, allowBlank: true,
@ -189,6 +180,32 @@ module.exports = Self => {
return regexp.test(value); return regexp.test(value);
} }
Self.observe('before save', async ctx => {
const changes = ctx.data || ctx.instance;
const orgData = ctx.currentInstance;
const businessTypeFk = changes && changes.businessTypeFk || orgData && orgData.businessTypeFk;
const isTaxDataChecked = changes && changes.isTaxDataChecked || orgData && orgData.isTaxDataChecked;
let invalidBusinessType = false;
if (!ctx.isNewInstance) {
const isWorker = await Self.app.models.UserAccount.findById(orgData.id);
const changedFields = Object.keys(changes);
const hasChangedOtherFields = changedFields.some(key => key !== 'businessTypeFk');
if (!businessTypeFk && !isTaxDataChecked && !isWorker && !hasChangedOtherFields)
invalidBusinessType = true;
}
if (ctx.isNewInstance) {
if (!businessTypeFk && !isTaxDataChecked)
invalidBusinessType = true;
}
if (invalidBusinessType)
throw new UserError(`The type of business must be filled in basic data`);
});
Self.observe('before save', async function(ctx) { Self.observe('before save', async function(ctx) {
const changes = ctx.data || ctx.instance; const changes = ctx.data || ctx.instance;
const orgData = ctx.currentInstance; const orgData = ctx.currentInstance;
@ -206,7 +223,7 @@ module.exports = Self => {
&& orgData.isTaxDataChecked != isTaxDataChecked; && orgData.isTaxDataChecked != isTaxDataChecked;
if ((socialNameChanged || dataCheckedChanged) && !isAlpha(socialName)) if ((socialNameChanged || dataCheckedChanged) && !isAlpha(socialName))
throw new UserError('The socialName has an invalid format'); throw new UserError(`The social name has an invalid format`);
if (changes.salesPerson === null) { if (changes.salesPerson === null) {
changes.credit = 0; changes.credit = 0;
@ -267,7 +284,7 @@ module.exports = Self => {
replyTo: worker.email replyTo: worker.email
}; };
await got.get(`${origin}/api/email/payment-update`, { await got.get(`${origin}/api/email/payment-update`, {
query: params searchParams: params
}); });
} }

View File

@ -16,10 +16,7 @@
"uri": { "uri": {
"type": "String" "type": "String"
}, },
"user": { "apiKey": {
"type": "String"
},
"password": {
"type": "String" "type": "String"
}, },
"title": { "title": {

View File

@ -26,8 +26,7 @@
"required": true "required": true
}, },
"statusCode": { "statusCode": {
"type": "Number", "type": "Number"
"required": true
}, },
"status": { "status": {
"type": "String" "type": "String"

View File

@ -82,7 +82,7 @@ class Controller extends Dialog {
} }
set amountToReturn(value) { set amountToReturn(value) {
if (!value) return; if (isNaN(value)) return;
value = value.toFixed(2); value = value.toFixed(2);

View File

@ -58,7 +58,7 @@ module.exports = Self => {
}, myOptions); }, myOptions);
const response = got.stream(`${origin}/api/report/invoice`, { const response = got.stream(`${origin}/api/report/invoice`, {
query: { searchParams: {
authorization: auth.id, authorization: auth.id,
invoiceId: id invoiceId: id
} }

View File

@ -23,6 +23,6 @@ describe('InvoiceOut download()', () => {
const result = await models.InvoiceOut.download(ctx, invoiceId); const result = await models.InvoiceOut.download(ctx, invoiceId);
expect(result[1]).toEqual('application/pdf'); expect(result[1]).toEqual('application/pdf');
expect(result[2]).toEqual('filename="2021T1111111.pdf"'); expect(result[2]).toMatch(/filename="\d{4}T1111111.pdf"/);
}); });
}); });

View File

@ -112,7 +112,10 @@ module.exports = Self => {
case 'search': case 'search':
return /^\d+$/.test(value) return /^\d+$/.test(value)
? {or: [{'i.id': value}, codeWhere]} ? {or: [{'i.id': value}, codeWhere]}
: {or: [{'i.name': {like: `%${value}%`}}, codeWhere]}; : {or: [
{'i.name': {like: `%${value}%`}},
{'i.longName': {like: `%${value}%`}},
codeWhere]};
case 'id': case 'id':
case 'isActive': case 'isActive':
case 'typeFk': case 'typeFk':

View File

@ -5,6 +5,7 @@ Search tickets: Buscar tickets
Delete selected elements: Eliminar los elementos seleccionados Delete selected elements: Eliminar los elementos seleccionados
All the selected elements will be deleted. Are you sure you want to continue?: Todos los elementos seleccionados serán eliminados. ¿Seguro que quieres continuar? All the selected elements will be deleted. Are you sure you want to continue?: Todos los elementos seleccionados serán eliminados. ¿Seguro que quieres continuar?
Component lack: Faltan componentes Component lack: Faltan componentes
Ticket too little: Ticket demasiado pequeño
Minimize/Maximize: Minimizar/Maximizar Minimize/Maximize: Minimizar/Maximizar
Problems: Problemas Problems: Problemas
Theoretical: Teórica Theoretical: Teórica

View File

@ -122,6 +122,12 @@
class="bright" class="bright"
icon="icon-components"> icon="icon-components">
</vn-icon> </vn-icon>
<vn-icon
ng-show="::ticket.isTooLittle"
translate-attr="{title: 'Ticket too little'}"
class="bright"
icon="icon-isTooLittle">
</vn-icon>
</td> </td>
<td> <td>
<span <span

View File

@ -10,7 +10,12 @@
], ],
"card": [] "card": []
}, },
"keybindings": [], "keybindings": [
{
"key": "m",
"state": "monitor.index"
}
],
"routes": [ "routes": [
{ {
"url": "/monitor", "url": "/monitor",

View File

@ -15,6 +15,12 @@
{"state": "order.card.line", "icon": "icon-lines"} {"state": "order.card.line", "icon": "icon-lines"}
] ]
}, },
"keybindings": [
{
"key": "o",
"state": "order.index"
}
],
"routes": [ "routes": [
{ {
"url": "/order", "url": "/order",

View File

@ -38,7 +38,6 @@ module.exports = Self => {
'payDemFk', 'payDemFk',
'payDay', 'payDay',
'account', 'account',
'isFarmer',
'sageTaxTypeFk', 'sageTaxTypeFk',
'sageTransactionTypeFk', 'sageTransactionTypeFk',
'sageWithholdingFk', 'sageWithholdingFk',
@ -102,6 +101,11 @@ module.exports = Self => {
] ]
}; };
let supplier = await Self.app.models.Supplier.findOne(filter); let supplier = await Self.app.models.Supplier.findOne(filter);
const farmerCode = 2;
if (supplier.sageWithholdingFk == farmerCode)
supplier.isFarmer = true;
return supplier; return supplier;
}; };
}; };

View File

@ -25,4 +25,12 @@ describe('Supplier getSummary()', () => {
expect(payMethod.name).toEqual('PayMethod one'); expect(payMethod.name).toEqual('PayMethod one');
}); });
it(`should get if supplier is farmer by sageWithholdingFk`, async() => {
const supplier = await app.models.Supplier.findById(2);
const supplierSummary = await app.models.Supplier.getSummary(2);
expect(supplier.isFarmer).toBeUndefined();
expect(supplierSummary.isFarmer).toEqual(true);
});
}); });

View File

@ -7,12 +7,14 @@ module.exports = Self => {
}); });
async function ibanValidation(err, done) { async function ibanValidation(err, done) {
let filter = { const supplier = await Self.app.models.Supplier.findById(this.supplierFk);
const filter = {
fields: ['code'], fields: ['code'],
where: {id: this.countryFk} where: {id: supplier.countryFk}
}; };
let country = await Self.app.models.Country.findOne(filter);
let code = country ? country.code.toLowerCase() : null; const country = await Self.app.models.Country.findOne(filter);
const code = country ? country.code.toLowerCase() : null;
if (code != 'es') if (code != 'es')
return done(); return done();

View File

@ -114,6 +114,6 @@ module.exports = Self => {
&& orgData.socialName != socialName; && orgData.socialName != socialName;
if ((socialNameChanged) && !isAlpha(socialName)) if ((socialNameChanged) && !isAlpha(socialName))
throw new UserError('The socialName has an invalid format'); throw new UserError('The social name has an invalid format');
}); });
}; };

View File

@ -27,9 +27,6 @@
"nif": { "nif": {
"type": "string" "type": "string"
}, },
"isFarmer": {
"type": "boolean"
},
"phone": { "phone": {
"type": "number" "type": "number"
}, },

View File

@ -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;
}
};
};

View File

@ -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;
}
});
});

View File

@ -118,10 +118,19 @@ module.exports = Self => {
const isProductionBoss = await models.Account.hasRole(userId, 'productionBoss', myOptions); const isProductionBoss = await models.Account.hasRole(userId, 'productionBoss', myOptions);
if (!isProductionBoss) { if (!isProductionBoss) {
const zoneShipped = await models.Agency.getShipped(args.landed, args.addressFk, args.agencyModeFk, args.warehouseFk, myOptions); const zoneShipped = await models.Agency.getShipped(
args.landed,
args.addressFk,
args.agencyModeFk,
args.warehouseFk,
myOptions);
if (!zoneShipped || zoneShipped.zoneFk != args.zoneFk) if (!zoneShipped || zoneShipped.zoneFk != args.zoneFk) {
throw new UserError(`You don't have privileges to change the zone`); const error = `You don't have privileges to change the zone or
for these parameters there are more than one shipping options, talk to agencies`;
throw new UserError(error);
}
} }
if (args.isWithoutNegatives) { if (args.isWithoutNegatives) {
const query = `CALL ticket_getVisibleAvailable(?,?)`; const query = `CALL ticket_getVisibleAvailable(?,?)`;

View File

@ -87,8 +87,12 @@ module.exports = Self => {
args.warehouseId, args.warehouseId,
myOptions); myOptions);
if (!zoneShipped || zoneShipped.zoneFk != args.zoneId) if (!zoneShipped || zoneShipped.zoneFk != args.zoneId) {
throw new UserError(`You don't have privileges to change the zone`); const error = `You don't have privileges to change the zone or
for these parameters there are more than one shipping options, talk to agencies`;
throw new UserError(error);
}
} }
const items = await models.Sale.find({ const items = await models.Sale.find({

View File

@ -58,13 +58,63 @@ describe('sale transferSales()', () => {
expect(error.message).toEqual(`The sales of the receiver ticket can't be modified`); expect(error.message).toEqual(`The sales of the receiver ticket can't be modified`);
}); });
it('should transfer the sales from one ticket to a new one then send them back and delete the created ticket', async() => { it('should throw an error if any of the sales has a claim', async() => {
const tx = await models.Ticket.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const ticketId = 11;
const receiverTicketId = null;
const sales = await models.Ticket.getSales(ticketId, options);
await models.Ticket.transferSales(ctx, ticketId, receiverTicketId, sales, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error.message).toEqual(`Can't transfer claimed sales`);
});
it('should be able to transfer claimed sales if the role is claimManager', async() => {
const tx = await models.Ticket.beginTransaction({});
let error;
try {
const options = {transaction: tx};
const claimManagerId = 72;
const myActiveCtx = {
accessToken: {userId: claimManagerId},
};
const myCtx = {req: myActiveCtx};
const ticketId = 11;
const receiverTicketId = null;
const sales = await models.Ticket.getSales(ticketId, options);
await models.Ticket.transferSales(myCtx, ticketId, receiverTicketId, sales, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).toBeUndefined();
});
it('should transfer the sales from a ticket to a new one', async() => {
const tx = await models.Ticket.beginTransaction({}); const tx = await models.Ticket.beginTransaction({});
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
const formerTicketId = 11; const formerTicketId = 22;
let createdTicketId = undefined; let createdTicketId = undefined;
let formerTicketSales = await models.Ticket.getSales(formerTicketId, options); let formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
@ -77,23 +127,11 @@ describe('sale transferSales()', () => {
createdTicketId = createdTicket.id; createdTicketId = createdTicket.id;
formerTicketSales = await models.Ticket.getSales(formerTicketId, options); formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
createdTicketSales = await models.Ticket.getSales(createdTicketId, options); let createdTicketSales = await models.Ticket.getSales(createdTicketId, options);
expect(formerTicketSales.length).toEqual(0); expect(formerTicketSales.length).toEqual(0);
expect(createdTicketSales.length).toEqual(2); expect(createdTicketSales.length).toEqual(2);
await models.Ticket.transferSales(
ctx, createdTicketId, formerTicketId, createdTicketSales, options);
formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
createdTicketSales = await models.Ticket.getSales(createdTicketId, options);
createdTicket = await models.Ticket.findById(createdTicketId, null, options);
expect(createdTicket.isDeleted).toBeTruthy();
expect(formerTicketSales.length).toEqual(2);
expect(createdTicketSales.length).toEqual(0);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
await tx.rollback(); await tx.rollback();
@ -109,10 +147,9 @@ describe('sale transferSales()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
const currentTicket = await models.Ticket.findById(11, null, options); const currentTicketId = 22;
const currentTicketSales = await models.Ticket.getSales(currentTicket.id, options); const currentTicketSales = await models.Ticket.getSales(currentTicketId, options);
const currentTicketId = currentTicket.id;
const receiverTicketId = undefined; const receiverTicketId = undefined;
currentTicketSales[0].quantity = 99; currentTicketSales[0].quantity = 99;
@ -135,7 +172,7 @@ describe('sale transferSales()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
const formerTicketId = 11; const formerTicketId = 22;
let createdTicketId = undefined; let createdTicketId = undefined;
let formerTicketSales = await models.Ticket.getSales(formerTicketId, options); let formerTicketSales = await models.Ticket.getSales(formerTicketId, options);
@ -143,7 +180,7 @@ describe('sale transferSales()', () => {
const completeSaleId = formerTicketSales[1].id; const completeSaleId = formerTicketSales[1].id;
let partialSaleTotalQuantity = formerTicketSales[0].quantity; let partialSaleTotalQuantity = formerTicketSales[0].quantity;
expect(partialSaleTotalQuantity).toEqual(15); expect(partialSaleTotalQuantity).toEqual(30);
formerTicketSales[0].quantity = 1; formerTicketSales[0].quantity = 1;

View File

@ -75,6 +75,13 @@ module.exports = Self => {
for (const sale of originalSales) for (const sale of originalSales)
map.set(sale.id, sale); map.set(sale.id, sale);
const saleIds = sales.map(sale => sale.id);
const isClaimManager = await models.Account.hasRole(userId, 'claimManager');
const hasClaimedSales = await models.ClaimBeginning.findOne({where: {saleFk: {inq: saleIds}}});
if (hasClaimedSales && !isClaimManager)
throw new UserError(`Can't transfer claimed sales`);
for (const sale of sales) { for (const sale of sales) {
const originalSale = map.get(sale.id); const originalSale = map.get(sale.id);
const originalSaleData = { // <-- Loopback modifies original instance on save const originalSaleData = { // <-- Loopback modifies original instance on save

View File

@ -6,6 +6,7 @@ module.exports = Self => {
require('../methods/sale/updateQuantity')(Self); require('../methods/sale/updateQuantity')(Self);
require('../methods/sale/updateConcept')(Self); require('../methods/sale/updateConcept')(Self);
require('../methods/sale/recalculatePrice')(Self); require('../methods/sale/recalculatePrice')(Self);
require('../methods/sale/payBack')(Self);
require('../methods/sale/canEdit')(Self); require('../methods/sale/canEdit')(Self);
Self.validatesPresenceOf('concept', { Self.validatesPresenceOf('concept', {

View File

@ -490,4 +490,9 @@
ng-if="$ctrl.isEditable && $ctrl.hasReserves()"> ng-if="$ctrl.isEditable && $ctrl.hasReserves()">
Unmark as reserved Unmark as reserved
</vn-item> </vn-item>
<vn-item translate
name="payBack"
ng-click="$ctrl.createPayBack()">
Pay Back
</vn-item>
</vn-menu> </vn-menu>

View File

@ -40,7 +40,9 @@ class Controller extends Section {
const landedPlusWeek = new Date(this.ticket.landed); const landedPlusWeek = new Date(this.ticket.landed);
landedPlusWeek.setDate(landedPlusWeek.getDate() + 7); landedPlusWeek.setDate(landedPlusWeek.getDate() + 7);
return landedPlusWeek >= new Date(); const hasClaimManagerRole = this.aclService.hasAny(['claimManager']);
return landedPlusWeek >= new Date() || hasClaimManagerRole;
} }
return false; return false;
} }
@ -460,6 +462,18 @@ class Controller extends Section {
}); });
} }
createPayBack() {
const sales = this.selectedValidSales();
if (!sales) return;
const params = {sales: sales, ticketId: this.ticket.id};
const query = `Sales/payBack`;
this.resetChanges();
this.$http.post(query, params).then(res => {
this.$state.go('ticket.card.sale', {id: res.data});
});
}
itemSearchFunc($search) { itemSearchFunc($search) {
return /^\d+$/.test($search) return /^\d+$/.test($search)
? {id: $search} ? {id: $search}

View File

@ -701,6 +701,22 @@ describe('Ticket', () => {
}); });
}); });
describe('createPayBack()', () => {
it('should make an HTTP POST query and then call to the $state go() method', () => {
jest.spyOn(controller, 'selectedValidSales').mockReturnValue(controller.sales);
jest.spyOn(controller, 'resetChanges');
jest.spyOn(controller.$state, 'go');
const expectedId = 9999;
$httpBackend.expect('POST', `Sales/payBack`).respond(200, expectedId);
controller.createPayBack();
$httpBackend.flush();
expect(controller.resetChanges).toHaveBeenCalledWith();
expect(controller.$state.go).toHaveBeenCalledWith('ticket.card.sale', {id: expectedId});
});
});
describe('itemSearchFunc()', () => { describe('itemSearchFunc()', () => {
it('should return the filter by id property for an input of a number', () => { it('should return the filter by id property for an input of a number', () => {
const itemId = 1; const itemId = 1;

View File

@ -35,4 +35,5 @@ Address: Dirección
Warehouse: Almacen Warehouse: Almacen
Agency: Agencia Agency: Agencia
Shipped: F. envio Shipped: F. envio
Packaging: Encajado Packaging: Encajado
Pay Back: Abono

View File

@ -11,14 +11,14 @@
Ticket #{{$ctrl.summary.id}} - {{$ctrl.summary.client.name}} Ticket #{{$ctrl.summary.id}} - {{$ctrl.summary.client.name}}
({{$ctrl.summary.client.id}}) - {{$ctrl.summary.nickname}} ({{$ctrl.summary.client.id}}) - {{$ctrl.summary.nickname}}
</span> </span>
<vn-button <vn-button-menu
disabled="!$ctrl.isEditable" disabled="!$ctrl.isEditable"
class="flat" class="message"
style="color: inherit;" label="Change state"
label="SET OK" value-field="code"
ng-click="$ctrl.setOkState()" url="States/editableStates"
vn-tooltip="Change ticket state to 'Ok'"> on-change="$ctrl.changeState(value)">
</vn-button> </vn-button-menu>
<vn-ticket-descriptor-menu <vn-ticket-descriptor-menu
ng-if="!$ctrl.isTicketModule" ng-if="!$ctrl.isTicketModule"
ticket-id="$ctrl.summary.id" ticket-id="$ctrl.summary.id"

View File

@ -59,10 +59,11 @@ class Controller extends Summary {
this.$.invoiceOutDescriptor.show(event.target, this.summary.invoiceOut.id); this.$.invoiceOutDescriptor.show(event.target, this.summary.invoiceOut.id);
} }
setOkState() { changeState(value) {
const params = {ticketFk: 'id' in this.ticket ? this.ticket.id : this.$params.id}; const params = {
ticketFk: 'id' in this.ticket ? this.ticket.id : this.$params.id,
params.code = 'OK'; code: value
};
this.$http.post(`TicketTrackings/changeState`, params) this.$http.post(`TicketTrackings/changeState`, params)
.then(() => { .then(() => {

View File

@ -42,5 +42,20 @@ describe('Ticket', () => {
expect(controller.formattedAddress).toEqual('1007 Mountain Drive - 46060 - Gotham (Gotham)'); expect(controller.formattedAddress).toEqual('1007 Mountain Drive - 46060 - Gotham (Gotham)');
}); });
}); });
describe('changeState()', () => {
it('should change the state', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
const value = 'myTicketState';
let res = {id: 1, nickname: 'myNickname'};
$httpBackend.when('GET', `Tickets/1/summary`).respond(200, res);
$httpBackend.expectPOST(`TicketTrackings/changeState`).respond(200, 'ok');
controller.changeState(value);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
});
});
}); });
}); });

View File

@ -2,4 +2,5 @@ Address phone: Tel. consignatario
Address mobile: Móv. consignatario Address mobile: Móv. consignatario
Client phone: Tel. cliente Client phone: Tel. cliente
Client mobile: Móv. cliente Client mobile: Móv. cliente
Go to the ticket: Ir al ticket Go to the ticket: Ir al ticket
Change state: Cambiar estado

View File

@ -24,6 +24,12 @@
{"state": "worker.card.workerLog", "icon": "history"} {"state": "worker.card.workerLog", "icon": "history"}
] ]
}, },
"keybindings": [
{
"key": "w",
"state": "worker.index"
}
],
"routes": [ "routes": [
{ {
"url": "/worker", "url": "/worker",

26129
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@
"bmp-js": "^0.1.0", "bmp-js": "^0.1.0",
"compression": "^1.7.3", "compression": "^1.7.3",
"fs-extra": "^5.0.0", "fs-extra": "^5.0.0",
"got": "^6.7.1", "got": "^10.7.0",
"helmet": "^3.21.2", "helmet": "^3.21.2",
"i18n": "^0.8.4", "i18n": "^0.8.4",
"image-type": "^4.1.0", "image-type": "^4.1.0",

View File

@ -23,6 +23,10 @@
<td class="font gray uppercase">{{$t('clientId')}}</td> <td class="font gray uppercase">{{$t('clientId')}}</td>
<th>{{client.id}}</th> <th>{{client.id}}</th>
</tr> </tr>
<tr>
<td class="font gray uppercase">{{$t('phone')}}</td>
<th>{{client.phone}}</th>
</tr>
<tr> <tr>
<td class="font gray uppercase">{{$t('date')}}</td> <td class="font gray uppercase">{{$t('date')}}</td>
<th>{{dated}}</th> <th>{{dated}}</th>

View File

@ -9,6 +9,7 @@ reference: Referencia
concept: Concepto concept: Concepto
clientSignature: Firma del cliente clientSignature: Firma del cliente
claim: Reclamación {0} claim: Reclamación {0}
phone: Teléfono
sections: sections:
agency: agency:
description: 'Para agilizar su recogida, por favor, póngase en contacto con la oficina description: 'Para agilizar su recogida, por favor, póngase en contacto con la oficina

View File

@ -8,7 +8,8 @@ SELECT
a.street, a.street,
a.nickname, a.nickname,
p.name AS province, p.name AS province,
ct.country ct.country,
IFNULL(c.phone, cc.phone) AS phone
FROM claim cl FROM claim cl
JOIN client c ON c.id = cl.clientFk JOIN client c ON c.id = cl.clientFk
JOIN account.user u ON u.id = c.id JOIN account.user u ON u.id = c.id
@ -17,4 +18,6 @@ FROM claim cl
LEFT JOIN province p ON p.id = a.provinceFk LEFT JOIN province p ON p.id = a.provinceFk
LEFT JOIN autonomy amy ON amy.id = p.autonomyFk LEFT JOIN autonomy amy ON amy.id = p.autonomyFk
LEFT JOIN country ct ON ct.id = amy.countryFk LEFT JOIN country ct ON ct.id = amy.countryFk
WHERE cl.id = ? LEFT JOIN clientContact cc ON cc.clientFk = c.id
WHERE cl.id = ?
LIMIT 1;