;examples: invoicing: new invoice script example
This commit is contained in:
		
							parent
							
								
									7c4f5dc2bf
								
							
						
					
					
						commit
						7edcf77eae
					
				
							
								
								
									
										79
									
								
								examples/invoicing/invoice-script/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								examples/invoicing/invoice-script/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
|  | ... | ||||||
|  | 
 | ||||||
|  | { 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 | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
							
								
								
									
										117
									
								
								examples/invoicing/invoice-script/abinvoice.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								examples/invoicing/invoice-script/abinvoice.css
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | }   | ||||||
							
								
								
									
										33
									
								
								examples/invoicing/invoice-script/abinvoice.tmpl.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								examples/invoicing/invoice-script/abinvoice.tmpl.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | --- | ||||||
|  | papersize: letter | ||||||
|  | margin-left: 20mm | ||||||
|  | margin-right: 25mm | ||||||
|  | margin-top: 20mm | ||||||
|  | margin-bottom: 20mm | ||||||
|  | ... | ||||||
|  | 
 | ||||||
|  | { 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! | ||||||
							
								
								
									
										180
									
								
								examples/invoicing/invoice-script/invoice
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										180
									
								
								examples/invoicing/invoice-script/invoice
									
									
									
									
									
										Executable file
									
								
							| @ -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 <<EOF | ||||||
|  | -------------------------------------------------------------------------------- | ||||||
|  | invoice: | ||||||
|  | Make markdown or pdf invoices, optionally including last month's time | ||||||
|  | and expenses, from a markdown template and similarly-named .css file. | ||||||
|  | Requires hledger, pandoc, awk, GNU date, envsubst, python3, sed, tail. | ||||||
|  | 
 | ||||||
|  | $ invoice | ||||||
|  |   Show this help. | ||||||
|  | 
 | ||||||
|  | $ invoice TEMPLATEFILE [TIMEACCTORAMT [EXPACCTORAMT]] [FLAGS] | ||||||
|  |   Print a markdown invoice on stdout. | ||||||
|  |   TIMEACCTORAMT and EXPACCTORAMT are time and expense accounts to query | ||||||
|  |   with hledger, or if numeric, the hours and expense amounts directly. | ||||||
|  |   With --md and/or --pdf, save it as markdown / PDF in current directory. | ||||||
|  |   With --txn, print sample hledger journal entries on stdout. | ||||||
|  | 
 | ||||||
|  | EOF | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ARGS=() | ||||||
|  | while [[ $# -gt 0 ]]; do | ||||||
|  |     key="$1" | ||||||
|  |     case $key in | ||||||
|  |         -h|--help) | ||||||
|  |             HELP=1 | ||||||
|  |             shift | ||||||
|  |             ;; | ||||||
|  |         --md) | ||||||
|  |             MD=1 | ||||||
|  |             shift | ||||||
|  |             ;; | ||||||
|  |         --pdf) | ||||||
|  |             PDF=1 | ||||||
|  |             shift | ||||||
|  |             ;; | ||||||
|  |         --txn) | ||||||
|  |             TXN=1 | ||||||
|  |             shift | ||||||
|  |             ;; | ||||||
|  |         *) | ||||||
|  |             if [[ "$1" != -* ]] | ||||||
|  |             then  | ||||||
|  |                 ARGS+=("$1") | ||||||
|  |                 shift | ||||||
|  |             else | ||||||
|  |                 echo "Error: unknown option $1" | ||||||
|  |                 exit 1 | ||||||
|  |             fi | ||||||
|  |             ;; | ||||||
|  |     esac | ||||||
|  | done | ||||||
|  | if [[ $HELP = 1 || ${#ARGS} -eq 0 ]]; then usage; exit; fi | ||||||
|  | 
 | ||||||
|  | DEFTIMEACCT=0 | ||||||
|  | DEFEXPACCT=0 | ||||||
|  | TEMPLATE="${ARGS[0]}" | ||||||
|  | TIMEACCT="${ARGS[1]:-$DEFTIMEACCT}" | ||||||
|  | EXPACCT="${ARGS[2]:-$DEFEXPACCT}" | ||||||
|  | 
 | ||||||
|  | # XXX FIXEDEXPS and RATE here, and printf widths below, must be kept synced with TEMPLATE | ||||||
|  | FIXEDEXPS=$(python3 -c "print(sum([ 1111, 2222, 333 ]))") | ||||||
|  | RATE=444 | ||||||
|  | TIMELOG=./time.timedot | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | # if changing this period, hledger and date may need different values | ||||||
|  | HLEDGERPERIOD='last month' | ||||||
|  | DATECMDPERIOD='last month' | ||||||
|  | 
 | ||||||
|  | NUMRE="^[0-9]+([.][0-9]+)?$" | ||||||
|  | if [[ $TIMEACCT =~ $NUMRE ]] | ||||||
|  | then | ||||||
|  |     HRS=$TIMEACCT | ||||||
|  | else | ||||||
|  |     HRS=$(hledger -f "$TIMELOG" bal "$TIMEACCT" -1 "date:$HLEDGERPERIOD" -N | tail -1 | awk '{print $1}') | ||||||
|  | fi | ||||||
|  | if [[ $EXPACCT =~ $NUMRE ]] | ||||||
|  | then | ||||||
|  |     EXP=$EXPACCT | ||||||
|  | else | ||||||
|  |     EXP=$(hledger bal "$EXPACCT" "date:$HLEDGERPERIOD" amt:'>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' <<EOF | ||||||
|  | $YEAR-$MM-$DD (${YEAR}${LMM}) $CLIENT | invoice \$$TOT | ||||||
|  |     (assets:receivable:$CLIENT:consulting)      \$$REV ; $LM hourly & $MON fixed fees | ||||||
|  |     ;(assets:receivable:$CLIENT:reimbursement)  \$$EXP ; $LM reimbursable expenses | ||||||
|  | 
 | ||||||
|  | ; $YEAR-$MM-$DD (${YEAR}${LMM}) $CLIENT | payment | ||||||
|  | ;     ; receive full amount of invoice | ||||||
|  | ;     assets:bank:checking                \$$TOT | ||||||
|  | ;     assets:receivable:$CLIENT:reimbursement    \$-$EXP | ||||||
|  | ;     assets:receivable:$CLIENT:consulting       \$-$REV = $0 | ||||||
|  | ;     ; recognise revenue (cash accounting) | ||||||
|  | ;     (revenues:$CLIENT)                         \$-$REV | ||||||
|  | ;     ; estimate tax due, tax-saved-on:  ?, TODO: | ||||||
|  | ;     (liabilities:tax:us:2021)              \$-$USTAX  ; 28% | ||||||
|  | ;     (liabilities:tax:st:2021)              \$-$STTAX  ;  8% | ||||||
|  | ;     ; Total tax:                               \$$TOTTAX  ; 36% | ||||||
|  | ;     ; Post-tax income:                         \$$PTINC | ||||||
|  | 
 | ||||||
|  | ; $YEAR-$MM-$DD save estimated tax from $CLIENT ${YEAR}${LMM}, received $YEAR-$MM-$DD | ||||||
|  | ;     assets:bank:checking               \$-$TOTTAX | ||||||
|  | ;     assets:bank:savings:tax:us:2021     \$$USTAX | ||||||
|  | ;     assets:bank:savings:tax:st:2021     \$$STTAX | ||||||
|  | 
 | ||||||
|  | EOF | ||||||
|  | fi | ||||||
							
								
								
									
										0
									
								
								examples/invoicing/invoice-script/logo.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								examples/invoicing/invoice-script/logo.jpg
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										2
									
								
								examples/invoicing/invoice-script/time.timedot
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								examples/invoicing/invoice-script/time.timedot
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | 2021-11-15 | ||||||
|  | client.ab.dev   .... .... | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user