Merge pull request 'Tarea-#2242-zone-upcomingDeliveries' (#260) from Tarea-#2242-zone-upcomingDeliveries into dev
gitea/salix/pipeline/head This commit looks good Details

Reviewed-by: Juan Ferrer <juan@verdnatura.es>
This commit is contained in:
Javi Gallego 2020-05-19 10:21:33 +00:00
commit 692af6078c
25 changed files with 294 additions and 23 deletions

View File

@ -0,0 +1,20 @@
USE `util`;
DROP procedure IF EXISTS `time_createTable`;
DELIMITER $$
USE `util`$$
CREATE DEFINER=`root`@`%` PROCEDURE `time_createTable`(vStarted DATE, vEnded DATE)
BEGIN
DECLARE vCurrentDate DATE;
DROP TEMPORARY TABLE IF EXISTS tmp.time;
CREATE TEMPORARY TABLE tmp.time (dated DATE PRIMARY KEY) ENGINE = MEMORY;
SET vCurrentDate = vStarted;
WHILE vCurrentDate <= vEnded DO
INSERT INTO tmp.time (dated) VALUES (vCurrentDate);
SET vCurrentDate = DATE_ADD(vCurrentDate, INTERVAL 1 DAY);
END WHILE;
END$$
DELIMITER ;

View File

@ -0,0 +1,2 @@
ALTER TABLE `vn`.`zoneConfig`
ADD COLUMN `forwardDays` INT(10) NOT NULL DEFAULT 7 COMMENT 'days forward to show zone_upcomingDeliveries' AFTER `scope`;

View File

@ -0,0 +1,80 @@
USE `vn`;
DROP procedure IF EXISTS `zone_upcomingDeliveries`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `zone_upcomingDeliveries`()
BEGIN
DECLARE vForwardDays INT;
SELECT forwardDays INTO vForwardDays FROM zoneConfig;
CALL util.time_createTable(CURDATE(), DATE_ADD(CURDATE(), INTERVAL vForwardDays DAY));
DROP TEMPORARY TABLE IF EXISTS tLandings;
CREATE TEMPORARY TABLE tLandings
(INDEX (eventFk))
ENGINE = MEMORY
SELECT e.id eventFk,
@travelingDays := IFNULL(e.travelingDays, z.travelingDays) travelingDays,
TIMESTAMPADD(DAY, @travelingDays, ti.dated) landed,
ti.dated shipped
FROM zone z
JOIN zoneEvent e ON e.zoneFk = z.id
JOIN tmp.time ti ON ti.dated BETWEEN curdate() AND TIMESTAMPADD(DAY, vForwardDays, curdate());
DROP TEMPORARY TABLE IF EXISTS tmp.zoneOption;
CREATE TEMPORARY TABLE tmp.zoneOption
ENGINE = MEMORY
SELECT *
FROM (
SELECT z.id zoneFk,
TIME(IFNULL(e.`hour`, z.`hour`)) `hour`,
l.travelingDays,
IFNULL(e.price, z.price) price,
IFNULL(e.bonus, z.bonus) bonus,
l.landed,
l.shipped
FROM zone z
JOIN zoneEvent e ON e.zoneFk = z.id
JOIN tLandings l ON l.eventFk = e.id
WHERE (
e.`type` = 'day'
AND e.`dated` = l.landed
) OR (
e.`type` != 'day'
AND e.weekDays & (1 << WEEKDAY(l.landed))
AND (e.`started` IS NULL OR l.landed >= e.`started`)
AND (e.`ended` IS NULL OR l.landed <= e.`ended`)
)
ORDER BY
zoneFk,
CASE
WHEN e.`type` = 'day'
THEN 1
WHEN e.`type` = 'range'
THEN 2
ELSE 3
END
) t
GROUP BY zoneFk, landed;
DELETE t FROM tmp.zoneOption t
JOIN zoneExclusion e
ON e.zoneFk = t.zoneFk AND e.`dated` = t.landed;
SELECT MAX(zo.`hour`) `hour`, zg.`name`, zo.shipped
FROM tmp.zoneOption zo
JOIN `zone` z ON z.id = zo.zoneFk
JOIN agencyMode am ON am.id = z.agencyModeFk
JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
JOIN zoneIncluded zi ON zi.zoneFk = z.id
JOIN zoneGeo zg ON zg.id = zi.geoFk AND zg.type = 'province'
WHERE dm.code = 'DELIVERY'
GROUP BY shipped, zg.`name`
ORDER BY shipped, zg.`name`;
DROP TEMPORARY TABLE tmp.time, tLandings;
END$$
DELIMITER ;

View File

@ -1711,11 +1711,11 @@ INSERT INTO `vn`.`zoneIncluded` (`zoneFk`, `geoFk`, `isIncluded`)
(2, 1, 1),
(3, 3, 0),
(3, 4, 0),
(3, 5, 0),
(3, 5, 0),
(3, 1, 1),
(4, 3, 0),
(4, 4, 0),
(4, 5, 0),
(4, 5, 0),
(4, 1, 1),
(5, 3, 1),
(5, 4, 0),
@ -1732,7 +1732,8 @@ INSERT INTO `vn`.`zoneIncluded` (`zoneFk`, `geoFk`, `isIncluded`)
(8, 3, 0),
(8, 4, 0),
(8, 5, 0),
(8, 1, 1);
(8, 1, 1),
(10, 10, 1);
INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `dated`)
VALUES
@ -1943,7 +1944,9 @@ INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `dated`)
(7, 'day', DATE_ADD(CURDATE(), INTERVAL +6 DAY));
INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `weekDays`)
VALUES (8, 'indefinitely', 'mon,tue,wed,thu,fri,sat,sun');
VALUES
(8, 'indefinitely', 'mon,tue,wed,thu,fri,sat,sun'),
(10, 'indefinitely', 'mon,tue,wed,thu,fri,sat,sun');
INSERT INTO `vn`.`workerTimeControl`(`userFk`, `timed`, `manual`, `direction`)
VALUES

View File

@ -31,7 +31,7 @@ describe('Client Edit billing data path', () => {
expect(message.text).toBe('That payment method requires an IBAN');
});
// 2215: Windows (hidden mode): Entity code doesn't get the focus, '9999' is written in entity name.
// 2256: Windows (hidden mode): Entity code doesn't get the focus, '9999' is written in entity name.
xit(`should create a new BIC code`, async() => {
await page.waitToClick(selectors.clientBillingData.newBankEntityButton);
await page.write(selectors.clientBillingData.newBankEntityName, 'Gotham City Bank');

View File

@ -39,7 +39,7 @@ export default class WeekDays {
new WeekDay('wed', 'Wednesday'),
new WeekDay('thu', 'Thursday'),
new WeekDay('fri', 'Friday'),
new WeekDay('sat', 'Friday')
new WeekDay('sat', 'Saturday')
];
this.map = {};

View File

@ -61,7 +61,6 @@ module.exports = Self => {
}, options);
}
let query = `
CALL vn.item_getVisibleAvailable(?,curdate(),?,?)`;
@ -106,7 +105,6 @@ module.exports = Self => {
return ticket.id;
}
async function getTicketId(params, options) {
const minDate = new Date();
minDate.setHours(0, 0, 0, 0);

View File

@ -15,7 +15,6 @@ describe('regularize()', () => {
it('should create a new ticket and add a line', async() => {
let ctx = {req: {accessToken: {userId: 18}}};
let query = `CALL vn.item_getVisibleAvailable(?,curdate(),?,?)`;
let options = [itemFk, warehouseFk, true];

View File

@ -24,9 +24,8 @@ describe('sale updatePrice()', () => {
done();
});
it('should throw an error if the ticket is not editable', async() => {
let ctx = {req: {accessToken: {userId: 9}}};
let ctx = {req: {accessToken: {userId: 18}}};
let immutableSaleId = 1;
let price = 5;
@ -40,7 +39,7 @@ describe('sale updatePrice()', () => {
});
it('should return 0 if the price is an empty string', async() => {
let ctx = {req: {accessToken: {userId: 9}}};
let ctx = {req: {accessToken: {userId: 18}}};
let price = '';
await app.models.Sale.updatePrice(ctx, saleId, price);
@ -50,7 +49,7 @@ describe('sale updatePrice()', () => {
});
it('should now set price as a decimal number in a string', async() => {
let ctx = {req: {accessToken: {userId: 9}}};
let ctx = {req: {accessToken: {userId: 18}}};
let price = '8';
await app.models.Sale.updatePrice(ctx, saleId, price);
@ -60,7 +59,7 @@ describe('sale updatePrice()', () => {
});
it('should set price as a decimal number and check the sale has the mana component', async() => {
let ctx = {req: {accessToken: {userId: 9}}};
let ctx = {req: {accessToken: {userId: 18}}};
let price = 5.4;
await app.models.Sale.updatePrice(ctx, saleId, price);

View File

@ -55,8 +55,8 @@ module.exports = Self => {
if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`);
let salesPerson = sale.ticket().client().salesPersonFk;
let usesMana = await models.WorkerMana.findOne({where: {workerFk: salesPerson}, fields: 'amount'}, options);
const userId = ctx.req.accessToken.userId;
let usesMana = await models.WorkerMana.findOne({where: {workerFk: userId}, fields: 'amount'}, options);
let componentCode = usesMana ? 'mana' : 'buyerDiscount';
let discount = await models.Component.findOne({where: {code: componentCode}}, options);
@ -83,8 +83,8 @@ module.exports = Self => {
await sale.updateAttributes({price: newPrice}, options);
query = `call vn.manaSpellersRequery(?)`;
await Self.rawSql(query, [salesPerson], options);
query = `CALL vn.manaSpellersRequery(?)`;
await Self.rawSql(query, [userId], options);
await tx.commit();

View File

@ -163,7 +163,8 @@ module.exports = Self => {
if (value) {
return {and: [
{'st.alertLevel': 0},
{'st.code': {neq: 'OK'}}
{'st.code': {neq: 'OK'}},
{'st.code': {neq: 'BOARDING'}}
]};
} else {
return {and: [

View File

@ -76,7 +76,7 @@ module.exports = Self => {
});
const alertLevel = state ? state.alertLevel : null;
if (isLocked || (!isSalesPerson && alertLevel > 0 ))
if (isLocked || (!isSalesPerson && alertLevel > 0))
throw new UserError(`The sales of this ticket can't be modified`);
const ticket = await models.Ticket.findById(id, {

View File

@ -14,7 +14,7 @@ Show pallet report: Ver hoja de pallet
Change shipped hour: Cambiar hora de envío
Shipped hour: Hora de envío
Make a payment: "Verdnatura le comunica:\rSu pedido está pendiente de pago.\rPor favor, entre en la página web y efectue el pago con tarjeta.\rMuchas gracias."
Minimum is needed: "Verdnatura le recuerda:\rEs necesario llegar a un importe mínimo de 50€ (Sin IVA) en su pedido {{ticketId}} del día {{created | date: 'dd/MM/yyyy'}} para recibirlo sin portes adicionales."
Minimum is needed: "Verdnatura le recuerda:\rEs necesario un importe mínimo de 50€ (Sin IVA) en su pedido {{ticketId}} del día {{created | date: 'dd/MM/yyyy'}} para recibirlo sin portes adicionales."
Ticket invoiced: Ticket facturado
Make invoice: Crear factura
Regenerate invoice: Regenerar factura

View File

@ -0,0 +1,41 @@
module.exports = Self => {
Self.remoteMethod('getUpcomingDeliveries', {
description: 'Returns the upcomings deliveries',
accessType: 'READ',
accepts: [],
returns: {
type: ['Object'],
root: true
},
http: {
path: `/getUpcomingDeliveries`,
verb: 'GET'
}
});
Self.getUpcomingDeliveries = async() => {
const [zones] = await Self.rawSql(`CALL vn.zone_upcomingDeliveries()`);
const details = [];
for (let zone of zones) {
const shipped = zone.shipped;
let zoneDetail = details.find(zone => {
return zone.shipped.toString() == shipped.toString();
});
if (!zoneDetail) {
zoneDetail = {
shipped: shipped,
lines: []
};
details.push(zoneDetail);
}
zoneDetail.lines.push(zone);
}
return details;
};
};

View File

@ -0,0 +1,16 @@
const app = require('vn-loopback/server/server');
describe('zone getUpcomingDeliveries()', () => {
it('should check returns data', async() => {
let result = await app.models.Zone.getUpcomingDeliveries();
const firstResultLines = result[0].lines;
const secondResultLines = result[1].lines;
const thirdResultLines = result[2].lines;
expect(result.length).toEqual(8);
expect(firstResultLines.length).toEqual(1);
expect(secondResultLines.length).toEqual(1);
expect(thirdResultLines.length).toEqual(1);
});
});

View File

@ -3,6 +3,7 @@ module.exports = Self => {
require('../methods/zone/getLeaves')(Self);
require('../methods/zone/getEvents')(Self);
require('../methods/zone/toggleIsIncluded')(Self);
require('../methods/zone/getUpcomingDeliveries')(Self);
Self.validatesPresenceOf('agencyModeFk', {
message: `Agency cannot be blank`

View File

@ -61,7 +61,7 @@ describe('Zone Component vnZoneDeliveryDays', () => {
expect(controller.$.data).toEqual(expectedData);
});
});
// Petición #2259 cread
xdescribe('onSelection()', () => {
it('should not call the show popover method if events array is empty', () => {
jest.spyOn(controller.$.zoneEvents, 'show');

View File

@ -14,3 +14,4 @@ import './events';
import './calendar';
import './location';
import './calendar';
import './upcoming-deliveries';

View File

@ -24,6 +24,7 @@ Range of dates: Rango de fechas
Search zone by id or name: Buscar zonas por identificador o nombre
This zone will be removed: La zona será eliminada
To: Hasta
Upcoming deliveries: Próximos repartos
Volumetric: Volumétrico
Warehouse: Almacén
Warehouses: Almacenes

View File

@ -7,7 +7,8 @@
"menus": {
"main": [
{"state": "zone.index", "icon": "icon-zone"},
{"state": "zone.deliveryDays", "icon": "today"}
{"state": "zone.deliveryDays", "icon": "today"},
{"state": "zone.upcomingDeliveries", "icon": "today"}
],
"card": [
{"state": "zone.card.basicData", "icon": "settings"},
@ -33,6 +34,11 @@
"state": "zone.deliveryDays",
"component": "vn-zone-delivery-days",
"description": "Delivery days"
}, {
"url": "/upcoming-deliveries",
"state": "zone.upcomingDeliveries",
"component": "vn-upcoming-deliveries",
"description": "Upcoming deliveries"
}, {
"url": "/create",
"state": "zone.create",

View File

@ -0,0 +1,30 @@
<vn-crud-model auto-load="true"
vn-id="model"
url="Zones/getUpcomingDeliveries"
data="details">
</vn-crud-model>
<vn-data-viewer model="model">
<vn-card>
<section ng-repeat="detail in details" class="vn-pa-md">
<vn-horizontal class="header">
<h5>{{$ctrl.getWeekDay(detail.shipped)}} - {{detail.shipped | date: 'dd/MM/yyyy'}}</h5>
</vn-horizontal>
<vn-table>
<vn-thead>
<vn-tr>
<vn-th class="waste-family">Province</vn-th>
<vn-th number>Closing</vn-th>
<vn-th number>Id</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="zone in detail.lines">
<vn-td class="waste-family">{{::zone.name}}</vn-td>
<vn-td number>{{::zone.hour}}</vn-td>
<vn-td number>{{::zone.zoneFk}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</section>
</vn-card>
</vn-data-viewer>

View File

@ -0,0 +1,23 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $, vnWeekDays) {
super($element, $);
this.days = vnWeekDays.days;
}
getWeekDay(jsonDate) {
const weekDay = new Date(jsonDate).getDay();
return this.days[weekDay].locale;
}
}
Controller.$inject = ['$element', '$scope', 'vnWeekDays'];
ngModule.component('vnUpcomingDeliveries', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,22 @@
import './index';
describe('component vnUpcomingDeliveries', () => {
let $scope;
let controller;
beforeEach(ngModule('zone'));
beforeEach(angular.mock.inject(($componentController, $rootScope) => {
$scope = $rootScope.$new();
const $element = angular.element(`<vn-upcoming-deliveries></vn-upcoming-deliveries>`);
controller = $componentController('vnUpcomingDeliveries', {$element, $scope});
}));
fdescribe('getWeekDay()', () => {
it('should retrieve a weekday for a json passed', () => {
let jsonDate = '1970-01-01T22:00:00.000Z';
expect(controller.getWeekDay(jsonDate)).toEqual('Thursday');
});
});
});

View File

@ -0,0 +1,3 @@
Family: Familia
Percentage: Porcentaje
Dwindle: Mermas

View File

@ -0,0 +1,25 @@
@import "variables";
vn-upcoming-deliveries {
.header {
margin-bottom: 16px;
text-transform: uppercase;
font-size: 1.25rem;
line-height: 1;
padding: 7px;
padding-bottom: 7px;
padding-bottom: 4px;
font-weight: lighter;
background-color: $color-main-light;
border-bottom: 1px solid $color-primary;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
vn-table vn-th.waste-family,
vn-table vn-td.waste-family {
max-width: 64px;
width: 64px
}
}