Added client consumption campaign filter #400

Merged
joan merged 6 commits from 2481-campaign_filter into dev 2020-10-06 06:50:34 +00:00
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()', () => {
carlosjr marked this conversation as resolved
Review

add a test for the filter getting results for previous years

add a test for the filter getting results for previous years
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()', () => {
joan marked this conversation as resolved
Review

some tests should be added for dinamic dates

some tests should be added for dinamic dates
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": { "Bank": {
"dataSource": "vn" "dataSource": "vn"
}, },
"Campaign": {
"dataSource": "vn"
},
"Country": { "Country": {
"dataSource": "vn" "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; this.input.value = display;
if (this.translateFields) { if (this.translateFields && this.selection) {
if (this.translateFields.indexOf(this.showField) > -1) const translations = [];
this.input.value = this.$t(display); 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"> ng-model="filter.categoryId">
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </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-horizontal>
<vn-date-picker <vn-date-picker
vn-one vn-one

View File

@ -1,7 +1,39 @@
import ngModule from '../module'; import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel'; 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', { ngModule.vnComponent('vnConsumptionSearchPanel', {
template: require('./index.html'), 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 Item id: Id artículo
From: Desde 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()', () => { describe('Worker createAbsence()', () => {
const workerId = 18; 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() => { it('should return an error for a user without enough privileges', async() => {
const ctx = {req: {accessToken: {userId: 18}}}; const ctx = {req: {accessToken: {userId: 18}}};
@ -40,12 +34,15 @@ describe('Worker createAbsence()', () => {
const absenceTypeId = 1; const absenceTypeId = 1;
const dated = new Date(); 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 expectedBusinessId = 18;
const expectedAbsenceTypeId = 1; const expectedAbsenceTypeId = 1;
expect(createdAbsence.businessFk).toEqual(expectedBusinessId); expect(createdAbsence.businessFk).toEqual(expectedBusinessId);
expect(createdAbsence.dayOffTypeFk).toEqual(expectedAbsenceTypeId); 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'); const LoopBackContext = require('loopback-context');
describe('Worker deleteAbsence()', () => { describe('Worker deleteAbsence()', () => {
const businessId = 18;
const workerId = 18; const workerId = 18;
let createdAbsence;
const activeCtx = { const activeCtx = {
accessToken: {userId: 19}, accessToken: {userId: 106},
headers: {origin: 'http://localhost'} headers: {origin: 'http://localhost'}
}; };
const ctx = {req: activeCtx}; const ctx = {req: activeCtx};
ctx.req.__ = value => { ctx.req.__ = value => {
return value; return value;
}; };
let createdAbsence;
it('should return an error for a user without enough privileges', async() => { beforeEach(async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx active: activeCtx
}); });
activeCtx.accessToken.userId = 106;
const businessId = 18;
createdAbsence = await app.models.Calendar.create({ createdAbsence = await app.models.Calendar.create({
businessFk: businessId, businessFk: businessId,
dayOffTypeFk: 1, dayOffTypeFk: 1,
dated: new Date() 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; let error;
await app.models.Worker.deleteAbsence(ctx, 18, createdAbsence.id).catch(e => { 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() => { it('should create a new absence', async() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
activeCtx.accessToken.userId = 19; activeCtx.accessToken.userId = 19;
const businessId = 18;
expect(createdAbsence.businessFk).toEqual(businessId); expect(createdAbsence.businessFk).toEqual(businessId);

View File

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