ref: b9b79118f7e75751dfb71e082fda4ad095aa4202
author: sirjofri <sirjofri@sirjofri.de>
date: Tue Aug 5 17:45:29 EDT 2025
adds first script files
--- /dev/null
+++ b/README
@@ -1,0 +1,76 @@
+FOLDERS
+
+- /bin contains helper scripts that don't fit into the mkfile
+- /client contains all clients and their information
+- /invoice contains all invoices
+- /template contains all invoice templates
+
+The mkfile is used to generate the invoice PDF files (and later more)
+
+bin/mkinvoice generates a new invoice folder with stub files.
+
+
+FOLDERS IN DETAIL
+
+The invoice files are:
+----------------------
+
+- client: name of the client (matches folder in /client)
+- data.tsv: invoice positions
+- date: date of the invoice (unix timestamp)
+- msg: (optional) letter message. If this does not exist, it uses the client/msg instead, if that doesn't exist, template/name.msg
+- template: name of the template file to use (matches file in /template)
+
+Generated files:
+
+- invoice-ID.pdf: resulting PDF file
+- md5sum: sum of relevant files. This sum is also set as the ID in the PDF file
+
+Additional files (TBD):
+
+- paid: contains infos about payment
+- history: list of events that happened around this invoice (storno, ...)
+- those files should influence the calculation, e. g. for payment warnings
+
+
+The template files are:
+-----------------------
+
+- name: actual letter template
+- name.msg: default message for that template
+
+
+The client files are:
+---------------------
+
+- addr: address lines for that client
+- msg: (optional) letter message. If this does not exist, it uses the template/name.msg. Can be overridden by a invoice/msg file.
+- vcard.vcf: vcard file with contact info (TBD)
+
+
+INDIVIDUAL FILES
+
+
+The message file (msg):
+-----------------------
+
+- troff format
+- First line is the subject (paragraph)
+- \*(iv (string register) is the invoice ID/number
+- .IV (macro) prints the invoice positions (values)
+- These macros can also be used in template files
+
+
+The data.tsv file:
+------------------
+
+- tab separated values
+- lines that start with '#' are comments
+- fields: amount, price, VAT/Tax, name/description
+
+
+The client/addr file:
+---------------------
+
+- troff format, no fill (newlines are newlines)
+- Address like on an envelope
--- /dev/null
+++ b/bin/formatdata.awk
@@ -1,0 +1,50 @@
+#!/bin/awk -F '\t' -f
+
+BEGIN {+ print ".TS"
+ print "expand linesize(5);"
+ print "l l l l l"
+ print "l n n n n"
+ print "."
+ print "_"
+ printf "Name\tCount\tPrice\tTax\tTotal\n"
+ print "="
+}
+
+!/^#/ && NF >= 4 {+ number = $1
+ single = $2
+ vat = $3
+ name = $4
+
+ total = number * single
+
+ vats[vat] += total
+ final += total
+
+ vv = "\\R-"
+ if (vat > 0)
+ vv = sprintf("%.2f %%", vat);+
+ printf "%s\t%.2f\t%.2f\t%s\t%.2f EUR\n", name, number, single, vv, total
+}
+
+END {+ print "_"
+ # net sum
+ printf "Sum\t\t\t\t%.2f EUR\n", final
+ print "_"
+
+ for (i in vats) {+ if (i == 0)
+ continue;
+ v = vats[i] * i / 100.
+ printf "VAT / MwSt. %.2f %%\t\t\t\t%.2f EUR\n", i, v
+ final += v
+ }
+ print "_"
+ print ".B"
+ printf "Total\t\t\t\t%.2f EUR\n", final
+ print "_"
+ print ".TE"
+}
--- /dev/null
+++ b/bin/genpdfmark
@@ -1,0 +1,19 @@
+#!/bin/rc
+# generate pdfmark files for PDF metadata
+# $1 is the invoice number
+# assume temp files already in /tmp
+
+rfork e
+
+# read and construct date
+dt=`{cat invoice/$1/date}+dt=`{date -u -f 'YYYYMMDDhhmmss+00''00''' $dt}+
+# read subject line
+subject=`{cat /tmp/pre.tmp /tmp/msg.tmp | tbl | nroff -ms | sed '/^$/d' | sed 1q}+
+echo '[ /Author (Joel Fridolin Meyer)'
+echo ' /Subject ('^$"subject^')'+echo ' /CreationDate (D:'^$dt^')'
+echo ' /ModDate (D:'^$dt^')'
+echo ' /DOCINFO pdfmark'
--- /dev/null
+++ b/bin/mkinvoice
@@ -1,0 +1,41 @@
+#!/bin/rc
+
+rfork en
+
+parts=`{+ cd invoice
+ walk -d -n1 | awk -F '-' '
+NF == 2 {+ if ($1 > year)
+ year = $1
+ if ($2 > nr)
+ nr = $2
+}
+END {+ printf "%d\t%d\n", year, nr+1
+}
+'}
+
+id=`{echo $parts(1)^-^$parts(2)}+
+mkdir invoice/$id
+touch invoice/$id/^(data.tsv client date msg template)
+
+cat <<EOF >invoice/$id/msg
+Invoice number \*(iv
+.LP
+Hi, here's the invoice:
+.LP
+.IV
+.LP
+Have a nice day!
+EOF
+
+cat <<EOF >invoice/$id/data.tsv
+# Amount Price (1) VAT Name
+EOF
+
+echo 'default' >invoice/$id/template
+date -n >invoice/$id/date
+
+echo $id
--- /dev/null
+++ b/bin/pdfid
@@ -1,0 +1,24 @@
+#!/bin/rc
+# set the pdf ID to a given ID
+# $1 is pdf file, $2 is the checksum
+# assume ramfs in /tmp
+
+rfork en
+
+# get size of the file
+size=`{du -n $1}+size=$size(1)
+
+todump=`{echo $size - 160 | hoc}+# 160 characters should make sure that we actually have the ID, and that we skipped the binary part
+
+# split file $1 into parts
+dd -if $1 -bs 1 -count $todump -quiet 1 > /tmp/pdf_part1
+dd -if $1 -bs 1 -count 160 -iseek $todump -quiet 1 > /tmp/pdf_part2
+
+# replace ID
+sed 's:^/ID \[<[A-Z0-9]+><[A-Z0-9]+>\]$:/ID [<'^$2^'><'^$2^'>]:g' /tmp/pdf_part2 > /tmp/pdf_part2_edited
+
+# recombine file to stdout, and cleanup
+cat /tmp/pdf_part1 /tmp/pdf_part2_edited
+rm /tmp/pdf_part1 /tmp/pdf_part2 /tmp/pdf_part2_edited
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,49 @@
+invoices=`{cd invoice; walk -d -n 1}+targets=`{for (f in $invoices) { echo invoice/^$f^/invoice-^$f^.pdf }}+
+all:V: $targets
+
+test:VQ:
+
+
+invoice/([^/]+)/invoice-([^/]+).pdf:RQ: invoice/\1/template invoice/\1/data.tsv
+ rfork en
+ ramfs
+
+ # data
+ bin/formatdata.awk <invoice/$stem1/data.tsv | tbl >/tmp/positions.tmp
+ cat <<EOF > /tmp/pre.tmp
+ .ds iv "$stem1
+ .de IV
+ .LP
+ .so /tmp/positions.tmp
+ ..
+ EOF
+
+ # template
+ t=`{cat invoice/$stem1/template}+ cp template/$t /tmp/template.tmp
+ cp template/$t.msg /tmp/msg.tmp
+
+ # client info
+ cl=`{cat invoice/$stem1/client}+ cl=$cl(1)
+ cp client/$cl/addr /tmp/client_addr.tmp
+ test -f client/$cl/msg && cp client/$cl/msg /tmp/msg.tmp
+
+ # invoice overrides
+ test -f invoice/$stem1/msg && cp invoice/$stem1/msg /tmp/msg.tmp
+
+ # generate unique ID
+ sum=`{cat /tmp/pre.tmp /tmp/template.tmp /tmp/msg.tmp | md5sum | tr 'a-z' 'A-Z'}+ echo $sum >invoice/$stem1/md5sum
+
+ # pdfmark overrides
+ bin/genpdfmark $stem1 > /tmp/info.pdfmark
+
+ # build
+ cat /tmp/pre.tmp /tmp/template.tmp | troff -ms | lp -dstdout > /tmp/invoice.ps
+
+ cat /tmp/invoice.ps /tmp/info.pdfmark | ps2pdf '-sPAPERSIZE=a4' > /tmp/invoice.pdf
+ bin/pdfid /tmp/invoice.pdf $sum > $target
+ echo built $target `{cat invoice/$stem1/md5sum}--
⑨