This commit is contained in:
seb
2026-07-15 03:11:30 +02:00
parent 056c9e18cd
commit 1de4144ae2
23 changed files with 921 additions and 229 deletions

View File

@@ -35,6 +35,11 @@ struct Config {
int customerNumberSequence = 6;
};
constexpr int VERSANDPOSITION_TYPE = 2;
constexpr int ZAHLUNG_TYPE_ZAHLUNG = 10;
constexpr int NIST_READONLY_NICHT_AENDERBAR = 2;
constexpr int NIST_EXTERNE_RECHNUNG_KEINE = 2;
Config g_config;
std::unique_ptr<Defaults> g_resolved_defaults;
std::map<std::string, nlohmann::json> g_zahlungsart_cache;
@@ -128,6 +133,12 @@ inline int json_int(const nlohmann::json& obj, const char* key, int fallback = 0
return to_int(*it, fallback);
}
inline double json_double(const nlohmann::json& obj, const char* key, double fallback = 0) {
auto it = obj.find(key);
if (it == obj.end()) return fallback;
return to_number(*it, fallback);
}
inline int steuerklasse_for_vat(double vat) {
if (vat >= 15) return 1;
if (vat > 0) return 2;
@@ -232,38 +243,158 @@ inline int allocate_pk(OdbcPool::Connection* c, const std::string& tableName) {
return std::stoi(rs[0][0].str);
}
inline int parse_import_setting(const nlohmann::json& order) {
if (!order.contains("settings") || order["settings"].is_null()) return 0;
return json_int(order["settings"], "importSetting", 0);
}
inline int parse_invoice_setting(const nlohmann::json& order) {
if (!order.contains("settings") || order["settings"].is_null()) return 0;
return json_int(order["settings"], "invoiceSetting", 0);
}
inline int resolve_n_ist_readonly(const nlohmann::json& order) {
return parse_import_setting(order) == 0 ? NIST_READONLY_NICHT_AENDERBAR : 0;
}
inline int resolve_n_ist_externe_rechnung(const nlohmann::json& order) {
const int import_setting = parse_import_setting(order);
const int invoice_setting = parse_invoice_setting(order);
if (import_setting >= 2 && import_setting <= 5) return 0;
if (invoice_setting & 1) return 0;
if (import_setting == 0) return NIST_EXTERNE_RECHNUNG_KEINE;
return 0;
}
struct VersandArtRow {
int kVersandArt = 0;
std::string cName;
double fPrice = 0;
double fMwSt = 19;
};
inline bool is_versandposition(const nlohmann::json& item) {
return json_int(item, "type", 0) == VERSANDPOSITION_TYPE;
}
inline bool has_versandposition(const nlohmann::json& items) {
if (!items.is_array()) return false;
for (const auto& item : items) {
if (is_versandposition(item)) return true;
}
return false;
}
inline bool has_non_return_sale_items(const nlohmann::json& items) {
if (!items.is_array()) return false;
for (const auto& item : items) {
if (json_double(item, "isReturn", 0) == 0) return true;
}
return false;
}
inline bool should_inject_selbstabholer_shipping(const nlohmann::json& items) {
if (has_versandposition(items)) return false;
return has_non_return_sale_items(items);
}
inline std::optional<VersandArtRow> lookup_versand_art(OdbcPool::Connection* c,
const std::string& shipping_name) {
auto try_lookup = [&](const std::string& cName) -> std::optional<VersandArtRow> {
std::vector<Param> ps = {{ParamType::NVarChar, cName, 0}};
ResultSet rs;
if (!get_pool().execute(c,
"SELECT TOP 1 kVersandArt, cName, fPrice, fMwSt "
"FROM dbo.tVersandArt WHERE cName = ?", ps, rs)
|| rs.empty() || rs[0].empty()) {
return std::nullopt;
}
VersandArtRow row;
row.kVersandArt = std::stoi(rs[0][0].str);
row.cName = rs[0][1].str;
row.fPrice = rs[0].size() > 2 ? to_number(nlohmann::json(rs[0][2].str), 0) : 0;
row.fMwSt = rs[0].size() > 3 ? to_number(nlohmann::json(rs[0][3].str), 19) : 19;
return row;
};
std::string name = shipping_name;
if (name.empty()) name = "Selbstabholer";
if (auto row = try_lookup(name)) return row;
if (name != "Selbstabholer") return try_lookup("Selbstabholer");
return std::nullopt;
}
inline nlohmann::json synthetic_shipping_item(const VersandArtRow& versand_art) {
const double vat = versand_art.fMwSt;
const double gross = versand_art.fPrice;
const double net = gross / (1 + vat / 100);
return {
{"type", std::to_string(VERSANDPOSITION_TYPE)},
{"quantity", "1"},
{"name", versand_art.cName},
{"priceGross", std::to_string(gross)},
{"priceNet", std::to_string(net)},
{"vat", std::to_string(vat)},
{"isReturn", "0"}
};
}
inline nlohmann::json resolve_zahlungsart(OdbcPool::Connection* c, const std::string& name) {
std::string key = name.empty() ? "bar" : name;
std::transform(key.begin(), key.end(), key.begin(), [](unsigned char ch) { return std::tolower(ch); });
auto it = g_zahlungsart_cache.find(key);
const std::string lookup_name = name.empty() ? "Bar" : name;
const std::string cache_key = [&]() {
std::string key = lookup_name;
std::transform(key.begin(), key.end(), key.begin(),
[](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
return key;
}();
auto it = g_zahlungsart_cache.find(cache_key);
if (it != g_zahlungsart_cache.end()) return it->second;
std::vector<Param> ps = {{ParamType::NVarChar, key, 0}};
ResultSet rs;
get_pool().execute(c, "SELECT TOP 1 kZahlungsart, cName FROM dbo.tZahlungsart WHERE cName = ?", ps, rs);
auto fetch_row = [&](const char* sql) -> std::optional<std::pair<int, std::string>> {
std::vector<Param> ps = {{ParamType::NVarChar, lookup_name, 0}};
ResultSet rs;
if (!get_pool().execute(c, sql, ps, rs) || rs.empty() || rs[0].empty()) {
return std::nullopt;
}
return std::make_pair(std::stoi(rs[0][0].str), rs[0][1].str);
};
if (!rs.empty() && !rs[0].empty()) {
nlohmann::json z = {{"kZahlungsart", std::stoi(rs[0][0].str)}, {"cName", rs[0][1].str}};
g_zahlungsart_cache[key] = z;
return z;
std::optional<std::pair<int, std::string>> row =
fetch_row("SELECT TOP 1 kZahlungsart, cName FROM dbo.tZahlungsart WHERE cName = ?");
if (!row) {
row = fetch_row(
"SELECT TOP 1 z.kZahlungsart, z.cName "
"FROM dbo.tZahlungsArtSprache zs "
"INNER JOIN dbo.tZahlungsart z ON z.kZahlungsart = zs.kZahlungsart "
"WHERE zs.cName = ?");
}
if (!row) {
row = fetch_row(
"SELECT TOP 1 kZahlungsart, cName FROM dbo.tZahlungsart WHERE UPPER(cName) = UPPER(?)");
}
int kZahlungsart = allocate_pk(c, "tZahlungsart");
const char* insert =
"INSERT INTO dbo.tZahlungsart"
" (kZahlungsart, cName, cPrtString, nLastschrift, cPrtStringVor, cPaymentOption, cKonto,"
" nAusliefernVorZahlung, nPrioritaet, nMahnwesenAktiv, fSkontoWert, nSkontoZeitraum,"
" nMatchingOptionen, nIstStandard, nAktiv)"
" VALUES (?, ?, '', 0, '', '', '', 0, 0, 0, 0, 0, 0, 0, 1)";
std::vector<Param> ps2 = {
{ParamType::Int, "", kZahlungsart},
{ParamType::NVarChar, key, 0}
};
get_pool().execute(c, insert, ps2, rs);
nlohmann::json zahlungsart;
if (row) {
zahlungsart = {{"kZahlungsart", row->first}, {"cName", row->second}};
} else {
int kZahlungsart = allocate_pk(c, "tZahlungsart");
const char* insert =
"INSERT INTO dbo.tZahlungsart"
" (kZahlungsart, cName, cPrtString, nLastschrift, cPrtStringVor, cPaymentOption, cKonto,"
" nAusliefernVorZahlung, nPrioritaet, nMahnwesenAktiv, fSkontoWert, nSkontoZeitraum,"
" nMatchingOptionen, nIstStandard, nAktiv)"
" VALUES (?, ?, '', 0, '', '', '', 0, 0, 0, 0, 0, 0, 0, 1)";
std::vector<Param> ps2 = {
{ParamType::Int, "", kZahlungsart},
{ParamType::NVarChar, lookup_name, 0}
};
ResultSet rs;
get_pool().execute(c, insert, ps2, rs);
zahlungsart = {{"kZahlungsart", kZahlungsart}, {"cName", lookup_name}};
}
nlohmann::json z = {{"kZahlungsart", kZahlungsart}, {"cName", key}};
g_zahlungsart_cache[key] = z;
return z;
g_zahlungsart_cache[cache_key] = zahlungsart;
return zahlungsart;
}
inline std::string next_customer_number(OdbcPool::Connection* c) {
@@ -303,21 +434,6 @@ inline std::string resolve_auftrag_c_kunden_nr(const nlohmann::json& order) {
return order.value("customerNumber", "");
}
// POS settings.invoiceSetting: 0 = external/POS invoice (nIstExterneRechnung=1).
inline int resolve_n_ist_externe_rechnung(const nlohmann::json& order) {
if (!order.contains("settings") || order["settings"].is_null()) return 1;
const auto& settings = order["settings"];
if (!settings.contains("invoiceSetting")) return 1;
const auto& inv = settings["invoiceSetting"];
if (inv.is_boolean()) return inv.get<bool>() ? 0 : 1;
if (inv.is_number_integer()) return inv.get<int>() == 1 ? 0 : 1;
if (inv.is_string()) {
std::string s = inv.get<std::string>();
return (s == "1" || s == "true") ? 0 : 1;
}
return 1;
}
inline std::pair<int,int> create_customer(OdbcPool::Connection* c, const std::string& customer_number,
const nlohmann::json& address, const Defaults& defaults) {
const nlohmann::json a = address.is_null() ? nlohmann::json::object() : address;
@@ -328,8 +444,8 @@ inline std::pair<int,int> create_customer(OdbcPool::Connection* c, const std::st
const char* sql =
"DECLARE @returnValue INT;"
"DECLARE @p1 dbo.TYPE_spkundeInsert;"
"INSERT INTO @p1"
"DECLARE @kunde_daten dbo.TYPE_spkundeInsert;"
"INSERT INTO @kunde_daten"
" (kInetKunde, kKundenKategorie, cKundenNr, cFirma, cAnrede, cTitel, cVorname, cName,"
" cStrasse, cPLZ, cOrt, cLand, cTel, cFax, cEMail, dErstellt, cMobil, fRabatt, cUSTID, cNewsletter,"
" cZusatz, cEbayName, kBuyer, cAdressZusatz, cGeburtstag, cWWW, cSperre, cPostID, kKundenGruppe,"
@@ -342,7 +458,7 @@ inline std::pair<int,int> create_customer(OdbcPool::Connection* c, const std::st
" 0, ?, ?, ?, ?, ?, N'', 0,"
" ?, N'', 0, 0, 0, 0, 0,"
" NULL, 0, 0, 0);"
"EXEC @returnValue = Kunde.spKundeInsert @daten = @p1;"
"EXEC @returnValue = Kunde.spKundeInsert @daten = @kunde_daten;"
"SELECT @returnValue AS kKunde";
std::vector<Param> ps = {
@@ -360,7 +476,7 @@ inline std::pair<int,int> create_customer(OdbcPool::Connection* c, const std::st
{ParamType::NVarChar, a.value("fax", ""), 0},
{ParamType::NVarChar, a.value("email", ""), 0},
{ParamType::NVarChar, a.value("mobile", ""), 0},
{ParamType::Double, "", 0, to_number(a["discount"], 0)},
{ParamType::Double, "", 0, json_double(a, "discount", 0)},
{ParamType::NVarChar, a.value("addressAddition", ""), 0},
{ParamType::NVarChar, a.value("birthday", ""), 0},
{ParamType::Int, "", kKundengruppe},
@@ -445,17 +561,18 @@ inline void insert_order_address(OdbcPool::Connection* c, int kAuftrag, int kKun
}
inline int insert_order_item(OdbcPool::Connection* c, int kAuftrag, const nlohmann::json& item) {
double vat = to_number(item["vat"], 19);
double quantity = to_number(item["quantity"], 1);
double price_gross = to_number(item["priceGross"], 0);
double price_net = to_number(item["priceNet"], price_gross / (1 + vat / 100));
double discount = to_number(item["discountPercent"], 0);
double vat = json_double(item, "vat", 19);
double quantity = json_double(item, "quantity", 1);
double price_gross = json_double(item, "priceGross", 0);
double price_net = json_double(item, "priceNet", price_gross / (1 + vat / 100));
double discount = json_double(item, "discountPercent", 0);
int kSteuerklasse = steuerklasse_for_vat(vat);
std::string sku = item.value("sku", "");
const int position_type = is_versandposition(item) ? VERSANDPOSITION_TYPE : json_int(item, "type", 0);
int kArtikel = 0;
bool has_artikel = false;
if (!sku.empty()) {
if (!sku.empty() && position_type != VERSANDPOSITION_TYPE) {
std::vector<Param> ps = {{ParamType::NVarChar, sku, 0}};
ResultSet rs;
get_pool().execute(c, "SELECT TOP 1 kArtikel FROM dbo.tArtikel WHERE cArtNr = ?", ps, rs);
@@ -465,18 +582,20 @@ inline int insert_order_item(OdbcPool::Connection* c, int kAuftrag, const nlohma
}
}
const int nType = position_type == VERSANDPOSITION_TYPE ? VERSANDPOSITION_TYPE : (has_artikel ? 1 : 0);
const int nReserviert = nType == VERSANDPOSITION_TYPE ? 0 : 1;
const char* sql =
"DECLARE @t TABLE ([kAuftragPosition] INT);"
"INSERT INTO Verkauf.tAuftragPosition"
" (kArtikel, kAuftrag, cArtNr, nReserviert, cName, cHinweis, fAnzahl, fVkNetto, fMwSt,"
" cNameStandard, kSteuerklasse, nType, cEinheit, fFaktor, kSteuerschluessel, fRabatt)"
" OUTPUT inserted.kAuftragPosition INTO @t"
" VALUES (?, ?, ?, 1, ?, ?, ?, ?, ?,"
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?,"
" ?, ?, ?, ?, 1.0, 3, ?);"
"SELECT kAuftragPosition FROM @t";
Param cartnr{ParamType::NVarChar, has_artikel ? sku : std::string(), 0, 0.0, !has_artikel};
Param pk_art{ParamType::Int, "", kArtikel, 0.0, !has_artikel};
std::string name = item.value("name", sku.empty() ? std::string("Position") : sku);
@@ -484,6 +603,7 @@ inline int insert_order_item(OdbcPool::Connection* c, int kAuftrag, const nlohma
pk_art,
{ParamType::Int, "", kAuftrag},
cartnr,
{ParamType::Int, "", nReserviert},
{ParamType::NVarChar, name, 0},
{ParamType::NVarChar, item.value("note", ""), 0},
{ParamType::Double, "", 0, quantity},
@@ -491,7 +611,7 @@ inline int insert_order_item(OdbcPool::Connection* c, int kAuftrag, const nlohma
{ParamType::Double, "", 0, vat},
{ParamType::NVarChar, name, 0},
{ParamType::Int, "", kSteuerklasse},
{ParamType::Int, "", has_artikel ? 1 : 0},
{ParamType::Int, "", nType},
{ParamType::NVarChar, item.value("unit", ""), 0},
{ParamType::Double, "", 0, discount}
};
@@ -608,6 +728,39 @@ inline bool is_order_delivered(const nlohmann::json& order) {
return true;
}
// ODBC maps ? placeholders to @p1, @p2, … — never DECLARE @p1 in the same batch.
inline void recalculate_auftrag_eckdaten(OdbcPool::Connection* c, int kAuftrag) {
const char* sql =
"DECLARE @eckdaten_calc Verkauf.TYPE_spAuftragEckdatenBerechnen;"
"INSERT INTO @eckdaten_calc VALUES (?);"
"EXEC Verkauf.spAuftragEckdatenBerechnen @auftrag = @eckdaten_calc";
std::vector<Param> ps = {{ParamType::Int, "", kAuftrag}};
ResultSet rs;
if (!get_pool().execute(c, sql, ps, rs)) {
throw std::runtime_error("spAuftragEckdatenBerechnen failed for kAuftrag="
+ std::to_string(kAuftrag));
}
}
inline std::optional<double> get_offener_auftragswert(OdbcPool::Connection* c, int kAuftrag) {
const char* sql =
"SELECT ROUND(tAuftragEckdaten.fOffenerWertOhneStorno, 2) AS fOffenerAuftragswert "
"FROM Verkauf.tAuftrag "
"LEFT JOIN Verkauf.tAuftragEckdaten ON tAuftragEckdaten.kAuftrag = tAuftrag.kAuftrag "
"WHERE tAuftrag.kAuftrag = ?";
std::vector<Param> ps = {{ParamType::Int, "", kAuftrag}};
ResultSet rs;
if (!get_pool().execute(c, sql, ps, rs) || rs.empty() || rs[0].empty()
|| rs[0][0].type == CellType::Null) {
return std::nullopt;
}
return to_number(nlohmann::json(rs[0][0].str), 0);
}
inline bool is_new_payment(const nlohmann::json& payment) {
return json_int(payment, "paymentId", 0) <= 0;
}
inline void insert_payment(OdbcPool::Connection* c, int kAuftrag, const nlohmann::json& payment,
const nlohmann::json& order, const std::tm& order_date_tm) {
std::string payment_name = payment.value("paymentMethodName", "");
@@ -615,25 +768,46 @@ inline void insert_payment(OdbcPool::Connection* c, int kAuftrag, const nlohmann
nlohmann::json zahlungsart = resolve_zahlungsart(c, payment_name);
int kZahlung = allocate_pk(c, "tZahlung");
recalculate_auftrag_eckdaten(c, kAuftrag);
auto fOffenerWert = get_offener_auftragswert(c, kAuftrag);
if (!fOffenerWert) {
throw std::runtime_error("no open order amount for kAuftrag=" + std::to_string(kAuftrag));
}
const char* sql =
"INSERT INTO dbo.tZahlung"
" (kZahlung, cName, dDatum, fBetrag, kBestellung, kBenutzer, nAnzahlung, cHinweis, kZahlungsart,"
" nKeinExport, cExternalTransactionId, nZuweisungstyp, nZahlungstyp, cZuweisungsinfo, nZuweisungswertung)"
" VALUES (?, ?, ?, ?, ?, ?, 0, '', ?,"
" 0, ?, 0, 0, '', 0)";
"IF EXISTS ("
" SELECT 1 FROM Verkauf.tAuftragEckdaten"
" WHERE kAuftrag = ?"
" AND ROUND(fOffenerWertOhneStorno, 2) = ROUND(?, 2)"
")"
"BEGIN"
" INSERT INTO dbo.tZahlung"
" (kZahlung, cName, dDatum, fBetrag, kBestellung, kBenutzer, nAnzahlung, cHinweis, kZahlungsart,"
" nKeinExport, cExternalTransactionId, nZuweisungstyp, nZahlungstyp, cZuweisungsinfo, nZuweisungswertung)"
" VALUES (?, ?, ?, ?, ?, ?, 0, '', ?,"
" 0, ?, 0, ?, '', 0);"
"END;"
"SELECT @@ROWCOUNT AS inserted";
std::vector<Param> ps = {
{ParamType::Int, "", kAuftrag},
{ParamType::Double, "", 0, *fOffenerWert},
{ParamType::Int, "", kZahlung},
{ParamType::NVarChar, zahlungsart.value("cName", ""), 0},
{ParamType::NVarChar, order_date_sql(order_date_tm), 0},
{ParamType::Double, "", 0, to_number(payment["amount"], 0)},
{ParamType::Double, "", 0, json_double(payment, "amount", 0)},
{ParamType::Int, "", kAuftrag},
{ParamType::Int, "", g_config.kBenutzer},
{ParamType::Int, "", json_int(zahlungsart, "kZahlungsart", 0)},
{ParamType::NVarChar, order.value("externalOrderNumber", ""), 0}
{ParamType::NVarChar, order.value("externalOrderNumber", ""), 0},
{ParamType::Int, "", ZAHLUNG_TYPE_ZAHLUNG},
};
ResultSet rs;
get_pool().execute(c, sql, ps, rs);
if (!get_pool().execute(c, sql, ps, rs) || rs.empty() || rs[0].empty()
|| std::stoi(rs[0][0].str) == 0) {
throw std::runtime_error("payment insert skipped: open amount changed for kAuftrag="
+ std::to_string(kAuftrag));
}
}
inline nlohmann::json create_order(const nlohmann::json& order) {
@@ -678,7 +852,18 @@ inline nlohmann::json create_order(const nlohmann::json& order) {
nlohmann::json zahlungsart = resolve_zahlungsart(c, order.value("paymentMethodName", "Bar"));
std::string cAuftragsNr = next_order_number(c, order_date_tm);
nlohmann::json order_items = order.contains("orderItems") ? order["orderItems"] : nlohmann::json::array();
if (!order_items.is_array()) order_items = nlohmann::json::array();
auto versand_art = lookup_versand_art(c, order.value("shippingName", ""));
int kVersandArt = versand_art ? versand_art->kVersandArt : defaults.kVersandArt;
if (should_inject_selbstabholer_shipping(order_items) && versand_art) {
order_items.push_back(synthetic_shipping_item(*versand_art));
}
int active_shop = get_active_shop_id();
const int nIstReadOnly = resolve_n_ist_readonly(order);
const int nIstExterneRechnung = resolve_n_ist_externe_rechnung(order);
const char* insert_auftrag =
"DECLARE @t TABLE ([kAuftrag] INT);"
@@ -700,7 +885,6 @@ inline nlohmann::json create_order(const nlohmann::json& order) {
std::string currency_iso = order.value("currencyIso", "EUR");
std::string cKundenNr = resolve_auftrag_c_kunden_nr(order);
int nIstExterneRechnung = resolve_n_ist_externe_rechnung(order);
std::vector<Param> ps = {
{ParamType::NVarChar, cAuftragsNr, 0},
{ParamType::NVarChar, order_date_sql(order_date_tm), 0},
@@ -715,13 +899,13 @@ inline nlohmann::json create_order(const nlohmann::json& order) {
{ParamType::Int, "", active_shop == 0 ? -1 : active_shop},
{ParamType::NVarChar, cKundenNr, 0},
{ParamType::NVarChar, shipping_iso, 0},
{ParamType::Int, "", defaults.kVersandArt},
{ParamType::Int, "", kVersandArt},
{ParamType::Int, "", json_int(zahlungsart, "kZahlungsart", 0)},
{ParamType::Int, "", kKundengruppe},
{ParamType::NVarChar, order.value("externalOrderNumber", ""), 0},
{ParamType::Int, "", nIstExterneRechnung},
{ParamType::NVarChar, "Y", 0},
{ParamType::Int, "", 2},
{ParamType::Int, "", nIstReadOnly},
};
if (active_shop == 0) {
ps[11] = Param::null_int();
@@ -738,22 +922,25 @@ inline nlohmann::json create_order(const nlohmann::json& order) {
insert_order_address(c, kAuftrag, kKunde, order.value("billingAddress", nlohmann::json::object()), 1);
std::vector<delivery::DeliveredItem> delivered_items;
const nlohmann::json& items = order.contains("orderItems") ? order["orderItems"] : nlohmann::json::array();
for (const auto& item : items) {
for (const auto& item : order_items) {
int kAuftragPosition = insert_order_item(c, kAuftrag, item);
insert_pos_order_position_mapping(c, kAuftragPosition, item.value("externalId", ""));
if (kAuftragPosition > 0) {
delivered_items.push_back({kAuftragPosition, to_number(item["quantity"], 1)});
const std::string external_id = item.value("externalId", "");
if (!external_id.empty()) {
insert_pos_order_position_mapping(c, kAuftragPosition, external_id);
}
if (kAuftragPosition > 0 && !is_versandposition(item)) {
delivered_items.push_back({kAuftragPosition, json_double(item, "quantity", 1)});
}
}
const nlohmann::json& payments = order.contains("payments") ? order["payments"] : nlohmann::json::array();
for (const auto& payment : payments) {
if (!is_new_payment(payment)) continue;
insert_payment(c, kAuftrag, payment, order, order_date_tm);
}
if (is_order_delivered(order)) {
delivery::deliver_order(c, g_config.kBenutzer, kAuftrag, defaults.kVersandArt, delivered_items);
delivery::deliver_order(c, g_config.kBenutzer, kAuftrag, kVersandArt, delivered_items);
}
const char* calc =