]> git.plutz.net Git - invoices/blob - invoices.sh
b97c6e79db6c5c9ba8a1a0dd10dd3ec054fae3ff
[invoices] / invoices.sh
1 #!/bin/sh
2
3 sender_list(){
4   local select="$1" n name address iban bic
5   [ -d senders/ ] && for n in '' senders/*; do
6     [ "$n" ] &&  read -r address iban bic x<"$n"
7     name="$(UNSTRING "${address#address=}" |sed q |HTML)"
8     [ "${n#senders/}" = "$select" ] \
9     && printf '<option value="%s" selected=selected>%s</option>' "${n#senders/}" "$name" \
10     || printf '<option value="%s">%s</option>' "${n#senders/}" "$name"
11   done
12 }
13
14 client_list(){
15   local select="$1" n address hourly name
16   [ -d clients/ ] && for n in '' clients/*; do
17     [ "$n" ] && read -r address hourly x<"$n"
18     name="$(UNSTRING "${address#address=}" |sed q |HTML)"
19     [ "${n#clients/}" = "$select" ] \
20     && printf '<option value="%s" selected=selected>%s</option>' "${n#clients/}" "$name" \
21     || printf '<option value="%s">%s</option>' "${n#clients/}" "$name"
22   done
23 }
24
25 list_invoices(){
26   local sender client date number vat vatrate iban bic hourly \
27         taxtype nett tax gross total
28
29   [ -d invoices/ ] && for i in invoices/*; do
30     read -r sender client date number vat vatrate hourly x<<-EOF
31         $(sed q "$i")
32         EOF
33
34     [ ! -f "senders/${sender#sender=}" ] \
35     && sender="(unset)" \
36     || read -r sender iban bic x<"senders/${sender#sender=}"
37
38     [ ! -f "clients/${client#client=}" ] \
39     && client="(unset)" \
40     || read -r client hourly x<"clients/${client#client=}"
41
42     [ "${date#date=}" -ge 0 ] 2>&- \
43     && date="$(date -d "@${date#date=}" +%x)" \
44     || date="(unset)"
45
46     read -r taxtype nett tax gross x<<-EOF
47         $(invoice_total "${i#invoices/}")
48         EOF
49     case $taxtype in
50       nett)  total="${nett} € + VAT";;
51       gross) total="${gross} € incl. VAT";;
52       *) total="${gross} €";;
53     esac
54
55     printf '[div .invoice
56       [h2
57           %s]
58       [label From:] %s [label To:] %s [label on] %s
59       [label Amount:] %s
60       [a href="/invoices/%s" Edit]
61     ]' "$(UNSTRING "${number#number=}" |HTML)" \
62        "$(UNSTRING "${sender#address=}" |sed q |HTML)" \
63        "$(UNSTRING "${client#address=}" |sed q |HTML)" "$(HTML "$date")" \
64        "$total" \
65        "$(HTML ${i#invoices/})"
66   done
67 }
68
69 edit_invoice(){
70   local id="$1" sender client date number vat vatrate caddress hourly \
71         taxtype nett tax gross
72
73   if [ -f "invoices/$id" ]; then
74     read -r sender client date number vat vatrate hourly x<<-EOF
75         $(sed q "invoices/$id")
76         EOF
77   fi
78
79   [ "${date#date=}" -ge 0 ] 2>&- \
80   && date="$(date -d "@${date#date=}" +%F)" \
81   || date="$(date +%F)"
82   [ "${number#number=}" ] \
83   && number="${number#number=}" \
84   || number="$(date +%s)"
85   [ "${vatrate#vatrate=}" -ge 0 ] 2>&- \
86   && vatrate="${vatrate#vatrate=}" \
87   || vatrate=19
88
89   [ -f "clients/${client#client=}" ] \
90   && read -r caddress chourly x<"clients/${client#client=}"
91   [ "${chourly#hourly=}" -ge 0 ] 2>&- \
92   && chourly="${chourly#hourly=}" \
93   || chourly=0
94   [ "${hourly#hourly=}" -ge 0 ] 2>&- \
95   && hourly="${hourly#hourly=}" \
96   || hourly="${chourly}"
97
98   tid="$(transid "invoices/$id")"
99
100   read -r taxtype nett tax gross x<<-EOF
101         $(invoice_total "$id")
102         EOF
103
104   cat <<-EOF 
105         [form method="POST" action="/update_invoice"
106           [hidden "id" "$(HTML "$id")"]
107         
108           [label Sender:]
109           [select name=sender
110             $(sender_list "${sender#sender=}")
111           ]
112         
113           [label Client:]
114           [select name=client
115             $(client_list "${client#client=}")
116           ]
117         
118           [label for=number Invoice Number:]
119           [input #number name=number value="$(UNSTRING "${number}" |HTML)"]
120         
121           [label for=date Date:]
122           [input #date name=date value="${date}" placeholder="YYYY-MM-TT"]
123         
124           [label for=hourly Hourly Rate:]
125           [input #hourly type=number name=hourly value="${hourly}"]
126
127           [radio "vat" "smallbusiness" #vatsb $([ "${vat#vat=}" = smallbusiness ] && printf checked) ]
128           [label for=vatsb Small business exemption from VAT]
129           [radio "vat" "nett" #vatnett $([ "${vat#vat=}" = nett ] && printf checked)]
130           [label for=vatnett Nett]
131           [radio "vat" "gross" #vatgross $([ "${vat#vat=}" = gross ] && printf checked)]
132           [label for=vatgross Gross]
133           [label for=vatrate VAT Rate: [input type=number name="vatrate" value="${vatrate}"]% ]
134         
135           [table
136             [tr [th Date] [th Work] [th Hours] [th Price] ]
137 $({ sed 1d "invoices/$id"; printf 'time= work= hours=\n'; } \
138   | while read -r time work hours x; do
139     hours="$(UNSTRING "${hours#hours=}" |grep -m1 -xE '[0-9]+' || printf 0)"
140     printf '[tr
141             [td [textarea name=time
142 %s] ]
143             [td [textarea name=work
144 %s] ]
145             [td [input type=number name=hours value="%s"] ]
146             [td %s]
147    ]' "$(UNSTRING "${time#time=}" |HTML)" \
148       "$(UNSTRING "${work#work=}" |HTML)" \
149       "$hours" "$((hours * hourly)) €"
150   done
151 )
152             [tr [td colspan=4 
153             $(case $taxtype in
154               (nett)  printf 'Sum: %7.2f €[br] + VAT: %7.2f €[br] [strong Total:] %7.2f €' \
155                       $nett $tax $gross ;;
156               (gross) printf '[strong Total:] %7.2f €[br] incl. nett: %7.2f €[br] + VAT: %7.2f €' \
157                       $gross $nett $tax ;;
158               (*) printf '[strong Total:] %.2f €' $nett ;;
159             esac)
160             ]]
161           ]
162           [submit "genpdf" "$tid" Export PDF]
163           [submit "update" "$tid" Update]
164         ]
165         EOF
166 }
167
168 invoice_total(){
169   local id="$1" sender client date number vat vatrate \
170         total=0 caddress hourly time work hours
171
172   if [ -f "invoices/$id" ]; then
173     read -r sender client date number vat vatrate hourly x<<-EOF
174         $(sed q "invoices/$id")
175         EOF
176
177     [ "${hourly#hourly=}" -gt 0 ] 2>&- \
178     && hourly="${hourly#hourly=}" \
179     || hourly=0
180     [ "${vatrate#vatrate=}" -ge 0 ] 2>&- \
181     && vatrate="${vatrate#vatrate=}" \
182     || vatrate=19
183
184     sed 1d "invoices/$id" \
185     | { while read -r time work hours; do
186         [ "${hours#hours=}" -gt 0 ] 2>&- \
187         && hours="${hours#hours=}" \
188         || hours=0
189         total=$((total + hours * hourly))
190       done
191       case $vat in
192         vat=nett)
193           awk "BEGIN {
194             printf \"nett       %.2f    %.2f    %.2f\",
195               $total, int($total * $vatrate + .5) / 100,
196               $total + int($total * $vatrate + .5) / 100
197           }" ;;
198         vat=gross)
199           awk "BEGIN {
200             printf \"gross      %.2f    %.2f    %.2f\",
201               $total - int($total / (100 + $vatrate) * $vatrate * 100 + .5) / 100,
202               int($total / (100 + $vatrate) * $vatrate * 100 + .5) / 100, $total
203           }" ;;
204         *)
205           awk "BEGIN {
206             printf \"notax      %.2f    %.2f    %.2f\",
207               $total, 0, $total
208           }" ;;
209       esac
210     }
211   else
212     printf %.2f\\n 0
213   fi
214 }
215
216 update_invoice(){
217   local id="$(POST id |checkid)" extra=0 tid
218   tid="$(transid invoices/$id)"
219
220   if [ "$(POST update)" = "$tid" ] || [ "$(POST genpdf)" = "$tid" ]; then
221     mkdir -p invoices
222
223     for n in "$(POST_COUNT time)" "$(POST_COUNT work)" "$(POST_COUNT hours)"; do
224       [ "$n" -gt "$extra" ] && extra="$n"
225     done
226
227     { printf 'sender=%s client=%s       date=%s number=%s       vat=%s  vatrate=%s      hourly=%s\n' \
228         "$(POST sender)" "$(POST client)" \
229         "$(date -d "$(POST date)" +%s)" \
230         "$(POST number |STRING)" \
231         "$(POST vat |grep -m1 -xE 'smallbusiness|gross|nett')" \
232         "$(POST vatrate |grep -m1 -xE '[0-9]+')" \
233         "$(POST hourly |grep -m1 -xE '[0-9]+')"
234       for n in $(seq 1 $extra); do
235         printf 'time=%s work=%s hours=%s\n' \
236           "$(POST time $n |STRING)" "$(POST work $n |STRING)" \
237           "$(POST hours $n |STRING)" \
238         | grep -xvF 'time=      work=   hours=0'
239       done
240     } >"invoices/$id"
241
242     [ -d .git ] && {
243       git add "invoices/$id"
244       git commit -m 'Update invoice info for "'"$(POST number)"'"' -- "invoices/$id"
245     } >/dev/null
246   fi
247   if [ "$(POST genpdf)" ]; then
248     read -r sender client date x<"invoices/$id"
249     read -r saddress x <"senders/${sender#sender=}"
250     read -r caddress x <"clients/${client#client=}"
251     filename="Rechnung $(UNSTRING "${saddress#address=}" |sed 1q) an $(UNSTRING "${caddress#address=}" |sed 1q) $(date -d@"${date#date=}" +%F).pdf"
252
253     . $_EXEC/odtgen.sh
254     genpdf "$id"
255     REDIRECT "/export/${id}.pdf/$(URL "${filename}" |sed s/%0D//g)"
256     exit 0
257   fi
258   REDIRECT "/invoices/$id"
259 }