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',

if a single result is expected should then be singular campain

if a single result is expected should then be singular campain
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;
}
}

delete this

delete this

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`, () => {

information is missing

information is missing
$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);