Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 1798-e2e-extensions

This commit is contained in:
Carlos Jimenez Ruiz 2020-01-29 14:56:05 +01:00
commit 2a2ecdd438
20 changed files with 440 additions and 24 deletions

View File

@ -0,0 +1,5 @@
CREATE TABLE `vn`.`zoneClosure` (
`zoneFk` INT NOT NULL,
`dated` DATE NOT NULL,
`hour` TIME NOT NULL,
PRIMARY KEY (`zoneFk`, `dated`));

View File

@ -0,0 +1,50 @@
DROP procedure IF EXISTS vn.`zoneClosure_recalc`;
DELIMITER $$
CREATE DEFINER=`root`@`%` PROCEDURE vn.`zoneClosure_recalc`()
proc: BEGIN
/**
* Recalculates the delivery time (hour) for every zone in days + scope in future
*/
DECLARE vScope INT;
DECLARE vCounter INT DEFAULT 0;
DECLARE vShipped DATE DEFAULT CURDATE();
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
DO RELEASE_LOCK('vn.zoneClosure_recalc');
RESIGNAL;
END;
IF NOT GET_LOCK('vn.zoneClosure_recalc', 0) THEN
LEAVE proc;
END IF;
SELECT scope INTO vScope
FROM zoneConfig;
DROP TEMPORARY TABLE IF EXISTS tmp.zone;
CREATE TEMPORARY TABLE tmp.zone
(INDEX (id))
ENGINE = MEMORY
SELECT id FROM zone;
TRUNCATE TABLE zoneClosure;
REPEAT
CALL zone_getOptionsForShipment(vShipped);
INSERT INTO zoneClosure(zoneFk, dated, `hour`)
SELECT zoneFk, vShipped, `hour` FROM tmp.zoneOption;
SET vCounter = vCounter + 1;
SET vShipped = TIMESTAMPADD(DAY, 1, vShipped);
UNTIL vCounter > vScope
END REPEAT;
DROP TEMPORARY TABLE tmp.zone;
DO RELEASE_LOCK('vn.zoneClosure_recalc');
END$$
DELIMITER ;

View File

@ -0,0 +1,11 @@
CREATE TABLE `vn`.`zoneConfig` (
`id` INT UNSIGNED NOT NULL,
`scope` INT UNSIGNED NOT NULL,
PRIMARY KEY (`id`));
ALTER TABLE `vn`.`zoneConfig`
CHANGE COLUMN `id` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT ;
INSERT INTO `vn`.`zoneConfig` (`scope`) VALUES ('1');
INSERT INTO `bs`.`nightTask` (`order`, `schema`, `procedure`) VALUES ('100', 'vn', 'zoneClosure_recalc');

View File

@ -0,0 +1,240 @@
DROP procedure IF EXISTS `hedera`.`order_confirmWithUser`;
DELIMITER $$
CREATE DEFINER=`root`@`%` PROCEDURE `hedera`.`order_confirmWithUser`(IN `vOrder` INT, IN `vUserId` INT)
BEGIN
/**
* Confirms an order, creating each of its tickets on the corresponding
* date, store and user.
*
* @param vOrder The order identifier
* @param vUser The user identifier
*/
DECLARE vOk BOOL;
DECLARE vDone BOOL DEFAULT FALSE;
DECLARE vWarehouse INT;
DECLARE vShipment DATETIME;
DECLARE vTicket INT;
DECLARE vNotes VARCHAR(255);
DECLARE vItem INT;
DECLARE vConcept VARCHAR(30);
DECLARE vAmount INT;
DECLARE vPrice DECIMAL(10,2);
DECLARE vSale INT;
DECLARE vRate INT;
DECLARE vRowId INT;
DECLARE vDelivery DATE;
DECLARE vAddress INT;
DECLARE vIsConfirmed BOOL;
DECLARE vClientId INT;
DECLARE vCompanyId INT;
DECLARE vAgencyModeId INT;
DECLARE TICKET_FREE INT DEFAULT 2;
DECLARE cDates CURSOR FOR
SELECT zgs.shipped, r.warehouse_id
FROM `order` o
JOIN order_row r ON r.order_id = o.id
LEFT JOIN tmp.zoneGetShipped zgs ON zgs.warehouseFk = r.warehouse_id
WHERE o.id = vOrder AND r.amount != 0
GROUP BY r.warehouse_id;
DECLARE cRows CURSOR FOR
SELECT r.id, r.item_id, i.name, r.amount, r.price, r.rate
FROM order_row r
JOIN vn.item i ON i.id = r.item_id
WHERE r.amount != 0
AND r.warehouse_id = vWarehouse
AND r.order_id = vOrder
ORDER BY r.rate DESC;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET vDone = TRUE;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
-- Carga los datos del pedido
SELECT o.date_send, o.address_id, o.note,
o.confirmed, a.clientFk, o.company_id, o.agency_id
INTO vDelivery, vAddress, vNotes,
vIsConfirmed, vClientId, vCompanyId, vAgencyModeId
FROM hedera.`order` o
JOIN vn.address a ON a.id = o.address_id
WHERE o.id = vOrder;
-- Comprueba que el pedido no está confirmado
IF vIsConfirmed THEN
CALL util.throw ('ORDER_ALREADY_CONFIRMED');
END IF;
-- Comprueba que el pedido no está vacío
SELECT COUNT(*) > 0 INTO vOk
FROM order_row WHERE order_id = vOrder AND amount > 0;
IF !vOk THEN
CALL util.throw ('ORDER_EMPTY');
END IF;
-- Carga las fechas de salida de cada almacén
CALL vn.zone_getShippedWarehouse (vDelivery, vAddress, vAgencyModeId);
-- Trabajador que realiza la acción
IF vUserId IS NULL THEN
SELECT employeeFk INTO vUserId FROM orderConfig;
END IF;
-- Crea los tickets del pedido
START TRANSACTION;
OPEN cDates;
lDates:
LOOP
SET vTicket = NULL;
SET vDone = FALSE;
FETCH cDates INTO vShipment, vWarehouse;
IF vDone THEN
LEAVE lDates;
END IF;
-- Busca un ticket existente que coincida con los parametros
SELECT t.id INTO vTicket
FROM vn.ticket t
LEFT JOIN vn.ticketState tls on tls.ticket = t.id
JOIN `order` o
ON o.address_id = t.addressFk
AND vWarehouse = t.warehouseFk
AND o.agency_id = t.agencyModeFk
AND o.date_send = t.landed
AND vShipment = DATE(t.shipped)
WHERE o.id = vOrder
AND t.invoiceOutFk IS NULL
AND IFNULL(tls.alertLevel,0) = 0
AND t.clientFk <> 1118
LIMIT 1;
-- Crea el ticket en el caso de no existir uno adecuado
IF vTicket IS NULL
THEN
CALL vn.ticketCreateWithUser(
vClientId,
IFNULL(vShipment, CURDATE()),
vWarehouse,
vCompanyId,
vAddress,
vAgencyModeId,
NULL,
vDelivery,
vUserId,
vTicket
);
ELSE
INSERT INTO vncontrol.inter
SET Id_Ticket = vTicket,
Id_Trabajador = vUserId,
state_id = TICKET_FREE;
END IF;
INSERT IGNORE INTO vn.orderTicket
SET orderFk = vOrder,
ticketFk = vTicket;
-- Añade las notas
IF vNotes IS NOT NULL AND vNotes != ''
THEN
INSERT INTO vn.ticketObservation SET
ticketFk = vTicket,
observationTypeFk = 4 /* salesperson */ ,
`description` = vNotes
ON DUPLICATE KEY UPDATE
`description` = CONCAT(VALUES(`description`),'. ', `description`);
END IF;
-- Añade los movimientos y sus componentes
OPEN cRows;
lRows:
LOOP
SET vDone = FALSE;
FETCH cRows INTO vRowId, vItem, vConcept, vAmount, vPrice, vRate;
IF vDone THEN
LEAVE lRows;
END IF;
INSERT INTO vn.sale
SET
itemFk = vItem,
ticketFk = vTicket,
concept = vConcept,
quantity = vAmount,
price = vPrice,
priceFixed = 0,
isPriceFixed = TRUE;
SET vSale = LAST_INSERT_ID();
INSERT INTO vn.saleComponent
(saleFk, componentFk, `value`)
SELECT vSale, cm.component_id, cm.price
FROM order_component cm
JOIN vn.component c ON c.id = cm.component_id
WHERE cm.order_row_id = vRowId
GROUP BY vSale, cm.component_id;
UPDATE order_row SET Id_Movimiento = vSale
WHERE id = vRowId;
END LOOP;
CLOSE cRows;
-- Fija el coste
DROP TEMPORARY TABLE IF EXISTS tComponents;
CREATE TEMPORARY TABLE tComponents
(INDEX (saleFk))
ENGINE = MEMORY
SELECT SUM(sc.`value`) valueSum, sc.saleFk
FROM vn.saleComponent sc
JOIN vn.component c ON c.id = sc.componentFk
JOIN vn.componentType ct ON ct.id = c.typeFk AND ct.isBase
JOIN vn.sale s ON s.id = sc.saleFk
WHERE s.ticketFk = vTicket
GROUP BY sc.saleFk;
UPDATE vn.sale s
JOIN tComponents mc ON mc.saleFk = s.id
SET s.priceFixed = valueSum;
DROP TEMPORARY TABLE tComponents;
END LOOP;
CLOSE cDates;
DELETE FROM basketOrder WHERE orderFk = vOrder;
UPDATE `order` SET confirmed = TRUE, confirm_date = NOW()
WHERE id = vOrder;
COMMIT;
END$$
DELIMITER ;

View File

@ -487,7 +487,7 @@ export default {
priceInput: 'vn-ticket-request-create [ng-model="$ctrl.ticketRequest.price"]', priceInput: 'vn-ticket-request-create [ng-model="$ctrl.ticketRequest.price"]',
firstRemoveRequestButton: 'vn-ticket-request-index vn-icon[icon="delete"]:nth-child(1)', firstRemoveRequestButton: 'vn-ticket-request-index vn-icon[icon="delete"]:nth-child(1)',
saveButton: 'vn-ticket-request-create button[type=submit]', saveButton: 'vn-ticket-request-create button[type=submit]',
firstDescription: 'vn-ticket-request-index vn-table vn-tr:nth-child(1) > vn-td:nth-child(2)', firstDescription: 'vn-ticket-request-index vn-table vn-tr:nth-child(1) > vn-td:nth-child(2) vn-textfield',
}, },
ticketLog: { ticketLog: {

View File

@ -38,7 +38,7 @@ describe('Ticket purchase request path', () => {
it(`should confirm the new request was added`, async() => { it(`should confirm the new request was added`, async() => {
await page.reloadSection('ticket.card.request.index'); await page.reloadSection('ticket.card.request.index');
const result = await page.waitToGetProperty(selectors.ticketRequests.firstDescription, 'innerText'); const result = await page.waitToGetProperty(`${selectors.ticketRequests.firstDescription} input`, 'value');
expect(result).toEqual('New stuff'); expect(result).toEqual('New stuff');
}); });

View File

@ -1,8 +1,7 @@
import selectors from '../../helpers/selectors.js'; import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer'; import getBrowser from '../../helpers/puppeteer';
// #2050 Order.lines confirm error describe('Order lines', () => {
xdescribe('Order lines', () => {
let browser; let browser;
let page; let page;
beforeAll(async() => { beforeAll(async() => {

View File

@ -11,7 +11,7 @@
"Zone": { "Zone": {
"dataSource": "vn" "dataSource": "vn"
}, },
"ZoneGeo": { "ZoneClosure": {
"dataSource": "vn" "dataSource": "vn"
}, },
"ZoneEvent": { "ZoneEvent": {
@ -20,6 +20,9 @@
"ZoneExclusion": { "ZoneExclusion": {
"dataSource": "vn" "dataSource": "vn"
}, },
"ZoneGeo": {
"dataSource": "vn"
},
"ZoneIncluded": { "ZoneIncluded": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -0,0 +1,13 @@
module.exports = Self => {
Self.doRecalc = async function() {
try {
await Self.rawSql(`
CREATE EVENT zoneClosure_doRecalc
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 15 SECOND
DO CALL zoneClosure_recalc;
`);
} catch (err) {
if (err.code != 'ER_EVENT_ALREADY_EXISTS') throw err;
}
};
};

View File

@ -0,0 +1,30 @@
{
"name": "ZoneClosure",
"base": "VnModel",
"options": {
"mysql": {
"table": "zoneClosure"
}
},
"properties": {
"zoneFk": {
"id": true,
"type": "Number"
},
"dated": {
"type": "Date",
"required": true
},
"hour": {
"type": "date",
"required": true
}
},
"relations": {
"zone": {
"type": "belongsTo",
"model": "Zone",
"foreignKey": "zoneFk"
}
}
}

View File

@ -1,3 +1,5 @@
const app = require('vn-loopback/server/server');
module.exports = Self => { module.exports = Self => {
Self.validate('range', function(err) { Self.validate('range', function(err) {
if (this.type == 'range' if (this.type == 'range'
@ -32,4 +34,12 @@ module.exports = Self => {
}, { }, {
message: `You should mark at least one week day` message: `You should mark at least one week day`
}); });
Self.observe('after save', async function() {
await app.models.ZoneClosure.doRecalc();
});
Self.observe('after delete', async function() {
await app.models.ZoneClosure.doRecalc();
});
}; };

View File

@ -0,0 +1,11 @@
const app = require('vn-loopback/server/server');
module.exports = Self => {
Self.observe('after save', async function() {
await app.models.ZoneClosure.doRecalc();
});
Self.observe('after delete', async function() {
await app.models.ZoneClosure.doRecalc();
});
};

View File

@ -1,3 +1,5 @@
const app = require('vn-loopback/server/server');
module.exports = Self => { module.exports = Self => {
require('../methods/zone/clone')(Self); require('../methods/zone/clone')(Self);
require('../methods/zone/getLeaves')(Self); require('../methods/zone/getLeaves')(Self);
@ -7,4 +9,12 @@ module.exports = Self => {
Self.validatesPresenceOf('agencyModeFk', { Self.validatesPresenceOf('agencyModeFk', {
message: `Agency cannot be blank` message: `Agency cannot be blank`
}); });
Self.observe('after save', async function() {
await app.models.ZoneClosure.doRecalc();
});
Self.observe('after delete', async function() {
await app.models.ZoneClosure.doRecalc();
});
}; };

View File

@ -11,7 +11,7 @@
<vn-icon-button <vn-icon-button
icon="delete" icon="delete"
translate-attr="{title: 'Delete'}" translate-attr="{title: 'Delete'}"
ng-click="$ctrl.onDelete($index)"> ng-click="$ctrl.onDelete(row)">
</vn-icon-button> </vn-icon-button>
</vn-td> </vn-td>
</vn-tr> </vn-tr>
@ -49,5 +49,5 @@
vn-id="confirm" vn-id="confirm"
message="This item will be deleted" message="This item will be deleted"
question="Are you sure you want to continue?" question="Are you sure you want to continue?"
on-response="$ctrl.delete($response)"> on-accept="$ctrl.delete()">
</vn-confirm> </vn-confirm>

View File

@ -34,19 +34,19 @@ class Controller extends Component {
return false; return false;
} }
onDelete(index) { onDelete(row) {
this.$.confirm.show(); this.$.confirm.show();
this.deleteIndex = index; this.deleteRow = row;
} }
delete(response) { delete() {
if (response != 'accept') return; let row = this.deleteRow;
let id = this.$.data[this.deleteIndex].id; if (!row) return;
if (!id) return; return this.$http.delete(`${this.path}/${row.id}`)
this.$http.delete(`${this.path}/${id}`)
.then(() => { .then(() => {
this.$.data.splice(this.deleteIndex, 1); let index = this.$.data.indexOf(row);
this.deleteIndex = null; if (index !== -1) this.$.data.splice(index, 1);
this.deleteRow = null;
}); });
} }
} }

View File

@ -17,6 +17,7 @@
</vn-label-value> </vn-label-value>
<vn-label-value label="Mobile" <vn-label-value label="Mobile"
value="{{$ctrl.summary.mobile}}"> value="{{$ctrl.summary.mobile}}">
</vn-label-value>
<vn-label-value label="Email" ellipsize="false" <vn-label-value label="Email" ellipsize="false"
value="{{$ctrl.summary.email}}"> value="{{$ctrl.summary.email}}">
</vn-label-value> </vn-label-value>

View File

@ -3,7 +3,7 @@
url="TicketPackagings" url="TicketPackagings"
fields="['id', 'ticketFk', 'packagingFk', 'quantity', 'created']" fields="['id', 'ticketFk', 'packagingFk', 'quantity', 'created']"
link="{ticketFk: $ctrl.$stateParams.id}" link="{ticketFk: $ctrl.$stateParams.id}"
data="packages" on-data-change="$ctrl.onDataChange()" data="packages"
auto-load="true"> auto-load="true">
</vn-crud-model> </vn-crud-model>
<vn-watcher <vn-watcher

View File

@ -31,30 +31,51 @@
<vn-tbody> <vn-tbody>
<vn-tr ng-repeat="request in purchaseRequests"> <vn-tr ng-repeat="request in purchaseRequests">
<vn-td number>{{::request.id}}</vn-td> <vn-td number>{{::request.id}}</vn-td>
<vn-td expand>{{::request.description}}</vn-td>
<vn-td number>{{::request.created | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td expand> <vn-td expand>
<vn-textfield
vn-one
disabled="$ctrl.isEditable(request.isOk)"
ng-model="::request.description"
on-change="$ctrl.updateData()">
</vn-textfield>
</vn-td>
<vn-td number>{{::request.created | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td>
<span <span
class="link" class="link"
ng-click="$ctrl.showWorkerDescriptor($event, request.requesterFk)"> ng-click="$ctrl.showWorkerDescriptor($event, request.requesterFk)">
{{::request.requester.user.nickname | dashIfEmpty}} {{::request.requester.user.nickname | dashIfEmpty}}
</span> </span>
</vn-td> </vn-td>
<vn-td expand> <vn-td>
<span <span
class="link" class="link"
ng-click="$ctrl.showWorkerDescriptor($event, request.attenderFk)"> ng-click="$ctrl.showWorkerDescriptor($event, request.attenderFk)">
{{::request.atender.user.nickname | dashIfEmpty}} {{::request.atender.user.nickname | dashIfEmpty}}
</span> </span>
</vn-td> </vn-td>
<vn-td number>{{::request.quantity}}</vn-td> <vn-td>
<vn-td number>{{::request.price | currency: 'EUR': 2}}</vn-td> <vn-input-number
min="1"
disabled="$ctrl.isEditable(request.isOk)"
ng-model="::request.quantity"
on-change="$ctrl.updateData()">
</vn-input-number>
</vn-td>
<vn-td>
<vn-input-number
step="0.01"
disabled="$ctrl.isEditable(request.isOk)"
ng-model="::request.price"
on-change="$ctrl.updateData()">
</vn-input-number>
</vn-td>
<vn-td number> <vn-td number>
<span <span
ng-show="::request.saleFk" ng-show="::request.saleFk"
ng-click="$ctrl.showItemDescriptor($event, request.sale.itemFk)" ng-click="$ctrl.showItemDescriptor($event, request.sale.itemFk)"
class="link"> class="link">
{{request.saleFk | zeroFill:6}} {{::request.saleFk | zeroFill:6}}
</span> </span>
</vn-td> </vn-td>
<vn-td number> <vn-td number>

View File

@ -78,6 +78,18 @@ class Controller {
return 'Acepted'; return 'Acepted';
} }
} }
updateData() {
this.$.model.save().then(() => {
this.$.watcher.notifySaved();
this.$.watcher.updateOriginalData();
});
}
isEditable(isOk) {
if (isOk != null)
return true;
}
} }
Controller.$inject = ['$scope', '$stateParams']; Controller.$inject = ['$scope', '$stateParams'];

View File

@ -168,7 +168,7 @@
<span ng-class="{'link': $ctrl.isEditable}" <span ng-class="{'link': $ctrl.isEditable}"
title="{{$ctrl.isEditable ? 'Edit discount' : ''}}" title="{{$ctrl.isEditable ? 'Edit discount' : ''}}"
ng-click="$ctrl.showEditDiscountPopover($event, sale)"> ng-click="$ctrl.showEditDiscountPopover($event, sale)">
{{sale.discount | percentage}} {{(sale.discount / 100) | percentage}}
</span> </span>
</vn-td> </vn-td>
<vn-td number> <vn-td number>