Skip to content
This repository was archived by the owner on Jan 14, 2020. It is now read-only.

Commit e2b5aaa

Browse files
Merge pull request #22 from fielded/versioned-tp
Use versioned target population
2 parents 1bcaf99 + 1acaec1 commit e2b5aaa

File tree

4 files changed

+178
-60
lines changed

4 files changed

+178
-60
lines changed

.babelrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"presets": [
3-
"es2015-rollup"
3+
["es2015", { "modules": false }]
44
]
55
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"devDependencies": {
3232
"angular-mocks": "^1.5.5",
3333
"babel-core": "^6.8.0",
34-
"babel-preset-es2015-rollup": "^1.1.1",
34+
"babel-preset-es2015": "^6.18.0",
3535
"ghooks": "^1.3.2",
3636
"jasmine-core": "^2.4.1",
3737
"karma": "^0.13.22",

src/thresholds.service.js

+106-44
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,101 @@ const isVersion = (version, item) => item.version === version
1313
const isId = (id, item) => item._id === id
1414

1515
// Zones config
16-
const zonesPlan = {
17-
min: 0,
18-
reOrder: 3,
19-
max: 6
16+
const zonePlans = {
17+
weeksOfStock: {
18+
min: 0,
19+
reOrder: 3,
20+
max: 6
21+
}
22+
}
23+
24+
const getFactorVersion = (stockCount, factor, options) => {
25+
if (options.version === 'last') {
26+
return options.version
27+
}
28+
if (!(stockCount[factor] && stockCount[factor].version)) {
29+
return 1
30+
}
31+
return stockCount[factor].version
32+
}
33+
34+
const getFactor = (location, versions, version) => {
35+
if (version === 'last') {
36+
return versions[versions.length - 1]
37+
}
38+
39+
return find(versions, isVersion.bind(null, version))
40+
}
41+
42+
const getFactors = (stockCount, location, options) => {
43+
// centralized for whenever we implement #16
44+
const somethingIsWrong = () => undefined
45+
46+
const getWeeklyLevels = () => {
47+
if (!(location.allocations && location.allocations.length)) {
48+
somethingIsWrong()
49+
}
50+
51+
const allocationsVersion = getFactorVersion(stockCount, 'allocations', options)
52+
53+
if (typeof allocationsVersion === 'undefined') {
54+
somethingIsWrong()
55+
}
56+
57+
const allocations = getFactor(location, location.allocations, allocationsVersion)
58+
return allocations && allocations.weeklyLevels
59+
}
60+
61+
const getWeeksOfStock = () => {
62+
if (location.level !== 'zone' && !(location.plans && location.plans.length)) {
63+
somethingIsWrong()
64+
}
65+
66+
const plansVersion = getFactorVersion(stockCount, 'plans', options)
67+
68+
if (typeof plansVersion === 'undefined') {
69+
somethingIsWrong()
70+
}
71+
72+
let plans = zonePlans
73+
if (location.level !== 'zone') {
74+
plans = getFactor(location, location.plans, plansVersion)
75+
}
76+
77+
return plans && plans.weeksOfStock
78+
}
79+
80+
const getMonthlyTargetPopulations = () => {
81+
let monthlyTargetPopulations
82+
if (location.targetPopulations) {
83+
if (!location.targetPopulations.length) {
84+
somethingIsWrong()
85+
}
86+
const targetPopulationVersion = getFactorVersion(stockCount, 'targetPopulations', options)
87+
88+
if (typeof targetPopulationVersion === 'undefined') {
89+
somethingIsWrong()
90+
}
91+
92+
const targetPopulations = getFactor(location, location.targetPopulations, targetPopulationVersion)
93+
monthlyTargetPopulations = targetPopulations && targetPopulations.monthlyTargetPopulations
94+
} else {
95+
// For backwards compatibility with the old style location docs,
96+
// since we have no control about when the dashboards are going
97+
// to replicate the new location docs
98+
if (!(location.targetPopulation && location.targetPopulation.length)) {
99+
somethingIsWrong()
100+
}
101+
monthlyTargetPopulations = location.targetPopulation
102+
}
103+
return monthlyTargetPopulations
104+
}
105+
106+
return {
107+
weeksOfStock: getWeeksOfStock(),
108+
weeklyLevels: getWeeklyLevels(),
109+
targetPopulations: getMonthlyTargetPopulations()
110+
}
20111
}
21112

22113
class ThresholdsService {
@@ -31,55 +122,25 @@ class ThresholdsService {
31122
// the week, that information is passed as an optional param (`requiredStateStoresAllocation`).
32123
// That param is only used for zones.
33124
calculateThresholds (location, stockCount, products, requiredStateStoresAllocation = {}, options = {}) {
34-
if (!location || !location.allocations || !location.allocations.length ||
35-
!location.plans || !location.plans.length || !location.level) {
36-
return
37-
}
38-
39125
if (!stockCount) {
40126
return
41127
}
42128

43-
if (options.version !== 'last' &&
44-
!(stockCount.allocations && typeof stockCount.allocations.version !== undefined &&
45-
stockCount.plans && typeof stockCount.plans.version !== undefined)) {
129+
if (!location && location.level) {
46130
return
47131
}
48132

49133
if (!products || !products.length) {
50134
return
51135
}
52136

53-
let allocation
54-
if (options.version === 'last') {
55-
allocation = location.allocations[location.allocations.length - 1]
56-
} else {
57-
allocation = find(location.allocations, isVersion.bind(null, stockCount.allocations.version))
58-
}
137+
const { weeklyLevels, weeksOfStock, targetPopulations } = getFactors(stockCount, location, options)
59138

60-
if (!(allocation && allocation.weeklyLevels)) {
139+
if (!(weeklyLevels && weeksOfStock && targetPopulations)) {
61140
return
62141
}
63142

64-
const weeklyLevels = allocation.weeklyLevels
65-
66-
let weeksOfStock = zonesPlan
67-
68-
if (location.level !== 'zone') {
69-
let plan
70-
if (options.version === 'last') {
71-
plan = location.plans[location.plans.length - 1]
72-
} else {
73-
plan = find(location.plans, isVersion.bind(null, stockCount.plans.version))
74-
}
75-
76-
if (!(plan && plan.weeksOfStock)) {
77-
return
78-
}
79-
weeksOfStock = plan.weeksOfStock
80-
}
81-
82-
let thresholds = Object.keys(weeklyLevels).reduce((index, productId) => {
143+
return Object.keys(weeklyLevels).reduce((index, productId) => {
83144
index[productId] = Object.keys(weeksOfStock).reduce((productThresholds, threshold) => {
84145
const level = weeklyLevels[productId] * weeksOfStock[threshold]
85146
const product = find(products, isId.bind(null, productId))
@@ -102,14 +163,10 @@ class ThresholdsService {
102163
return productThresholds
103164
}, {})
104165

105-
if (location.targetPopulation) {
106-
index[productId].targetPopulation = location.targetPopulation[productId]
107-
}
166+
index[productId].targetPopulation = targetPopulations[productId]
108167

109168
return index
110169
}, {})
111-
112-
return thresholds
113170
}
114171

115172
getThresholdsFor (stockCounts, products) {
@@ -129,7 +186,12 @@ class ThresholdsService {
129186
const id = this.smartId.idify(scLocation, locationIdPattern)
130187
const allocations = stockCount.allocations || { version: 1 }
131188
const plans = stockCount.plans || { version: 1 }
132-
index[id] = angular.merge({}, { allocations: allocations, plans: plans })
189+
const targetPopulations = stockCount.targetPopulations || { version: 1 }
190+
index[id] = angular.merge({}, {
191+
allocations,
192+
plans,
193+
targetPopulations
194+
})
133195

134196
if (scLocation.lga) {
135197
if (!promises.lga) {

test/thresholds.service.spec.js

+70-14
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,22 @@ describe('thresholds service', function () {
4747
}
4848
}
4949
],
50-
targetPopulation: {
51-
'product:a': 1000,
52-
'product:b': 2000
53-
},
50+
targetPopulations: [
51+
{
52+
version: 1,
53+
monthlyTargetPopulations: {
54+
'product:a': 500,
55+
'product:b': 1000
56+
}
57+
},
58+
{
59+
version: 2,
60+
monthlyTargetPopulations: {
61+
'product:a': 1000,
62+
'product:b': 2000
63+
}
64+
}
65+
],
5466
plans: [
5567
{
5668
version: 1,
@@ -73,15 +85,10 @@ describe('thresholds service', function () {
7385

7486
var stockCount = {
7587
allocations: { version: 2 },
76-
plans: { version: 1 }
88+
plans: { version: 1 },
89+
targetPopulations: { version: 2 }
7790
}
7891

79-
var stockCounts = [
80-
// { location: { zone: 'nc' }, allocations: { version: 2 }, plans: { version: 1 } },
81-
{ location: { zone: 'nc', state: 'kogi' }, allocations: { version: 2 }, plans: { version: 1 } },
82-
{ location: { zone: 'nc', state: 'kogi', lga: 'adavi' } }
83-
]
84-
8592
var products = [
8693
// TODO: presentation should be ints
8794
{ _id: 'product:a', presentation: '10' },
@@ -179,7 +186,7 @@ describe('thresholds service', function () {
179186
var actual = thresholdsService.calculateThresholds(unroundedLocation, stockCount, products)
180187
expect(actual).toEqual(expected)
181188
})
182-
it('uses the last version of plans and allocations if { version: "last"} is passed as option', function () {
189+
it('uses the last version of all factors if { version: "last"} is passed as option', function () {
183190
var expected = {
184191
'product:a': {
185192
min: 200,
@@ -197,10 +204,57 @@ describe('thresholds service', function () {
197204
var actual = thresholdsService.calculateThresholds(location, stockCount, products, null, { version: 'last' })
198205
expect(actual).toEqual(expected)
199206
})
207+
it('uses 1 as default version for all factors if no version is provided', function () {
208+
var expected = {
209+
'product:a': {
210+
min: 50,
211+
reOrder: 100,
212+
max: 250,
213+
targetPopulation: 500
214+
},
215+
'product:b': {
216+
min: 100,
217+
reOrder: 200,
218+
max: 500,
219+
targetPopulation: 1000
220+
}
221+
}
222+
var actual = thresholdsService.calculateThresholds(location, {}, products)
223+
expect(actual).toEqual(expected)
224+
})
225+
it('still works if the location doc contains a non versioned targetPopulation field', function () {
226+
var oldStyleTargetPopulations = {
227+
'product:a': 500,
228+
'product:b': 1000
229+
}
230+
var oldStyleLocation = angular.extend({}, location, { targetPopulation: oldStyleTargetPopulations })
231+
delete oldStyleLocation.targetPopulations
232+
var expected = {
233+
'product:a': {
234+
min: 50,
235+
reOrder: 100,
236+
max: 250,
237+
targetPopulation: 500
238+
},
239+
'product:b': {
240+
min: 100,
241+
reOrder: 200,
242+
max: 500,
243+
targetPopulation: 1000
244+
}
245+
}
246+
var actual = thresholdsService.calculateThresholds(oldStyleLocation, {}, products)
247+
expect(actual).toEqual(expected)
248+
})
200249
})
201250

202251
describe('getThresholdsFor', function () {
203252
it('takes an array of objects with location, allocations and plans fields and returns an object of location thresholds', function (done) {
253+
var stockCounts = [
254+
// { location: { zone: 'nc' }, allocations: { version: 2 }, plans: { version: 1 } },
255+
{ location: { zone: 'nc', state: 'kogi' }, allocations: { version: 2 }, plans: { version: 1 }, targetPopulations: { version: 2 } },
256+
{ location: { zone: 'nc', state: 'kogi', lga: 'adavi' } }
257+
]
204258
var expected = {
205259
// 'zone:nc': {
206260
// thresholds: {
@@ -211,6 +265,7 @@ describe('thresholds service', function () {
211265
'zone:nc:state:kogi': {
212266
allocations: { version: 2 },
213267
plans: { version: 1 },
268+
targetPopulations: { version: 2 },
214269
thresholds: {
215270
'product:a': { min: 100, reOrder: 200, max: 500, targetPopulation: 1000 },
216271
'product:b': { min: 200, reOrder: 400, max: 1000, targetPopulation: 2000 }
@@ -219,9 +274,10 @@ describe('thresholds service', function () {
219274
'zone:nc:state:kogi:lga:adavi': {
220275
allocations: { version: 1 },
221276
plans: { version: 1 },
277+
targetPopulations: { version: 1 },
222278
thresholds: {
223-
'product:a': { min: 50, reOrder: 100, max: 250, targetPopulation: 1000 },
224-
'product:b': { min: 100, reOrder: 200, max: 500, targetPopulation: 2000 }
279+
'product:a': { min: 50, reOrder: 100, max: 250, targetPopulation: 500 },
280+
'product:b': { min: 100, reOrder: 200, max: 500, targetPopulation: 1000 }
225281
}
226282
}
227283
}

0 commit comments

Comments
 (0)