summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcos <cos>2022-05-01 17:04:54 +0200
committercos <cos>2022-05-01 17:07:16 +0200
commitdf47b7fd1a982143c2571acfbba0f3f1aad8cd06 (patch)
tree4af638340b3faf8084201bed21135f2b8de27216
downloaddeklarationsskript-df47b7fd1a982143c2571acfbba0f3f1aad8cd06.zip
Initial commitHEADmain
-rw-r--r--README.md37
-rw-r--r--bin/combine-ne.pl9
-rwxr-xr-xbin/deklarera.sh411
-rwxr-xr-xbin/ink1-ifyllning.pl51
-rwxr-xr-xbin/k4-ifyllning.pl65
-rwxr-xr-xbin/moms-ifyllning.pl55
-rwxr-xr-xbin/ne-ifyllning.pl116
7 files changed, 744 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ef5f8f1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+Skatteverket in .se requires taxes to be declared every year. While they do
+provide "e-tjänster" to do this online, those are essentially worthless¹ and
+filling in paper forms are practically still the easiest way to get this stuff
+done.
+
+Manually filling in papers is boring and error prone. Thus the existance of
+these scripts. They are only written to work for me and no warranties are
+given or implied.
+
+Seriously. Please hold no expectations that these scripts could be of any use
+for you. It's low quality scripting, run at most once per year. I publish it on
+the odd chance that possibly someone might find inspiration on how to deal with
+instutitions still depending on actual paper forms for things.
+
+The idea is that running `bin/deklarera.sh` should interactively go through the
+steps necessary to file taxes. It is basically just a checklist. Some auxiliary
+scripts prepares svg files with numbers placed in the right boxes. Fragile and
+only implemented for forms and fields that I have encountered.
+
+Consider it all to be in the public domain. Patches are welcome.
+
+1. Skatteverket's e-tjänster have arbitrary unavailability planned for parts of
+ each and every day, and require the use of e-legitimation which practically
+ means BankID. While BankID has legitimate use cases, that authentication
+ solution is unsuitable for filing taxes. As with any task required to be
+ performed on a tight schedule, it is best done without potentially involving
+ the requirement to run out and buy a new MacBook and/or spending a few days
+ messing with a bank's customer service. In addition, I assume all the usual
+ disadvantages of modern websites exist. All too quick automatic logout on
+ percieved inactivity and user interfaces that change between every time? It
+ is likely is about as buggy as one can expect.
+ In case anyone at Skatteverket with sufficient power ever happens to read
+ this; If you'd wish to process less paper forms, could you please launch
+ initiatives to incent some e-legitimation to again be usable by open source
+ software? If you've noticed a bump in the trend, remember that BankID used
+ to work with fribid.se before those monopolists started to actively block
+ auditable and secure software for no good reason.
diff --git a/bin/combine-ne.pl b/bin/combine-ne.pl
new file mode 100644
index 0000000..4c657a5
--- /dev/null
+++ b/bin/combine-ne.pl
@@ -0,0 +1,9 @@
+use strict;
+use PDF::Reuse;
+
+prFile("ne.pdf");
+
+prDoc('ne-framsida.pdf');
+prDoc('ne-baksida.pdf');
+
+prEnd();
diff --git a/bin/deklarera.sh b/bin/deklarera.sh
new file mode 100755
index 0000000..e17672f
--- /dev/null
+++ b/bin/deklarera.sh
@@ -0,0 +1,411 @@
+#!/bin/bash -eu
+#
+# Require bash due to readline version of `read`. Also used for lower casing.
+#
+# Debian dependencies: (and likely others)
+# datefudge evince inkscape libpdf-reuse-perl libgd-svg-perl mupdf pdf2svg
+
+YEAR="$(datefudge '6 months ago' date +%Y)"
+SKIP=0
+CLEAR='no'
+
+usage() {
+ echo "${0} [--help] [--skip=<integer>]"
+ exit 1
+}
+
+show_help() {
+ echo '(y)es Run the command. Advances to the next on success.'
+ echo '(n)o Skip the command.'
+ echo '(c)lear Clear the screen.'
+ echo '(r)epeat Run the command. Asks again, never advances.'
+ echo '(h)elp Show this help'
+ echo '(q)uit Exit script.'
+}
+
+handle_arg() {
+ local arg="${1}"
+ local param="${arg%=*}" value="${arg#*=}"
+ local ret=0
+
+ if [ "${arg}" = "${param}" ]; then
+ value="${2}"
+ ret=1
+ fi
+
+ case "${param}" in
+ '--help' | '-h')
+ usage
+ exit 0
+ ;;
+ '--skip' | '-s')
+ SKIP="${value}"
+ return "${ret}"
+ ;;
+ *)
+ echo "Unknown parameter: ${param}" >&2
+ exit 1
+ ;;
+ esac
+
+ return 0
+}
+
+SHIFT_NEXT=''
+for ARG in "${@}"; do
+ shift
+ [ ! "${SHIFT_NEXT}" ] || { SHIFT_NEXT=''; continue; }
+
+ if [ "${ARG#-}" != "${ARG}" ]; then
+ handle_arg "${ARG}" "${1}" || SHIFT_NEXT='y'
+ continue
+ fi
+ set -- "$@" "${ARG}"
+done
+
+do_cmd() {
+ local start end duration pipe tee_pid
+
+ if eval "${CMD}"; then
+ DONE='yes'
+ else
+ FAILED='yes'
+ fi
+ if [ "${FAILED}" != 'no' ]; then
+ echo 'Command failed.' >&2
+ fi
+ [ "${CLEAR}" = 'no' ] || clear
+}
+
+input() {
+ local file="${1}" var="${2}" prompt="${3}" value=${4:-}
+ local exists input tmp_file=$( mktemp )
+
+ if grep -q "^${var}" "${file}" 2>/dev/null; then
+ exists='y'
+ else
+ exists=''
+ fi
+
+ if [ "${exists}" ]; then
+ if [ -e "${file}" ]; then
+ value=$( sed -n "s/^${var}: //p" <"${file}" )
+ else
+ value=''
+ fi
+ fi
+ read -p "${prompt}" -i "${value}" -e input
+
+ if [ "${exists}" ]; then
+ sed "s/^${var}: .*/${var}: ${input}/" <"${file}" >"${tmp_file}"
+ elif [ -e "${file}" ]; then
+ ( cat "${file}" 2>/dev/null; echo "${var}: ${input}" ) >>"${tmp_file}"
+ elif [ "${value}" ]; then
+ echo "${var}: ${input}" >>"${tmp_file}"
+ fi
+ mv "${tmp_file}" "${file}"
+}
+
+###############################################################################
+
+copy_template() {
+ # Skatteverket used to publish 215003.pdf, a regular editable pdf. Now they
+ # seem to have fully gone for xfa forms. :(
+ # (https://unix.stackexchange.com/questions/265845/xfa-forms)
+ cp --recursive 'mall/0001-forenklad-arsbokslut/' "${YEAR}"
+}
+
+mata_in_arsbokslut() {
+ local values="${YEAR}/0001-forenklad-arsbokslut/values.txt"
+ local b9 b10 b14 r1 r5 r11
+
+ input "${values}" 'B9' 'B9 Kassa och bank: '
+ input "${values}" 'B10' 'B10 Eget kapital: '
+ input "${values}" 'B14' 'B14 Skatteskulder: '
+ input "${values}" 'R1' 'R1 Försäljning och utfört arbete…: '
+ input "${values}" 'R5' 'R5 Varor, material och tjänster: '
+ input "${values}" 'R11' 'R11 Bokfört resultat: '
+ echo "${values}"
+ echo "${values}" | sed 's/./-/g'
+ cat "${values}"
+
+ b9=$( sed -n 's/^B9: //p' <"${values}" )
+ b10=$( sed -n 's/^B10: //p' <"${values}" )
+ b14=$( sed -n 's/^B14: //p' <"${values}" )
+ r1=$( sed -n 's/^R1: //p' <"${values}" )
+ r5=$( sed -n 's/^R5: //p' <"${values}" )
+ r11=$( sed -n 's/^R11: //p' <"${values}" )
+
+ if [ "$(( b10 + b14 ))" = "${b9}" ]; then
+ echo 'Balance seems to be ok.'
+ else
+ echo "Invalid numbers: B10 + B14 != B9. ($(( b10 + b14 )))"
+ return 1
+ fi
+ if [ "$(( r1 - r5 ))" = "${r11}" ]; then
+ echo 'Result seems to be ok.'
+ else
+ echo "Invalid numbers: R1 - R5 != R11. ($(( r1 + r5 )))"
+ return 1
+ fi
+}
+
+mata_in_ne() {
+ local values="${YEAR}/0003-ne-enskild_näringsverksamhet/values.txt"
+
+ # FIXME Get value från R43 of previous year.
+ input "${values}" 'R40' 'R40 Medgivna avdrag för egenavgifter…: '
+ input "${values}" 'R41' 'R41 Påförda egenavgifter och…: '
+ # FIXME Get these too from previous year.
+ input "${values}" 'Personnummer' 'Personnummer: '
+ input "${values}" 'SNI-kategori' 'SNI-kategori: '
+ echo 'https://www.verksamt.se/driva/skatter-och-avgifter/enskild-naringsverksamhet/preliminar-skatt-och-egenavgifter'
+ input "${values}" 'egenavgifter' 'egenavgifter: '
+
+ echo "${values}"
+ echo "${values}" | sed 's/./-/g'
+ cat "${values}"
+}
+
+mata_in_k4() {
+ local values="${YEAR}/0004-k4/values.txt"
+
+ input "${values}" 'Antal' 'Antal: '
+ input "${values}" 'Beteckning' 'Beteckning: '
+ input "${values}" 'Forsaljningspris' 'Försäljninspris: '
+ input "${values}" 'Omkostnadsbelopp' 'Omkostnadsbelopp: '
+ input "${values}" 'Vinst' 'Vinst: '
+
+ echo "${values}"
+ echo "${values}" | sed 's/./-/g'
+ cat "${values}"
+}
+
+overfor_till_ink1() {
+ local ne="${1}"
+ local k4="${2}"
+ local values="${YEAR}/0005-ink1/values.txt"
+ local p7_4 p10_1
+
+ p7_4=$( sed -n 's/^Vinst: //p' <"${k4}" )
+ p10_1=$( sed -n 's/^R47: //p' <"${ne}" )
+
+ input "${values}" 'p7_4' '7.4: ' "${p7_4}"
+ input "${values}" 'p10_1' '10.1: ' "${p10_1}"
+ input "${values}" 'email' 'E-postadress: '
+ input "${values}" 'phone' 'Telefonnummer: '
+
+ echo "${values}"
+ echo "${values}" | sed 's/./-/g'
+ cat "${values}"
+}
+
+overfor_till_moms() {
+ local bokslut="${1}"
+ local ink1="${2}"
+ local values="${YEAR}/0006-moms/values.txt"
+ local a05 g49 email phone
+
+ a05=$( sed -n 's/^R1: //p' <"${bokslut}" )
+ g49=$( sed -n 's/^B14: //p' <"${bokslut}" )
+ email=$( sed -n 's/^email: //p' <"${ink1}" )
+ phone=$( sed -n 's/^phone: //p' <"${ink1}" )
+
+ input "${values}" 'A05' 'A05 Momspliktig försäljning…: ' "${a05}"
+ input "${values}" 'B10' 'B10 Utgående moms 25%: '
+ input "${values}" 'F48' 'F48 Ingående moms: '
+ input "${values}" 'G49' 'G49 Moms att betala eller få tillbaka: '
+ input "${values}" 'email' 'E-postadress: ' "${email}"
+ input "${values}" 'phone' 'Telefonnummer: ' "${phone}"
+
+ echo "${values}"
+ echo "${values}" | sed 's/./-/g'
+ cat "${values}"
+}
+
+ladda_ner_neblankett() {
+ echo "Please use your web browser to download SKV 2161"
+ echo 'https://skatteverket.se/funktioner/sok/sok.4.64a656d113f4c7597011b3.html?query=ne'
+ echo "Must be the 'Icke ifyllnadsbar pdf för utskrift' one."
+}
+
+ladda_ner_k4blankett() {
+ echo "Please use your web browser to download SKV 2104"
+ echo 'https://skatteverket.se/funktioner/sok/sok.4.64a656d113f4c7597011b3.html?query=k4'
+ echo "Must be the 'Icke ifyllnadsbar pdf för utskrift' one."
+}
+
+gor_neblankett_redigerbar() {
+ cd "${YEAR}/0003-ne-enskild_näringsverksamhet/"
+ pdf2svg 2161_*_20??-01-01?20??-12-31.pdf ne-%d.svg all
+ cd ../../
+}
+
+gor_k4blankett_redigerbar() {
+ cd "${YEAR}/0004-k4/"
+ pdf2svg 2104_*_web_20??*.pdf k4-%d.svg all
+ cd ../../
+}
+
+skriv_under() {
+cat <<EOB
+ Remember
+
+ #### ##### # #### #### ######
+ # # # # # # # # # # # #
+ ## # ##### # ###### # #####
+ # # # # # # # # # # #
+ #### # ###### # # #### ######
+
+ #### ### #### # # ## ##### # # ##### ######
+ # # # # ## # # # # # # # # #
+ #### # # # # # # # # # # # # #####
+ # # # ### # # # ###### # # # ##### #
+ # # # # # # ## # # # # # # # #
+ #### ### #### # # # # # #### # # ######
+
+ on every document!
+EOB
+}
+
+###############################################################################
+
+show_help
+echo ''
+COUNT=0
+for CMD in "mkdir '${YEAR}'" \
+ 'copy_template' \
+ \
+ 'mata_in_arsbokslut' \
+ "mupdf '$(( YEAR-1 ))/0001-forenklad-arsbokslut/215003-ifylld.pdf' &" \
+ "evince '${YEAR}/0001-forenklad-arsbokslut/215003.pdf'" \
+ "mupdf '${YEAR}/0001-forenklad-arsbokslut/215003-ifylld.pdf'" \
+ "lp '${YEAR}/0001-forenklad-arsbokslut/215003-ifylld.pdf'" \
+ \
+ 'ladda_ner_neblankett' \
+ "mkdir '${YEAR}/0003-ne-enskild_näringsverksamhet/'" \
+ "mv ~/Downloads/2161_*_20??-01-01?20??-12-31.pdf \
+ '${YEAR}/0003-ne-enskild_näringsverksamhet/'" \
+ 'gor_neblankett_redigerbar' \
+ "cp '${YEAR}/0001-forenklad-arsbokslut/values.txt' \
+ '${YEAR}/0003-ne-enskild_näringsverksamhet/'" \
+ 'mata_in_ne' \
+ "( cd '${YEAR}/0003-ne-enskild_näringsverksamhet/'; \
+ ../../bin/ne-ifyllning.pl 'values.txt' )" \
+ "mupdf $(( YEAR-1 ))/0003-ne-enskild_näringsverksamhet/ne-framsida.pdf &" \
+ "cp '${YEAR}/0003-ne-enskild_näringsverksamhet/ne-1.svg' \
+ '${YEAR}/0003-ne-enskild_näringsverksamhet/ne-framsida.svg'" \
+ "inkscape '${YEAR}/0003-ne-enskild_näringsverksamhet/ne1-overlay.svg' &" \
+ "inkscape '${YEAR}/0003-ne-enskild_näringsverksamhet/ne-framsida.svg'" \
+ "inkscape --export-filename \
+ '${YEAR}/0003-ne-enskild_näringsverksamhet/ne-framsida.pdf' \
+ '${YEAR}/0003-ne-enskild_näringsverksamhet/ne-framsida.svg'" \
+ "mupdf $(( YEAR-1 ))/0003-ne-enskild_näringsverksamhet/ne-baksida.pdf &" \
+ "cp '${YEAR}/0003-ne-enskild_näringsverksamhet/ne-2.svg' \
+ '${YEAR}/0003-ne-enskild_näringsverksamhet/ne-baksida.svg'" \
+ "inkscape '${YEAR}/0003-ne-enskild_näringsverksamhet/ne2-overlay.svg' &" \
+ "inkscape '${YEAR}/0003-ne-enskild_näringsverksamhet/ne-baksida.svg'" \
+ "inkscape --export-filename \
+ '${YEAR}/0003-ne-enskild_näringsverksamhet/ne-baksida.pdf' \
+ '${YEAR}/0003-ne-enskild_näringsverksamhet/ne-baksida.svg'" \
+ "(cd '${YEAR}/0003-ne-enskild_näringsverksamhet/'; \
+ perl ../../bin/combine-ne.pl )" \
+ "mupdf '${YEAR}/0003-ne-enskild_näringsverksamhet/ne.pdf' "\
+ "lp '${YEAR}/0003-ne-enskild_näringsverksamhet/ne.pdf' "\
+ \
+ 'ladda_ner_k4blankett' \
+ "mkdir '${YEAR}/0004-k4/'" \
+ "mv ~/Downloads/2104_*.pdf \
+ '${YEAR}/0004-k4/'" \
+ 'gor_k4blankett_redigerbar' \
+ "cp '${YEAR}/0003-ne-enskild_näringsverksamhet/values.txt' \
+ '${YEAR}/0004-k4/'" \
+ 'mata_in_k4' \
+ "cp '${YEAR}/0004-k4/k4-1.svg' \
+ '${YEAR}/0004-k4/k4-framsida.svg'" \
+ "cp '${YEAR}/0004-k4/k4-2.svg' \
+ '${YEAR}/0004-k4/k4-baksida.svg'" \
+ "( cd '${YEAR}/0004-k4/'; \
+ ../../bin/k4-ifyllning.pl 'values.txt' )" \
+ "inkscape '${YEAR}/0004-k4/k4_1-overlay.svg' &" \
+ "mupdf $(( YEAR-1 ))/0004-k4/k4-framsida.pdf &" \
+ "inkscape '${YEAR}/0004-k4/k4-framsida.svg'" \
+ "inkscape --export-filename \
+ '${YEAR}/0004-k4/k4-framsida.pdf' \
+ '${YEAR}/0004-k4/k4-framsida.svg'" \
+ "mupdf $(( YEAR-1 ))/0004-k4/k4-baksida.pdf &" \
+ "inkscape '${YEAR}/0004-k4/k4-baksida.svg'" \
+ "inkscape --export-filename \
+ '${YEAR}/0004-k4/k4-baksida.pdf' \
+ '${YEAR}/0004-k4/k4-baksida.svg'" \
+ "mupdf '${YEAR}/0004-k4/k4-framsida.pdf'" \
+ "mupdf '${YEAR}/0004-k4/k4-baksida.pdf'" \
+ "lp '${YEAR}/0004-k4/k4-framsida.pdf'" \
+ "lp '${YEAR}/0004-k4/k4-baksida.pdf'" \
+ \
+ "mkdir '${YEAR}/0005-ink1/'" \
+ "overfor_till_ink1 \
+ '${YEAR}/0003-ne-enskild_näringsverksamhet/values.txt-out' \
+ '${YEAR}/0004-k4/values.txt'" \
+ "( cd '${YEAR}/0005-ink1/'; \
+ ../../bin/ink1-ifyllning.pl 'values.txt' )" \
+ "inkscape '${YEAR}/0005-ink1/ink1_1-overlay.svg'" \
+ "inkscape --export-filename \
+ '${YEAR}/0005-ink1/ink1-framsida.pdf' \
+ '${YEAR}/0005-ink1/ink1_1-overlay.svg'" \
+ "mupdf '${YEAR}/0005-ink1/ink1-framsida.pdf'" \
+ "lp '${YEAR}/0005-ink1/ink1-framsida.pdf'" \
+ "inkscape '${YEAR}/0005-ink1/ink1_2-overlay.svg'" \
+ "inkscape --export-filename \
+ '${YEAR}/0005-ink1/ink1-baksida.pdf' \
+ '${YEAR}/0005-ink1/ink1_2-overlay.svg'" \
+ "mupdf '${YEAR}/0005-ink1/ink1-baksida.pdf'" \
+ "lp '${YEAR}/0005-ink1/ink1-baksida.pdf'" \
+ "mkdir '${YEAR}/0006-moms/'" \
+ "overfor_till_moms '${YEAR}/0001-forenklad-arsbokslut/values.txt' \
+ '${YEAR}/0005-ink1/values.txt'" \
+ "( cd '${YEAR}/0006-moms/'; \
+ ../../bin/moms-ifyllning.pl 'values.txt' )" \
+ "inkscape '${YEAR}/0006-moms/moms-overlay.svg'" \
+ "inkscape --export-filename \
+ '${YEAR}/0006-moms/mervärdesskattedeklaration.pdf' \
+ '${YEAR}/0006-moms/moms-overlay.svg'" \
+ "mupdf '${YEAR}/0006-moms/mervärdesskattedeklaration.pdf'" \
+ "lp '${YEAR}/0006-moms/mervärdesskattedeklaration.pdf'" \
+ 'skriv_under'
+do
+ COUNT=$(( COUNT + 1 ))
+ [ "${COUNT}" -gt "${SKIP}" ] || continue
+
+ DONE='no'
+ FAILED='no'
+ while [ "${DONE}" = 'no' ]; do
+ printf "About to execute (%s):\n'%s'\n" "${COUNT}" "${CMD}"
+ printf 'Should I? (Y/n/c/r/h/q) '
+ read DUMMY
+
+ case ${DUMMY,,} in
+ 'n' | 'no')
+ DONE='yes'
+ ;;
+ 'c' | 'clear')
+ clear
+ ;;
+ 'q' | 'quit')
+ exit 0
+ ;;
+ 'y' | 'yes' | '')
+ do_cmd
+ ;;
+ 'r' | 'repeat')
+ do_cmd
+ DONE='no'
+ ;;
+ 'h' | 'help' | '')
+ show_help
+ ;;
+ *)
+ echo 'Invalid input.' >&2 ;;
+ esac
+ done
+done
diff --git a/bin/ink1-ifyllning.pl b/bin/ink1-ifyllning.pl
new file mode 100755
index 0000000..a5f7fb0
--- /dev/null
+++ b/bin/ink1-ifyllning.pl
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use GD::SVG;
+
+open FILE, $ARGV[0] or die $!;
+my %values;
+my ($key, $value);
+while (my $line = <FILE>) {
+ chomp($line);
+ ($key, $value) = $line =~ /^(.*): *(.*)/g;
+ $values{$key} = $value;
+}
+close FILE;
+
+my $ink1_1 = GD::SVG::Image->new(792, 1122);
+my $ink1_2 = GD::SVG::Image->new(792, 1122);
+
+my $ink1_1_white = $ink1_1->colorAllocate(255,255,255);
+my $ink1_1_black = $ink1_1->colorAllocate(0,0,0);
+my $ink1_2_white = $ink1_2->colorAllocate(255,255,255);
+my $ink1_2_black = $ink1_2->colorAllocate(0,0,0);
+my $font = gdLargeFont;
+
+$ink1_1->startGroup('ink1-1');
+$ink1_1->rectangle(0,0,792,1122, $ink1_1_white);
+
+$ink1_1->string($font, 688, 432, $values{'p7_4'}, $ink1_1_black);
+
+$ink1_1->endGroup;
+
+$ink1_2->startGroup('ink1-2');
+$ink1_2->rectangle(0,0,792,1122, $ink1_2_white);
+
+print "Before\n";
+$ink1_2->string($font, 212, 124, $values{'p10_1'}, $ink1_2_black);
+print "After\n";
+
+# 266 is too far left, 280 is too far right.
+my $contact_column = 273;
+$ink1_2->string($font, $contact_column, 984, $values{'email'}, $ink1_2_black);
+$ink1_2->string($font, $contact_column+356, 984, $values{'phone'}, $ink1_2_black);
+
+$ink1_1->endGroup;
+
+open(SVGFILE, ">ink1_1-overlay.svg");
+print SVGFILE $ink1_1->svg;
+open(SVGFILE, ">ink1_2-overlay.svg");
+print SVGFILE $ink1_2->svg;
diff --git a/bin/k4-ifyllning.pl b/bin/k4-ifyllning.pl
new file mode 100755
index 0000000..c5a9a00
--- /dev/null
+++ b/bin/k4-ifyllning.pl
@@ -0,0 +1,65 @@
+#!/usr/bin/perl
+# FIXME Rewrite to handle multiple entries.
+
+use strict;
+use warnings;
+
+use GD::SVG;
+use User::pwent;
+
+my ($full_name) = (User::pwent::getpwnam(getlogin)->gecos)[0] =~ /([^,]*),+$/;
+
+open FILE, $ARGV[0] or die $!;
+my %values;
+my ($key, $value);
+while (my $line = <FILE>) {
+ chomp($line);
+ ($key, $value) = $line =~ /^(.*): *(.*)/g;
+ $values{$key} = $value;
+}
+close FILE;
+
+my $k4_1 = GD::SVG::Image->new(792, 1122);
+my $k4_2 = GD::SVG::Image->new(792, 1122);
+
+my $k4_1_white = $k4_1->colorAllocate(255,255,255);
+my $k4_1_black = $k4_1->colorAllocate(0,0,0);
+my $k4_2_white = $k4_2->colorAllocate(255,255,255);
+my $k4_2_black = $k4_2->colorAllocate(0,0,0);
+my $font = gdLargeFont;
+
+$k4_1->startGroup('k4-1');
+$k4_1->rectangle(0,0,792,1122, $k4_1_white);
+
+$k4_1->string($font, 580, 112, `date +%Y-%m-%d`, $k4_1_black);
+
+$k4_1->string($font, 60, 225, $full_name, $k4_1_black);
+$k4_1->string($font, 580, 225, $values{'Personnummer'}, $k4_1_black);
+
+# Antal
+$k4_1->string($font, 80, 321, $values{'Antal'}, $k4_1_black);
+# Beteckning
+$k4_1->string($font, 165, 321, $values{'Beteckning'}, $k4_1_black);
+# Forsaljningspris
+$k4_1->string($font, 290, 321, $values{'Forsaljningspris'}, $k4_1_black);
+# Omkostnadsbelopp
+$k4_1->string($font, 405, 321, $values{'Omkostnadsbelopp'}, $k4_1_black);
+# Vinst
+$k4_1->string($font, 520, 321, $values{'Vinst'}, $k4_1_black);
+# Forlust
+#$k4_1->string($font, 635, 321, "0", $k4_1_black);
+
+# Summa
+# Forsaljningspris
+$k4_1->string($font, 290, 615, $values{'Forsaljningspris'}, $k4_1_black);
+# Omkostnadsbelopp
+$k4_1->string($font, 405, 615, $values{'Omkostnadsbelopp'}, $k4_1_black);
+# Vinst
+$k4_1->string($font, 520, 615, $values{'Vinst'}, $k4_1_black);
+# Forlust
+#$k4_1->string($font, 635, 615, "0", $k4_1_black);
+
+$k4_1->endGroup;
+
+open(SVGFILE, ">k4_1-overlay.svg");
+print SVGFILE $k4_1->svg;
diff --git a/bin/moms-ifyllning.pl b/bin/moms-ifyllning.pl
new file mode 100755
index 0000000..8f8d46b
--- /dev/null
+++ b/bin/moms-ifyllning.pl
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use GD::SVG;
+use User::pwent;
+
+my ($full_name) = (User::pwent::getpwnam(getlogin)->gecos)[0] =~ /([^,]*),+$/;
+
+open FILE, $ARGV[0] or die $!;
+my %values;
+my ($key, $value);
+while (my $line = <FILE>) {
+ chomp($line);
+ ($key, $value) = $line =~ /^(.*): *(.*)/g;
+ $values{$key} = $value;
+}
+close FILE;
+
+if (int($values{'A05'}*0.25+0.5) ne $values{'B10'}) {
+ print STDERR "Utgående moms stämmer icke! (A05, B10)\n";
+ exit 1;
+}
+
+if ($values{'B10'}-$values{'F48'} ne $values{'G49'}) {
+ print STDERR "Momsberäkning balanserar icke! (B10-F48 != G49)\n";
+ print STDERR "B10 - F48 = $values{'B10'} - $values{'F48'} == "
+ .($values{'B10'}-$values{'F48'})."\n";
+ print STDERR "G49 = $values{'G49'} == ".($values{'G49'})."\n";
+ exit 1;
+}
+
+my $moms = GD::SVG::Image->new(792, 1122);
+
+my $moms_white = $moms->colorAllocate(255,255,255);
+my $moms_black = $moms->colorAllocate(0,0,0);
+my $font = gdLargeFont;
+
+$moms->startGroup('ne-1');
+$moms->rectangle(0,0,792,1122, $moms_white);
+
+$moms->string($font, 219, 297, $values{'A05'}, $moms_black);
+$moms->string($font, 563, 297, $values{'B10'}, $moms_black);
+$moms->string($font, 563, 786, $values{'F48'}, $moms_black);
+$moms->string($font, 563, 851, $values{'G49'}, $moms_black);
+
+$moms->string($font, 427, 945, $full_name, $moms_black);
+$moms->string($font, 427, 977, $full_name, $moms_black);
+$moms->string($font, 427, 1008, $values{'phone'}, $moms_black);
+
+$moms->endGroup;
+
+open(SVGFILE, ">moms-overlay.svg");
+print SVGFILE $moms->svg;
diff --git a/bin/ne-ifyllning.pl b/bin/ne-ifyllning.pl
new file mode 100755
index 0000000..27c0459
--- /dev/null
+++ b/bin/ne-ifyllning.pl
@@ -0,0 +1,116 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use GD::SVG;
+use User::pwent;
+
+my ($full_name) = (User::pwent::getpwnam(getlogin)->gecos)[0] =~ /([^,]*),+$/;
+
+open FILE, $ARGV[0] or die $!;
+my %values;
+my ($key, $value);
+while (my $line = <FILE>) {
+ chomp($line);
+ ($key, $value) = $line =~ /^(.*): *(.*)/g;
+ $values{$key} = $value;
+}
+close FILE;
+
+if ($values{'B9'}-$values{'B14'} ne $values{'B10'}) {
+ print STDERR "Balanserar icke (B1-B12)!\n";
+ exit 1;
+}
+
+if ($values{'R1'}-$values{'R5'} ne $values{'R11'}) {
+ print STDERR "Balanserar icke! (R1-R11)\n";
+ exit 1;
+}
+
+# Bokfört resultat (förs över från B11)
+$values{'R12'} = $values{'R11'};
+# Sammanlagt resultat av verksamheten
+$values{'R17'} = $values{'R12'};
+# Överskott (+) / Underskott (-)
+$values{'R29'} = $values{'R12'};
+# Överskott (+) / Underskott (-), periodiseringsfond
+$values{'R33'} = $values{'R29'};
+# Överskott (+) / Underskott (-), expansionsfond
+$values{'R35'} = $values{'R33'};
+# Överskott (+) / Underskott (-), före egenavgifter och löneskatt
+$values{'R42'} = $values{'R35'}+$values{'R40'}-$values{'R41'};
+# Årets beräknade avdrag för egenavgifter och särskild löneskatt
+my $R43_max_deduct = int(($values{'R42'} * 0.075) + 0.5);
+if ($R43_max_deduct > 15000) {
+ $R43_max_deduct = 15000;
+}
+#my $covid_100k = 100000;
+#my $R43 = int(($covid_100k * 0.1021) + (($R42 - $covid_100k) * 0.2897) + 0.5) - $R43_max_deduct;
+$values{'R43'} = int(($values{'R42'} * $values{'egenavgifter'}) + 0.5) - $R43_max_deduct;
+# Överskott (+)
+$values{'R47'} = $values{'R42'}-$values{'R43'};
+
+my $ne1 = GD::SVG::Image->new(792, 1122);
+my $ne2 = GD::SVG::Image->new(792, 1122);
+
+my $ne1_white = $ne1->colorAllocate(255,255,255);
+my $ne1_black = $ne1->colorAllocate(0,0,0);
+my $ne2_white = $ne2->colorAllocate(255,255,255);
+my $ne2_black = $ne2->colorAllocate(0,0,0);
+my $font = gdLargeFont;
+
+$ne1->startGroup('ne-1');
+$ne1->rectangle(0,0,792,1122, $ne1_white);
+
+$ne1->string($font, 420, 152, `date +%Y-%m-%d`, $ne1_black);
+$ne1->string($font, 60, 200, $full_name, $ne1_black);
+$ne1->string($font, 600, 200, $values{'Personnummer'}, $ne1_black);
+$ne1->string($font, 60, 250, $values{'SNI-kategori'}, $ne1_black);
+$ne1->string($font, 600, 296, $values{'Personnummer'}, $ne1_black);
+
+$ne1->string($font, 300, 704, $values{'B9'}, $ne1_black);
+$ne1->string($font, 645, 400, $values{'B10'}, $ne1_black);
+$ne1->string($font, 645, 641, $values{'B14'}, $ne1_black);
+
+$ne1->string($font, 300, 785, $values{'R1'}, $ne1_black);
+$ne1->string($font, 300, 945, $values{'R5'}, $ne1_black);
+$ne1->string($font, 645, 898, $values{'R11'}, $ne1_black);
+
+# Uppdragstagare har biträtt vid upprättandet
+$ne1->string(gdGiantFont, 583, 980, 'x', $ne1_black);
+
+$ne1->endGroup;
+
+my $ne2_left_col = 320;
+my $ne2_right_col = 675;
+# my $ne2_line_height = 35.78; # Lines differ in height...
+
+$ne2->startGroup('ne-2');
+$ne2->rectangle(0, 0, 792, 1122, $ne2_white);
+
+$ne2->string($font, 600, 50, $values{'Personnummer'}, $ne2_black);
+
+$ne2->string($font, $ne2_left_col, 112, $values{'R12'}, $ne2_black);
+$ne2->string($font, $ne2_left_col, 271, $values{'R17'}, $ne2_black);
+$ne2->string($font, $ne2_right_col, 206, $values{'R33'}, $ne2_black);
+$ne2->string($font, $ne2_right_col, 271, $values{'R35'}, $ne2_black);
+$ne2->string($font, $ne2_right_col, 451, $values{'R40'}, $ne2_black);
+$ne2->string($font, $ne2_right_col, 485, $values{'R41'}, $ne2_black);
+$ne2->string($font, $ne2_right_col, 519, $values{'R42'}, $ne2_black);
+$ne2->string($font, $ne2_right_col, 553, $values{'R43'}, $ne2_black);
+$ne2->string($font, $ne2_right_col, 703, $values{'R47'}, $ne2_black);
+$ne2->string($font, $ne2_left_col, 735, $values{'R29'}, $ne2_black);
+
+$ne2->endGroup;
+
+open(SVGFILE, ">ne1-overlay.svg");
+print SVGFILE $ne1->svg;
+open(SVGFILE, ">ne2-overlay.svg");
+print SVGFILE $ne2->svg;
+
+open(FILE, '>', $ARGV[0]."-out");
+ for (keys %values ) {
+ print FILE $_.": ".$values{$_}."\n";
+ }
+close FILE;