Merge pull request 'Added client consumption campaign filter' (#400) from 2481-campaign_filter into dev
gitea/salix/pipeline/head This commit looks good Details

Reviewed-on: #400
Reviewed-by: Carlos Jimenez <carlosjr@verdnatura.es>
This commit is contained in:
Joan Sanchez 2020-10-06 06:50:24 +00:00
commit 530a4026f8
17 changed files with 344 additions and 33 deletions

View File

@ -0,0 +1,41 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => {
Self.remoteMethod('latest', {
description: 'Returns the lastest campaigns',
accessType: 'READ',
accepts: [{
arg: 'filter',
type: 'Object',
description: `Filter defining where, order, offset, and limit - must be a JSON-encoded string`
}],
returns: {
type: ['object'],
root: true
},
http: {
path: `/latest`,
verb: 'GET'
}
});
Self.latest = async filter => {
const conn = Self.dataSource.connector;
const minDate = new Date();
minDate.setFullYear(minDate.getFullYear() - 1);
minDate.setMonth(0);
minDate.setDate(1);
const where = {dated: {gte: minDate}};
filter = mergeFilters(filter, {where});
const stmt = new ParameterizedSQL(
`SELECT * FROM campaign`);
stmt.merge(conn.makeWhere(filter.where));
stmt.merge('GROUP BY code');
stmt.merge(conn.makePagination(filter));
return conn.executeStmt(stmt);
};
};

View File

@ -0,0 +1,34 @@
const app = require('vn-loopback/server/server');
describe('campaign latest()', () => {
it('should return the campaigns from the last year', async() => {
let result = await app.models.Campaign.latest();
const lastYearDate = new Date();
lastYearDate.setFullYear(lastYearDate.getFullYear() - 1);
const lastYear = lastYearDate.getFullYear();
const randomIndex = Math.floor(Math.random() * result.length);
const campaignDated = result[randomIndex].dated;
const campaignYear = campaignDated.getFullYear();
expect(result.length).toEqual(3);
expect(campaignYear).toEqual(lastYear);
});
it('should return the campaigns from the current year', async() => {
const currentDate = new Date();
const currentYear = currentDate.getFullYear();
const result = await app.models.Campaign.latest({
where: {dated: {like: `%${currentYear}%`}}
});
const randomIndex = Math.floor(Math.random() * result.length);
const campaignDated = result[randomIndex].dated;
const campaignYear = campaignDated.getFullYear();
expect(result.length).toEqual(3);
expect(campaignYear).toEqual(currentYear);
});
});

View File

@ -0,0 +1,16 @@
const app = require('vn-loopback/server/server');
describe('campaign upcoming()', () => {
it('should return the upcoming campaign but from the last year', async() => {
let response = await app.models.Campaign.upcoming();
const lastYearDate = new Date();
lastYearDate.setFullYear(lastYearDate.getFullYear() - 1);
const lastYear = lastYearDate.getFullYear();
const campaignDated = response.dated;
const campaignYear = campaignDated.getFullYear();
expect(campaignYear).toEqual(lastYear);
});
});

View File

@ -0,0 +1,29 @@
module.exports = Self => {
Self.remoteMethod('upcoming', {
description: 'Returns the upcoming campaign',
accessType: 'READ',
accepts: [],
returns: {
type: ['object'],
root: true
},
http: {
path: `/upcoming`,
verb: 'GET'
}
});
Self.upcoming = async() => {
const minDate = new Date();
minDate.setFullYear(minDate.getFullYear() - 1);
return Self.findOne({
where: {
dated: {
gte: minDate
}
},
order: 'dated ASC'
});
};
};

View File

@ -8,6 +8,9 @@
"Bank": {
"dataSource": "vn"
},
"Campaign": {
"dataSource": "vn"
},
"Country": {
"dataSource": "vn"
},

4
back/models/campaign.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = Self => {
require('../methods/campaign/latest')(Self);
require('../methods/campaign/upcoming')(Self);
};

33
back/models/campaign.json Normal file
View File

@ -0,0 +1,33 @@
{
"name": "Campaign",
"base": "VnModel",
"options": {
"mysql": {
"table": "campaign"
}
},
"properties": {
"id": {
"type": "number",
"required": true
},
"code": {
"type": "string",
"required": true
},
"dated": {
"type": "date"
},
"scopeDays": {
"type": "number"
}
},
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
}

View File

@ -0,0 +1,37 @@
CREATE TABLE `vn`.campaign
(
id INT AUTO_INCREMENT,
code ENUM('mothersDay', 'allSaints', 'valentinesDay') NOT NULL,
dated DATE DEFAULT CURDATE() NOT NULL,
scopeDays INT NOT NULL DEFAULT '15',
CONSTRAINT campaign_pk
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX campaign_dated_uindex
ON `vn`.campaign (dated);
-- TODOS SANTOS
INSERT INTO `vn`.campaign(code, dated)
SELECT 'allSaints' AS code, dated
FROM `vn`.time
WHERE dated >= CONCAT(YEAR(CURDATE()) - 1, '-01-01')
AND month = 11
AND day = 1;
-- SAN VALENTIN
INSERT INTO `vn`.campaign(code, dated)
SELECT 'valentinesDay' AS code, dated
FROM `vn`.time
WHERE dated >= CONCAT(YEAR(CURDATE()) - 1, '-01-01')
AND month = 2
AND day = 14;
-- DIA DE LA MADRE
INSERT INTO `vn`.campaign(code, dated)
SELECT 'mothersDay' AS code, dated
FROM `vn`.time
WHERE dated >= CONCAT(YEAR(CURDATE()) - 1, '-01-01')
AND month = 5
AND WEEK(dated, 5) - WEEK(DATE_SUB(dated, INTERVAL DAYOFMONTH(dated) - 1 DAY), 5) + 1 = 1 -- WEEK OF MONTH
AND DAYOFWEEK(dated) = 1;

View File

@ -190,9 +190,24 @@ export default class Autocomplete extends Field {
this.input.value = display;
if (this.translateFields) {
if (this.translateFields.indexOf(this.showField) > -1)
this.input.value = this.$t(display);
if (this.translateFields && this.selection) {
const translations = [];
for (let field of this.translateFields) {
const fieldValue = this._selection[field];
translations.push({
original: fieldValue,
value: this.$t(fieldValue)
});
}
for (let translation of translations) {
const orgValue = translation.original;
const value = translation.value;
display = display.replace(orgValue, value);
}
this.input.value = display;
}
}

View File

@ -49,6 +49,22 @@
ng-model="filter.categoryId">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
url="Campaigns/latest"
label="Campaign"
translate-fields="['code']"
show-field="code"
value-field="id"
ng-model="filter.campaign"
order="dated DESC"
selection="$ctrl.campaignSelection"
search-function="{dated: {like: '%'+ $search +'%'}}">
<tpl-item>
{{code}} {{dated | date: 'yyyy'}}
</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
vn-one

View File

@ -1,7 +1,39 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
class Controller extends SearchPanel {
constructor($, $element) {
super($, $element);
this.getUpcomingCampaing();
}
getUpcomingCampaing() {
this.$http.get('Campaigns/upcoming').then(res => {
const filter = this.$.filter;
filter.campaign = res.data.id;
});
}
get campaignSelection() {
return this._campaignSelection;
}
set campaignSelection(value) {
this._campaignSelection = value;
if (!value) return;
const filter = this.$.filter;
const from = new Date(value.dated);
from.setDate(from.getDate() - value.scopeDays);
filter.to = value.dated;
filter.from = from;
}
}
ngModule.vnComponent('vnConsumptionSearchPanel', {
template: require('./index.html'),
controller: SearchPanel
controller: Controller
});

View File

@ -0,0 +1,31 @@
import './index.js';
describe('Client', () => {
describe('Component vnConsumptionSearchPanel', () => {
let $httpBackend;
let $element;
let controller;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$element = angular.element(`<div></div>`);
controller = $componentController('vnConsumptionSearchPanel', {$element});
controller.$.filter = {};
$httpBackend.expect('GET', 'Campaigns/upcoming').respond(200, {id: 1});
}));
describe('getUpcomingCampaing()', () => {
it(`should make an HTTP query and then set the campaign property`, () => {
$httpBackend.expect('GET', 'Campaigns/upcoming').respond(200, {id: 2, code: 'allSaints'});
controller.getUpcomingCampaing();
$httpBackend.flush();
const filter = controller.$.filter;
expect(filter.campaign).toEqual(2);
});
});
});
});

View File

@ -0,0 +1,3 @@
allSaints: All Saints Day
valentinesDay: Valentine's Day
mothersDay: Mother's day

View File

@ -1,3 +1,7 @@
Item id: Id artículo
From: Desde
To: Hasta
To: Hasta
Campaign: Campaña
allSaints: Día de todos los Santos
valentinesDay: Día de San Valentín
mothersDay: Día de la madre

View File

@ -3,12 +3,6 @@ const LoopBackContext = require('loopback-context');
describe('Worker createAbsence()', () => {
const workerId = 18;
let createdAbsence;
afterAll(async() => {
const absence = await app.models.Calendar.findById(createdAbsence.id);
await absence.destroy();
});
it('should return an error for a user without enough privileges', async() => {
const ctx = {req: {accessToken: {userId: 18}}};
@ -40,12 +34,15 @@ describe('Worker createAbsence()', () => {
const absenceTypeId = 1;
const dated = new Date();
createdAbsence = await app.models.Worker.createAbsence(ctx, workerId, absenceTypeId, dated);
const createdAbsence = await app.models.Worker.createAbsence(ctx, workerId, absenceTypeId, dated);
const expectedBusinessId = 18;
const expectedAbsenceTypeId = 1;
expect(createdAbsence.businessFk).toEqual(expectedBusinessId);
expect(createdAbsence.dayOffTypeFk).toEqual(expectedAbsenceTypeId);
// Restores
await app.models.Calendar.destroyById(createdAbsence.id);
});
});

View File

@ -2,29 +2,35 @@ const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('Worker deleteAbsence()', () => {
const businessId = 18;
const workerId = 18;
let createdAbsence;
const activeCtx = {
accessToken: {userId: 19},
accessToken: {userId: 106},
headers: {origin: 'http://localhost'}
};
const ctx = {req: activeCtx};
ctx.req.__ = value => {
return value;
};
let createdAbsence;
it('should return an error for a user without enough privileges', async() => {
beforeEach(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
activeCtx.accessToken.userId = 106;
const businessId = 18;
createdAbsence = await app.models.Calendar.create({
businessFk: businessId,
dayOffTypeFk: 1,
dated: new Date()
});
});
afterEach(async() => {
await app.models.Calendar.destroyById(createdAbsence.id);
});
it('should return an error for a user without enough privileges', async() => {
activeCtx.accessToken.userId = 106;
let error;
await app.models.Worker.deleteAbsence(ctx, 18, createdAbsence.id).catch(e => {
@ -37,12 +43,7 @@ describe('Worker deleteAbsence()', () => {
});
it('should create a new absence', async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
activeCtx.accessToken.userId = 19;
const businessId = 18;
expect(createdAbsence.businessFk).toEqual(businessId);

View File

@ -1,22 +1,37 @@
const app = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
describe('Worker updateAbsence()', () => {
const workerId = 106;
const businessId = 106;
const activeCtx = {
accessToken: {userId: 106},
headers: {origin: 'http://localhost'}
};
const ctx = {req: activeCtx};
ctx.req.__ = value => {
return value;
};
let createdAbsence;
afterAll(async() => {
const absence = await app.models.Calendar.findById(createdAbsence.id);
await absence.destroy();
});
it('should return an error for a user without enough privileges', async() => {
const ctx = {req: {accessToken: {userId: 106}}};
const expectedAbsenceTypeId = 2;
beforeEach(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
createdAbsence = await app.models.Calendar.create({
businessFk: 106,
businessFk: businessId,
dayOffTypeFk: 1,
dated: new Date()
});
});
afterEach(async() => {
await app.models.Calendar.destroyById(createdAbsence.id);
});
it('should return an error for a user without enough privileges', async() => {
activeCtx.accessToken.userId = 106;
const expectedAbsenceTypeId = 2;
let error;
await app.models.Worker.updateAbsence(ctx, workerId, createdAbsence.id, expectedAbsenceTypeId).catch(e => {
@ -29,7 +44,7 @@ describe('Worker updateAbsence()', () => {
});
it('should create a new absence', async() => {
const ctx = {req: {accessToken: {userId: 37}}};
activeCtx.accessToken.userId = 37;
const expectedAbsenceTypeId = 2;
const updatedAbsence = await app.models.Worker.updateAbsence(ctx, workerId, createdAbsence.id, expectedAbsenceTypeId);