Add everything
This commit is contained in:
commit
63d7293fc5
14 changed files with 221 additions and 0 deletions
21
README.md
Normal file
21
README.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
make sure taskdb is actual and everything relevant is tagged
|
||||||
|
|
||||||
|
cp customer-name/prev-month-date customer-name/new-month-date
|
||||||
|
cd customer-name/new-month-date
|
||||||
|
make clean
|
||||||
|
make timesheet.txt
|
||||||
|
cat timesheet.txt
|
||||||
|
|
||||||
|
create payment link with data from timesheet.txt
|
||||||
|
edit pay.url
|
||||||
|
edit env.sh.inc
|
||||||
|
edit email-template/*
|
||||||
|
|
||||||
|
make email-draft.txt
|
||||||
|
review it
|
||||||
|
|
||||||
|
edit invoice-top.html
|
||||||
|
make invoice.pdf
|
||||||
|
review it
|
||||||
|
|
||||||
|
make email.txt
|
74
invoice-system-common/Makefile
Normal file
74
invoice-system-common/Makefile
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
SHELL := /bin/bash
|
||||||
|
CUSTOMER := $(shell basename $(shell dirname $(shell pwd) ) )
|
||||||
|
INVOICE_DATE := $(shell basename $(shell pwd) )
|
||||||
|
|
||||||
|
#.PHONY: timesheet.txt timesheet.html invoice.html invoice.pdf
|
||||||
|
|
||||||
|
default: invoice.pdf
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -fv amount.sh.inc pay.url invoice-top.html billable.sql complete.sql timesheet.txt timesheet.html email.txt email-draft.txt invoice.html invoice.pdf
|
||||||
|
|
||||||
|
amount.sh.inc: amount.sql billable.sql
|
||||||
|
cat billable.sql amount.sql | ssh taskdb psql --tuples-only --quiet --no-align > $@
|
||||||
|
cat $@
|
||||||
|
|
||||||
|
pay.url: amount.sh.inc timesheet.txt
|
||||||
|
# Double-check timesheet.txt
|
||||||
|
# Duplicate product from previous month
|
||||||
|
# Change amount and month in item description
|
||||||
|
# Create payment link
|
||||||
|
# When done, put the link into pay.url
|
||||||
|
false
|
||||||
|
|
||||||
|
invoice-top.html: invoice-top.html.in pay.url amount.sh.inc
|
||||||
|
#false # render
|
||||||
|
export CUSTOMER=$(CUSTOMER) INVOICE_DATE=$(INVOICE_DATE); source <(cat env.sh.inc amount.sh.inc | sed -e 's/^.*=/export &/') && envsubst < $< > $@
|
||||||
|
|
||||||
|
amount.sql: amount.sql.in env.sh.inc
|
||||||
|
export CUSTOMER=$(CUSTOMER) INVOICE_DATE=$(INVOICE_DATE); source <(cat env.sh.inc | sed -e 's/^.*=/export &/') && envsubst < $< > $@.tmp
|
||||||
|
mv $@.tmp $@
|
||||||
|
|
||||||
|
billable.sql: billable.sql.in env.sh.inc
|
||||||
|
export CUSTOMER=$(CUSTOMER) INVOICE_DATE=$(INVOICE_DATE); source <(cat env.sh.inc | sed -e 's/^.*=/export &/') && envsubst < $< > $@.tmp
|
||||||
|
mv $@.tmp $@
|
||||||
|
|
||||||
|
complete.sql: complete.sql.in billable.sql env.sh.inc
|
||||||
|
cat billable.sql > $@.tmp
|
||||||
|
export CUSTOMER=$(CUSTOMER) INVOICE_DATE=$(INVOICE_DATE); source <(cat env.sh.inc | sed -e 's/^.*=/export &/') && envsubst < $< >> $@.tmp
|
||||||
|
mv $@.tmp $@
|
||||||
|
|
||||||
|
timesheet.txt: complete.sql
|
||||||
|
ssh taskdb psql --quiet < complete.sql > $@.tmp
|
||||||
|
mv $@.tmp $@
|
||||||
|
|
||||||
|
timesheet.html: complete.sql
|
||||||
|
ssh taskdb psql -H --quiet < complete.sql > $@.tmp
|
||||||
|
# avoid overflow on the right in PDF:
|
||||||
|
sed -i -e 's/<table border="1">/<table border="1" style="font-size: 70%;">/g' $@.tmp
|
||||||
|
mv $@.tmp $@
|
||||||
|
|
||||||
|
upload:
|
||||||
|
rsync -rtv $(PWD) webserver:/var/www/zorg.example/html/tmp/$(CUSTOMER)
|
||||||
|
echo https://zorg.example/tmp/$(CUSTOMER)/$(shell basename $(shell pwd))/timesheet.txt
|
||||||
|
echo https://zorg.example/tmp/$(CUSTOMER)/$(shell basename $(shell pwd))/invoide.html
|
||||||
|
echo https://zorg.example/tmp/$(CUSTOMER)/$(shell basename $(shell pwd))/invoice.pdf
|
||||||
|
|
||||||
|
email-draft.txt: pay.url amount.sh.inc generate-email invoice.pdf
|
||||||
|
./generate-email | tee $@.tmp
|
||||||
|
mv $@.tmp $@
|
||||||
|
|
||||||
|
email.txt: email-draft.txt invoice.pdf
|
||||||
|
mutt -H email-draft.txt -a invoice.pdf
|
||||||
|
mv email-draft.txt email.txt
|
||||||
|
|
||||||
|
invoice.html: invoice-top.html timesheet.html
|
||||||
|
cat invoice-top.html > $@.tmp
|
||||||
|
cat timesheet.html >> $@.tmp
|
||||||
|
echo '<br>Thank you! <body> </html>' >> $@.tmp
|
||||||
|
mv $@.tmp $@
|
||||||
|
|
||||||
|
invoice.pdf: invoice.html
|
||||||
|
weasyprint invoice.html invoice.pdf
|
||||||
|
|
||||||
|
echo Now do '"make email-draft.txt"', and then '"make email.txt"'
|
9
invoice-system-common/amount.sql.in
Normal file
9
invoice-system-common/amount.sql.in
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
\pset footer off
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
'HOURS=' || ROUND(SUM(duration)/3600.0, 2)::text
|
||||||
|
FROM billable
|
||||||
|
UNION
|
||||||
|
SELECT
|
||||||
|
'AMOUNT=' || (ROUND(SUM(duration)/3600.0, 2) * ${RATE})::text
|
||||||
|
FROM billable
|
12
invoice-system-common/billable.sql.in
Normal file
12
invoice-system-common/billable.sql.in
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
CREATE TEMPORARY TABLE billable
|
||||||
|
AS
|
||||||
|
SELECT * FROM depgraph(alias('$CUSTOMER'))
|
||||||
|
WHERE
|
||||||
|
NOT COALESCE(tags, '') ~ 'nonbillable'
|
||||||
|
AND scheduled >= '$PERIOD_START'
|
||||||
|
AND scheduled < '$PERIOD_NEXT_START'
|
||||||
|
AND scheduled < CURRENT_TIMESTAMP -- CURRENT_DATE
|
||||||
|
AND COALESCE(duration, 0) > 0
|
||||||
|
EXCEPT
|
||||||
|
SELECT * FROM depgraph(alias('$CUSTOMER-nonbillable'))
|
||||||
|
;
|
21
invoice-system-common/complete.sql.in
Normal file
21
invoice-system-common/complete.sql.in
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
\pset footer off
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
scheduled AS date,
|
||||||
|
ROUND(duration/3600.0, 2) AS hours,
|
||||||
|
regexp_replace(description, '^${CUSTOMER}: ', '', 'i') AS description
|
||||||
|
FROM billable
|
||||||
|
ORDER BY scheduled
|
||||||
|
;
|
||||||
|
SELECT
|
||||||
|
date_trunc('week', scheduled)::date AS week,
|
||||||
|
ROUND(SUM(duration)/3600.0, 2) AS hours_in_week
|
||||||
|
FROM billable
|
||||||
|
GROUP BY week
|
||||||
|
ORDER BY week
|
||||||
|
;
|
||||||
|
SELECT
|
||||||
|
ROUND(SUM(duration)/3600.0, 2) AS "hours total",
|
||||||
|
${RATE} AS "hourly rate, ${RATE_CURRENCY}",
|
||||||
|
ROUND(SUM(duration)/3600.0, 2) * ${RATE} AS "amount billed"
|
||||||
|
FROM billable
|
22
invoice-system-common/generate-email
Executable file
22
invoice-system-common/generate-email
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
source env.sh.inc
|
||||||
|
|
||||||
|
if ! cat email-template/headers.txt; then
|
||||||
|
echo "To: $RECIPIENTS"
|
||||||
|
echo "Cc: $RECIPIENTS_CC"
|
||||||
|
fi
|
||||||
|
echo "Subject: Invoice for $COVERED_PERIOD_STR"
|
||||||
|
echo # end of headers, start of body
|
||||||
|
|
||||||
|
cat << EOF
|
||||||
|
$GREETING
|
||||||
|
|
||||||
|
Please see the timesheet and invoice attached.
|
||||||
|
|
||||||
|
The payment link:
|
||||||
|
$(cat pay.url)
|
||||||
|
|
||||||
|
Thanks!
|
||||||
|
EOF
|
35
invoice-system-common/invoice-top.html.in
Normal file
35
invoice-system-common/invoice-top.html.in
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<!-- <body style="width: 210mm;"> -->
|
||||||
|
<body>
|
||||||
|
<h1>Invoice</h1>
|
||||||
|
<h2>Jean-Baptiste Emanuel Zorg</h2>
|
||||||
|
<table style="width: 100%;">
|
||||||
|
<tr>
|
||||||
|
<td style="vertical-align: top;">
|
||||||
|
Invoice number: $INVOICE_ID
|
||||||
|
<br>Date of issue: $ISSUED_DATE
|
||||||
|
<br>Date due: $DUE_DATE
|
||||||
|
<br>Jean-Baptiste Emanuel Zorg
|
||||||
|
<br><a href="mailto:customer-support@zorg.example">customer-support@zorg.example</a>
|
||||||
|
<br>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td style="vertical-align: top;">
|
||||||
|
Bill to:
|
||||||
|
$BILL_TO
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<table>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<h2>
|
||||||
|
$CURRENCY_SYMBOL$AMOUNT due $DUE_DATE
|
||||||
|
<br>
|
||||||
|
<a href="$PAY_URL">Pay online</a>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<br>Software development work for $PROJECT in $COVERED_PERIOD_STR, $HOURS hr @ $RATE $CURRENCY/hr
|
||||||
|
|
||||||
|
<br>Amount: $CURRENCY_SYMBOL$AMOUNT
|
1
ultimate-evil/2024-10-01/Makefile
Symbolic link
1
ultimate-evil/2024-10-01/Makefile
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../invoice-system-common/Makefile
|
1
ultimate-evil/2024-10-01/amount.sql.in
Symbolic link
1
ultimate-evil/2024-10-01/amount.sql.in
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../invoice-system-common/amount.sql.in
|
1
ultimate-evil/2024-10-01/billable.sql.in
Symbolic link
1
ultimate-evil/2024-10-01/billable.sql.in
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../invoice-system-common/billable.sql.in
|
1
ultimate-evil/2024-10-01/complete.sql.in
Symbolic link
1
ultimate-evil/2024-10-01/complete.sql.in
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../invoice-system-common/complete.sql.in
|
21
ultimate-evil/2024-10-01/env.sh.inc
Normal file
21
ultimate-evil/2024-10-01/env.sh.inc
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
GREETING="Hi Mr. Shadow!"
|
||||||
|
RECIPIENTS="Mr. Shadow <shadow@ultimate-evil.example>, accounting@corp.ultimate-evil.example, evil@example.com"
|
||||||
|
RECIPIENTS_CC="mail@zorg.example" # recommended to CC to your other mailbox for archival purposes
|
||||||
|
PROJECT="Peace on Earth"
|
||||||
|
RATE="1000000"
|
||||||
|
RATE_CURRENCY="USD"
|
||||||
|
CURRENCY_SYMBOL="$"
|
||||||
|
|
||||||
|
INVOICE_ID="ZG-UE-20241001"
|
||||||
|
COVERED_PERIOD_STR="September 2024"
|
||||||
|
PERIOD_START='2024-09-01'
|
||||||
|
PERIOD_NEXT_START='2024-10-01'
|
||||||
|
ISSUED_DATE='October 1, 2024'
|
||||||
|
DUE_DATE='November 1, 2024'
|
||||||
|
PAY_URL=$(cat pay.url)
|
||||||
|
|
||||||
|
BILL_TO="
|
||||||
|
<br>Ultimate Evil
|
||||||
|
<br>Mr. Shadow
|
||||||
|
<br>shadow@ultimate-evil.example
|
||||||
|
"
|
1
ultimate-evil/2024-10-01/generate-email
Symbolic link
1
ultimate-evil/2024-10-01/generate-email
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../invoice-system-common/generate-email
|
1
ultimate-evil/2024-10-01/invoice-top.html.in
Symbolic link
1
ultimate-evil/2024-10-01/invoice-top.html.in
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../invoice-system-common/invoice-top.html.in
|
Loading…
Reference in a new issue