Skip to content

Instantly share code, notes, and snippets.

@schoettl
Last active November 15, 2020 19:18
Show Gist options
  • Save schoettl/6b987f587f3c6ba06378fa318b7781ef to your computer and use it in GitHub Desktop.
Save schoettl/6b987f587f3c6ba06378fa318b7781ef to your computer and use it in GitHub Desktop.
Script to generate CSV for DHL Online Frankierung CSV-Import
#!/bin/bash
# Script to generate CSV for DHL Online Frankierung CSV-Import.
# Dependencies: perl fzf xclip awk iconv
printUsage() {
cat <<EOF
Usage: $PROGNAME [options]
Generiert CSV für den CSV-Import bei DHL Online Frankierung:
https://www.dhl.de/onlinefrankierung
EspoCRM Account URL muss vorher ins Clipboard kopiert werden.
Wenn verfügbar wird automatisch ein Coupon Code gewählt
(außer wenn -d ist gegeben).
Options:
-p
interactivly select product
-c COUPON_CODE
coupon code to use
-s SENDER
sender company prefix (s[xxx] or i[xxx])
default: ixxx
-f CSV_FILE
append data to given CSV file
-r
reverse (swap recipient and sender)
-h
print help message
EOF
}
set -o errexit -o pipefail
shopt -s nullglob
readonly PROGNAME=${0##*/}
readonly COUPON_CODES_DIR=${XDG_DATA_HOME:-~/.local/share/dhl}
# ESPOCRM_API_KEY ESPOCRM_API_URL
source ~/.parameters-used-in-scripts.sh
declare -g SENDER_COMPANY=
declare -g SENDER_EMAIL=
readonly SENDER_STREET=
readonly SENDER_NUMBER=
readonly SENDER_ZIP=
readonly SENDER_CITY=
# $1: error message
exitWithError() {
declare msg=${1:-}
echo "$msg" >&2
exit 1
}
# $*: command line arguments = "$@"
parseCommandLine() {
declare option
while getopts 'phs:f:c:r' option; do
case $option in
h)
printUsage
exit 0
;;
p) declare -gr SELECT_PRODUCT=1
;;
r) declare -gr REVERSE=1
;;
c) declare -gr COUPON_CODE=$OPTARG
;;
f) declare -gr CSV_FILE=$OPTARG
;;
s)
case $OPTARG in
i*) ;;
s*) SENDER_COMPANY=
SENDER_EMAIL=
;;
*) exitWithError "Error: invalid argument for -s option."
esac
;;
*) printUsage >&2
# prints usage after the default error message (invalid option or missing option argument).
# default error messages are disabled if the first character of optstring is ":".
exit 1
;;
esac
done
shift $((OPTIND-1))
if (( $# != 0 )); then
exitWithError "error: no command line arguments allowed."
fi
return 0
}
main() {
parseCommandLine "$@"
declare url
url=$(xclip -o -selection clipboard || true)
if [[ $url =~ ^http.*:// ]]; then
if ! [[ $url =~ ^http.*://.*/#(Account|Contact)/view/ ]]; then
exitWithError "Error: No valid EspoCRM Account URL in clipboard."
fi
else
url=
fi
declare id=${url##*/}
declare jsonFile addressFile
jsonFile=$(mktemp)
addressFile=$(mktemp)
case $url in
'') declare tmp
tmp=$(mktemp)
echo -e "# Name\n# Zusatz (kann weggelassen werden)\n# Straße Hausnummer\n# Ort\n# PLZ\n# Land (oder leere Zeile)" > "$tmp"
vim "$tmp"
if ! grep -vqE '^#|^ *$' "$tmp"; then
exitWithError "Error: Aborting due to empty recepient."
fi
grep -vE '^#' "$tmp" > "$addressFile"
;;
*/\#Account/*)
entity=Account
curl -s -H "X-Api-Key: $ESPOCRM_API_KEY" "$ESPOCRM_API_URL/$entity/$id" > "$jsonFile"
printFieldsFromJson name shippingAddressStreet shippingAddressCity shippingAddressPostalCode shippingAddressCountry < "$jsonFile" > "$addressFile"
if (( $(grep . "$addressFile" | wc -l) < 4 )); then
echo "Warning: No shipping address. Using billing address instead."
echo
printFieldsFromJson name billingAddressStreet billingAddressCity billingAddressPostalCode billingAddressCountry < "$jsonFile" > "$addressFile"
fi
;;
*/\#Contact/*)
entity=Contact
curl -s -H "X-Api-Key: $ESPOCRM_API_KEY" "$ESPOCRM_API_URL/$entity/$id" > "$jsonFile"
printFieldsFromJson name addressStreet addressCity addressPostalCode addressCountry < "$jsonFile" > "$addressFile"
;;
esac
declare nLines
nLines=$(wc -l < "$addressFile")
if (( nLines > 6 )); then
exitWithError "Error: Too many lines in EspoCRM address fields."
fi
if (( nLines < 5 )); then
exitWithError "Error: Too few lines in EspoCRM address fields."
fi
#if [[ -n $SHELL ]]; then
# vim "$addressFile"
# if ! grep . "$addressFile" > /dev/null; then
# exitWithError "Empty address file. Canceled."
# fi
#fi
declare name line2 street number zip city streetNumber country
#cat "$addressFile"
{
read -r name
if (( nLines == 6 )); then
read -r line2
fi
read -r streetNumber
read -r city
read -r zip
read -r country
} < "$addressFile"
street=${streetNumber% *}
number=${streetNumber##* }
if [[ -z "$country" ]]; then
:
elif (( $(printIso3CountryCodes | grep -F --count "$country") == 1 )); then
country=$(printIso3CountryCodes | grep -F "$country" | awk '{print $1}')
else
country=$(printIso3CountryCodes | fzf --delimiter '\t' --query "$country" | awk '{print $1}' || exitWithError "error: no country selected.")
fi
declare product couponCode
if [[ -n $SELECT_PRODUCT ]]; then
product=$(printProducts | filterCountry "$country" | fzf --delimiter '\t' | awk '{print $1}')
fi
if [[ -z $COUPON_CODE && -n $product ]]; then
file=$COUPON_CODES_DIR/coupon-codes-$product.txt
echo "Trying to lookup coupon code in file:"
echo "$file"
echo
couponCode=$(tryUsingCouponCodeFromList "$file")
else
couponCode=$COUPON_CODE
fi
echo "Address:"
echo "$name"
echo "${line2:-(no second line)}"
echo "$street $number"
echo "$zip $city"
echo "${country:-(default country)}"
echo
echo "Product: ${product:-(not selected)}"
echo "Coupon Code: ${couponCode:-(not selected)}"
echo
declare targetCsvFile
if [[ -z $CSV_FILE ]]; then
targetCsvFile=$(mktemp "/tmp/$(date -I)-$(echo "$name" | tr -dc 'a-zA-Z0-9')-XXXXX.csv")
printCsvHeader > "$targetCsvFile"
else
targetCsvFile=$CSV_FILE
fi
if [[ -z $REVERSE ]]; then
linesToCsvLatin1 << TEXT >> "$targetCsvFile"
$SENDER_COMPANY
$SENDER_STREET
$SENDER_NUMBER
$SENDER_ZIP
$SENDER_CITY
$name
$line2
$street
$number
$zip
$city
$country
$product
$couponCode
$SENDER_EMAIL
TEXT
else
linesToCsvLatin1 << TEXT >> "$targetCsvFile"
$name
$line2
$street
$number
$zip
$city
$country
$SENDER_COMPANY
$SENDER_STREET
$SENDER_NUMBER
$SENDER_ZIP
$SENDER_CITY
$product
$couponCode
$SENDER_EMAIL
TEXT
fi
echo "Open the DHL page in the browser:"
echo "https://www.dhl.de/de/privatkunden/pakete-versenden/online-frankieren.html?type=ShoppingCartImport"
echo
echo "Copied filename to clipboard:"
echo "$targetCsvFile"
echo -n "$targetCsvFile" | xclip -i -selection clipboard
if (( ${#name} > 50 || ${#line2} > 50 || ${#street} > 50 || ${#city} > 38 )); then
echo
echo "Warning: Receiver data too long: name (50), name2 (50), street (50), city (38)"
echo " Fix this in the output CSV file, before uploading!"
fi >&2
}
linesToCsvLatin1() {
sed -e 's/"/""/g' -e 's/^/"/' -e 's/$/"/' \
| paste -sd';' \
| iconv -futf8 -tlatin1
}
printCsvHeader() {
cat <<TEXT
SEND_NAME1;SEND_NAME2;SEND_STREET;SEND_HOUSENUMBER;SEND_PLZ;SEND_CITY;SEND_COUNTRY;RECV_NAME1;RECV_NAME2;RECV_STREET;RECV_HOUSENUMBER;RECV_PLZ;RECV_CITY;RECV_COUNTRY;PRODUCT;COUPON;SEND_EMAIL
TEXT
}
# https://www.dhl.de/de/privatkunden/pakete-versenden/deutschlandweit-versenden/preise-national.html
# https://www.dhl.de/de/privatkunden/pakete-versenden/weltweit-versenden/preise-international.html
printProducts() {
cat <<TEXT
PAECKS.DEU Päckchen S
PAECK.DEU Päckchen M
PAK02.DEU Paket 2 kg bis 60 x 30 x 15 cm
PAK05.DEU Paket 5 kg bis 120 x 60 x 60 cm**
PAK10.DEU Paket 10 kg bis 120 x 60 x 60 cm**
PAK31.DEU Paket 31,5 kg bis 120 x 60 x 60 cm
PAECKXS.EU EU-Päckchen XS EU bis 2 kg
PAECK.EU EU-Päckchen
PAK02.EU EU-Paket 2 kg bis 60 x 30 x 15 cm
PAK05.EU EU-Paket 5 kg bis 120 x 60 x 60 cm
PAK10.EU EU-Paket 10 kg bis 120 x 60 x 60 cm
PAK20.EU EU-Paket 20 kg bis 120 x 60 x 60 cm
PAK315.EU EU-Paket 31,5 kg bis 120 x 60 x 60
TEXT
}
# from https://unstats.un.org/unsd/tradekb/knowledgebase/country-code
# modified with some german country names
printIso3CountryCodes() {
cat <<TEXT
ABW Aruba
AFG Afghanistan
AGO Angola
AIA Anguilla
ALA Åland Islands
ALB Albania
AND Andorra
ANT Netherlands Antilles
ARE United Arab Emirates
ARG Argentina
ARM Armenia
ASM American Samoa
ATA Antarctica
ATF French Southern Territories
ATG Antigua and Barbuda
AUS Australia
AUT Austria / Österreich
AZE Azerbaijan
BDI Burundi
BEL Belgium
BEN Benin
BFA Burkina Faso
BGD Bangladesh
BGR Bulgaria
BHR Bahrain
BHS Bahamas
BIH Bosnia and Herzegovina
BLM Saint Barthélemy
BLR Belarus
BLZ Belize
BMU Bermuda
BOL Bolivia, Plurinational State of
BRA Brazil
BRB Barbados
BRN Brunei Darussalam
BTN Bhutan
BVT Bouvet Island
BWA Botswana
CAF Central African Republic
CAN Canada
CCK Cocos (Keeling) Islands
CHE Switzerland / Schweiz
CHL Chile
CHN China
CIV Côte d'Ivoire
CMR Cameroon
COD Congo, the Democratic Republic of the
COG Congo
COK Cook Islands
COL Colombia
COM Comoros
CPV Cape Verde
CRI Costa Rica
CUB Cuba
CXR Christmas Island
CYM Cayman Islands
CYP Cyprus
CZE Czech Republic
DEU Germany / Deutschland
DJI Djibouti
DMA Dominica
DNK Denmark
DOM Dominican Republic
DZA Algeria
ECU Ecuador
EGY Egypt
ERI Eritrea
ESH Western Sahara
ESP Spain
EST Estonia
ETH Ethiopia
FIN Finland
FJI Fiji
FLK Falkland Islands (Malvinas)
FRA France
FRO Faroe Islands
FSM Micronesia, Federated States of
GAB Gabon
GBR United Kingdom
GEO Georgia
GGY Guernsey
GHA Ghana
GIB Gibraltar
GIN Guinea
GLP Guadeloupe
GMB Gambia
GNB Guinea-Bissau
GNQ Equatorial Guinea
GRC Greece
GRD Grenada
GRL Greenland
GTM Guatemala
GUF French Guiana
GUM Guam
GUY Guyana
HKG Hong Kong
HMD Heard Island and McDonald Islands
HND Honduras
HRV Croatia
HTI Haiti
HUN Hungary
IDN Indonesia
IMN Isle of Man
IND India
IOT British Indian Ocean Territory
IRL Ireland
IRN Iran, Islamic Republic of
IRQ Iraq
ISL Iceland
ISR Israel
ITA Italy
JAM Jamaica
JEY Jersey
JOR Jordan
JPN Japan
KAZ Kazakhstan
KEN Kenya
KGZ Kyrgyzstan
KHM Cambodia
KIR Kiribati
KNA Saint Kitts and Nevis
KOR Korea, Republic of
KWT Kuwait
LAO Lao People's Democratic Republic
LBN Lebanon
LBR Liberia
LBY Libyan Arab Jamahiriya
LCA Saint Lucia
LIE Liechtenstein
LKA Sri Lanka
LSO Lesotho
LTU Lithuania
LUX Luxembourg
LVA Latvia
MAC Macao
MAF Saint Martin (French part)
MAR Morocco
MCO Monaco
MDA Moldova, Republic of
MDG Madagascar
MDV Maldives
MEX Mexico
MHL Marshall Islands
MKD Macedonia, the former Yugoslav Republic of
MLI Mali
MLT Malta
MMR Myanmar
MNE Montenegro
MNG Mongolia
MNP Northern Mariana Islands
MOZ Mozambique
MRT Mauritania
MSR Montserrat
MTQ Martinique
MUS Mauritius
MWI Malawi
MYS Malaysia
MYT Mayotte
NAM Namibia
NCL New Caledonia
NER Niger
NFK Norfolk Island
NGA Nigeria
NIC Nicaragua
NIU Niue
NLD Netherlands
NOR Norway
NPL Nepal
NRU Nauru
NZL New Zealand
OMN Oman
PAK Pakistan
PAN Panama
PCN Pitcairn
PER Peru
PHL Philippines
PLW Palau
PNG Papua New Guinea
POL Poland
PRI Puerto Rico
PRK Korea, Democratic People's Republic of
PRT Portugal
PRY Paraguay
PSE Palestinian Territory, Occupied
PYF French Polynesia
QAT Qatar
REU Réunion
ROU Romania
RUS Russian Federation
RWA Rwanda
SAU Saudi Arabia
SDN Sudan
SEN Senegal
SGP Singapore
SGS South Georgia and the South Sandwich Islands
SHN Saint Helena, Ascension and Tristan da Cunha
SJM Svalbard and Jan Mayen
SLB Solomon Islands
SLE Sierra Leone
SLV El Salvador
SMR San Marino
SOM Somalia
SPM Saint Pierre and Miquelon
SRB Serbia
STP Sao Tome and Principe
SUR Suriname
SVK Slovakia
SVN Slovenia
SWE Sweden / Schweden
SWZ Swaziland
SYC Seychelles
SYR Syrian Arab Republic
TCA Turks and Caicos Islands
TCD Chad
TGO Togo
THA Thailand
TJK Tajikistan
TKL Tokelau
TKM Turkmenistan
TLS Timor-Leste
TON Tonga
TTO Trinidad and Tobago
TUN Tunisia
TUR Turkey
TUV Tuvalu
TWN Taiwan, Province of China
TZA Tanzania, United Republic of
UGA Uganda
UKR Ukraine
UMI United States Minor Outlying Islands
URY Uruguay
USA United States
UZB Uzbekistan
VAT Holy See (Vatican City State)
VCT Saint Vincent and the Grenadines
VEN Venezuela, Bolivarian Republic of
VGB Virgin Islands, British
VIR Virgin Islands, U.S.
VNM Viet Nam
VUT Vanuatu
WLF Wallis and Futuna
WSM Samoa
YEM Yemen
ZAF South Africa
ZMB Zambia
ZWE Zimbabwe
TEXT
}
tryUsingCouponCodeFromList() {
declare file=$1
if [[ -r "$file" ]]; then
# Print the first unused coupon code:
grep -v '^#' "$file" | head -n1 || true
# Now comment the first unused coupon code:
sed -i -r '0,/^[^#]/ s/^([^#])/#\1/' "$file"
fi
}
printFieldsFromJson() {
perl -MJSON -e '$RS=undef; my $dat=JSON::decode_json(<STDIN>); binmode(STDOUT, ":utf8"); for(@ARGV) {print $dat->{$_} . "\n"}' "$@"
}
filterCountry() {
declare country=$1
if [[ -z $country || $country == DEU ]]; then
grep '\.DEU'
else
cat
fi
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment