Skip to content

Commit da0f40a

Browse files
author
Andreas Müller
committed
add invoice
1 parent 843339e commit da0f40a

File tree

3 files changed

+150
-2
lines changed

3 files changed

+150
-2
lines changed

src/gtt-report.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const Output = {
1414
csv: require('./output/csv'),
1515
pdf: require('./output/pdf'),
1616
markdown: require('./output/markdown'),
17+
invoice: require('./output/invoice'),
1718
dump: require('./output/dump'),
1819
xlsx: require('./output/xlsx')
1920
};
@@ -64,6 +65,13 @@ program
6465
.option('--verbose', 'show verbose output')
6566
.option('--show_without_times', 'show issues/merge requests without time records')
6667
.option('--from_dump <file>', 'instead of querying gitlab, use data from the given dump file')
68+
.option('--invoiceTitle <title>', 'title on invoice')
69+
.option('--invoiceAddress [address...]', 'address')
70+
.option('--invoiceCurrency <currency>', 'currecnty on invoice')
71+
.option('--invoiceCurrencyPerHour <number>', 'hourly wage rate on invoice')
72+
.option('--invoiceVAT <number>', 'vat decimal (20% = 0.2)')
73+
.option('--invoiceDate <number>', 'date string')
74+
.option('--invoiceCurrencyMaxUnit <number>', 'rouning invoice total, e.g. 0.01, 0.05 or 1')
6775
.parse(process.argv);
6876

6977
// init helpers
@@ -123,6 +131,16 @@ config
123131
.set('type', program.opts().type)
124132
.set('subgroups', program.opts().subgroups)
125133
.set('_verbose', program.opts().verbose)
134+
<<<<<<< Updated upstream
135+
=======
136+
.set('invoiceTitle', program.opts().invoiceTitle)
137+
.set('invoiceAddress', program.opts().invoiceAddress)
138+
.set('invoiceCurrency', program.opts().invoiceCurrency)
139+
.set('invoiceCurrencyPerHour', program.opts().invoiceCurrencyPerHour)
140+
.set('invoiceVAT', program.opts().invoiceVAT)
141+
.set('invoiceDate', program.opts().invoiceDate)
142+
.set('invoiceCurrencyMaxUnit', program.opts().invoiceCurrencyMaxUnit)
143+
>>>>>>> Stashed changes
126144
.set('_createDump', program.opts().output === 'dump');
127145

128146
// date shortcuts
@@ -150,10 +168,10 @@ let reports = new ReportCollection(config),
150168
output;
151169

152170
// warnings
153-
if (config.get('iids').length > 1 && config.get('query').length > 1) {
171+
if (config.get('iids').length >= 1 && config.get('query').length > 1) {
154172
Cli.warn(`The ids argument is ignored when querying issues and merge requests`);
155173
}
156-
if (config.get('iids').length > 1 && (config.get('type') !== 'project' || projects.length > 1)) {
174+
if (config.get('iids').length >= 1 && (config.get('type') !== 'project' || projects.length > 1)) {
157175
Cli.warn(`The ids argument is ignored when querying multiple projects`);
158176
}
159177
if ((config.get('report').includes('issues') && !config.get('query').includes('issues'))) {

src/output/base.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ class base {
143143
'total spent': this.config.toHumanReadable(totalSpent, 'stats'),
144144
'spent': this.config.toHumanReadable(spent, 'stats')
145145
};
146+
this.totalEstimate = totalEstimate;
147+
this.spent = spent;
148+
this.totalSpent = totalSpent;
146149
}
147150

148151
/**

src/output/invoice.js

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
const _ = require('underscore');
2+
3+
const Table = require('markdown-table');
4+
const Base = require('./base');
5+
6+
const format = {
7+
headline: h => `\n### ${h}\n`,
8+
warning: w => `${w}`
9+
};
10+
11+
/**
12+
* stdout table output
13+
*/
14+
class invoice extends Base {
15+
constructor(config, report) {
16+
super(config, report);
17+
this.format = format;
18+
this.invoiceCurrency = this.config.get('invoiceCurrency');
19+
this.invoiceCurrencyPerHour = this.config.get('invoiceCurrencyPerHour');
20+
this.invoiceVAT = this.config.get('invoiceVAT');
21+
this.invoiceCurrencyMaxUnit = this.config.get('invoiceCurrencyMaxUnit');
22+
this.totalhForInvoice = this.spent / 3600.0;
23+
this.totalForInvoiceExkl = this.totalhForInvoice * this.invoiceCurrencyPerHour;
24+
this.totalForInvoiceMwst = this.totalForInvoiceExkl * this.invoiceVAT;
25+
this.totalForInvoice = this.totalForInvoiceExkl + this.totalForInvoiceMwst;
26+
// round
27+
this.totalForInvoice = Math.round(this.totalForInvoice/this.invoiceCurrencyMaxUnit)*this.invoiceCurrencyMaxUnit;
28+
}
29+
30+
31+
concat(data, separator) {
32+
if(null == data) {
33+
return "";
34+
}
35+
if(!Array.isArray(data)) {
36+
return data;
37+
}
38+
return data.join(separator);
39+
}
40+
41+
makeStats() {
42+
43+
let stats = '';
44+
45+
_.each(this.stats, (time, name) => stats += `\n* **${name}**: ${time}`);
46+
stats += `\n`;
47+
48+
if (this.projects.length > 1) {
49+
_.each(this.projects, (time, name) => stats += `\n* **${name.red}**: ${time}`);
50+
stats += `\n`;
51+
}
52+
// REMOVE
53+
// _.each(this.users, (time, name) => stats += `\n* **${name}**: ${time}`);
54+
let to = this.concat(this.config.get('invoiceAddress'), '</br>');
55+
let from = this.concat(this.config.get('invoiceSettings').from, '</br>');
56+
let opening = this.concat(this.config.get('invoiceSettings').opening, '</br>');
57+
let closing = this.concat(this.config.get('invoiceSettings').closing, '</br>');
58+
let positionText = this.concat(this.config.get('invoiceSettings').positionText, '</br>');
59+
60+
this.out +=
61+
`<div class="senderBox">${from}</div>
62+
63+
<div class="addressBox">
64+
<div class="addressFrom">${this.config.get('invoiceSettings').fromShort}</div>
65+
<div class="addressTo">${to}</div>
66+
</div>
67+
<div class="dateBox">${this.config.get('invoiceDate')}</div>
68+
69+
# ${this.config.get('invoiceTitle')}
70+
71+
${opening}
72+
73+
<div class="positionBox">
74+
<div class="positionDesc">${positionText} (${this.totalhForInvoice.toFixed(2)} Stunden zu ${this.invoiceCurrencyPerHour} ${this.invoiceCurrency})</div>
75+
<div class="positionValue">${this.invoiceCurrency} ${this.totalForInvoiceExkl.toFixed(2)}</div>
76+
<div class="positionDesc">MWST (${this.invoiceVAT*100}%)</div>
77+
<div class="positionValue">${this.invoiceCurrency} ${this.totalForInvoiceMwst.toFixed(2)}</div>
78+
<div class="positionDescTot">Rechnungsbetrag</div>
79+
<div class="positionValueTot">${this.invoiceCurrency} ${this.totalForInvoice.toFixed(2)}</div>
80+
</div>
81+
82+
${this.config.get('invoiceSettings').bankAccount}
83+
84+
${closing}
85+
86+
87+
<h1 style="page-break-before: always;"><br/><br/>Stundenrapport</h1>`;
88+
89+
this.headline('Total');
90+
this.write(stats.substr(1));
91+
}
92+
93+
makeIssues() {
94+
this.headline('Issues');
95+
96+
if (this.report.issues.length === 0)
97+
return this.warning('No issues found');
98+
99+
let issues = [this.config.get('issueColumns').map(c => c.replace('_', ' '))];
100+
this.report.issues.forEach(issue => issues.push(this.prepare(issue, this.config.get('issueColumns'))));
101+
102+
this.write(Table(issues));
103+
}
104+
105+
makeMergeRequests() {
106+
this.headline('Merge Requests');
107+
108+
if (this.report.mergeRequests.length === 0)
109+
return this.warning('No merge requests found');
110+
111+
let mergeRequests = [this.config.get('mergeRequestColumns').map(c => c.replace('_', ' '))];
112+
this.report.mergeRequests.forEach(mergeRequest => mergeRequests.push(this.prepare(mergeRequest, this.config.get('mergeRequestColumns'))));
113+
114+
this.write(Table(mergeRequests));
115+
}
116+
117+
makeRecords() {
118+
this.headline('Details');
119+
120+
let times = [this.config.get('recordColumns').map(c => c.replace('_', ' '))];
121+
this.times.forEach(time => times.push(this.prepare(time, this.config.get('recordColumns'))));
122+
123+
this.write(Table(times));
124+
}
125+
}
126+
127+
module.exports = invoice;

0 commit comments

Comments
 (0)