$(card_item "$card" CATEGORIES)
]
[div .control
+ [a .button .item href="${_BASE}/ledgers/account.sh?card=${cardfile##*/}" $(l10n ledger)]
[a .button .item href="${_BASE}/cards/edit_card.sh?card=${cardfile##*/}" $(l10n edit)]
[a .button .item href="${_BASE}/cards/export_card.sh?card=${cardfile##*/}" $(l10n vcf_export)]
]
#!/bin/env awk -f
+function debug(t) { printf "%s\n", t >>"/dev/stderr"; }
+
function PATH( str, seg, out ) {
while ( str ) {
seg = str;
split("", _GET); split("", _POST); split("", _REF);
split("", _HEADER); split("", _COOKIE);
- if ( ENVIRON["REQUEST_METHOD"] )
+ if ( ENVIRON["REQUEST_METHOD"] ) {
_cgilite_headers();
- else
+ } else {
_cgilite_request();
+ }
}
color: #068;
word-break: break-word;
}
-a.button {
+a.button, label.button {
font-style: inherit;
text-decoration: inherit;
color: inherit;
}
h2 { font-size: 1.125em; }
-select, input, button, textarea, a.button {
+select, input, button, textarea, a.button, label.button {
display: inline-block;
color: #000; background-color: #FFF;
border: .5pt solid;
}
input[type=number] { text-align: right; padding-right: 0; }
-button, input[type=button], a.button {
+button, input[type=button], a.button, label.button {
box-shadow: .125em .125em .25em;
cursor: pointer;
}
printf '%s\n' "$out"
}
+RXLITERAL(){
+ # sed -E 's;[].*+?^${}()|\[];\\&;g'
+ local in out=''
+ [ $# -gt 0 ] && in="$*" || in="$(cat)"
+ while [ "$in" ]; do case $in in
+ [.+^\$\{\}\(\)\[\]\*\?\|\\]*)
+ out="${out}\\${in%"${in#?}"}"; in="${in#?}";
+ ;;
+ *)out="${out}${in%%[.+^\$\{\}\(\)\[\]\*\?\|\\]*}"
+ in="${in#"${in%%[.+^\$\{\}\(\)\[\]\*\?\|\\]*}"}"
+ ;;
+ esac; done
+ printf '%s\n' "$out"
+}
+
DBM() {
local file="$1" cmd="$2"
local k v key value
--- /dev/null
+#!/bin/sh
+
+[ "$include_datetime" ] && return 0
+include_datetime="$0"
+
+# Copyright 2023 - 2024 Paul Hänsch
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+isdate(){
+ local date="$1" y m d
+
+ if printf %s "$date" \
+ | grep -xEq '[0-9]{4}-((01|03|05|07|08|10|12)-(0[1-9]|[12][0-9]|3[01])|(04|06|09|11)-(0[1-9]|[12][0-9]|30)|02-(0[1-9]|[12][0-9]))'
+ then # y-m-d (ISO Date)
+ y="${date%%-*}" d="${date##*-}" m="${date%-*}" m="${m#*-}"
+ elif printf %s "$date" \
+ | grep -xEq '((0?1|0?3|0?5|0?7|0?8|10|12)/(0?[1-9]|[12][0-9]|3[01])|(0?4|0?6|0?9|11)/(0?[1-9]|[12][0-9]|30)|0?2-(0[1-9]|[12][0-9]))/([0-9]{2}|[0-9]{4})'
+ then # m/d/y (US Date)
+ y="${date##*/}" m="${date%%/*}" d="${date%/*}" d="${d#*/}"
+ elif printf %s "$date" \
+ | grep -xEq '((0?[1-9]|[12][0-9]|3[01])[\./](0?1|0?3|0?5|0?7|0?8|10|12)|(0?[1-9]|[12][0-9]|30)[\./](0?4|0?6|0?9|11)|(0[1-9]|[12][0-9])[\./]0?2)[\./]([0-9]{2}|[0-9]{4})'
+ then # d/m/y or d.m.y (European Date / German Date)
+ y="${date##*.}" d="${date%%.*}" m="${date%.*}" m="${m#*.}"
+ else
+ return 1
+ fi
+ [ $y -lt 100 -a $y -gt 50 ] && y=$((y + 1900))
+ [ $y -lt 100 -a $y -le 50 ] && y=$((y + 2000))
+ date="$(printf "%04i-%02i-%02i" $y ${m#0} ${d#0})"
+
+ # leap year
+ if [ "$m" -eq 2 -a "$d" -eq 29 ]; then
+ if [ "$((y % 400))" -eq 0 ]; then
+ :
+ elif [ "$((y % 100))" -eq 0 ]; then
+ return 1
+ elif [ "$((y % 4))" -eq 0 ]; then
+ :
+ else
+ return 1
+ fi
+ fi
+
+ printf '%04i-%02i-%02i\n' "$y" "${m#0}" "${d#0}"
+ return 0
+}
+
+istime(){
+ time="$1" h= m=
+
+ if printf %s "$time" | grep -xEq '(0?[1-9]|1[012])(:[0-5][0-9])? ?(am|AM)\.?'; then
+ time="${time%?[aA][mM]}" h="${time%:*}" h="$(h % 12)"
+ [ "$h" != "$time" ] && m="${time#*:}" || m=0
+ elif printf %s "$time" | grep -xEq '(0?[1-9]|1[012])(:[0-5][0-9])? ?(pm|PM)\.?'; then
+ time="${time%?[aA][mM]}" h="${time%:*}" h="$(h % 12 + 12)"
+ [ "$h" != "$time" ] && m="${time#*:}" || m=0
+ elif printf %s "$time" | grep -xEq '(0?[0-9]|1[0-9]|2[0-3]):[0-5][0-9]'; then
+ time="${time%?[aA][mM]}" h="${time%:*}" m="${time#*:}"
+ else
+ return 1
+ fi
+
+ printf '%02i:%02i\n' "${h#0}" "${m#0}"
+ return 0
+}
+
+numdays(){
+ # return number of days in a month (i.e. 28, 29, 30, or 31)
+ local y="$1" m="${2#0}"
+
+ case $m in
+ 1|3|5|7|10|12)
+ printf 31\\n
+ ;;
+ 4|6|8|9|11)
+ printf 30\\n
+ ;;
+ 2) if [ "$((y % 400))" -eq 0 ]; then
+ printf 29\\n
+ elif [ "$((y % 100))" -eq 0 ]; then
+ printf 28\\n
+ elif [ "$((y % 4))" -eq 0 ]; then
+ printf 29\\n
+ else
+ printf 28\\n
+ fi
+ ;;
+ *) return 1;;
+ esac
+}
edit) printf %s "Bearbeiten";;
edit_categories) printf %s "Kategorien Bearbeiten";;
vcf_export) printf %s "Vcard Exportieren";;
+ ledger) printf %s "Buchungen";;
control) printf %s "Aktionen";;
delete) printf %s "entfernen";;
edit_update) printf %s "Daten übernehmen";;
filter_cancel) printf %s "Filter löschen";;
export_csv) printf %s "Liste als CSV-Datei";;
+ # Accounting page
+ Ledgers) printf %s "Buchungslisten";;
+ '%i IBANs are unassigned') printf %s "%i IBANs sind nicht zugewiesen";;
+ 'IBAN Assignments') printf %s "IBAN Zuweisung";;
+ 'Assign IBANs') printf %s "IBANs Zuweisen";;
+ 'Account') printf %s "Konto";;
+
+ 'Accept Suggestions') printf %s "Vorschläge Akzeptieren";;
+ 'Ignore Suggestions') printf %s "Vorschläge Ignorieren";;
+ 'Submit Changes') printf %s "Änderungen Übernehmen";;
+
+ 'Payments') printf %s "Zahlungen";;
+ 'Date') printf %s "Datum";;
+ 'Originator') printf %s "Auftraggeber";;
+ 'Reference Text') printf %s "Verwendungszweck";;
+ 'Amount') printf %s "Betrag";;
+ 'Balance') printf %s "Kto.Stand";;
+ 'Manual Record') printf %s "Manueller Eintrag";;
+ 'Recur Monthly') printf %s "[strike monat­lich wie­der­holen]";;
+ 'Credit Account') printf %s "Guthaben­konto";;
+ 'Submit') printf %s "Eintragen";;
+
# UI Labels Special
course_attendance) printf %s "Kurs­teil­nahme";;
vcf_seed_label) printf "Anmeld. Vorn. Nachn. Geb.Tag Geb.Monat Geb.Jahr Tel. Mobil () EMail () Notiz";;
--- /dev/null
+#!/bin/sh
+
+credit() {
+ printf '%+04i\n' "$1" \
+ | sed -E 's;[0-9]{2}$;d&;; :0 s;([0-9])([0-9]{3}[dm]);\1m\2;; t0; y;dm;,.;'
+}
+
+if uid="$(POST uid)"; then
+ cfile="$(grep -lxF "UID;:${uid}" "${_DATA}/vcard/"*.vcf || grep -lxF "UID:${uid}" "${_DATA}/vcard/"*.vcf)"
+ REDIRECT "${_BASE}/ledgers/account.sh?card=${cfile##*/}"
+fi
+
+. "${_EXEC}/cgilite/storage.sh"
+. "${_EXEC}/pdiread.sh"
+. "${_EXEC}/cards/l10n.sh"
+. "${_EXEC}/cards/widgets.sh"
+. "${_EXEC}/datetime.sh"
+
+cardfile="$(GET card |PATH)" cardfile="${cardfile##*/}"
+if [ ! -f "${_DATA}/vcard/$cardfile" ]; then
+ SET_COOKIE 0 message="Invalid account: $cardfile"
+ REDIRECT "${_BASE}/ledgers/"
+fi
+
+cledger="${_DATA}/ledgers/vcf.${cardfile%.vcf}.account"
+
+if tid="$(POST tid)"; then
+ if [ "$tid" != "$(transid "$cledger")" ]; then
+ SET_COOKIE 0 message="Ledger was changed since last edit"
+ REDIRECT "$REQUEST_URI"
+ fi
+ tdate="$(isdate "$(POST tdate)")"
+ tref="$(POST tref |grep -m1 -xE '.+')"
+ tamount="$(POST tamount \
+ | sed -E '
+ s;^([\+-]?[0-9]+)[\.,]([0-9][0-9])$;\1\2;;
+ s;^([\+-]?[0-9]+)$;&00;;
+ ' | grep -m1 -xE '[\+-]?[0-9]+')"
+ # debug "TDATE: $tdate TREF: $tref AMOUNT: $tamount"
+ if ! [ "$tdate" -a "$tref" -a "$tamount" ]; then
+ SET_COOKIE 0 message="Transaction info invalid"
+ REDIRECT "$REQUEST_URI"
+ fi
+ tdtstamp="$(date -ud "$tdate" +%s)"
+ printf '%s %i %s \ %s %i\n' \
+ "${tdate}" "${tdtstamp}" "$(STRING "${cardfile%.vcf}")" \
+ "$(STRING "$tref")" "${tamount}" \
+ >>"${cledger}"
+ REDIRECT "$REQUEST_URI"
+fi
+
+{ card="$(pdi_load "${_DATA}/vcard/$cardfile")"
+ cat <<-EOF
+ [h1 $(l10n Payments)]
+ [div .card #${cardfile}
+ [div .section .basic . $(
+ card_item "$card" FN GENDER NICKNAME BDAY X-ZACK-JOINDATE X-ZACK-LEAVEDATE SOUND PHOTO LOGO
+ )]
+ [div .section .phone . $(card_item "$card" TEL)]
+ [div .section .message . $(card_item "$card" EMAIL IMPP URL)]
+ [div .section .address . $(card_item "$card" ADR X-IBAN)]
+ [div .section .note . $(card_item "$card" NOTE)]
+ [div .section .attendance [h3 $(l10n course_attendance) ] [ul
+ $(grep -F " ${cardfile}" "$_DATA/mappings/attendance" |while read each discard; do
+ printf '[li [a .item .attendance href="%s/courses#%s" . %s]]' \
+ "${_BASE}" "$each" \
+ "$(pdi_value "$(pdi_load "$_DATA/ical/$each")" SUMMARY || l10n "(unnamed course)" |unescape |HTML)"
+ done |sort -k7)]
+ $(card_item "$card" CATEGORIES)
+ ]
+ ]
+ EOF
+ cat <<-EOF
+ [form method=POST
+ [hidden "tid" "$(transid "${cledger}")"]
+ [table .transactions
+ [thead [tr
+ [th .date . $(l10n Date)][th .orig . $(l10n Originator)]
+ [th .reference . $(l10n "Reference Text")]
+ [th .amount . $(l10n Amount)][th .balance . $(l10n Balance)]
+ ]]
+ [tbody
+ EOF
+ cnt="$(pdi_count "$card" X-IBAN)"
+ { while [ "$cnt" -gt 0 ]; do
+ pdi_value "$card" X-IBAN "$cnt" |RXLITERAL
+ cnt=$((cnt - 1))
+ done
+ RXLITERAL "${cardfile%.vcf}"; echo
+ } |debug \
+ | while read -r iban; do
+ grep -hE "^[^\t]+ [^\t]+ ${iban} " "${_DATA}/ledgers/"*
+ done \
+ | sort -n -k2 \
+ | { total=0
+ while read -r date dtstamp iban accname reftext amount; do
+ total=$((total + amount))
+ if [ "$iban" = "${cardfile%.vcf}" ]; then
+ printf '[tr [td .date . %s][td .orig [span . %s][span . %s]][td .reference . %s]
+ [td .amount . %s][td .balance . %s]]' \
+ "$date" "$(l10n "Credit Account")" \
+ "$(UNSTRING "$accname" |HTML)" "$(UNSTRING "$reftext" |HTML)" \
+ "$(credit "$amount")" "$(credit "$total")"
+ else
+ printf '[tr [td .date . %s][td .orig [span . %s][span . %s]][td .reference . %s]
+ [td .amount . %s][td .balance . %s]]' \
+ "$date" "$(HTML "$iban")" \
+ "$(UNSTRING "$accname" |HTML)" "$(UNSTRING "$reftext" |HTML)" \
+ "$(credit "$amount")" "$(credit "$total")"
+ fi
+ done
+ }
+ cat <<-EOF
+ [tr [th colspan=5 . $(l10n 'Manual Record')]]
+ [tr [td .date
+ [input type=date placeholder="$(l10n Date)" name=tdate value="$(date +%F)"]
+ [input type=checkbox id=rr_month name=recur value=monthly]
+ [label for=rr_month $(l10n Recur Monthly)]
+ ][td .orig ]
+ [td .reference . [textarea placeholder="$(l10n "Reference Text")" name=tref]]
+ [td .amount [input type=number placeholder="$(l10n Amount)" name=tamount value=0.00 step=.01]]
+ [td .balance [button type=submit . $(l10n Submit)]]
+ ]]]]
+ EOF
+} \
+| yield_page ledgers
--- /dev/null
+#!/bin/sh
+
+delete="$(POST delete |PATH)"
+dtable="${_DATA}/ledgers/${delete##*/}"
+
+if [ "$dtable" ]; then
+ rm -- "${dtable}" \
+ || SET_COOKIE 0 message="Unable to delete ledger: \"$(HTML "$delete")\""
+else
+ SET_COOKIE 0 message="No such ledger: \"$(HTML "$delete")\""
+fi
+
+REDIRECT "${_BASE}/ledgers/"
#!/bin/awk -f
+function dbg( text ) { print text >>"/dev/stderr"; }
+
function STRING( inp ) {
gsub(/\\/, "\\\\", inp);
gsub(/\n/, "\\n", inp);
fn = n = uid = iban = tmp = ""; split("", ibans);
}
-strftime("%Y-%m-%d", $1, "UTC") == $2 && strftime("%Y-%m-%d", $3, "UTC") == $4 { ledger = 1; }
+/^BEGIN:LEDGERS$/ { ledger = 1; }
ledger && strftime("%Y-%m-%d", $2, "UTC") == $1 {
if ($3 in iban_uid) {
}
END {
- for (iban in sure) {
- line = "sure " iban;
- split(iban_uid[iban], uids, / /);
- for (uid in uids) line = line " " STRING(uid "/" uid_fn[uid]);
- print line;
- }
for (iban in unsure) {
line = "guess " iban " " STRING(unsure_rec[iban]);
split(unsure[iban], uids, / /);
line = "unknown " iban " " unknown[iban];
print line;
}
+ for (iban in sure) {
+ line = "sure " iban;
+ split(sure[iban], uids, / /);
+ for (k in uids) line = line " " STRING(uids[k] "/" uid_fn[uids[k]]);
+ print line;
+ }
}
--- /dev/null
+#!/bin/sh
+
+credit() {
+ printf '%03i\n' "$1" \
+ | sed -E 's;[0-9]{2}$;d&;; :0 s;([0-9])([0-9]{3}[dm]);\1m\2;; t0; y;dm;,.;'
+}
+
+{ printf '
+ [h1 . %s]
+ [form .ibanassign action="%s/ledgers/set_iban.sh" method=POST
+ [input type=hidden name=session_id value="%s"]
+ ' "$(l10n "IBAN Assignments")" "${_BASE}" "$SESSION_ID"
+ printf '[datalist id=lattendants .'
+ pdi_load "${_DATA}"/vcard/*.vcf |sed -n '/^FN\;:/!b; s;^FN\;:;;; p;' \
+ | while read name; do
+ printf '[option value="%s"]' "$(HTML "$name")"
+ done
+ printf ']'
+ l10n_attendant="$(l10n attendant)"
+ printf %s\\n "$IBAN_ASSIGN" \
+ | while read -r state iban data; do
+ iban="$(UNSTRING "$iban")"
+ [ ! "$iban" ] && iban="??????????"
+ printf '[input type=checkbox id="use_%s" name="use_%s" value=true]' "$iban" "$iban"
+ printf '[fieldset .iban .%s [legend . %s ]' \
+ "$state" "$iban"
+ if [ $state = sure ]; then
+ for card in $data; do
+ uid="${card%%/*}" name="$(UNSTRING "${card#*/}")"
+ printf '[span .card . %s]' "$(HTML "${name}")"
+ done
+ :
+ elif [ $state = guess ]; then
+ record="$(UNSTRING "${data%% *}")"
+ cards="${data#* }"
+ date="${record%% *}"
+ principal="${record#* * * }" principal="${principal%% *}"
+ subject="${record#* * * * }" subject="${subject%% *}"
+ amount="${record#* * * * * }" amount="${amount%% *}"
+ printf '[p .principal . %s][p .date %s][p .amount %s][p .subject . %s]' \
+ "$(UNSTRING "$principal" |HTML)" "$date" "$(credit "$amount")" "$(UNSTRING "$subject" |HTML)"
+ n=0; for card in $cards; do
+ n=$((n+1)); uid="${card%%/*}" name="$(UNSTRING "${card#*/}")"
+ cat <<-EOF
+ [input type=checkbox id="check_${iban}_$n" name="check_${iban}_$n" value=true checked=checked]
+ [input .card name="fn_${iban}_$n" value="$(HTML "$name")" .disabled tabindex="-1"]
+ [label .del for="check_${iban}_$n" . -]
+ EOF
+ done
+ for m in 1 2 3 4 5 6 7 8; do
+ cat <<-EOF
+ [input type=checkbox id="check_${iban}_$((n+m))" name="check_${iban}_$((n+m))" value=true]
+ [input .card name="fn_${iban}_$((n+m))" value="" placeholder="${l10n_attendant}" list="lattendants"]
+ [label .add for="check_${iban}_$((n+m))" . +]
+ EOF
+ done
+ printf '[label .button for="use_%s" . %s]' "$iban" "$(l10n Accept Suggestions)"
+ printf '[label .button for="use_%s" . %s]' "$iban" "$(l10n Ignore Suggestions)"
+ elif [ $state = unknown ]; then
+ date="${data%% *}"
+ principal="${data#* * * }" principal="${principal%% *}"
+ subject="${data#* * * * }" subject="${subject%% *}"
+ amount="${data#* * * * * }" amount="${amount%% *}"
+ printf '[p .principal . %s][p .date %s][p .amount %s][p .subject . %s]' \
+ "$(UNSTRING "$principal" |HTML)" "$date" "$(credit "$amount")" "$(UNSTRING "$subject" |HTML)"
+ printf '[input name="check_" type=hidden][input type=hidden][label .del]'
+ n=0; for m in 1 2 3 4 5 6 7 8; do
+ cat <<-EOF
+ [input type=checkbox id="check_${iban}_$((n+m))" name="check_${iban}_$((n+m))" value=false]
+ [input .card name="fn_${iban}_$((n+m))" value="" placeholder="${l10n_attendant}" list="lattendants"]
+ [label .add for="check_${iban}_$((n+m))" . +]
+ EOF
+ done
+ printf '[label .button for="use_%s" . %s]' "$iban" "$(l10n Accept Suggestions)"
+ printf '[label .button for="use_%s" . %s]' "$iban" "$(l10n Ignore Suggestions)"
+ fi
+ printf ']'
+ done
+ printf '[button type=submit . %s]' "$(l10n Submit Changes)"
+ printf ' ]'
+} | yield_page ledgers
. "$_EXEC/cgilite/storage.sh"
. "$_EXEC/pdiread.sh"
+if [ "$(POST show_account)" ]; then
+ uid="$(POST show_account)"
+ cfile="$(grep -lxF "UID;:${uid}" "${_DATA}/vcard/"*.vcf || grep -lxF "UID:${uid}" "${_DATA}/vcard/"*.vcf)"
+ REDIRECT "${_BASE}/ledgers/account.sh?card=${cfile##*/}"
+ exit 0;
+fi
+
credit() {
printf '%03i\n' "$1" \
| sed -E 's;[0-9]{2}$;d&;; :0 s;([0-9])([0-9]{3}[dm]);\1m\2;; t0; y;dm;,.;'
}
+IBAN_ASSIGN="$(
+ { pdi_load "${_DATA}"/vcard/*.vcf
+ printf 'BEGIN:LEDGERS\n'
+ cat "${_DATA}"/ledgers/????-??-??\ -\ ????-??-??\ -\ ????.tbl
+ } | "${_EXEC}"/ledgers/iban_assign.awk
+ printf '\n'
+)"
+
+if [ "${PATH_INFO%/iban_assign/}" != "${PATH_INFO}" ]; then
+ . "${_EXEC}/ledgers/iban_assign.sh"
+ exit 0
+fi
+
{ printf '
[form .upload action="%s/ledgers/csv_upload.sh" method="POST" enctype="multipart/form-data"
[label for=ledger_upload . %s:]
"$(HTML "${ledger% - ????.tbl}")" "$(HTML "$ledger")" "$(l10n delete)"
done
printf ' ]'
- printf '
- [form .ibanassign action="%s/ledgers/iban_assign.sh" method=POST
- [input type=hidden name=session_id value="%s"]
- [h3 . %s]
- ' "${_BASE}" "$SESSION_ID" "$(l10n "IBAN Assignments")"
- printf '[datalist id=lattendants .'
- pdi_load "${_DATA}"/vcard/*.vcf |sed -n '/^FN\;:/!b; s;^FN\;:;;; p;' \
- | while read name; do
- printf '[option value="%s"]' "$(HTML "$name")"
- done
- printf ']'
- { pdi_load "${_DATA}"/vcard/*.vcf
- cat "${_DATA}"/ledgers/????-??-??\ -\ ????-??-??\ -\ ????.tbl
- } | "${_EXEC}"/ledgers/iban_assign.awk \
- | while read -r state iban data; do
- printf '[fieldset .iban .%s [legend . %s ]' \
- "$state" "$iban"
- if [ $state = sure ]; then
- :
- elif [ $state = guess ]; then
- record="$(UNSTRING "${data%% *}")"
- cards="${data#* }"
- principal="${record#* * * }" principal="${principal%% *}"
- subject="${record#* * * * }" subject="${subject%% *}"
- amount="${record#* * * * * }" amount="${amount%% *}"
- printf '[p .principal . %s][p .amount %s][p .subject . %s]' \
- "$(UNSTRING "$principal" |HTML)" "$(credit "$amount")" "$(UNSTRING "$subject" |HTML)"
- printf '[h4 . %s]' "$(l10n Guesses)"
- for card in $cards; do
- uid="${card%%/*}" name="$(UNSTRING "${card#*/}")"
- printf '[input .card key="cardfn" value="%s" placeholder="%s" list=lattendants]' "$(HTML "${name}")" "$(l10n attendent)"
- done
- elif [ $state = unknown ]; then
- principal="${data#* * * }" principal="${principal%% *}"
- subject="${data#* * * * }" subject="${subject%% *}"
- amount="${data#* * * * * }" amount="${amount%% *}"
- printf '[p .principal . %s][p .amount %s][p .subject . %s]' \
- "$(UNSTRING "$principal" |HTML)" "$(credit "$amount")" "$(UNSTRING "$subject" |HTML)"
- fi
- printf ']'
- done
- printf ' ]'
+ unassigned="$(printf %s\\n "$IBAN_ASSIGN" |grep -E '^guess|^unknown' |wc -l)"
+ cat <<-EOF
+ [div
+ [h1 . $(l10n IBAN Assignments)]
+ $(printf "$(l10n "%i IBANs are unassigned")" "$unassigned")
+ [a href="${_BASE}/ledgers/iban_assign/" . $(l10n Assign IBANs)]
+ ]
+ [form action="${_BASE}/ledgers/account.sh" method=POST
+ [select name=uid
+ $(printf %s\\n "$IBAN_ASSIGN" \
+ | sed -E '
+ /^sure /!d;
+ s;^sure [^\t]+;;;
+ s;([^\t]+)/([^\t]+);\1 \2\n;g;
+ s;\n$;;
+ ' \
+ | while read uid fn; do
+ uid="$(HTML "$uid")"
+ fn="$(UNSTRING "$fn" |HTML)"
+ printf '[option value="%s" . %s]' "$uid" "$fn"
+ done)
+ ]
+ [button type="submit" . $(l10n Account)]
+ ]
+ EOF
} | yield_page ledgers
--- /dev/null
+#!/bin/sh
+
+. "$_EXEC/pdiread.sh"
+. "$_EXEC/session_lock.sh"
+
+UIDLIST="$(
+ pdi_load "$_DATA/vcard/"*.vcf \
+ | sed -Ez '
+ s/\nBEGIN;:VCARD\n([^\n]+\n)*FN;:([^\n]+)\n([^\n]+\n)*UID;:([^\n]+)\n([^\n]+\n)*END;:VCARD\n/UID:\4 FN:\2/g
+ '
+ echo
+)"
+
+for key in $(POST_KEYS); do case $key in
+ use_*)
+ use_iban="${use_iban} ${key#use_} "
+ ;;
+esac; done
+
+[ "$use_iban" ] && for key in $(POST_KEYS); do case $key in
+ check_*_*)
+ iban="${key#check_}" iban="${iban%_*}"
+ [ ! "${use_iban##* "${iban}" *}" ] && check="${check} ${key#check_} "
+ ;;
+esac; done
+
+{ printf '[ul .results'
+ for use in $check; do
+ iban="${use%_*}"
+ fn="$(POST "fn_${use}")"
+ uid="${UIDLIST%% FN:"$fn"${BR}*}" uid="${uid##*${BR}UID:}"
+
+ # cfile="${_DATA}/vcard/${uid}.vcf"
+ cfile="$(grep -lxF "UID;:${uid}" "${_DATA}/vcard/"*.vcf || grep -lxF "UID:${uid}" "${_DATA}/vcard/"*.vcf)"
+ if SLOCK "$cfile" >/dev/null; then
+ card="$(pdi_load "$cfile")"
+ cnum="$(pdi_count "$card" X-IBAN)"
+ pdi_update_value "$card" X-IBAN "$((cnum + 1))" "$iban" >"$cfile"
+ printf '[li .success . [span .name . %s] [span .uid . (UID: %s)] assigned IBAN [span .iban . %s]]' \
+ "$(HTML "$fn")" "$(HTML "$uid")" "$(HTML "$iban")"
+ RELEASE_SLOCK "$cfile"
+ else
+ printf '[li .error . [span .name . %s] [span .uid . (UID: %s)] is being edited elsewhere]'
+ "$(HTML "$fn")" "$(HTML "$uid")"
+ fi
+ done
+ printf ']'
+ printf '[a .button href="%s/ledgers/iban_assign/" . %s]' "${_BASE}" "$(l10n Back)"
+} | yield_page ledgers_assign
display: inline-block;
}
+
+/* ======== Ledgers Page ======== */
+
form.ibanassign,
form.ledgers {
padding: .125em 1em 0 1em;
.ibanassign fieldset.iban p.principal {
font-size: .875em;
}
-.ibanassign fieldset.iban p.amount,
-.ibanassign fieldset.iban p.subject {
+.ibanassign fieldset.iban p.date,
+.ibanassign fieldset.iban p.amount {
+ font-size: .875em;
display: inline-block;
vertical-align: top;
+ margin-right: .75em;
+ margin-bottom: 0;
}
.ibanassign fieldset.iban p.amount {
font-weight: bold;
- margin-right: .75em;
+}
+
+.ibanassign fieldset.iban.sure .card { margin-right: 1em; }
+
+.ibanassign fieldset.iban input[name^="fn_"].disabled {
+ pointer-events: none;
+}
+.ibanassign fieldset.iban input[name^="check_"],
+.ibanassign fieldset.iban input[name^="check_"] + input,
+.ibanassign fieldset.iban input[name^="check_"] + input + label {
+ display: none;
+}
+.ibanassign fieldset.iban input[name^="check_"]:checked + input,
+.ibanassign fieldset.iban input[name^="check_"]:checked + input + label.del,
+.ibanassign fieldset.iban input[name^="check_"] + input + label.del + input + input + label.add,
+.ibanassign fieldset.iban input[name^="check_"]:checked + input + label + input + input + label.add {
+ display: inline;
+}
+.ibanassign fieldset.iban input[name^="check_"]:checked + input + label.add,
+.ibanassign fieldset.iban input[name^="check_"] + input + label.del + input:checked + input + label.add,
+.ibanassign fieldset.iban input[name^="check_"]:checked + input + label + input:checked + input + label.add {
+ display: none;
+}
+
+.ibanassign fieldset.iban input[name^="check_"] + input + label {
+ vertical-align: bottom;
+ line-height: 2.5em;
+ padding: .375em .625em;
+ border: .5pt solid;
+}
+.ibanassign fieldset.iban input[name^="check_"] + input + label.add {
+ background-color: #DFD;
+ border-radius: 2pt;
+}
+.ibanassign fieldset.iban input[name^="check_"] + input + label.del {
+ margin: 0 .5em 0 -.25em;
+ background-color: #FDD;
+ border-radius: 0 2pt 2pt 0;
+}
+
+.ibanassign input[name^="use_"] {
+ display: none;
+}
+.ibanassign input[name^="use_"]:checked + fieldset.iban.guess { background-color: #EFD; }
+.ibanassign input[name^="use_"]:checked + fieldset.iban.unknown { background-color: #FED; }
+.ibanassign input[name^="use_"]:checked + fieldset.iban input {
+ background-color: #DFD;
+ pointer-events: none;
+ border-color: #888;
+}
+.ibanassign input[name^="use_"]:checked + fieldset.iban label.del,
+.ibanassign input[name^="use_"]:checked + fieldset.iban label.add {
+ display: none !important;
+}
+
+.ibanassign fieldset.iban label[for^="use_"] {
+ display: block;
+ float: right;
+ padding: .25em .5em;
+ background-color: #DDF;
+ border: 1pt solid;
+}
+
+.ibanassign input[name^="use_"] + fieldset.iban label[for^="use_"] { display: block; }
+.ibanassign input[name^="use_"] + fieldset.iban label[for^="use_"] + label[for^="use_"] { display: none; }
+.ibanassign input[name^="use_"]:checked + fieldset.iban label[for^="use_"] { display: none; }
+.ibanassign input[name^="use_"]:checked + fieldset.iban label[for^="use_"] + label[for^="use_"] { display: block; }
+
+.ibanassign > button {
+ position: sticky;
+ bottom: 0;
+ margin: auto;
+ display: block;
+}
+
+body.ledgers .transactions {
+ width: 98%;
+ width: calc(100% - 2em);
+ margin: auto;
+}
+body.ledgers .transactions thead {
+ position: sticky;
+ top: 0;
+ z-index: 1;
+}
+body.ledgers .transactions th {
+ text-align: left;
+ background-color: #FFF;
+}
+body.ledgers .transactions td {
+ vertical-align: top;
+ font-family: monospace;
+ font-size: 12pt;
+}
+
+body.ledgers .transactions td:nth-child(2n) {
+ background-color: #DDD;
+}
+body.ledgers .transactions td:nth-child(2n + 1) {
+ background-color: #EEE;
+}
+
+body.ledgers .transactions .date {
+ width: 10em;
+}
+body.ledgers .transactions .orig span {
+ display: block;
+}
+body.ledgers .transactions .amount,
+body.ledgers .transactions .balance {
+ vertical-align: bottom;
+ width: 8em;
+ text-align: right;
+}
+
+body.ledgers .transactions .reference textarea {
+ width: 100%;
+}
+body.ledgers .transactions .date input + label {
+ display: inline-block;
+ vertical-align: middle;
+ width: 7em;
+}
+body.ledgers .transactions .date input[type=date],
+body.ledgers .transactions .amount input[type=number] {
+ display: block;
+ width: 100%;
+}
+
+body.ledgers .transactions .orig input:not(:checked) + label + input[type=date] {
+ background-color: #DDD;
+ border-color: #888;
+ pointer-events: none;
}