@furkot/export-expense-report 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Readme.md ADDED
@@ -0,0 +1,36 @@
1
+ [![NPM version][npm-image]][npm-url]
2
+ [![Build Status][build-image]][build-url]
3
+ [![Dependency Status][deps-image]][deps-url]
4
+
5
+ # expense-report
6
+
7
+ Generate CSV expense report from [Furkot] trip data.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ $ npm install --save @furkot/export-expense-report
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```js
18
+ const expenseReport = require('@furkot/export-expense-report');
19
+
20
+ expenseReport(trip);
21
+ ```
22
+
23
+ ## License
24
+
25
+ MIT © [Natalia Kowalczyk](https://melitele.me)
26
+
27
+ [Furkot]: https://trips.furkot.com
28
+
29
+ [npm-image]: https://img.shields.io/npm/v/@furkot/export-expense-report
30
+ [npm-url]: https://npmjs.org/package/@furkot/export-expense-report
31
+
32
+ [build-url]: https://github.com/furkot/export-expense-report/actions/workflows/check.yaml
33
+ [build-image]: https://img.shields.io/github/actions/workflow/status/furkot/export-expense-report/check.yaml?branch=main
34
+
35
+ [deps-image]: https://img.shields.io/librariesio/release/npm/@furkot/export-expense-report
36
+ [deps-url]: https://libraries.io/npm/@furkot%2Fexport-expense-report
package/index.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./lib/expense-report');
@@ -0,0 +1,136 @@
1
+ const format = require('./format');
2
+
3
+ exports = module.exports = expenseReport;
4
+
5
+ exports.contentType = 'text/csv';
6
+ exports.extension = 'csv';
7
+ exports.encoding = 'utf8';
8
+
9
+ const types = init(
10
+ [98, 161],
11
+ 'parking',
12
+ init(
13
+ [10, 25, 178, 179],
14
+ 'fuel',
15
+ init(
16
+ [9, 24, 27, 28, 30, 31, 32, 90, 139, 143, 156, 158, 159, 172],
17
+ 'meal',
18
+ Object.create(null)
19
+ )
20
+ )
21
+ );
22
+
23
+ function init(arr, tp, r) {
24
+ return arr.reduce((r, v) => {
25
+ r[v] = tp;
26
+ return r;
27
+ }, r);
28
+ }
29
+ function prepare(line) {
30
+ return line.map(function (item) {
31
+ if (typeof item === 'number') {
32
+ return '' + item;
33
+ }
34
+ if (typeof item === 'string') {
35
+ // quote strings
36
+ return '"' + item.replace(/"/g, '""') + '"';
37
+ }
38
+ // empty string for everything else
39
+ return '';
40
+ }).join(',') + '\n';
41
+ }
42
+
43
+ function getType({
44
+ nights,
45
+ sym
46
+ }) {
47
+ if (nights) {
48
+ return 'lodging';
49
+ }
50
+ return types[sym] || '';
51
+ }
52
+
53
+ function* expenseReport(options) {
54
+ const {
55
+ metadata: {
56
+ currency,
57
+ units,
58
+ mileageRate
59
+ },
60
+ routes
61
+ } = options;
62
+
63
+ const header = [
64
+ 'Description',
65
+ 'Date',
66
+ 'Amount',
67
+ 'Type',
68
+ 'Notes'
69
+ ];
70
+
71
+ const steps = routes[0].points;
72
+ let from;
73
+
74
+ function getLines(i) {
75
+ const {
76
+ address,
77
+ arrival_time,
78
+ cost,
79
+ costRoute,
80
+ distance,
81
+ name,
82
+ notes,
83
+ tags
84
+ } = steps[i];
85
+ const to = name || address;
86
+ const date = format.date(new Date(arrival_time));
87
+ const lines = [];
88
+
89
+ if (mileageRate && distance) {
90
+ const line = [];
91
+
92
+ line.push(format.description(
93
+ [from, to].join(' - '),
94
+ format.distance(distance, 1, units, true),
95
+ currency
96
+ ));
97
+ line.push(date);
98
+ line.push(format.distance(distance * mileageRate / 100, 2, units));
99
+ line.push('mileage');
100
+
101
+ lines.push(prepare(line));
102
+ }
103
+ if (costRoute) {
104
+ const line = [];
105
+
106
+ line.push(format.description([from, to].join(' - '), currency));
107
+ line.push(date);
108
+ line.push(format.amount(costRoute / 100, 2));
109
+ line.push('tolls');
110
+
111
+ lines.push(prepare(line));
112
+ }
113
+ if (cost) {
114
+ const line = [];
115
+
116
+ line.push(format.description(name || address, tags, currency));
117
+ line.push(date);
118
+ line.push(format.amount(cost / 100, 2));
119
+ line.push(getType(steps[i]));
120
+ line.push(notes);
121
+
122
+ lines.push(prepare(line));
123
+ }
124
+
125
+ from = to;
126
+ return lines;
127
+ }
128
+
129
+ yield prepare(header);
130
+ for (let i = 0; i < steps.length; i += 1) {
131
+ const lines = getLines(i);
132
+ for (let j = 0; j < lines.length; j += 1) {
133
+ yield lines[j];
134
+ }
135
+ }
136
+ }
package/lib/format.js ADDED
@@ -0,0 +1,43 @@
1
+ module.exports = {
2
+ amount,
3
+ date,
4
+ description,
5
+ distance
6
+ };
7
+
8
+ function pad(n) {
9
+ return n < 10 ? '0' + n : n;
10
+ }
11
+
12
+ function amount(amt, precision) {
13
+ precision = precision || 0;
14
+ return amt.toFixed(precision);
15
+ }
16
+
17
+ function date(d) {
18
+ return [d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate()].map(pad).join('-');
19
+ }
20
+
21
+ function description() {
22
+ return Array.prototype.reduce.call(arguments, (r, v) => {
23
+ if (v) {
24
+ if (Array.isArray(v)) {
25
+ r.push(...v);
26
+ }
27
+ else {
28
+ r.push(v);
29
+ }
30
+ }
31
+ return r;
32
+ }, []).join(' / ');
33
+ }
34
+
35
+ function distance(dist, precision, units, withUnits) {
36
+ const div = units === 'km' ? 1000 : 1609.344;
37
+ precision = precision || 0;
38
+ const result = (dist / div).toFixed(precision);
39
+ if (withUnits) {
40
+ return `${result} ${units}`;
41
+ }
42
+ return result;
43
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@furkot/export-expense-report",
3
+ "version": "0.0.1",
4
+ "description": "Generate CSV expense report from Furkot trip data.",
5
+ "author": {
6
+ "name": "Natalia Kowalczyk",
7
+ "email": "melitele@code42day.com",
8
+ "url": "https://melitele.me"
9
+ },
10
+ "repository": "furkot/export-expense-report",
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "expense-report",
14
+ "furkot",
15
+ "roadtrip",
16
+ "export",
17
+ "planner",
18
+ "CSV"
19
+ ],
20
+ "dependencies": {},
21
+ "devDependencies": {
22
+ "@pirxpilot/jshint": "~3",
23
+ "should": "~13"
24
+ },
25
+ "scripts": {
26
+ "test": "make check"
27
+ },
28
+ "files": [
29
+ "index.js",
30
+ "lib"
31
+ ]
32
+ }