PageRenderTime 25ms CodeModel.GetById 19ms RepoModel.GetById 15ms app.codeStats 0ms

/FileCabinet/SuiteScripts/sku-analytics/total-monthly-by-reseller/sa-TotalMonthlyByReseller.js

https://gitlab.com/stoicsoftware/netsuite-sku-analytics
JavaScript | 342 lines | 95 code | 22 blank | 225 comment | 0 complexity | 95ed62aa9e1bf63cb76b030a870107e6 MD5 | raw file
  1. define([
  2. "N/search",
  3. "../lib/ramda.min",
  4. "../lib/moment.min",
  5. "../lib/sa-DateUtilities"
  6. ], function (s, R, moment, d) {
  7. /**
  8. * Business logic and data manipulation methods for the Total Monthly
  9. * Sales by Reseller data
  10. *
  11. * @exports sa/total-monthly-by-reseller
  12. *
  13. * @requires N/search
  14. * @requires ramda
  15. * @requires moment
  16. * @requires sa/date-util
  17. *
  18. * @copyright 2016 Stoic Software
  19. * @author Eric T Grubaugh <eric@stoic.software>
  20. *
  21. * @NApiVersion 2.x
  22. * @NModuleScope Public
  23. */
  24. var exports = {};
  25. /**
  26. * The data format used to define Total Monthly Sales by Reseller data
  27. *
  28. * @typedef {Object} MonthlySalesByResellerData
  29. *
  30. * @property resellerId {Number} Internal ID of the Reseller
  31. * @property resellerName {String} Display name of the Reseller
  32. * @property month {moment} A moment representing the first of the month
  33. * @property sales {Number} The revenue from sales in the month for the
  34. * Reseller
  35. */
  36. /**
  37. * The current timestamp at the time this module is loaded
  38. *
  39. * @type {moment}
  40. *
  41. * @private
  42. * @property now
  43. */
  44. var now = R.always(moment());
  45. /**
  46. * monthToData :: moment -> MonthlySalesByResellerData
  47. *
  48. * Translates a moment representing a month in the year into a processable
  49. * data object
  50. *
  51. * @governance 0
  52. *
  53. * @param m {moment} The moment to translate
  54. *
  55. * @return {MonthlySalesByResellerData} Translated moment
  56. *
  57. * @private
  58. * @function monthToData
  59. */
  60. function monthToData(m) {
  61. return {
  62. "month": m,
  63. "sales": 0
  64. };
  65. }
  66. /**
  67. * monthsToData :: [moment] -> [MonthlySalesByResellerData]
  68. *
  69. * Translates a list of moments representing a month in the year into a
  70. * list of processable data objects
  71. *
  72. * @governance 0
  73. *
  74. * @param data {moment[]} The moments to translate
  75. *
  76. * @return {MonthlySalesByResellerData[]} Translated moments
  77. *
  78. * @private
  79. * @function monthsToData
  80. */
  81. var monthsToData = R.map(monthToData);
  82. /**
  83. * generateYear :: moment -> [MonthlySalesByResellerData]
  84. *
  85. * @governance 0
  86. *
  87. * @param seedDate {moment} The moment to seed year generation
  88. *
  89. * @return {MonthlySalesByResellerData[]} Full year of empty sales data
  90. *
  91. * @private
  92. * @function generateYear
  93. */
  94. var generateYear = R.pipe(
  95. d.monthsSameYear,
  96. monthsToData
  97. );
  98. var currentRollingYear = generateYear(now());
  99. /**
  100. * emptyYear :: [MonthlySalesByResellerData] ->
  101. * [MonthlySalesByResellerData]
  102. *
  103. * Accepts a list of Monthly Sales by Reseller data and ensures that a
  104. * value
  105. * exists for every month in the current rolling year
  106. *
  107. * @governance 0
  108. *
  109. * @param data {MonthlySalesByResellerData[]} The Monthly Sales by Reseller
  110. * data to fill out
  111. *
  112. * @return {MonthlySalesByResellerData[]} A full year of Monthly Sales by
  113. * Reseller data
  114. *
  115. * @private
  116. * @function emptyYear
  117. */
  118. var emptyYear = R.flip(
  119. R.useWith(R.map, [
  120. R.pipe(R.head, R.merge),
  121. R.identity
  122. ])
  123. )(currentRollingYear);
  124. /**
  125. * resultToData :: search.Result -> MonthlySalesByResellerData
  126. *
  127. * Translates a Total Monthly Sales by Reseller search result into a
  128. * processable data object
  129. *
  130. * @governance 0
  131. *
  132. * @param result {search.Result} The search result to translate
  133. *
  134. * @return {MonthlySalesByResellerData} Translated search result
  135. *
  136. * @private
  137. * @function resultToData
  138. */
  139. function resultToData(result) {
  140. var month = moment(
  141. result.getValue({
  142. "name": "formulatext",
  143. "summary": s.Summary.GROUP
  144. }),
  145. "MMMM YYYY"
  146. );
  147. return {
  148. "resellerId": result.getValue({
  149. "name": "partner",
  150. "summary": s.Summary.GROUP
  151. }),
  152. "resellerName": result.getText({
  153. "name": "partner",
  154. "summary": s.Summary.GROUP
  155. }),
  156. "month": month.startOf("month"),
  157. "sales": parseFloat(result.getValue({
  158. "name": "amount",
  159. "summary": s.Summary.SUM
  160. }))
  161. };
  162. }
  163. /**
  164. * resultsToData :: [search.Result] -> [MonthlySalesByResellerData]
  165. *
  166. * Translates a list of Total Monthly Sales search results into a list of
  167. * processable data object
  168. *
  169. * @governance 0
  170. *
  171. * @param data {search.Result[]} The list of search results to translate
  172. *
  173. * @return {MonthlySalesByResellerData[]} List of Monthly Sales data objects
  174. *
  175. * @private
  176. * @function resultsToData
  177. */
  178. var resultsToData = R.map(resultToData);
  179. /**
  180. * sortChronologically :: [MonthlySalesBySkuData] ->
  181. * [MonthlySalesBySkuData]
  182. *
  183. * Sorts a list of Monthly Sales by SKU data chronologically in ascending
  184. * order
  185. *
  186. * @governance 0
  187. *
  188. * @param data {MonthlySalesBySkuData[]} The list to sort
  189. *
  190. * @return {MonthlySalesBySkuData[]} The sorted list
  191. *
  192. * @private
  193. * @function sortChronologically
  194. */
  195. var sortChronologically = R.sortBy(R.pipe(
  196. R.prop("month"),
  197. R.invoker(0, "valueOf")
  198. ));
  199. var groupByReseller = R.groupBy(R.prop("resellerId"));
  200. var unionByMonth = R.unionWith(
  201. R.useWith(d.sameMonth, [R.prop("month"), R.prop("month")])
  202. );
  203. var fillYear = R.pipe(
  204. R.converge(unionByMonth, [R.identity, emptyYear]),
  205. sortChronologically
  206. );
  207. var fillYears = R.map(fillYear);
  208. /**
  209. * translateResults :: [search.Result] -> [MonthlySalesByResellerData]
  210. *
  211. * Translates the given search results to Monthly Sales by Reseller data
  212. *
  213. * @governance 0
  214. *
  215. * @param results {search.Result[]} List of Total Monthly Sales by Reseller
  216. * search results to translate into processable data Objects. Results
  217. * must be grouped by a formulatext column representing the month and
  218. * contain a summed quantity column representing the sales for that
  219. * month
  220. *
  221. * @return {MonthlySalesByResellerData[]} Data list processable by the Total
  222. * Monthly by Reseller module
  223. *
  224. * @static
  225. * @function translateResults
  226. */
  227. var translateResults = R.pipe(
  228. resultsToData,
  229. groupByReseller,
  230. fillYears
  231. );
  232. /**
  233. * label :: MonthlySalesByResellerData -> String
  234. *
  235. * Generates a label for a MonthlySalesByResellerData object that can be
  236. * used on, for instance, a chart axis
  237. *
  238. * @governance 0
  239. *
  240. * @param data {MonthlySalesByResellerData} The data object to generate a
  241. * label for
  242. *
  243. * @return {String} The label for the given data object
  244. *
  245. * @private
  246. * @function label
  247. */
  248. var label = R.pipe(
  249. R.prop("month"),
  250. d.format("MMM YYYY")
  251. );
  252. /**
  253. * labels :: [MonthlySalesByResellerData] -> [String]
  254. *
  255. * Generates the list of axis labels for the given dataset
  256. *
  257. * @governance 0
  258. *
  259. * @param data {MonthlySalesByResellerData[]} The dataset to transform to
  260. * labels
  261. *
  262. * @return {String[]} The list of labels to be used for the data
  263. *
  264. * @static
  265. * @function labels
  266. */
  267. var labels = R.always(R.pipe(
  268. sortChronologically,
  269. R.map(label)
  270. )(currentRollingYear));
  271. /**
  272. * resellerGroupToDataset :: [MonthlySalesByResellerData] -> Chart.Dataset
  273. *
  274. * Translates the Monthly Sales data for a single Reseller into a chartable
  275. * dataset
  276. *
  277. * @governance 0
  278. *
  279. * @param data {MonthlySalesByResellerData[]} The Monthly Sales data for a
  280. * single Reseller
  281. *
  282. * @return {Chart.Dataset} Chartable dataset for the item group
  283. *
  284. * @private
  285. * @function resellerGroupToDataset
  286. */
  287. function resellerGroupToDataset(data) {
  288. return {
  289. "label": R.pipe(R.head, R.prop("resellerName"))(data),
  290. "data": R.pluck("sales")(data)
  291. };
  292. }
  293. /**
  294. * datasets :: {k: MonthlySalesByResellerData} -> [Chart.Dataset]
  295. *
  296. * Translates an Object of Monthly Sales by Reseller data that is grouped
  297. * by Reseller into a list of chartable datasets
  298. *
  299. * @governance 0
  300. *
  301. * @param data {Object} The Monthly Sales data grouped by Reseller
  302. *
  303. * @return {Number[]} The values to be used for the chart
  304. *
  305. * @static
  306. * @function datasets
  307. */
  308. var datasets = R.pipe(
  309. R.map(resellerGroupToDataset),
  310. R.values,
  311. R.sortBy(R.pipe(R.prop("data"), R.sum)),
  312. // TODO Parameterize this somehow
  313. R.takeLast(5),
  314. R.tap(console.log)
  315. );
  316. exports.datasets = datasets;
  317. exports.labels = labels;
  318. exports.translateResults = translateResults;
  319. return exports;
  320. });