From 7edcf77eae2eba16df3b68e647601840e4ef6844 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Sat, 4 Dec 2021 10:41:41 -1000 Subject: [PATCH] ;examples: invoicing: new invoice script example --- examples/invoicing/invoice-script/README.md | 79 ++++++++ .../invoicing/invoice-script/abinvoice.css | 117 ++++++++++++ .../invoice-script/abinvoice.tmpl.md | 33 ++++ examples/invoicing/invoice-script/invoice | 180 ++++++++++++++++++ examples/invoicing/invoice-script/logo.jpg | 0 .../invoicing/invoice-script/time.timedot | 2 + 6 files changed, 411 insertions(+) create mode 100644 examples/invoicing/invoice-script/README.md create mode 100644 examples/invoicing/invoice-script/abinvoice.css create mode 100644 examples/invoicing/invoice-script/abinvoice.tmpl.md create mode 100755 examples/invoicing/invoice-script/invoice create mode 100644 examples/invoicing/invoice-script/logo.jpg create mode 100644 examples/invoicing/invoice-script/time.timedot diff --git a/examples/invoicing/invoice-script/README.md b/examples/invoicing/invoice-script/README.md new file mode 100644 index 000000000..227cc8f1a --- /dev/null +++ b/examples/invoicing/invoice-script/README.md @@ -0,0 +1,79 @@ +Scripts adapted from a real-world setup, not guaranteed to be current or working. + +Example: + +Show invoice preview: +``` +$ ./invoice abinvoice.tmpl.md client.ab.dev 200 +--- +papersize: letter +margin-left: 20mm +margin-right: 25mm +margin-top: 20mm +margin-bottom: 20mm +... + +![](logo.jpg){ width=100mm }\ +Joe Consultant | +1 (111) 111 1111 | joe@example.com | 500 Done Dr. #1, Work Ville, CA 10000, USA + +Carl Client\ +AB Inc.\ +PO Box 11111\ +CA 20000\ + +December 4, 2021 + +# Invoice 202111cw + +| Description | Rate | Qty | Total | +|:--------------------------------------------|-------:|-------:|--------:| +| Systems reliability engineering | $ 1111 | | $ 1111 | +| On-call monitoring & tech support | $ 2222 | | $ 2222 | +| Contractor/vendor management | $ 333 | | $ 333 | +| Custom SW development & maintenance (Nov) | $ 444 | 2.00 | $ 888 | +| Reimbursable expenses (Nov) | | | $ 200 | +|   | | | | +| Total due | | | $ 4754 | +| | | | | + + +Terms: Now due. Your business is appreciated, thank you! +``` + +Generate markdown and PDF invoices and sample journal entries: +``` +$ ./invoice abinvoice.tmpl.md client.ab.dev 200 --md --pdf --txn +wrote abinvoice202111.md +Loading pages (1/6) +Counting pages (2/6) +Resolving links (4/6) +Loading headers and footers (5/6) +Printing pages (6/6) +Done +wrote abinvoice202111.pdf + +-------------------------------------------------------------------------------- + +2021-12-04 (202111) abinvoice | invoice $ 4754 + (assets:receivable:abinvoice:consulting) $ 4554 ; Nov hourly & Dec fixed fees + ;(assets:receivable:abinvoice:reimbursement) $ 200 ; Nov reimbursable expenses + +; 2021-12-04 (202111) abinvoice | payment +; ; receive full amount of invoice +; assets:bank:checking $ 4754 +; assets:receivable:abinvoice:reimbursement $- 200 +; assets:receivable:abinvoice:consulting $- 4554 = ./invoice +; ; recognise revenue (cash accounting) +; (revenues:abinvoice) $- 4554 +; ; estimate tax due, tax-saved-on: ?, TODO: +; (liabilities:tax:us:2021) $-1275 ; 28% +; (liabilities:tax:st:2021) $-364 ; 8% +; ; Total tax: $1639 ; 36% +; ; Post-tax income: $2915 + +; 2021-12-04 save estimated tax from abinvoice 202111, received 2021-12-04 +; assets:bank:checking $-1639 +; assets:bank:savings:tax:us:2021 $1275 +; assets:bank:savings:tax:st:2021 $364 + +``` diff --git a/examples/invoicing/invoice-script/abinvoice.css b/examples/invoicing/invoice-script/abinvoice.css new file mode 100644 index 000000000..62e7634ed --- /dev/null +++ b/examples/invoicing/invoice-script/abinvoice.css @@ -0,0 +1,117 @@ +/* https://martinbetz.eu/articles/pandoc-invoices */ + +@charset "utf-8"; + +body { +/* font-size: 10.5pt; */ +/* font-family: */ +/* "Avenir Next", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", */ +/* "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; */ +hyphens: auto; +height: 250mm; /* 280 - 10 (top) - 20 (bottom) */ +line-height: 140%; +margin: 0; +padding: 0; +} + +code { +font-family: "Source Sans Code", Courier New, Courier, monospace; +margin-left: 1pt; +/* font-size:12pt; */ +} + +a { +color: black; +margin-left: 1pt; +} + +h1 { +font-size: 20pt; +margin-top: 10mm; +padding-top:0; +/* margin-top: 6pt; */ +/* margin-bottom: 0; */ +} + +h2 { +font-size: 16pt; +/* margin-top: 20pt; */ +margin-top: 10mm; +/* font-weight: normal; */ +/* margin-top: 0; */ +/* margin-bottom: 20pt; */ +} + +p { +width: 100%; +} + +p:first-of-type { +text-align: center; +font-size: 9pt; +word-spacing: 1pt; +} + +p:nth-of-type(2) { +margin-top: 10mm; +} + +p:nth-of-type(3) { +text-align: center; +font-weight:bold; +font-size:12pt; + +} + +/* p:nth-last-of-type(3) { */ +/* margin-top: 10mm; */ +/* } */ + +/* p:last-of-type { */ +/* text-align: center; */ +/* font-size: 9pt; */ +/* position: absolute; */ +/* bottom: 2mm; */ +/* margin-bottom: 0; */ +/* padding-bottom: 0; */ +/* color: #444; */ +/* } */ + +table { +width: 100%; +} + +table:nth-of-type(1) { +border: 1px solid black; +padding: 5pt; +} + +table:nth-of-type(1) td { +border-top: 1px solid #eee; +} + +table:nth-of-type(1) tr:nth-last-of-type(1) { +font-weight: bold; +} +table:nth-of-type(1) tr:nth-last-of-type(1) td { +/* border-top: 1px solid black; */ +padding-top: 1em; +} + +/* table:nth-of-type(1) td:nth-of-type(2) { */ +/* text-align: center; */ +/* } */ + +hr { +border: 1px solid #eee; +} + +hr:last-of-type { +position: absolute; +bottom: 14mm; +width: 100%; +} + +figure { +margin: 0; +} diff --git a/examples/invoicing/invoice-script/abinvoice.tmpl.md b/examples/invoicing/invoice-script/abinvoice.tmpl.md new file mode 100644 index 000000000..591210090 --- /dev/null +++ b/examples/invoicing/invoice-script/abinvoice.tmpl.md @@ -0,0 +1,33 @@ +--- +papersize: letter +margin-left: 20mm +margin-right: 25mm +margin-top: 20mm +margin-bottom: 20mm +... + +![](logo.jpg){ width=100mm }\ +Joe Consultant | +1 (111) 111 1111 | joe@example.com | 500 Done Dr. #1, Work Ville, CA 10000, USA + +Carl Client\ +AB Inc.\ +PO Box 11111\ +CA 20000\ + +$MONTH $DAY, $YEAR + +# Invoice ${YEAR}${LMM}cw + +| Description | Rate | Qty | Total | +|:--------------------------------------------|-------:|-------:|--------:| +| Systems reliability engineering | $ 1111 | | $ 1111 | +| On-call monitoring & tech support | $ 2222 | | $ 2222 | +| Contractor/vendor management | $ 333 | | $ 333 | +| Custom SW development & maintenance ($LM) | $ 444 | $HRS | $ $AMT | +| Reimbursable expenses ($LM) | | | $ $EXP | +|   | | | | +| Total due | | | $ $TOT | +| | | | | + + +Terms: Now due. Your business is appreciated, thank you! diff --git a/examples/invoicing/invoice-script/invoice b/examples/invoicing/invoice-script/invoice new file mode 100755 index 000000000..0877a0f9f --- /dev/null +++ b/examples/invoicing/invoice-script/invoice @@ -0,0 +1,180 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2016 +# Create invoices with hledger and pandoc. +# cf hledger/examples/invoicing, https://hledger.org/invoicing.html, https://martinbetz.eu/articles/pandoc-invoices +set -e + +#PROG=$(basename "$0") +function usage() { + cat <0' -N --layout=bare | tail -1 | awk '{print $1}') +fi + +# on mac, use homebrew-installed GNU date +if [ "$(builtin type -p gdate)" ]; then export date=gdate; else export date=date; fi + +YEAR=$($date +%Y) +MONTH=$($date +%B) +MON=$($date +%b) +MM=$($date +%m) +DD=$($date +%d) +DAY=$($date +%-d) +LM=$($date +%b --date "$DATECMDPERIOD") +LMM=$($date +%m --date "$DATECMDPERIOD") + +# shellcheck disable=SC2001 +INVOICEBASE=$(basename "$TEMPLATE" | sed -e 's/\..*//') +INVOICEDATED=$INVOICEBASE$YEAR$LMM +INVOICEMD=$INVOICEDATED".md" +INVOICEPDF=$INVOICEDATED".pdf" +CSS=$INVOICEBASE".css" + +HRS="${HRS:-0}" +HRS=$(printf %4s "$HRS") +EXP=$(printf %5.0f "$EXP") +AMT=$(python3 -c "print(round( $HRS * $RATE ))") +AMT=$(printf %5s "$AMT") +REV=$(python3 -c "print(sum([ $FIXEDEXPS, $AMT ]))") +REV=$(printf %5s "$REV") +TOT=$(python3 -c "print(sum([ $FIXEDEXPS, $AMT, $EXP ]))") +TOT=$(printf %5s "$TOT") +export YEAR MONTH DAY LMM LM HRS AMT REV EXP TOT + +if [[ $MD != 1 && $PDF != 1 ]]; then + # print markdown invoice + envsubst '$YEAR:$MONTH:$DAY:$LMM:$LM:$HRS:$EXP:$AMT:$TOT' <"$TEMPLATE" + +else + if [[ $MD = 1 ]]; then + # save markdown invoice + envsubst '$YEAR:$MONTH:$DAY:$LMM:$LM:$HRS:$EXP:$AMT:$TOT' <"$TEMPLATE" >"$INVOICEMD" + echo "wrote $INVOICEMD" + fi + if [[ $PDF = 1 ]]; then + # save pdf invoice + envsubst '$YEAR:$MONTH:$DAY:$LMM:$LM:$HRS:$EXP:$AMT:$TOT' <"$TEMPLATE" \ + | pandoc -t html5 --metadata title=" " --css "$CSS" -o "$INVOICEPDF" + echo "wrote $INVOICEPDF" + fi +fi + +if [[ $TXN = 1 ]]; then + # generate sample journal entries + printf "\n--------------------------------------------------------------------------------\n\n" + USTAXRATE=0.28 + STTAXRATE=0.08 + CLIENT=$INVOICEBASE + USTAX=$(python3 -c "print(round( $REV * $USTAXRATE))") + #USTAX=$(printf %5s "$USTAX") + STTAX=$(python3 -c "print(round( $REV * $STTAXRATE))") + #STTAX=$(printf %5s "$STTAX") + TOTTAX=$(python3 -c "print($USTAX + $STTAX)") + #TOTTAX=$(printf %5s "$TOTTAX") + PTINC=$(python3 -c "print($REV - $TOTTAX)") + #PTINC=$(printf %5s "$PTINC") + + envsubst '$CLIENT:$YEAR:$MM:$DD:$MON:$LMM:$LM:$REV:$EXP:$TOT:$USTAX:$STTAX:$TOTTAX:$PTINC' <