Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2176-contextmenu
gitea/salix/pipeline/head This commit has test failures
Details
gitea/salix/pipeline/head This commit has test failures
Details
This commit is contained in:
commit
181f2f0148
|
@ -8,7 +8,7 @@ RUN apt-get update \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
gnupg2 \
|
gnupg2 \
|
||||||
libfontconfig \
|
libfontconfig \
|
||||||
&& curl -sL https://deb.nodesource.com/setup_10.x | bash - \
|
&& curl -sL https://deb.nodesource.com/setup_12.x | bash - \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
nodejs \
|
nodejs \
|
||||||
&& apt-get purge -y --auto-remove \
|
&& apt-get purge -y --auto-remove \
|
||||||
|
|
|
@ -9,7 +9,7 @@ Salix is also the scientific name of a beautifull tree! :)
|
||||||
Required applications.
|
Required applications.
|
||||||
|
|
||||||
* Visual Studio Code
|
* Visual Studio Code
|
||||||
* Node.js = 10.15.3 LTS
|
* Node.js = 12.17.0 LTS
|
||||||
* Docker
|
* Docker
|
||||||
|
|
||||||
In Visual Studio Code we use the ESLint extension. Open Visual Studio Code, press Ctrl+P and paste the following command.
|
In Visual Studio Code we use the ESLint extension. Open Visual Studio Code, press Ctrl+P and paste the following command.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
INSERT IGNORE INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('WorkerLog', '*', 'READ', 'ALLOW', 'ROLE', 'hr');
|
|
@ -1 +1,4 @@
|
||||||
UPDATE `salix`.`ACL` SET `accessType`='WRITE' WHERE `id`='213';
|
UPDATE `salix`.`ACL` SET `accessType`='WRITE' WHERE `id`='213';
|
||||||
|
|
||||||
|
INSERT IGNORE INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('CustomsAgent', '*', '*', 'ALLOW', 'ROLE', 'employee');
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
ALTER TABLE `vn`.`route`
|
||||||
|
DROP FOREIGN KEY `fk_route_1`;
|
||||||
|
ALTER TABLE `vn`.`route`
|
||||||
|
ADD CONSTRAINT `fk_route_1`
|
||||||
|
FOREIGN KEY (`zoneFk`)
|
||||||
|
REFERENCES `vn`.`zone` (`id`)
|
||||||
|
ON DELETE SET NULL
|
||||||
|
ON UPDATE CASCADE;
|
|
@ -0,0 +1,99 @@
|
||||||
|
|
||||||
|
DROP procedure IF EXISTS `vn`.`sale_calculateComponent`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`sale_calculateComponent`(vSale INT, vOption INT)
|
||||||
|
proc: BEGIN
|
||||||
|
/**
|
||||||
|
* Actualiza los componentes
|
||||||
|
*
|
||||||
|
* @param vSale Delivery date
|
||||||
|
* @param vOption indica en que componente pone el descuadre, NULL en casos habituales
|
||||||
|
*/
|
||||||
|
DECLARE vShipped DATE;
|
||||||
|
DECLARE vWarehouseFk SMALLINT;
|
||||||
|
DECLARE vAgencyModeFk INT;
|
||||||
|
DECLARE vAddressFk INT;
|
||||||
|
DECLARE vTicketFk BIGINT;
|
||||||
|
DECLARE vItemFk BIGINT;
|
||||||
|
DECLARE vLanded DATE;
|
||||||
|
DECLARE vIsEditable BOOLEAN;
|
||||||
|
DECLARE vZoneFk INTEGER;
|
||||||
|
|
||||||
|
SELECT t.refFk IS NULL AND (IFNULL(ts.alertLevel, 0) = 0 OR s.price = 0),
|
||||||
|
s.ticketFk,
|
||||||
|
s.itemFk ,
|
||||||
|
t.zoneFk,
|
||||||
|
t.warehouseFk,
|
||||||
|
t.shipped,
|
||||||
|
t.addressFk,
|
||||||
|
t.agencyModeFk,
|
||||||
|
t.landed
|
||||||
|
INTO vIsEditable,
|
||||||
|
vTicketFk,
|
||||||
|
vItemFk,
|
||||||
|
vZoneFk,
|
||||||
|
vWarehouseFk,
|
||||||
|
vShipped,
|
||||||
|
vAddressFk,
|
||||||
|
vAgencyModeFk,
|
||||||
|
vLanded
|
||||||
|
FROM ticket t
|
||||||
|
JOIN sale s ON s.ticketFk = t.id
|
||||||
|
LEFT JOIN ticketState ts ON ts.ticketFk = t.id
|
||||||
|
WHERE s.id = vSale;
|
||||||
|
|
||||||
|
IF vLanded IS NULL OR vZoneFk IS NULL THEN
|
||||||
|
|
||||||
|
CALL zone_getLanded(vShipped, vAddressFk, vAgencyModeFk, vWarehouseFk);
|
||||||
|
|
||||||
|
IF (SELECT COUNT(*) FROM tmp.zoneGetLanded LIMIT 1) = 0 THEN
|
||||||
|
CALL util.throw('There is no zone for these parameters');
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
UPDATE ticket t
|
||||||
|
SET t.landed = (SELECT landed FROM tmp.zoneGetLanded LIMIT 1)
|
||||||
|
WHERE t.id = vTicketFk AND t.landed IS NULL;
|
||||||
|
|
||||||
|
IF vZoneFk IS NULL THEN
|
||||||
|
SELECT zoneFk INTO vZoneFk FROM tmp.zoneGetLanded LIMIT 1;
|
||||||
|
UPDATE ticket t
|
||||||
|
SET t.zoneFk = vZoneFk
|
||||||
|
WHERE t.id = vTicketFk AND t.zoneFk IS NULL;
|
||||||
|
END IF;
|
||||||
|
DROP TEMPORARY TABLE tmp.zoneGetLanded;
|
||||||
|
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- rellena la tabla buyUltimate con la ultima compra
|
||||||
|
CALL buyUltimate (vWarehouseFk, vShipped);
|
||||||
|
|
||||||
|
DELETE FROM tmp.buyUltimate WHERE itemFk != vItemFk;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.ticketLot;
|
||||||
|
CREATE TEMPORARY TABLE tmp.ticketLot
|
||||||
|
SELECT vWarehouseFk warehouseFk, NULL available, vItemFk itemFk, buyFk, vZoneFk zoneFk
|
||||||
|
FROM tmp.buyUltimate
|
||||||
|
WHERE itemFk = vItemFk;
|
||||||
|
|
||||||
|
CALL catalog_componentPrepare();
|
||||||
|
CALL catalog_componentCalculate(vZoneFk, vAddressFk, vShipped, vWarehouseFk);
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tmp.sale;
|
||||||
|
CREATE TEMPORARY TABLE tmp.sale
|
||||||
|
(PRIMARY KEY (saleFk)) ENGINE = MEMORY
|
||||||
|
SELECT vSale saleFk,vWarehouseFk warehouseFk;
|
||||||
|
|
||||||
|
IF vOption IS NULL THEN
|
||||||
|
SET vOption = IF(vIsEditable, 1, 6);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
CALL ticketComponentUpdateSale(vOption);
|
||||||
|
CALL catalog_componentPurge();
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE tmp.buyUltimate;
|
||||||
|
DROP TEMPORARY TABLE tmp.sale;
|
||||||
|
END$$
|
||||||
|
|
||||||
|
DELIMITER ;
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -18,15 +18,13 @@ describe('zone zone_getEvents()', () => {
|
||||||
|
|
||||||
]);
|
]);
|
||||||
stmts.push(stmt);
|
stmts.push(stmt);
|
||||||
let firstResultIndex = stmts.push(stmt) - 1;
|
|
||||||
let secondResultIndex = firstResultIndex + 1;
|
|
||||||
|
|
||||||
stmts.push('ROLLBACK');
|
stmts.push('ROLLBACK');
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
let sql = ParameterizedSQL.join(stmts, ';');
|
||||||
let result = await app.models.Ticket.rawStmt(sql);
|
let result = await app.models.Ticket.rawStmt(sql);
|
||||||
|
|
||||||
let zonesEvents = result[secondResultIndex];
|
let zonesEvents = result[1];
|
||||||
|
|
||||||
expect(zonesEvents.length).toBeGreaterThan(0);
|
expect(zonesEvents.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
|
@ -86,7 +86,7 @@ export default {
|
||||||
invoiceByAddressCheckbox: 'vn-client-fiscal-data vn-check[label="Invoice by address"]',
|
invoiceByAddressCheckbox: 'vn-client-fiscal-data vn-check[label="Invoice by address"]',
|
||||||
verifiedDataCheckbox: 'vn-client-fiscal-data vn-check[label="Verified data"]',
|
verifiedDataCheckbox: 'vn-client-fiscal-data vn-check[label="Verified data"]',
|
||||||
hasToInvoiceCheckbox: 'vn-client-fiscal-data vn-check[label="Has to invoice"]',
|
hasToInvoiceCheckbox: 'vn-client-fiscal-data vn-check[label="Has to invoice"]',
|
||||||
invoiceByMailCheckbox: 'vn-client-fiscal-data vn-check[label="Invoice by mail"]',
|
notifyByMailCheckbox: 'vn-client-fiscal-data vn-check[label="Notify by email"]',
|
||||||
viesCheckbox: 'vn-client-fiscal-data vn-check[label="Vies"]',
|
viesCheckbox: 'vn-client-fiscal-data vn-check[label="Vies"]',
|
||||||
saveButton: 'button[type=submit]',
|
saveButton: 'button[type=submit]',
|
||||||
acceptDuplicationButton: '.vn-confirm.shown button[response=accept]',
|
acceptDuplicationButton: '.vn-confirm.shown button[response=accept]',
|
||||||
|
@ -122,6 +122,12 @@ export default {
|
||||||
mobileInput: 'vn-textfield[ng-model="$ctrl.address.mobile"]',
|
mobileInput: 'vn-textfield[ng-model="$ctrl.address.mobile"]',
|
||||||
defaultAddress: 'vn-client-address-index div:nth-child(1) div[name="street"]',
|
defaultAddress: 'vn-client-address-index div:nth-child(1) div[name="street"]',
|
||||||
incoterms: 'vn-autocomplete[ng-model="$ctrl.address.incotermsId"]',
|
incoterms: 'vn-autocomplete[ng-model="$ctrl.address.incotermsId"]',
|
||||||
|
addNewCustomsAgent: 'vn-client-address-create vn-autocomplete[ng-model="$ctrl.address.customsAgentId"] vn-icon-button[icon="add_circle"]',
|
||||||
|
newCustomsAgentFiscalID: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.nif"]',
|
||||||
|
newCustomsAgentFiscalName: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.fiscalName"]',
|
||||||
|
newCustomsAgentStreet: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.street"]',
|
||||||
|
newCustomsAgentPhone: 'vn-textfield[ng-model="$ctrl.newCustomsAgent.phone"]',
|
||||||
|
saveNewCustomsAgentButton: 'button[response="accept"]',
|
||||||
customsAgent: 'vn-autocomplete[ng-model="$ctrl.address.customsAgentId"]',
|
customsAgent: 'vn-autocomplete[ng-model="$ctrl.address.customsAgentId"]',
|
||||||
secondMakeDefaultStar: 'vn-client-address-index vn-card div:nth-child(2) vn-icon-button[icon="star_border"]',
|
secondMakeDefaultStar: 'vn-client-address-index vn-card div:nth-child(2) vn-icon-button[icon="star_border"]',
|
||||||
firstEditAddress: 'vn-client-address-index div:nth-child(1) > a',
|
firstEditAddress: 'vn-client-address-index div:nth-child(1) > a',
|
||||||
|
@ -579,6 +585,7 @@ export default {
|
||||||
claimState: 'vn-claim-basic-data vn-autocomplete[ng-model="$ctrl.claim.claimStateFk"]',
|
claimState: 'vn-claim-basic-data vn-autocomplete[ng-model="$ctrl.claim.claimStateFk"]',
|
||||||
responsabilityInputRange: 'vn-range',
|
responsabilityInputRange: 'vn-range',
|
||||||
observation: 'vn-textarea[ng-model="$ctrl.claim.observation"]',
|
observation: 'vn-textarea[ng-model="$ctrl.claim.observation"]',
|
||||||
|
hasToPickUpCheckbox: 'vn-claim-basic-data vn-check[ng-model="$ctrl.claim.hasToPickUp"]',
|
||||||
saveButton: `button[type=submit]`
|
saveButton: `button[type=submit]`
|
||||||
},
|
},
|
||||||
claimDetail: {
|
claimDetail: {
|
||||||
|
@ -613,8 +620,7 @@ export default {
|
||||||
firstLineDestination: 'vn-claim-action vn-tr:nth-child(1) vn-autocomplete[ng-model="saleClaimed.claimDestinationFk"]',
|
firstLineDestination: 'vn-claim-action vn-tr:nth-child(1) vn-autocomplete[ng-model="saleClaimed.claimDestinationFk"]',
|
||||||
secondLineDestination: 'vn-claim-action vn-tr:nth-child(2) vn-autocomplete[ng-model="saleClaimed.claimDestinationFk"]',
|
secondLineDestination: 'vn-claim-action vn-tr:nth-child(2) vn-autocomplete[ng-model="saleClaimed.claimDestinationFk"]',
|
||||||
firstDeleteLine: 'vn-claim-action vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
|
firstDeleteLine: 'vn-claim-action vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
|
||||||
isPaidWithManaCheckbox: 'vn-claim-action vn-check[ng-model="$ctrl.claim.isChargedToMana"]',
|
isPaidWithManaCheckbox: 'vn-claim-action vn-check[ng-model="$ctrl.claim.isChargedToMana"]'
|
||||||
hasToPickUpCheckbox: 'vn-claim-action vn-check[ng-model="$ctrl.claim.hasToPickUp"]'
|
|
||||||
},
|
},
|
||||||
ordersIndex: {
|
ordersIndex: {
|
||||||
searchResult: 'vn-order-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
searchResult: 'vn-order-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
||||||
|
|
|
@ -72,7 +72,7 @@ describe('Client Edit fiscalData path', () => {
|
||||||
await page.waitToClick(selectors.clientFiscalData.frozenCheckbox);
|
await page.waitToClick(selectors.clientFiscalData.frozenCheckbox);
|
||||||
await page.waitToClick(selectors.clientFiscalData.hasToInvoiceCheckbox);
|
await page.waitToClick(selectors.clientFiscalData.hasToInvoiceCheckbox);
|
||||||
await page.waitToClick(selectors.clientFiscalData.viesCheckbox);
|
await page.waitToClick(selectors.clientFiscalData.viesCheckbox);
|
||||||
await page.waitToClick(selectors.clientFiscalData.invoiceByMailCheckbox);
|
await page.waitToClick(selectors.clientFiscalData.notifyByMailCheckbox);
|
||||||
await page.waitToClick(selectors.clientFiscalData.invoiceByAddressCheckbox);
|
await page.waitToClick(selectors.clientFiscalData.invoiceByAddressCheckbox);
|
||||||
await page.waitToClick(selectors.clientFiscalData.equalizationTaxCheckbox);
|
await page.waitToClick(selectors.clientFiscalData.equalizationTaxCheckbox);
|
||||||
await page.waitToClick(selectors.clientFiscalData.verifiedDataCheckbox);
|
await page.waitToClick(selectors.clientFiscalData.verifiedDataCheckbox);
|
||||||
|
@ -230,8 +230,8 @@ describe('Client Edit fiscalData path', () => {
|
||||||
expect(result).toBe('checked');
|
expect(result).toBe('checked');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm Invoice by mail checkbox is unchecked', async() => {
|
it('should confirm Notify by email checkbox is unchecked', async() => {
|
||||||
const result = await page.checkboxState(selectors.clientFiscalData.invoiceByMailCheckbox);
|
const result = await page.checkboxState(selectors.clientFiscalData.notifyByMailCheckbox);
|
||||||
|
|
||||||
expect(result).toBe('unchecked');
|
expect(result).toBe('unchecked');
|
||||||
});
|
});
|
||||||
|
|
|
@ -61,12 +61,18 @@ describe('Client Add address path', () => {
|
||||||
expect(message.text).toBe('Customs agent is required for a non UEE member');
|
expect(message.text).toBe('Customs agent is required for a non UEE member');
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should create a new address with all it's data`, async() => {
|
it(`should create a new custom agent and then save the address`, async() => {
|
||||||
await page.autocompleteSearch(selectors.clientAddresses.customsAgent, 'Agent one');
|
await page.waitToClick(selectors.clientAddresses.addNewCustomsAgent);
|
||||||
|
await page.write(selectors.clientAddresses.newCustomsAgentFiscalID, 'ID');
|
||||||
|
await page.write(selectors.clientAddresses.newCustomsAgentFiscalName, 'name');
|
||||||
|
await page.write(selectors.clientAddresses.newCustomsAgentStreet, 'street');
|
||||||
|
await page.write(selectors.clientAddresses.newCustomsAgentPhone, '555555555');
|
||||||
|
await page.waitToClick(selectors.clientAddresses.saveNewCustomsAgentButton);
|
||||||
|
|
||||||
await page.waitToClick(selectors.clientAddresses.saveButton);
|
await page.waitToClick(selectors.clientAddresses.saveButton);
|
||||||
const message = await page.waitForSnackbar();
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
expect(message.type).toBe('success');
|
expect(message.text).toBe('Data saved!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should navigate back to the addresses index`, async() => {
|
it(`should navigate back to the addresses index`, async() => {
|
||||||
|
|
|
@ -34,6 +34,15 @@ describe('Claim edit basic data path', () => {
|
||||||
await page.waitForState('claim.card.detail');
|
await page.waitForState('claim.card.detail');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should check the "Pick up" checkbox', async() => {
|
||||||
|
await page.reloadSection('claim.card.basicData');
|
||||||
|
await page.waitToClick(selectors.claimBasicData.hasToPickUpCheckbox);
|
||||||
|
await page.waitToClick(selectors.claimBasicData.saveButton);
|
||||||
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
expect(message.type).toBe('success');
|
||||||
|
});
|
||||||
|
|
||||||
it('should confirm the claim state was edited', async() => {
|
it('should confirm the claim state was edited', async() => {
|
||||||
await page.reloadSection('claim.card.basicData');
|
await page.reloadSection('claim.card.basicData');
|
||||||
await page.wait(selectors.claimBasicData.claimState);
|
await page.wait(selectors.claimBasicData.claimState);
|
||||||
|
@ -42,6 +51,12 @@ describe('Claim edit basic data path', () => {
|
||||||
expect(result).toEqual('Gestionado');
|
expect(result).toEqual('Gestionado');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should confirm the "is paid with mana" and "Pick up" checkbox are checked', async() => {
|
||||||
|
const hasToPickUpCheckbox = await page.checkboxState(selectors.claimBasicData.hasToPickUpCheckbox);
|
||||||
|
|
||||||
|
expect(hasToPickUpCheckbox).toBe('checked');
|
||||||
|
});
|
||||||
|
|
||||||
it('should confirm the claim observation was edited', async() => {
|
it('should confirm the claim observation was edited', async() => {
|
||||||
const result = await page
|
const result = await page
|
||||||
.waitToGetProperty(selectors.claimBasicData.observation, 'value');
|
.waitToGetProperty(selectors.claimBasicData.observation, 'value');
|
||||||
|
|
|
@ -72,19 +72,10 @@ describe('Claim action path', () => {
|
||||||
expect(message.type).toBe('success');
|
expect(message.type).toBe('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check the "Pick up" checkbox', async() => {
|
it('should confirm the "is paid with mana" is checked', async() => {
|
||||||
await page.waitToClick(selectors.claimAction.hasToPickUpCheckbox);
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.type).toBe('success');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should confirm the "is paid with mana" and "Pick up" checkbox are checked', async() => {
|
|
||||||
await page.reloadSection('claim.card.action');
|
await page.reloadSection('claim.card.action');
|
||||||
const isPaidWithManaCheckbox = await page.checkboxState(selectors.claimAction.isPaidWithManaCheckbox);
|
const isPaidWithManaCheckbox = await page.checkboxState(selectors.claimAction.isPaidWithManaCheckbox);
|
||||||
const hasToPickUpCheckbox = await page.checkboxState(selectors.claimAction.hasToPickUpCheckbox);
|
|
||||||
|
|
||||||
expect(isPaidWithManaCheckbox).toBe('checked');
|
expect(isPaidWithManaCheckbox).toBe('checked');
|
||||||
expect(hasToPickUpCheckbox).toBe('checked');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="icons pre">
|
<div class="icons pre">
|
||||||
<vn-icon
|
<vn-icon ng-show="::$ctrl.clearDisabled != true"
|
||||||
icon="clear"
|
icon="clear"
|
||||||
translate-attr="{title: 'Clear'}"
|
translate-attr="{title: 'Clear'}"
|
||||||
ng-click="$ctrl.onClear($event)">
|
ng-click="$ctrl.onClear($event)">
|
||||||
|
|
|
@ -203,6 +203,7 @@ ngModule.vnComponent('vnField', {
|
||||||
type: '@?',
|
type: '@?',
|
||||||
autocomplete: '@?',
|
autocomplete: '@?',
|
||||||
placeholder: '@?',
|
placeholder: '@?',
|
||||||
|
clearDisabled: '<?',
|
||||||
value: '=?',
|
value: '=?',
|
||||||
info: '@?',
|
info: '@?',
|
||||||
required: '<?',
|
required: '<?',
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
describe('Component vnField', () => {
|
||||||
|
let $element;
|
||||||
|
let controller;
|
||||||
|
|
||||||
|
beforeEach(ngModule('vnCore'));
|
||||||
|
|
||||||
|
beforeEach(inject(($compile, $rootScope) => {
|
||||||
|
$element = $compile(`<vn-textfield></vn-textfield>`)($rootScope);
|
||||||
|
controller = $element.controller('vnTextfield');
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
$element.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove this block
|
||||||
|
describe('clearDisabled binding', () => {
|
||||||
|
it(`should enable the show property`, () => {
|
||||||
|
controller.clearDisabled = true;
|
||||||
|
|
||||||
|
expect(controller.clearDisabled).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -78,7 +78,7 @@ export default class Searchbar extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchStateFilter(autoLoad) {
|
fetchStateFilter(autoLoad) {
|
||||||
let filter = null;
|
let filter = this.filter ? this.filter : null;
|
||||||
|
|
||||||
if (this.$state.is(this.searchState)) {
|
if (this.$state.is(this.searchState)) {
|
||||||
if (this.$params.q) {
|
if (this.$params.q) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import Field from '../field';
|
import Field from '../field';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
export default class Textarea extends Field {
|
export default class Textarea extends Field {
|
||||||
constructor($element, $scope, $compile) {
|
constructor($element, $scope, $compile) {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
.vn-textarea {
|
||||||
|
& > .container {
|
||||||
|
& > .icons {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,23 +12,34 @@ module.exports = function(Self) {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.observe('before save', async function(ctx) {
|
Self.observe('before save', async function(ctx) {
|
||||||
let options = {};
|
const appModels = ctx.Model.app.models;
|
||||||
|
const definition = ctx.Model.definition;
|
||||||
|
const options = {};
|
||||||
|
|
||||||
|
// Check for transactions
|
||||||
if (ctx.options && ctx.options.transaction)
|
if (ctx.options && ctx.options.transaction)
|
||||||
options.transaction = ctx.options.transaction;
|
options.transaction = ctx.options.transaction;
|
||||||
|
|
||||||
let oldInstance;
|
let oldInstance;
|
||||||
let oldInstanceFk;
|
|
||||||
let newInstance;
|
let newInstance;
|
||||||
|
|
||||||
if (ctx.data) {
|
if (ctx.data) {
|
||||||
oldInstanceFk = pick(ctx.currentInstance, Object.keys(ctx.data));
|
const changes = pick(ctx.currentInstance, Object.keys(ctx.data));
|
||||||
newInstance = await fkToValue(ctx.data, ctx);
|
newInstance = await fkToValue(ctx.data, ctx);
|
||||||
oldInstance = await fkToValue(oldInstanceFk, ctx);
|
oldInstance = await fkToValue(changes, ctx);
|
||||||
|
|
||||||
if (ctx.where && !ctx.currentInstance) {
|
if (ctx.where && !ctx.currentInstance) {
|
||||||
let fields = Object.keys(ctx.data);
|
const fields = Object.keys(ctx.data);
|
||||||
ctx.oldInstances = await ctx.Model.app.models[ctx.Model.definition.name].find({where: ctx.where, fields: fields}, options);
|
const modelName = definition.name;
|
||||||
|
|
||||||
|
ctx.oldInstances = await appModels[modelName].find({
|
||||||
|
where: ctx.where,
|
||||||
|
fields: fields
|
||||||
|
}, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get changes from created instance
|
||||||
if (ctx.isNewInstance)
|
if (ctx.isNewInstance)
|
||||||
newInstance = await fkToValue(ctx.instance.__data, ctx);
|
newInstance = await fkToValue(ctx.instance.__data, ctx);
|
||||||
|
|
||||||
|
@ -37,18 +48,24 @@ module.exports = function(Self) {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.observe('before delete', async function(ctx) {
|
Self.observe('before delete', async function(ctx) {
|
||||||
|
const appModels = ctx.Model.app.models;
|
||||||
|
const definition = ctx.Model.definition;
|
||||||
|
const relations = ctx.Model.relations;
|
||||||
|
|
||||||
let options = {};
|
let options = {};
|
||||||
if (ctx.options && ctx.options.transaction)
|
if (ctx.options && ctx.options.transaction)
|
||||||
options.transaction = ctx.options.transaction;
|
options.transaction = ctx.options.transaction;
|
||||||
|
|
||||||
if (ctx.where) {
|
if (ctx.where) {
|
||||||
let affectedModel = ctx.Model.definition.name;
|
let affectedModel = definition.name;
|
||||||
let definition = ctx.Model.definition;
|
let deletedInstances = await appModels[affectedModel].find({
|
||||||
let deletedInstances = await ctx.Model.app.models[affectedModel].find({where: ctx.where}, options);
|
where: ctx.where
|
||||||
|
}, options);
|
||||||
|
|
||||||
let relation = definition.settings.log.relation;
|
let relation = definition.settings.log.relation;
|
||||||
|
|
||||||
if (relation) {
|
if (relation) {
|
||||||
let primaryKey = ctx.Model.relations[relation].keyFrom;
|
let primaryKey = relations[relation].keyFrom;
|
||||||
|
|
||||||
let arrangedDeletedInstances = [];
|
let arrangedDeletedInstances = [];
|
||||||
for (let i = 0; i < deletedInstances.length; i++) {
|
for (let i = 0; i < deletedInstances.length; i++) {
|
||||||
|
@ -69,6 +86,8 @@ module.exports = function(Self) {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function logDeletedInstances(ctx, loopBackContext) {
|
async function logDeletedInstances(ctx, loopBackContext) {
|
||||||
|
const appModels = ctx.Model.app.models;
|
||||||
|
const definition = ctx.Model.definition;
|
||||||
let options = {};
|
let options = {};
|
||||||
if (ctx.options && ctx.options.transaction)
|
if (ctx.options && ctx.options.transaction)
|
||||||
options.transaction = ctx.options.transaction;
|
options.transaction = ctx.options.transaction;
|
||||||
|
@ -78,14 +97,12 @@ module.exports = function(Self) {
|
||||||
if (loopBackContext)
|
if (loopBackContext)
|
||||||
userFk = loopBackContext.active.accessToken.userId;
|
userFk = loopBackContext.active.accessToken.userId;
|
||||||
|
|
||||||
let definition = ctx.Model.definition;
|
|
||||||
|
|
||||||
let changedModelValue = definition.settings.log.changedModelValue;
|
let changedModelValue = definition.settings.log.changedModelValue;
|
||||||
let logRecord = {
|
let logRecord = {
|
||||||
originFk: instance.originFk,
|
originFk: instance.originFk,
|
||||||
userFk: userFk,
|
userFk: userFk,
|
||||||
action: 'delete',
|
action: 'delete',
|
||||||
changedModel: ctx.Model.definition.name,
|
changedModel: definition.name,
|
||||||
changedModelId: instance.id,
|
changedModelId: instance.id,
|
||||||
changedModelValue: instance[changedModelValue],
|
changedModelValue: instance[changedModelValue],
|
||||||
oldInstance: instance,
|
oldInstance: instance,
|
||||||
|
@ -95,26 +112,44 @@ module.exports = function(Self) {
|
||||||
delete instance.originFk;
|
delete instance.originFk;
|
||||||
|
|
||||||
let logModel = definition.settings.log.model;
|
let logModel = definition.settings.log.model;
|
||||||
await ctx.Model.app.models[logModel].create(logRecord, options);
|
await appModels[logModel].create(logRecord, options);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get log values from a foreign key
|
||||||
async function fkToValue(instance, ctx) {
|
async function fkToValue(instance, ctx) {
|
||||||
|
const appModels = ctx.Model.app.models;
|
||||||
|
const relations = ctx.Model.relations;
|
||||||
let options = {};
|
let options = {};
|
||||||
|
|
||||||
|
// Check for transactions
|
||||||
if (ctx.options && ctx.options.transaction)
|
if (ctx.options && ctx.options.transaction)
|
||||||
options.transaction = ctx.options.transaction;
|
options.transaction = ctx.options.transaction;
|
||||||
|
|
||||||
let cleanInstance = JSON.parse(JSON.stringify(instance));
|
const instanceCopy = JSON.parse(JSON.stringify(instance));
|
||||||
let result = {};
|
const result = {};
|
||||||
for (let key in cleanInstance) {
|
for (const key in instanceCopy) {
|
||||||
let val = cleanInstance[key];
|
let value = instanceCopy[key];
|
||||||
if (val === undefined || val === null) continue;
|
|
||||||
for (let key1 in ctx.Model.relations) {
|
if (value instanceof Object)
|
||||||
let val1 = ctx.Model.relations[key1];
|
continue;
|
||||||
if (val1.keyFrom == key && key != 'id') {
|
|
||||||
let recordSet = await ctx.Model.app.models[val1.modelTo.modelName].findById(val, null, options);
|
if (value === undefined || value === null) continue;
|
||||||
|
|
||||||
|
for (let relationName in relations) {
|
||||||
|
const relation = relations[relationName];
|
||||||
|
if (relation.keyFrom == key && key != 'id') {
|
||||||
|
const model = relation.modelTo;
|
||||||
|
const modelName = relation.modelTo.modelName;
|
||||||
|
const properties = model && model.definition.properties;
|
||||||
|
const settings = model && model.definition.settings;
|
||||||
|
|
||||||
|
const recordSet = await appModels[modelName].findById(value, null, options);
|
||||||
|
|
||||||
|
const hasShowField = settings.log && settings.log.showField;
|
||||||
|
let showField = hasShowField && recordSet
|
||||||
|
&& recordSet[settings.log.showField];
|
||||||
|
|
||||||
let showField = val1.modelTo && val1.modelTo.definition.settings.log && val1.modelTo.definition.settings.log.showField && recordSet && recordSet[val1.modelTo.definition.settings.log.showField];
|
|
||||||
if (!showField) {
|
if (!showField) {
|
||||||
const showFieldNames = [
|
const showFieldNames = [
|
||||||
'name',
|
'name',
|
||||||
|
@ -122,7 +157,10 @@ module.exports = function(Self) {
|
||||||
'code'
|
'code'
|
||||||
];
|
];
|
||||||
for (field of showFieldNames) {
|
for (field of showFieldNames) {
|
||||||
if (val1.modelTo.definition.properties && val1.modelTo.definition.properties[field] && recordSet && recordSet[field]) {
|
const propField = properties && properties[field];
|
||||||
|
const recordField = recordSet && recordSet[field];
|
||||||
|
|
||||||
|
if (propField && recordField) {
|
||||||
showField = field;
|
showField = field;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -130,25 +168,29 @@ module.exports = function(Self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showField && recordSet && recordSet[showField]) {
|
if (showField && recordSet && recordSet[showField]) {
|
||||||
val = recordSet[showField];
|
value = recordSet[showField];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
val = recordSet && recordSet.id || val;
|
value = recordSet && recordSet.id || value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result[key] = val;
|
result[key] = value;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logInModel(ctx, loopBackContext) {
|
async function logInModel(ctx, loopBackContext) {
|
||||||
let options = {};
|
const appModels = ctx.Model.app.models;
|
||||||
|
const definition = ctx.Model.definition;
|
||||||
|
const defSettings = ctx.Model.definition.settings;
|
||||||
|
const relations = ctx.Model.relations;
|
||||||
|
|
||||||
|
const options = {};
|
||||||
if (ctx.options && ctx.options.transaction)
|
if (ctx.options && ctx.options.transaction)
|
||||||
options.transaction = ctx.options.transaction;
|
options.transaction = ctx.options.transaction;
|
||||||
|
|
||||||
let definition = ctx.Model.definition;
|
|
||||||
let primaryKey;
|
let primaryKey;
|
||||||
for (let property in definition.properties) {
|
for (let property in definition.properties) {
|
||||||
if (definition.properties[property].id) {
|
if (definition.properties[property].id) {
|
||||||
|
@ -163,11 +205,11 @@ module.exports = function(Self) {
|
||||||
// RELATIONS LOG
|
// RELATIONS LOG
|
||||||
let changedModelId;
|
let changedModelId;
|
||||||
|
|
||||||
if (ctx.instance && !definition.settings.log.relation) {
|
if (ctx.instance && !defSettings.log.relation) {
|
||||||
originId = ctx.instance.id;
|
originId = ctx.instance.id;
|
||||||
changedModelId = ctx.instance.id;
|
changedModelId = ctx.instance.id;
|
||||||
} else if (definition.settings.log.relation) {
|
} else if (defSettings.log.relation) {
|
||||||
primaryKey = ctx.Model.relations[definition.settings.log.relation].keyFrom;
|
primaryKey = relations[defSettings.log.relation].keyFrom;
|
||||||
|
|
||||||
if (ctx.where && ctx.where[primaryKey])
|
if (ctx.where && ctx.where[primaryKey])
|
||||||
originId = ctx.where[primaryKey];
|
originId = ctx.where[primaryKey];
|
||||||
|
@ -181,12 +223,16 @@ module.exports = function(Self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the changedModelValue to save and the instances changed in case its an updateAll
|
// Sets the changedModelValue to save and the instances changed in case its an updateAll
|
||||||
let showField = definition.settings.log.showField;
|
let showField = defSettings.log.showField;
|
||||||
let where;
|
let where;
|
||||||
if (showField && (!ctx.instance || !ctx.instance[showField]) && ctx.where) {
|
if (showField && (!ctx.instance || !ctx.instance[showField]) && ctx.where) {
|
||||||
changedModelId = [];
|
changedModelId = [];
|
||||||
where = [];
|
where = [];
|
||||||
let changedInstances = await ctx.Model.app.models[definition.name].find({where: ctx.where, fields: ['id', showField, primaryKey]}, options);
|
let changedInstances = await appModels[definition.name].find({
|
||||||
|
where: ctx.where,
|
||||||
|
fields: ['id', showField, primaryKey]
|
||||||
|
}, options);
|
||||||
|
|
||||||
changedInstances.forEach(element => {
|
changedInstances.forEach(element => {
|
||||||
where.push(element[showField]);
|
where.push(element[showField]);
|
||||||
changedModelId.push(element.id);
|
changedModelId.push(element.id);
|
||||||
|
@ -195,7 +241,6 @@ module.exports = function(Self) {
|
||||||
} else if (ctx.hookState.oldInstance)
|
} else if (ctx.hookState.oldInstance)
|
||||||
where = ctx.instance[showField];
|
where = ctx.instance[showField];
|
||||||
|
|
||||||
|
|
||||||
// Set oldInstance, newInstance, userFk and action
|
// Set oldInstance, newInstance, userFk and action
|
||||||
let oldInstance = {};
|
let oldInstance = {};
|
||||||
if (ctx.hookState.oldInstance)
|
if (ctx.hookState.oldInstance)
|
||||||
|
@ -211,14 +256,18 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
let action = setActionType(ctx);
|
let action = setActionType(ctx);
|
||||||
|
|
||||||
removeUnloggableProperties(definition, oldInstance);
|
removeUnloggable(definition, oldInstance);
|
||||||
removeUnloggableProperties(definition, newInstance);
|
removeUnloggable(definition, newInstance);
|
||||||
|
|
||||||
|
// Prevent log with no new changes
|
||||||
|
const hasNewChanges = Object.keys(newInstance).length;
|
||||||
|
if (!hasNewChanges) return;
|
||||||
|
|
||||||
let logRecord = {
|
let logRecord = {
|
||||||
originFk: originId,
|
originFk: originId,
|
||||||
userFk: userFk,
|
userFk: userFk,
|
||||||
action: action,
|
action: action,
|
||||||
changedModel: ctx.Model.definition.name,
|
changedModel: definition.name,
|
||||||
changedModelId: changedModelId, // Model property with an different data type will throw a NaN error
|
changedModelId: changedModelId, // Model property with an different data type will throw a NaN error
|
||||||
changedModelValue: where,
|
changedModelValue: where,
|
||||||
oldInstance: oldInstance,
|
oldInstance: oldInstance,
|
||||||
|
@ -226,9 +275,9 @@ module.exports = function(Self) {
|
||||||
};
|
};
|
||||||
|
|
||||||
let logsToSave = setLogsToSave(where, changedModelId, logRecord, ctx);
|
let logsToSave = setLogsToSave(where, changedModelId, logRecord, ctx);
|
||||||
let logModel = definition.settings.log.model;
|
let logModel = defSettings.log.model;
|
||||||
|
|
||||||
await ctx.Model.app.models[logModel].create(logsToSave, options);
|
await appModels[logModel].create(logsToSave, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -236,7 +285,7 @@ module.exports = function(Self) {
|
||||||
* @param {*} definition Model definition
|
* @param {*} definition Model definition
|
||||||
* @param {*} properties Modified object properties
|
* @param {*} properties Modified object properties
|
||||||
*/
|
*/
|
||||||
function removeUnloggableProperties(definition, properties) {
|
function removeUnloggable(definition, properties) {
|
||||||
const propList = Object.keys(properties);
|
const propList = Object.keys(properties);
|
||||||
const propDefs = new Map();
|
const propDefs = new Map();
|
||||||
|
|
||||||
|
|
|
@ -88,32 +88,11 @@ module.exports = Self => {
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
let claim = await Self.findById(claimFk, {
|
let claim = await Self.findById(claimFk, null, options);
|
||||||
include: {
|
|
||||||
relation: 'client',
|
|
||||||
scope: {
|
|
||||||
include: {
|
|
||||||
relation: 'salesPerson'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, options);
|
|
||||||
claim = await claim.updateAttributes({
|
claim = await claim.updateAttributes({
|
||||||
claimStateFk: resolvedState
|
claimStateFk: resolvedState
|
||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
// Get sales person from claim client
|
|
||||||
const salesPerson = claim.client().salesPerson();
|
|
||||||
if (salesPerson && claim.hasToPickUp) {
|
|
||||||
const origin = ctx.req.headers.origin;
|
|
||||||
const message = $t('Claim will be picked', {
|
|
||||||
claimId: claim.id,
|
|
||||||
clientName: claim.client().name,
|
|
||||||
claimUrl: `${origin}/#!/claim/${claim.id}/summary`
|
|
||||||
});
|
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
await tx.commit();
|
await tx.commit();
|
||||||
|
|
||||||
return claim;
|
return claim;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('regularizeClaim()', () => {
|
// #2304
|
||||||
|
xdescribe('regularizeClaim()', () => {
|
||||||
const claimFk = 1;
|
const claimFk = 1;
|
||||||
const pendentState = 1;
|
const pendentState = 1;
|
||||||
const resolvedState = 3;
|
const resolvedState = 3;
|
||||||
|
@ -103,11 +104,9 @@ describe('regularizeClaim()', () => {
|
||||||
claimEnd.updateAttributes({claimDestinationFk: okDestination});
|
claimEnd.updateAttributes({claimDestinationFk: okDestination});
|
||||||
});
|
});
|
||||||
|
|
||||||
const claim = await app.models.Claim.findById(claimFk);
|
|
||||||
await claim.updateAttribute('hasToPickUp', true);
|
|
||||||
await app.models.Claim.regularizeClaim(ctx, claimFk);
|
await app.models.Claim.regularizeClaim(ctx, claimFk);
|
||||||
|
|
||||||
expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Bueno');
|
expect(chatModel.sendCheckingPresence).toHaveBeenCalledWith(ctx, 18, 'Bueno');
|
||||||
expect(chatModel.sendCheckingPresence).toHaveBeenCalledTimes(5);
|
expect(chatModel.sendCheckingPresence).toHaveBeenCalledTimes(4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -54,6 +54,7 @@ describe('Update Claim', () => {
|
||||||
let data = {
|
let data = {
|
||||||
observation: 'valid observation',
|
observation: 'valid observation',
|
||||||
claimStateFk: correctState,
|
claimStateFk: correctState,
|
||||||
|
hasToPickUp: false
|
||||||
};
|
};
|
||||||
let ctx = {
|
let ctx = {
|
||||||
req: {
|
req: {
|
||||||
|
@ -70,19 +71,25 @@ describe('Update Claim', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should change some sensible fields as salesAssistant', async() => {
|
it('should change some sensible fields as salesAssistant', async() => {
|
||||||
|
const chatModel = app.models.Chat;
|
||||||
|
spyOn(chatModel, 'sendCheckingPresence').and.callThrough();
|
||||||
|
|
||||||
const salesAssistantId = 21;
|
const salesAssistantId = 21;
|
||||||
let data = {
|
let data = {
|
||||||
claimStateFk: 3,
|
claimStateFk: 3,
|
||||||
workerFk: 5,
|
workerFk: 5,
|
||||||
observation: 'another valid observation'
|
observation: 'another valid observation',
|
||||||
|
hasToPickUp: true
|
||||||
};
|
};
|
||||||
let ctx = {
|
const ctx = {
|
||||||
req: {
|
req: {
|
||||||
accessToken: {
|
accessToken: {userId: salesAssistantId},
|
||||||
userId: salesAssistantId
|
headers: {origin: 'http://localhost'}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
ctx.req.__ = (value, params) => {
|
||||||
|
return params.nickname;
|
||||||
|
};
|
||||||
await app.models.Claim.updateClaim(ctx, newInstance.id, data);
|
await app.models.Claim.updateClaim(ctx, newInstance.id, data);
|
||||||
|
|
||||||
let claimUpdated = await app.models.Claim.findById(newInstance.id);
|
let claimUpdated = await app.models.Claim.findById(newInstance.id);
|
||||||
|
@ -90,5 +97,6 @@ describe('Update Claim', () => {
|
||||||
expect(claimUpdated.observation).toEqual(data.observation);
|
expect(claimUpdated.observation).toEqual(data.observation);
|
||||||
expect(claimUpdated.claimStateFk).toEqual(data.claimStateFk);
|
expect(claimUpdated.claimStateFk).toEqual(data.claimStateFk);
|
||||||
expect(claimUpdated.workerFk).toEqual(data.workerFk);
|
expect(claimUpdated.workerFk).toEqual(data.workerFk);
|
||||||
|
expect(chatModel.sendCheckingPresence).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,16 +27,44 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.updateClaim = async(ctx, id, data) => {
|
Self.updateClaim = async(ctx, id, data) => {
|
||||||
let models = Self.app.models;
|
const models = Self.app.models;
|
||||||
let claim = await models.Claim.findById(id);
|
const userId = ctx.req.accessToken.userId;
|
||||||
|
|
||||||
let canUpdate = await canChangeState(ctx, claim.claimStateFk);
|
const $t = ctx.req.__; // $translate
|
||||||
let hasRights = await canChangeState(ctx, data.claimStateFk);
|
const claim = await models.Claim.findById(id, {
|
||||||
|
include: {
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
include: {
|
||||||
|
relation: 'salesPerson'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!canUpdate || !hasRights)
|
const canUpdate = await canChangeState(ctx, claim.claimStateFk);
|
||||||
|
const hasRights = await canChangeState(ctx, data.claimStateFk);
|
||||||
|
const isSalesAssistant = await models.Account.hasRole(userId, 'salesAssistant');
|
||||||
|
const changedHasToPickUp = claim.hasToPickUp != data.hasToPickUp;
|
||||||
|
|
||||||
|
if (!canUpdate || !hasRights || changedHasToPickUp && !isSalesAssistant)
|
||||||
throw new UserError(`You don't have enough privileges to change that field`);
|
throw new UserError(`You don't have enough privileges to change that field`);
|
||||||
|
|
||||||
return await claim.updateAttributes(data);
|
const updatedClaim = await claim.updateAttributes(data);
|
||||||
|
|
||||||
|
// Get sales person from claim client
|
||||||
|
const salesPerson = claim.client().salesPerson();
|
||||||
|
if (salesPerson && changedHasToPickUp && updatedClaim.hasToPickUp) {
|
||||||
|
const origin = ctx.req.headers.origin;
|
||||||
|
const message = $t('Claim will be picked', {
|
||||||
|
claimId: claim.id,
|
||||||
|
clientName: claim.client().name,
|
||||||
|
claimUrl: `${origin}/#!/claim/${claim.id}/summary`
|
||||||
|
});
|
||||||
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedClaim;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function canChangeState(ctx, id) {
|
async function canChangeState(ctx, id) {
|
||||||
|
|
|
@ -17,10 +17,6 @@ module.exports = Self => {
|
||||||
arg: 'isChargedToMana',
|
arg: 'isChargedToMana',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
required: false
|
required: false
|
||||||
}, {
|
|
||||||
arg: 'hasToPickUp',
|
|
||||||
type: 'boolean',
|
|
||||||
required: false
|
|
||||||
}],
|
}],
|
||||||
returns: {
|
returns: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
|
|
@ -43,11 +43,6 @@
|
||||||
on-change="$ctrl.save({responsibility: value})">
|
on-change="$ctrl.save({responsibility: value})">
|
||||||
</vn-range>
|
</vn-range>
|
||||||
</vn-tool-bar>
|
</vn-tool-bar>
|
||||||
<vn-check vn-one class="vn-mr-md"
|
|
||||||
label="Pick up"
|
|
||||||
ng-model="$ctrl.claim.hasToPickUp"
|
|
||||||
on-change="$ctrl.save({hasToPickUp: value})">
|
|
||||||
</vn-check>
|
|
||||||
<vn-check vn-one
|
<vn-check vn-one
|
||||||
label="Is paid with mana"
|
label="Is paid with mana"
|
||||||
ng-model="$ctrl.claim.isChargedToMana"
|
ng-model="$ctrl.claim.isChargedToMana"
|
||||||
|
|
|
@ -9,5 +9,4 @@ Regularize: Regularizar
|
||||||
Do you want to insert greuges?: Desea insertar greuges?
|
Do you want to insert greuges?: Desea insertar greuges?
|
||||||
Insert greuges on client card: Insertar greuges en la ficha del cliente
|
Insert greuges on client card: Insertar greuges en la ficha del cliente
|
||||||
Greuge inserted: Greuge insertado
|
Greuge inserted: Greuge insertado
|
||||||
ClaimGreugeDescription: Reclamación id {{claimId}}
|
ClaimGreugeDescription: Reclamación id {{claimId}}
|
||||||
Pick up: Recoger
|
|
|
@ -53,6 +53,13 @@
|
||||||
rule>
|
rule>
|
||||||
</vn-textarea>
|
</vn-textarea>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-check vn-one class="vn-mr-md"
|
||||||
|
label="Pick up"
|
||||||
|
ng-model="$ctrl.claim.hasToPickUp"
|
||||||
|
vn-acl="salesAssistant">
|
||||||
|
</vn-check>
|
||||||
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-button-bar>
|
<vn-button-bar>
|
||||||
<vn-submit label="Save"></vn-submit>
|
<vn-submit label="Save"></vn-submit>
|
||||||
|
|
|
@ -3,4 +3,5 @@ Claim state: Estado de la reclamación
|
||||||
Is paid with mana: Cargado al maná
|
Is paid with mana: Cargado al maná
|
||||||
Responsability: Responsabilidad
|
Responsability: Responsabilidad
|
||||||
Company: Empresa
|
Company: Empresa
|
||||||
Sales/Client: Comercial/Cliente
|
Sales/Client: Comercial/Cliente
|
||||||
|
Pick up: Recoger
|
|
@ -26,11 +26,12 @@ module.exports = Self => {
|
||||||
Self.lastActiveTickets = async(id, ticketId) => {
|
Self.lastActiveTickets = async(id, ticketId) => {
|
||||||
const ticket = await Self.app.models.Ticket.findById(ticketId);
|
const ticket = await Self.app.models.Ticket.findById(ticketId);
|
||||||
const query = `
|
const query = `
|
||||||
SELECT t.id, t.shipped, a.name AS agencyName, w.name AS warehouseName
|
SELECT t.id, t.shipped, a.name AS agencyName, w.name AS warehouseName, ad.city AS address
|
||||||
FROM vn.ticket t
|
FROM vn.ticket t
|
||||||
JOIN vn.ticketState ts ON t.id = ts.ticketFk
|
JOIN vn.ticketState ts ON t.id = ts.ticketFk
|
||||||
JOIN vn.agencyMode a ON t.agencyModeFk = a.id
|
JOIN vn.agencyMode a ON t.agencyModeFk = a.id
|
||||||
JOIN vn.warehouse w ON t.warehouseFk = w.id
|
JOIN vn.warehouse w ON t.warehouseFk = w.id
|
||||||
|
JOIN vn.address ad ON t.addressFk = ad.id
|
||||||
WHERE t.shipped >= CURDATE() AND t.clientFk = ? AND ts.alertLevel = 0
|
WHERE t.shipped >= CURDATE() AND t.clientFk = ? AND ts.alertLevel = 0
|
||||||
AND t.id <> ? AND t.warehouseFk = ?
|
AND t.id <> ? AND t.warehouseFk = ?
|
||||||
ORDER BY t.shipped
|
ORDER BY t.shipped
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('client sendSms()', () => {
|
// #2294 - TLS version error
|
||||||
|
xdescribe('client sendSms()', () => {
|
||||||
let createdLog;
|
let createdLog;
|
||||||
|
|
||||||
afterAll(async done => {
|
afterAll(async done => {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
const soap = require('soap');
|
const soap = require('soap');
|
||||||
|
|
||||||
describe('sms send()', () => {
|
// #2294 - TLS version error
|
||||||
|
xdescribe('sms send()', () => {
|
||||||
it('should return the expected message and status code', async() => {
|
it('should return the expected message and status code', async() => {
|
||||||
const code = 200;
|
const code = 200;
|
||||||
const smsConfig = await app.models.SmsConfig.findOne();
|
const smsConfig = await app.models.SmsConfig.findOne();
|
||||||
|
|
|
@ -29,7 +29,7 @@ export default class Controller extends Section {
|
||||||
|
|
||||||
onCustomAgentAccept() {
|
onCustomAgentAccept() {
|
||||||
return this.$http.post(`CustomsAgents`, this.newCustomsAgent)
|
return this.$http.post(`CustomsAgents`, this.newCustomsAgent)
|
||||||
.then(res => this.address.customsAgentFk = res.data.id);
|
.then(res => this.address.customsAgentId = res.data.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
get town() {
|
get town() {
|
||||||
|
|
|
@ -123,7 +123,7 @@ describe('Client', () => {
|
||||||
controller.onCustomAgentAccept();
|
controller.onCustomAgentAccept();
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
expect(controller.address.customsAgentFk).toEqual(1);
|
expect(controller.address.customsAgentId).toEqual(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -64,7 +64,7 @@ describe('Client', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('onCustomAgentAccept()', () => {
|
describe('onCustomAgentAccept()', () => {
|
||||||
it(`should create a new customs agent and then set the customsAgentFk property on the address`, () => {
|
it(`should now create a new customs agent and then set the customsAgentFk property on the address`, () => {
|
||||||
const expectedResult = {id: 1, fiscalName: 'Customs agent one'};
|
const expectedResult = {id: 1, fiscalName: 'Customs agent one'};
|
||||||
$httpBackend.when('POST', 'CustomsAgents').respond(200, expectedResult);
|
$httpBackend.when('POST', 'CustomsAgents').respond(200, expectedResult);
|
||||||
controller.onCustomAgentAccept();
|
controller.onCustomAgentAccept();
|
||||||
|
|
|
@ -119,7 +119,7 @@
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-check
|
<vn-check
|
||||||
vn-one
|
vn-one
|
||||||
label="Invoice by mail"
|
label="Notify by email"
|
||||||
ng-model="$ctrl.client.isToBeMailed">
|
ng-model="$ctrl.client.isToBeMailed">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<vn-check
|
<vn-check
|
||||||
|
|
|
@ -4,7 +4,7 @@ Client: Cliente
|
||||||
client: cliente
|
client: cliente
|
||||||
Comercial Name: Comercial
|
Comercial Name: Comercial
|
||||||
Has to invoice: Factura
|
Has to invoice: Factura
|
||||||
Invoice by mail: Factura via e-mail
|
Notify by email: Notificar vía e-mail
|
||||||
Country: País
|
Country: País
|
||||||
Street: Domicilio fiscal
|
Street: Domicilio fiscal
|
||||||
City: Municipio
|
City: Municipio
|
||||||
|
|
|
@ -81,7 +81,7 @@
|
||||||
disabled="true">
|
disabled="true">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Invoice by mail"
|
label="Notify by email"
|
||||||
ng-model="$ctrl.summary.isToBeMailed"
|
ng-model="$ctrl.summary.isToBeMailed"
|
||||||
disabled="true">
|
disabled="true">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
|
|
|
@ -8,6 +8,12 @@
|
||||||
translate>
|
translate>
|
||||||
Regularize stock
|
Regularize stock
|
||||||
</vn-item>
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="clone.show()"
|
||||||
|
name="cloneItem"
|
||||||
|
translate>
|
||||||
|
Clone
|
||||||
|
</vn-item>
|
||||||
</slot-menu>
|
</slot-menu>
|
||||||
<slot-before>
|
<slot-before>
|
||||||
<div style="position: relative" text-center>
|
<div style="position: relative" text-center>
|
||||||
|
@ -83,4 +89,10 @@
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
<button response="accept" translate>Save</button>
|
<button response="accept" translate>Save</button>
|
||||||
</tpl-buttons>
|
</tpl-buttons>
|
||||||
</vn-dialog>
|
</vn-dialog>
|
||||||
|
<vn-confirm
|
||||||
|
vn-id="clone"
|
||||||
|
on-accept="$ctrl.onCloneAccept()"
|
||||||
|
question="Do you want to clone this item?"
|
||||||
|
message="All it's properties will be copied">
|
||||||
|
</vn-confirm>
|
||||||
|
|
|
@ -60,6 +60,11 @@ class Controller extends Descriptor {
|
||||||
this.warehouseFk = null;
|
this.warehouseFk = null;
|
||||||
this.quantity = null;
|
this.quantity = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCloneAccept() {
|
||||||
|
this.$http.post(`Items/${this.item.id}/clone`)
|
||||||
|
.then(res => this.$state.go('item.card.tags', {id: res.data.id}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnItemDescriptor', {
|
ngModule.vnComponent('vnItemDescriptor', {
|
||||||
|
|
|
@ -48,9 +48,8 @@
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
<span class="chip"
|
<span ng-class="::{link: sale.isTicket}"
|
||||||
ng-class="::{link: sale.isTicket}"
|
ng-click="$ctrl.showTicketDescriptor($event, sale)"
|
||||||
vn-click-stop="descriptor.show($event, sale.origin)"
|
|
||||||
name="origin">
|
name="origin">
|
||||||
{{::sale.origin | dashIfEmpty}}
|
{{::sale.origin | dashIfEmpty}}
|
||||||
</span>
|
</span>
|
||||||
|
@ -83,7 +82,7 @@
|
||||||
</vn-card>
|
</vn-card>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
<vn-ticket-descriptor-popover
|
<vn-ticket-descriptor-popover
|
||||||
vn-id="descriptor">
|
vn-id="ticket-descriptor">
|
||||||
</vn-ticket-descriptor-popover>
|
</vn-ticket-descriptor-popover>
|
||||||
<vn-client-descriptor-popover
|
<vn-client-descriptor-popover
|
||||||
vn-id="clientDescriptor">
|
vn-id="clientDescriptor">
|
||||||
|
|
|
@ -57,6 +57,12 @@ class Controller extends Section {
|
||||||
this.$location.hash(hash);
|
this.$location.hash(hash);
|
||||||
this.$anchorScroll();
|
this.$anchorScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showTicketDescriptor(event, sale) {
|
||||||
|
if (!sale.isTicket) return;
|
||||||
|
|
||||||
|
this.$.ticketDescriptor.show(event.target, sale.origin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$scope', '$anchorScroll', '$location'];
|
Controller.$inject = ['$element', '$scope', '$anchorScroll', '$location'];
|
||||||
|
|
|
@ -51,8 +51,11 @@
|
||||||
</vn-date-picker>
|
</vn-date-picker>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-one>
|
<vn-check vn-one
|
||||||
</vn-one>
|
triple-state="true"
|
||||||
|
label="For me"
|
||||||
|
ng-model="filter.mine">
|
||||||
|
</vn-check>
|
||||||
<vn-check
|
<vn-check
|
||||||
vn-one
|
vn-one
|
||||||
triple-state="true"
|
triple-state="true"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
Ink: Tinta
|
Ink: Tinta
|
||||||
Origin: Origen
|
Origin: Origen
|
||||||
Producer: Productor
|
Producer: Productor
|
||||||
|
For me: Para mi
|
|
@ -1,28 +1,30 @@
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
url="TicketRequests/filter"
|
url="TicketRequests/filter"
|
||||||
|
user-params="::$ctrl.filterParams"
|
||||||
limit="20"
|
limit="20"
|
||||||
data="requests"
|
data="requests"
|
||||||
order="shipped DESC, isOk ASC"
|
order="shippedDate ASC, isOk ASC"
|
||||||
auto-load="true">
|
auto-load="true">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-portal slot="topbar">
|
<vn-portal slot="topbar">
|
||||||
<vn-searchbar
|
<vn-searchbar
|
||||||
panel="vn-request-search-panel"
|
panel="vn-request-search-panel"
|
||||||
suggested-filter="$ctrl.filter.where"
|
suggested-filter="$ctrl.filterParams"
|
||||||
info="Search request by id or alias"
|
info="Search request by id or alias"
|
||||||
|
filter="$ctrl.filterParams"
|
||||||
model="model"
|
model="model"
|
||||||
auto-state="false">
|
auto-state="false">
|
||||||
</vn-searchbar>
|
</vn-searchbar>
|
||||||
</vn-portal>
|
</vn-portal>
|
||||||
<vn-data-viewer model="model">
|
<vn-data-viewer model="model">
|
||||||
<vn-card>
|
<vn-card>
|
||||||
<vn-table model="model">
|
<vn-table model="model" auto-load="false">
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th field="ticketFk" number>Ticket ID</vn-th>
|
<vn-th field="ticketFk" number>Ticket ID</vn-th>
|
||||||
<vn-th field="shipped">Shipped</vn-th>
|
<vn-th field="shipped" expand>Shipped</vn-th>
|
||||||
<vn-th field="description">Description</vn-th>
|
<vn-th field="description" expand>Description</vn-th>
|
||||||
<vn-th field="quantity" number editable>Requested</vn-th>
|
<vn-th field="quantity" number editable>Requested</vn-th>
|
||||||
<vn-th field="price" number>Price</vn-th>
|
<vn-th field="price" number>Price</vn-th>
|
||||||
<vn-th field="atenderNickname">Atender</vn-th>
|
<vn-th field="atenderNickname">Atender</vn-th>
|
||||||
|
@ -40,7 +42,7 @@
|
||||||
{{request.ticketFk}}
|
{{request.ticketFk}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td>
|
<vn-td expand>
|
||||||
<span title="{{::request.shipped | date: 'dd/MM/yyyy'}}"
|
<span title="{{::request.shipped | date: 'dd/MM/yyyy'}}"
|
||||||
class="chip {{$ctrl.compareDate(request.shipped)}}">
|
class="chip {{$ctrl.compareDate(request.shipped)}}">
|
||||||
{{::request.shipped | date: 'dd/MM/yyyy'}}
|
{{::request.shipped | date: 'dd/MM/yyyy'}}
|
||||||
|
@ -53,7 +55,7 @@
|
||||||
<span
|
<span
|
||||||
class="link"
|
class="link"
|
||||||
ng-click="workerDescriptor.show($event, request.attenderFk)">
|
ng-click="workerDescriptor.show($event, request.attenderFk)">
|
||||||
{{::request.atenderNickname}}
|
{{::request.attenderName}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td-editable disabled="request.isOk != null" number>
|
<vn-td-editable disabled="request.isOk != null" number>
|
||||||
|
|
|
@ -8,19 +8,16 @@ export default class Controller extends Section {
|
||||||
|
|
||||||
if (!this.$state.q) {
|
if (!this.$state.q) {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
today.setHours(23, 59, 59, 59);
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
const lastWeek = new Date();
|
const nextWeek = new Date();
|
||||||
lastWeek.setHours(0, 0, 0, 0);
|
nextWeek.setHours(23, 59, 59, 59);
|
||||||
lastWeek.setDate(lastWeek.getDate() - 7);
|
nextWeek.setDate(nextWeek.getDate() + 7);
|
||||||
|
|
||||||
this.filter = {
|
this.filterParams = {
|
||||||
where: {
|
mine: true,
|
||||||
isOk: false,
|
from: today,
|
||||||
mine: true,
|
to: nextWeek
|
||||||
from: lastWeek,
|
|
||||||
to: today
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.confirm = async ctx => {
|
Self.confirm = async ctx => {
|
||||||
|
const userId = ctx.req.accessToken.userId;
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const tx = await Self.beginTransaction({});
|
const tx = await Self.beginTransaction({});
|
||||||
const $t = ctx.req.__; // $translate
|
const $t = ctx.req.__; // $translate
|
||||||
|
@ -87,6 +88,23 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, requesterId, message);
|
await models.Chat.sendCheckingPresence(ctx, requesterId, message);
|
||||||
|
|
||||||
|
// loguejar
|
||||||
|
let logRecord = {
|
||||||
|
originFk: sale.ticketFk,
|
||||||
|
userFk: userId,
|
||||||
|
action: 'update',
|
||||||
|
changedModel: 'ticketRequest',
|
||||||
|
newInstance: {
|
||||||
|
destinationFk: sale.ticketFk,
|
||||||
|
quantity: sale.quantity,
|
||||||
|
concept: sale.concept,
|
||||||
|
itemId: sale.itemFk,
|
||||||
|
ticketId: sale.ticketFk,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await Self.app.models.TicketLog.create(logRecord);
|
||||||
|
|
||||||
await tx.commit();
|
await tx.commit();
|
||||||
|
|
||||||
return sale;
|
return sale;
|
||||||
|
|
|
@ -115,12 +115,13 @@ module.exports = Self => {
|
||||||
s.itemFk,
|
s.itemFk,
|
||||||
i.name AS itemDescription,
|
i.name AS itemDescription,
|
||||||
t.shipped,
|
t.shipped,
|
||||||
|
DATE(t.shipped) AS shippedDate,
|
||||||
t.nickname,
|
t.nickname,
|
||||||
t.warehouseFk,
|
t.warehouseFk,
|
||||||
t.clientFk,
|
t.clientFk,
|
||||||
w.name AS warehouse,
|
w.name AS warehouse,
|
||||||
u.nickname AS salesPersonNickname,
|
u.nickname AS salesPersonNickname,
|
||||||
ua.nickname AS atenderNickname,
|
ua.name AS attenderName,
|
||||||
c.salesPersonFk
|
c.salesPersonFk
|
||||||
FROM ticketRequest tr
|
FROM ticketRequest tr
|
||||||
LEFT JOIN ticketWeekly tw on tw.ticketFk = tr.ticketFk
|
LEFT JOIN ticketWeekly tw on tw.ticketFk = tr.ticketFk
|
||||||
|
|
|
@ -83,7 +83,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ticket = await models.Ticket.findById(id, {
|
const ticket = await models.Ticket.findById(id, {
|
||||||
include: {
|
include: [{
|
||||||
relation: 'client',
|
relation: 'client',
|
||||||
scope: {
|
scope: {
|
||||||
fields: ['id', 'salesPersonFk'],
|
fields: ['id', 'salesPersonFk'],
|
||||||
|
@ -97,9 +97,27 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}, {
|
||||||
|
relation: 'ship'
|
||||||
|
}, {
|
||||||
|
relation: 'stowaway'
|
||||||
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Change state to "fixing" if contains an stowaway
|
||||||
|
let otherTicketId;
|
||||||
|
if (ticket.stowaway())
|
||||||
|
otherTicketId = ticket.stowaway().shipFk;
|
||||||
|
else if (ticket.ship())
|
||||||
|
otherTicketId = ticket.ship().id;
|
||||||
|
|
||||||
|
if (otherTicketId) {
|
||||||
|
await models.TicketTracking.changeState(ctx, {
|
||||||
|
ticketFk: otherTicketId,
|
||||||
|
code: 'FIXING'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Send notification to salesPerson
|
// Send notification to salesPerson
|
||||||
const salesPerson = ticket.client().salesPerson();
|
const salesPerson = ticket.client().salesPerson();
|
||||||
if (salesPerson) {
|
if (salesPerson) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
let UserError = require('vn-loopback/util/user-error');
|
let UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
|
||||||
describe('ticket new()', () => {
|
describe('ticket new()', () => {
|
||||||
let ticket;
|
let ticket;
|
||||||
let today = new Date();
|
let today = new Date();
|
||||||
|
@ -69,7 +68,7 @@ describe('ticket new()', () => {
|
||||||
clientId: 104,
|
clientId: 104,
|
||||||
shipped: today,
|
shipped: today,
|
||||||
landed: today,
|
landed: today,
|
||||||
warehouseId: 1,
|
warehouseId: 2,
|
||||||
companyId: 442,
|
companyId: 442,
|
||||||
addressId: 4,
|
addressId: 4,
|
||||||
agencyModeId: 1
|
agencyModeId: 1
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('ticket sendSms()', () => {
|
// #2294 - TLS version error
|
||||||
|
xdescribe('ticket sendSms()', () => {
|
||||||
let logId;
|
let logId;
|
||||||
|
|
||||||
afterAll(async done => {
|
afterAll(async done => {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
const models = app.models;
|
const models = app.models;
|
||||||
|
|
||||||
describe('ticket deleted()', () => {
|
// 2301 Failing tests
|
||||||
|
xdescribe('ticket deleted()', () => {
|
||||||
let ticket;
|
let ticket;
|
||||||
let sale;
|
let sale;
|
||||||
|
let deletedClaim;
|
||||||
|
|
||||||
beforeAll(async done => {
|
beforeAll(async done => {
|
||||||
let originalTicket = await models.Ticket.findOne({where: {id: 16}});
|
let originalTicket = await models.Ticket.findOne({where: {id: 16}});
|
||||||
|
@ -27,8 +29,36 @@ describe('ticket deleted()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async done => {
|
afterAll(async done => {
|
||||||
|
const ticketId = 16;
|
||||||
|
const stowawayTicketId = 17;
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 106},
|
||||||
|
headers: {
|
||||||
|
origin: 'http://localhost:5000'
|
||||||
|
},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
await models.Ticket.destroyById(ticket.id);
|
await models.Ticket.destroyById(ticket.id);
|
||||||
|
const stowaway = await models.Stowaway.findOne({
|
||||||
|
where: {
|
||||||
|
id: stowawayTicketId,
|
||||||
|
shipFk: ticketId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await stowaway.destroy();
|
||||||
|
await models.Claim.create(deletedClaim);
|
||||||
|
await models.TicketTracking.changeState(ctx, {
|
||||||
|
ticketFk: ticketId,
|
||||||
|
code: 'OK'
|
||||||
|
});
|
||||||
|
await models.TicketTracking.changeState(ctx, {
|
||||||
|
ticketFk: stowawayTicketId,
|
||||||
|
code: 'OK'
|
||||||
|
});
|
||||||
|
const orgTicket = await models.Ticket.findById(ticketId);
|
||||||
|
await orgTicket.updateAttribute('isDeleted', false);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -103,4 +133,35 @@ describe('ticket deleted()', () => {
|
||||||
expect(error.translateArgs[0]).toEqual(2);
|
expect(error.translateArgs[0]).toEqual(2);
|
||||||
expect(error.message).toEqual('You must delete the claim id %d first');
|
expect(error.message).toEqual('You must delete the claim id %d first');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should delete the ticket and change the state to "FIXING" to the stowaway ticket', async() => {
|
||||||
|
const ticketId = 16;
|
||||||
|
const claimIdToRemove = 2;
|
||||||
|
const stowawayTicketId = 17;
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 106},
|
||||||
|
headers: {
|
||||||
|
origin: 'http://localhost:5000'
|
||||||
|
},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await app.models.Stowaway.rawSql(`
|
||||||
|
INSERT INTO vn.stowaway(id, shipFk)
|
||||||
|
VALUES (?, ?)`, [stowawayTicketId, ticketId]);
|
||||||
|
|
||||||
|
deletedClaim = await app.models.Claim.findById(claimIdToRemove);
|
||||||
|
await app.models.Claim.destroyById(claimIdToRemove);
|
||||||
|
await app.models.Ticket.setDeleted(ctx, ticketId);
|
||||||
|
|
||||||
|
const stowawayTicket = await app.models.TicketState.findOne({
|
||||||
|
where: {
|
||||||
|
ticketFk: stowawayTicketId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(stowawayTicket.code).toEqual('FIXING');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
},
|
},
|
||||||
"alertLevel": {
|
"alertLevel": {
|
||||||
"type": "Number"
|
"type": "Number"
|
||||||
|
},
|
||||||
|
"code": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -42,6 +42,9 @@
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"type": "Number"
|
"type": "Number"
|
||||||
|
},
|
||||||
|
"zoneFk": {
|
||||||
|
"type": "Number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -306,9 +306,10 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th number>Id</th>
|
<th number>Id</th>
|
||||||
<th number>F. envio</th>
|
<th number>Shipped</th>
|
||||||
<th number>Agencia</th>
|
<th number>Agency</th>
|
||||||
<th number>Almacen</th>
|
<th number>Warehouse</th>
|
||||||
|
<th number>Address</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -323,6 +324,7 @@
|
||||||
<td number>{{::ticket.shipped | date: 'dd/MM/yyyy'}}</td>
|
<td number>{{::ticket.shipped | date: 'dd/MM/yyyy'}}</td>
|
||||||
<td number>{{::ticket.agencyName}}</td>
|
<td number>{{::ticket.agencyName}}</td>
|
||||||
<td number>{{::ticket.warehouseName}}</td>
|
<td number>{{::ticket.warehouseName}}</td>
|
||||||
|
<td number>{{::ticket.address}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -29,4 +29,8 @@ Product not available: "Verdnatura le comunica:\rPedido {{ticketFk}} día {{crea
|
||||||
Continue anyway?: ¿Continuar de todas formas?
|
Continue anyway?: ¿Continuar de todas formas?
|
||||||
This ticket is now empty: El ticket ha quedado vacio
|
This ticket is now empty: El ticket ha quedado vacio
|
||||||
Do you want to delete it?: ¿Quieres eliminarlo?
|
Do you want to delete it?: ¿Quieres eliminarlo?
|
||||||
Recalculate price: Recalcular precio
|
Recalculate price: Recalcular precio
|
||||||
|
Address: Dirección
|
||||||
|
Warehouse: Almacen
|
||||||
|
Agency: Agencia
|
||||||
|
Shipped: F. envio
|
|
@ -37,6 +37,11 @@ class Controller extends Section {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showInvoiceOutDescriptor(event, refFk) {
|
||||||
|
if (!refFk) return;
|
||||||
|
this.$.invoiceOutDescriptor.show(event.target, this.summary.invoiceOut.id);
|
||||||
|
}
|
||||||
|
|
||||||
setOkState() {
|
setOkState() {
|
||||||
let params = {};
|
let params = {};
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>{{entry.id}} </vn-td>
|
<vn-td shrink>{{entry.id}} </vn-td>
|
||||||
<vn-td shrink>{{entry.supplierName}}</vn-td>
|
<vn-td expand>{{entry.supplierName}}</vn-td>
|
||||||
<vn-td shrink>{{entry.ref}}</vn-td>
|
<vn-td shrink>{{entry.ref}}</vn-td>
|
||||||
<vn-td shrink>{{entry.hb}}</vn-td>
|
<vn-td shrink>{{entry.hb}}</vn-td>
|
||||||
<vn-td shrink>{{entry.freightValue | currency: 'EUR': 2}}</vn-td>
|
<vn-td shrink>{{entry.freightValue | currency: 'EUR': 2}}</vn-td>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
Date: Fecha
|
||||||
Model: Modelo
|
Model: Modelo
|
||||||
Action: Acción
|
Action: Acción
|
||||||
Author: Autor
|
Author: Autor
|
||||||
|
|
|
@ -62,7 +62,8 @@
|
||||||
"url" : "/log",
|
"url" : "/log",
|
||||||
"state": "worker.card.workerLog",
|
"state": "worker.card.workerLog",
|
||||||
"component": "vn-worker-log",
|
"component": "vn-worker-log",
|
||||||
"description": "Log"
|
"description": "Log",
|
||||||
|
"acl": ["hr"]
|
||||||
}, {
|
}, {
|
||||||
"url": "/pbx",
|
"url": "/pbx",
|
||||||
"state": "worker.card.pbx",
|
"state": "worker.card.pbx",
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('deleteZone', {
|
||||||
|
description: 'Delete a zone',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: {
|
||||||
|
arg: 'id',
|
||||||
|
type: 'Number',
|
||||||
|
description: 'The zone id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
returns: {
|
||||||
|
type: 'Object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/deleteZone`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deleteZone = async id => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const tx = await Self.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const filter = {where: {zoneFk: id}};
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
|
const ticketList = await models.Ticket.find(filter, options);
|
||||||
|
ticketList.forEach(ticket => {
|
||||||
|
promises.push(ticket.updateAttributes({zoneFk: null}, options));
|
||||||
|
});
|
||||||
|
await Promise.all(promises);
|
||||||
|
await models.Zone.destroyById(id, options);
|
||||||
|
await tx.commit();
|
||||||
|
|
||||||
|
return id;
|
||||||
|
} catch (err) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,39 @@
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
|
// 2302
|
||||||
|
describe('zone deletezone()', () => {
|
||||||
|
let zoneId = 9;
|
||||||
|
let originalZoneTickets;
|
||||||
|
let originalZone;
|
||||||
|
let originalZoneIncluded;
|
||||||
|
|
||||||
|
beforeAll(async done => {
|
||||||
|
originalZone = await app.models.Zone.findById(zoneId);
|
||||||
|
originalZoneTickets = await app.models.Ticket.find({where: {zoneFk: zoneId}});
|
||||||
|
originalZoneIncluded = await app.models.ZoneIncluded.find({where: {zoneFk: zoneId}});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async done => {
|
||||||
|
await originalZone.save();
|
||||||
|
|
||||||
|
originalZoneTickets.forEach(async ticket => {
|
||||||
|
await ticket.updateAttributes({zoneFk: zoneId});
|
||||||
|
});
|
||||||
|
originalZoneIncluded.forEach(async zoneIncluded => {
|
||||||
|
await zoneIncluded.save();
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete a zone and update their tickets', async() => {
|
||||||
|
await app.models.Zone.deleteZone(zoneId);
|
||||||
|
|
||||||
|
let updatedZone = await app.models.Zone.findById(zoneId);
|
||||||
|
let zoneUpdatedTicket = await app.models.Ticket.findById(originalZoneTickets[0].id);
|
||||||
|
|
||||||
|
expect(updatedZone).toBeNull();
|
||||||
|
expect(zoneUpdatedTicket.zoneFk).not.toBe(zoneId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@ module.exports = Self => {
|
||||||
require('../methods/zone/getEvents')(Self);
|
require('../methods/zone/getEvents')(Self);
|
||||||
require('../methods/zone/toggleIsIncluded')(Self);
|
require('../methods/zone/toggleIsIncluded')(Self);
|
||||||
require('../methods/zone/getUpcomingDeliveries')(Self);
|
require('../methods/zone/getUpcomingDeliveries')(Self);
|
||||||
|
require('../methods/zone/deleteZone')(Self);
|
||||||
|
|
||||||
Self.validatesPresenceOf('agencyModeFk', {
|
Self.validatesPresenceOf('agencyModeFk', {
|
||||||
message: `Agency cannot be blank`
|
message: `Agency cannot be blank`
|
||||||
|
|
|
@ -3,10 +3,16 @@
|
||||||
description="$ctrl.zone.name">
|
description="$ctrl.zone.name">
|
||||||
<slot-menu>
|
<slot-menu>
|
||||||
<vn-item class="vn-item"
|
<vn-item class="vn-item"
|
||||||
ng-click="deleteZone.show()"
|
ng-click="$ctrl.onDelete()"
|
||||||
translate>
|
translate>
|
||||||
Delete
|
Delete
|
||||||
</vn-item>
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="clone.show()"
|
||||||
|
name="cloneZone"
|
||||||
|
translate>
|
||||||
|
Clone
|
||||||
|
</vn-item>
|
||||||
</slot-menu>
|
</slot-menu>
|
||||||
<slot-body>
|
<slot-body>
|
||||||
<div class="attributes">
|
<div class="attributes">
|
||||||
|
@ -39,7 +45,12 @@
|
||||||
</vn-descriptor-content>
|
</vn-descriptor-content>
|
||||||
<vn-confirm
|
<vn-confirm
|
||||||
vn-id="deleteZone"
|
vn-id="deleteZone"
|
||||||
on-accept="$ctrl.onDeleteAccept()"
|
on-accept="$ctrl.deleteZone()"
|
||||||
question="Are you sure you want to delete this zone?"
|
|
||||||
message="This zone will be removed">
|
message="This zone will be removed">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
|
<vn-confirm
|
||||||
|
vn-id="clone"
|
||||||
|
on-accept="$ctrl.onCloneAccept()"
|
||||||
|
question="Do you want to clone this zone?"
|
||||||
|
message="All it's properties will be copied">
|
||||||
|
</vn-confirm>
|
|
@ -10,9 +10,33 @@ class Controller extends Descriptor {
|
||||||
this.entity = value;
|
this.entity = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
onDeleteAccept() {
|
onDelete() {
|
||||||
return this.$http.delete(`Zones/${this.id}`)
|
const $t = this.$translate.instant;
|
||||||
.then(() => this.$state.go('zone.index'));
|
const today = new Date();
|
||||||
|
today.setHours(0, 0, 0, 0);
|
||||||
|
const filter = {where: {zoneFk: this.id, shipped: {gte: today}}};
|
||||||
|
this.$http.get(`Tickets`, {filter}).then(res => {
|
||||||
|
const ticketsAmount = res.data.length;
|
||||||
|
if (ticketsAmount) {
|
||||||
|
const params = {ticketsAmount};
|
||||||
|
console.log('ticketsAmount', res.data);
|
||||||
|
const question = $t('This zone contains tickets', params, null, null, 'sanitizeParameters');
|
||||||
|
this.$.deleteZone.question = question;
|
||||||
|
this.$.deleteZone.show();
|
||||||
|
} else
|
||||||
|
this.deleteZone();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteZone() {
|
||||||
|
return this.$http.post(`Zones/${this.id}/deleteZone`).then(() => {
|
||||||
|
this.$state.go('zone.index');
|
||||||
|
this.vnApp.showSuccess(this.$t('Zone deleted'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onCloneAccept() {
|
||||||
|
return this.$http.post(`Zones/${this.id}/clone`).
|
||||||
|
then(res => this.$state.go('zone.card.basicData', {id: res.data.id}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
This zone contains tickets: Esta zona contiene {{ticketsAmount}} tickets. ¿Seguro que quieres eliminar esta zona?
|
||||||
|
Do you want to clone this zone?: ¿Quieres clonar esta zona?
|
||||||
|
All it's properties will be copied: Todas sus propiedades serán copiadas
|
||||||
|
Zone deleted: Zona eliminada
|
|
@ -11410,9 +11410,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"loopback-connector-mysql": {
|
"loopback-connector-mysql": {
|
||||||
"version": "5.4.2",
|
"version": "5.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/loopback-connector-mysql/-/loopback-connector-mysql-5.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/loopback-connector-mysql/-/loopback-connector-mysql-5.4.3.tgz",
|
||||||
"integrity": "sha512-f5iIIcJdfUuBUkScGcK7m4dLZnpjFjl1iFG5OHTk8pFwDq7+Xap/0H99ulueRp2ljfqbULTUvt3Rg1y/W5smtw==",
|
"integrity": "sha512-HQ0Nnscyhhk+4zsDhXyR8dYdkhxIBN8r8N1futX5xznWjCZ4dpkG5svoPOMUjoNaDEtZuLr1I2E4CKb6f5u9Mw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"async": "^2.6.1",
|
"async": "^2.6.1",
|
||||||
"debug": "^3.1.0",
|
"debug": "^3.1.0",
|
||||||
|
@ -12231,14 +12231,35 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"version": "2.17.1",
|
"version": "2.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
|
||||||
"integrity": "sha512-7vMqHQ673SAk5C8fOzTG2LpPcf3bNt0oL3sFpxPEEFp1mdlDcrLK0On7z8ZYKaaHrHwNcQ/MTUz7/oobZ2OyyA==",
|
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bignumber.js": "7.2.1",
|
"bignumber.js": "9.0.0",
|
||||||
"readable-stream": "2.3.6",
|
"readable-stream": "2.3.7",
|
||||||
"safe-buffer": "5.1.2",
|
"safe-buffer": "5.1.2",
|
||||||
"sqlstring": "2.3.1"
|
"sqlstring": "2.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bignumber.js": {
|
||||||
|
"version": "9.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
|
||||||
|
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "2.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
|
||||||
|
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
|
||||||
|
"requires": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mysql2": {
|
"mysql2": {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
"loopback-boot": "^2.27.1",
|
"loopback-boot": "^2.27.1",
|
||||||
"loopback-component-explorer": "^6.5.0",
|
"loopback-component-explorer": "^6.5.0",
|
||||||
"loopback-component-storage": "^3.6.1",
|
"loopback-component-storage": "^3.6.1",
|
||||||
"loopback-connector-mysql": "^5.4.2",
|
"loopback-connector-mysql": "^5.4.3",
|
||||||
"loopback-connector-remote": "^3.4.1",
|
"loopback-connector-remote": "^3.4.1",
|
||||||
"loopback-context": "^3.4.0",
|
"loopback-context": "^3.4.0",
|
||||||
"md5": "^2.2.1",
|
"md5": "^2.2.1",
|
||||||
|
|
|
@ -72,9 +72,10 @@ class Email extends Component {
|
||||||
await getAttachments(this.path, this.attachments);
|
await getAttachments(this.path, this.attachments);
|
||||||
|
|
||||||
const localeSubject = await this.getSubject();
|
const localeSubject = await this.getSubject();
|
||||||
|
const replyTo = this.args.replyTo || this.args.auth.email;
|
||||||
const options = {
|
const options = {
|
||||||
to: this.args.recipient,
|
to: this.args.recipient,
|
||||||
replyTo: this.args.auth.email,
|
replyTo: replyTo,
|
||||||
subject: localeSubject,
|
subject: localeSubject,
|
||||||
html: rendered,
|
html: rendered,
|
||||||
attachments: attachments
|
attachments: attachments
|
||||||
|
|
|
@ -4,34 +4,64 @@ const smtp = require('../core/smtp');
|
||||||
const config = require('../core/config');
|
const config = require('../core/config');
|
||||||
|
|
||||||
module.exports = app => {
|
module.exports = app => {
|
||||||
app.get('/api/closure', async function(request, response) {
|
app.get('/api/closure/by-ticket', async function(req, res) {
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/api/closure/all', async function(req, res) {
|
||||||
|
res.status(200).json({
|
||||||
|
message: 'Task executed successfully'
|
||||||
|
});
|
||||||
|
|
||||||
const failedtickets = [];
|
const failedtickets = [];
|
||||||
const tickets = await db.rawSql(`
|
const tickets = await db.rawSql(`
|
||||||
SELECT
|
SELECT
|
||||||
t.id,
|
t.id,
|
||||||
t.clientFk,
|
t.clientFk,
|
||||||
c.email recipient
|
c.email recipient,
|
||||||
|
c.isToBeMailed,
|
||||||
|
c.salesPersonFk,
|
||||||
|
eu.email salesPersonEmail
|
||||||
FROM expedition e
|
FROM expedition e
|
||||||
JOIN ticket t ON t.id = e.ticketFk
|
JOIN ticket t ON t.id = e.ticketFk
|
||||||
JOIN client c ON c.id = t.clientFk
|
JOIN client c ON c.id = t.clientFk
|
||||||
JOIN warehouse w ON w.id = t.warehouseFk AND hasComission
|
JOIN warehouse wh ON wh.id = t.warehouseFk AND wh.hasComission
|
||||||
LEFT JOIN ticketState ts ON ts.ticketFk = t.id
|
JOIN ticketState ts ON ts.ticketFk = t.id
|
||||||
WHERE ts.code = 'PACKED'
|
JOIN alertLevel al ON al.alertLevel = ts.alertLevel
|
||||||
|
LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
|
||||||
|
WHERE al.code = 'PACKED'
|
||||||
AND DATE(t.shipped) BETWEEN DATE_ADD(CURDATE(), INTERVAL -2 DAY) AND CURDATE()
|
AND DATE(t.shipped) BETWEEN DATE_ADD(CURDATE(), INTERVAL -2 DAY) AND CURDATE()
|
||||||
AND t.refFk IS NULL
|
AND t.refFk IS NULL
|
||||||
GROUP BY e.ticketFk`);
|
GROUP BY e.ticketFk`);
|
||||||
|
|
||||||
for (const ticket of tickets) {
|
for (const ticket of tickets) {
|
||||||
try {
|
try {
|
||||||
await db.rawSql(`CALL vn.ticketClosureTicket(:ticketId)`, {
|
await db.rawSql(`CALL vn.ticket_closeByTicket(:ticketId)`, {
|
||||||
ticketId: ticket.id
|
ticketId: ticket.id
|
||||||
});
|
});
|
||||||
|
|
||||||
const args = {
|
if (!ticket.salesPersonFk || !ticket.isToBeMailed) continue;
|
||||||
|
|
||||||
|
if (!ticket.recipient) {
|
||||||
|
const body = `No se ha podido enviar el albarán <strong>${ticket.id}</strong>
|
||||||
|
al cliente <strong>${ticket.clientFk}</strong> porque no tiene un email especificado.<br/><br/>
|
||||||
|
Para dejar de recibir esta notificación, asígnale un email o desactiva la notificación por email para este cliente.`;
|
||||||
|
smtp.send({
|
||||||
|
to: ticket.salesPersonEmail,
|
||||||
|
subject: 'No se ha podido enviar el albarán',
|
||||||
|
html: body
|
||||||
|
});
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reqArgs = req.args;
|
||||||
|
const args = Object.assign({
|
||||||
ticketId: ticket.id,
|
ticketId: ticket.id,
|
||||||
recipientId: ticket.clientFk,
|
recipientId: ticket.clientFk,
|
||||||
recipient: ticket.recipient
|
recipient: ticket.recipient,
|
||||||
};
|
replyTo: ticket.salesPersonEmail
|
||||||
|
}, reqArgs);
|
||||||
|
|
||||||
const email = new Email('delivery-note-link', args);
|
const email = new Email('delivery-note-link', args);
|
||||||
await email.send();
|
await email.send();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -45,7 +75,7 @@ module.exports = app => {
|
||||||
|
|
||||||
// Send email with failed tickets
|
// Send email with failed tickets
|
||||||
if (failedtickets.length > 0) {
|
if (failedtickets.length > 0) {
|
||||||
let body = 'This following tickets has failed:<br/><br/>';
|
let body = 'This following tickets have failed:<br/><br/>';
|
||||||
|
|
||||||
for (ticket of failedtickets) {
|
for (ticket of failedtickets) {
|
||||||
body += `Ticket: <strong>${ticket.id}</strong>
|
body += `Ticket: <strong>${ticket.id}</strong>
|
||||||
|
@ -54,13 +84,9 @@ module.exports = app => {
|
||||||
|
|
||||||
smtp.send({
|
smtp.send({
|
||||||
to: config.app.reportEmail,
|
to: config.app.reportEmail,
|
||||||
subject: '[API] Nightly ticket closure has failed',
|
subject: '[API] Nightly ticket closure report',
|
||||||
html: body
|
html: body
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
response.status(200).json({
|
|
||||||
message: 'Closure executed successfully'
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,6 @@ description: The delivery note from the order <strong>{0}</strong> is now availa
|
||||||
You can download it by clicking <a href="https://www.verdnatura.es/#!form=ecomerce/ticket&ticket={0}">this link</a>.
|
You can download it by clicking <a href="https://www.verdnatura.es/#!form=ecomerce/ticket&ticket={0}">this link</a>.
|
||||||
copyLink: 'As an alternative, you can copy the following link in your browser:'
|
copyLink: 'As an alternative, you can copy the following link in your browser:'
|
||||||
poll: If you wish, you can answer our satisfaction survey to
|
poll: If you wish, you can answer our satisfaction survey to
|
||||||
help us provide better service. Your opinion is very important for us!
|
help us provide better service. Your opinion is very important for us!
|
||||||
help: Any questions that arise, do not hesitate to consult it, <strong>we are here to assist you!</strong>
|
help: Any questions that arise, do not hesitate to consult it, <strong>we are here to assist you!</strong>
|
||||||
conclusion: Thanks for your attention!
|
conclusion: Thanks for your attention!
|
||||||
|
|
|
@ -4,6 +4,6 @@ dear: Dear client
|
||||||
description: The delivery note from the order <strong>{0}</strong> is now available. <br/>
|
description: The delivery note from the order <strong>{0}</strong> is now available. <br/>
|
||||||
You can download it by clicking on the attachment of this email.
|
You can download it by clicking on the attachment of this email.
|
||||||
poll: If you wish, you can answer our satisfaction survey to
|
poll: If you wish, you can answer our satisfaction survey to
|
||||||
help us provide better service. Your opinion is very important for us!
|
help us provide better service. Your opinion is very important for us!
|
||||||
help: Any questions that arise, do not hesitate to consult it, <strong>we are here to assist you!</strong>
|
help: Any questions that arise, do not hesitate to consult it, <strong>we are here to assist you!</strong>
|
||||||
conclusion: Thanks for your attention!
|
conclusion: Thanks for your attention!
|
||||||
|
|
Loading…
Reference in New Issue