Пример #1
0
def kundenpreise(kundennr, gueltig_von=None, gueltig_bis=None):
    """Alle kundenspezifischen Preis für einen Kunden"""

    kundennr = remove_prefix(kundennr, 'SC')

    conditions = ["PNSANR=PRSANR",
                  "PRANW='A'",
                  "PRSTAT=' '",
                  "PNSTAT=' '",
                  "PRKDNR=%s" % pad('PRKDNR', kundennr)
                  ]

    if gueltig_von:
        conditions.append("PRDTVO>=%s" % sql_quote(date2softm(gueltig_von)))
    if gueltig_bis:
        conditions.append("PRDTBI>=%s" % sql_quote(date2softm(gueltig_bis)))

    rows = query(tables=['XPN00', 'XPR00'],
                 fields=['PRARTN', 'PNPRB', 'PRDTVO', 'PRDTBI', 'PRSANR'],
                 condition=' AND '.join(conditions),
                 ordering='PRDTVO'
                 )

    preise = {}
    for row in rows:
        preise[row['artnr']] = dict(preis=int(row['preis'] * 100),
                                    gueltig_von=row.get('gueltig_ab_date'),
                                    gueltig_bis=row.get('gueltig_bis_date'),
                                    satznr=str(row['satznr_xpr00']))
    return preise
Пример #2
0
def get_rechnung_by_date(startdate, enddate=None, limit=None):
    """Gib eine Liste von Rechnungsnummern zurück, die zwischen startdate und enddate erzeugt wurden."""

    conditions = ["FKSTAT <> 'X'",
                  "FKRGNR <> 0",
                  "FKDTER > %s" % date2softm(startdate)]
    if enddate:
        conditions.append("FKDTER <= %s" % date2softm(enddate))

    rows = query(tables=['AFK00'], condition=' AND '.join(conditions), fields=['FKRGNR', 'FKDTER'],
                 ordering=['FKDTER'], limit=limit, ua='husoftm2.rechnungen')
    rechnungsnr = []
    for row in rows:
        rechnungsnr.append("RG%s" % row['rechnungsnr'])
    return rechnungsnr
Пример #3
0
def preislisten(gueltig_bis=None):
    """Gib die 'Namen' aller Preislisten für Kundengruppen und Kunden zurück"""

    conditions = ["PNSANR=PRSANR",
                  "PRANW='A'",
                  "PRSTAT=' '",
                  "PNSTAT=' '",
                  ]

    if gueltig_bis:
        conditions.append("PRDTBI>=%s" % date2softm(gueltig_bis))

    preislisten = husoftm2.texte.get_map('PRL')

    rows = query(tables=['XPR00', 'XPN00'],
                 fields=['PRKDNR', 'PRPRLK', 'PRARTN', 'PNPRB', 'PRDTVO', 'PRDTBI'],
                 condition=' AND '.join(conditions))
    for row in rows:
        if row['kunde']:
            row['kunde'] = add_prefix(row['kunde'], 'SC')
        if row['preisliste_kunde']:
            row['preislistenname'] = preislisten.get(row['preisliste_kunde'],
                                                     "?=%s" % row['preisliste_kunde'])

    return rows
Пример #4
0
def durchschnittlicher_abgabepreis(artnr, kundennr=None, startdatum=None):
    """Gibt eine Liste mit den durchschnittlichen Rechnungspreisen pro Monat zurück.

    Liefert eine Liste von 4-Tuples (datum, AVG(preis), menge, umsatz)

    Wenn eine Kundennummer mitgeliefert wird, werden nur Rechungen für diesen Kunden betrachtet.
    Wenn ein startdatum angegebenw wird, werden nur vorgänge nach diesem Datum betrachtet.

    [ ...
    (datetime.date(2009, 2, 1), 3295, 2, 6590),
    (datetime.date(2009, 10, 1), 1744, 2, 3488)]

    Die Funktion ist ausgesprochen langsam - bis zu 8 Sekunden.
    """

    conditions = [
        "FUARTN=%s" % (sql_quote(artnr)),  # nur bestimmten Artikel beachten
        "FKRGNR=FURGNR",   # JOIN
        "FKAUFA<>'U'",     # Keine Umlagerung
        "FKSTAT<>'X'",     # nicht gelöscht
        "FKDTFA>0",        # Druckdatum nicht leer
        "FUKZRV=2",        # ?
        "FURGNI<>0",       # ?
        "FKFORM=' '",      # ?
        "FURGNR<>0",       # es gibt eine Rechnungsnummer
        "FUPNET>0",        # keine Gutschriften
        ]

    if kundennr:
        kundennr = remove_prefix(kundennr, 'SC')
        conditions = ["(FKKDNR=%s OR FKKDRG=%s)" % (sql_quote('%8s' % kundennr),
                                                    sql_quote('%8s' % kundennr))] + conditions
    if not startdatum:
        conditions = ["FKDTFA>'10501'"] + conditions  # keine legacy Daten
    else:
        conditions = ["FKDTFA>%s" % sql_quote(date2softm(startdatum))[:5]] + conditions

    rows = query(['AFU00', 'AFK00'], fields=["FKDTFA", 'SUM(FUMNG)', 'SUM(FUPNET)', 'COUNT(FKRGNR)'],
                 condition=' AND '.join(conditions),
                 grouping='FKDTFA', cachingtime=60 * 60 * 24 * 3,
                 querymappings={'SUM(FUMNG)': 'menge', 'SUM(FUPNET)': 'nettopreis',
                                'COUNT(FKRGNR)': 'rechnungen', 'FKDTFA': 'rechnung_date'})
    mengen = {}
    umsatz = {}
    for row in rows:
        menge = int(float(row['menge']))
        nettopreis = float(row['nettopreis']) * 100
        if menge:
            datum = datetime.date(row['rechnung_date'].year, row['rechnung_date'].month, 1)
            if datum not in mengen:
                mengen[datum] = umsatz[datum] = 0
            mengen[datum] += int(menge)
            umsatz[datum] += int(nettopreis)
    ret = []
    for datum in sorted(mengen.keys()):
        ret.append((datum, int(umsatz[datum] / mengen[datum]), mengen[datum], umsatz[datum]))
    return ret
Пример #5
0
def abgabepreis_kunde(artnr, kundennr, auftragsdatum=None):
    """
    Verkaufspreis für einen Artikel in Abhängigkeit von kundennr und Auftragsdatum ermitteln.

    Höchste Priorität hat der für einen Kunden hinterlegt Preis.
    Zweithöchste Priorität hat der für die Preisliste (Kundengruppe) hinterlegte Preis
    Niedrigste Priorität hat der Listenpreis aus den Artikelstammdaten.

    Rückgabe ist tuple mit Preis und Herkunft des Preises.

    >>> abgabepreis_kunde('04711', 99954)
    (1500, 'Preisliste 95')

    >>> abgabepreis_kunde('04711', 98000)
    (1400, 'Listenpreis')

    >>> abgabepreis_kunde('04711', 94763)
    (1300, 'Kundenpreis')

    """

    if not auftragsdatum:
        auftragsdatum = datetime.date.today()

    # Kundennr als Zeichenkette
    kundennr = remove_prefix(kundennr, 'SC')
    date_str = sql_quote(date2softm(auftragsdatum))

    # 1. Preis für Kunde hinterlegt?
    conditions = ["PNSANR=PRSANR",
                  "PRANW='A'",
                  "PRSTAT=' '",
                  "PNSTAT=' '",
                  "PRARTN=%s" % sql_quote(artnr),
                  "PRDTBI>=%s" % date_str,
                  "PRDTVO<=%s" % date_str,
                  ]
    condition_kunde = conditions + ["PRKDNR=%s" % pad('PRKDNR', kundennr)]
    rows = query(['XPN00', 'XPR00'], fields=['PNPRB'], condition=' AND '.join(condition_kunde),
                 ordering='PRDTVO', limit=1)
    if rows:
        return (int(rows[0][0] * 100), 'Kundenpreis')

    # 2. Preis aus Preislistennr. des Kunden ermitteln
    condition_gruppe = conditions + [
                            # "PRPRLK = %s" % sql_quote(kunde['kunden_gruppe']),
                            "KDKDNR=%s" % pad('KDKDNR', kundennr),
                            "PRPRLK=KDKGRP"
                       ]
    rows = query(['XPN00', 'XPR00', 'XKD00'], fields=['PNPRB', 'PRPRLK'],
                 ordering='PRDTVO', condition=' AND '.join(condition_gruppe), limit=1)
    if rows:
        return (int(rows[0]['preis'] * 100), 'Preisliste %s' % rows[0]['preisliste_kunde'])

    # 3. Listenpreis aus Artikelstammdaten
    return (listenpreis(artnr), 'Listenpreis')
Пример #6
0
def artikel_mengenumsatz_range(artnr, startdate, enddate):
    """Liefert die FakturirtenUmsatz-MEngen für den Interval [startdate; enddata[

    >>> artikel_mengenumsatz_range('10101', datetime.date(2009, 6, 2), datetime.date(2009, 6, 5))
    {datetime.date(2009, 6, 2): 500, datetime.date(2009, 6, 4): 1000, datetime.date(2009, 6, 3): 0}
    """

    conditions = ['FUDTRI>=%s' % date2softm(startdate),
                  'FUDTRI<%s' % date2softm(enddate),
                  'FUARTN=%s' % sql_quote(artnr),
                 ]

    rows = query(tables=['AFU00'], fields=['SUM(FUMNG)', 'FUDTRI'], grouping=['FUDTRI'],
             querymappings={'FUDTRI': 'tag', 'SUM(FUMNG)': 'menge'},
             condition=' AND '.join(conditions), ua='husoftm2.umsatz', cachingtime=86400)
    ret = {}
    for row in rows:
        ret[softm2date(row['tag'])] = as400_2_int(row['menge'])
    return ret
Пример #7
0
def verspaetete_auftraege(datum=None):
    """Gibt eine Liste von Auftragsnummern zurück, die vor <datum> ausgeliefert hätten werden müssen,
    aber noch nicht ausgeliefert sind.
    """

    if not datum:
        datum = datetime.date.today()

    conditions = ["AKKZVA=0",     # Auftrag nicht voll ausgeliefert
                  "APKZVA=0",     # Position nicht voll ausgeliefert
                  "AKAUFN>0",     # Auftragsnummer wurde vergeben
                  "AKSTAT<>'X'",  # Auftrag nicht glöscht
                  "APSTAT<>'X'",  # Position nicht gelöscht
                  'APDTLT < %s' % date2softm(datum),
                  ]
    rows = query(['AAK00'],
                     condition=' AND '.join(conditions),
                     grouping=['AKAUFN'], fields=['AKAUFN'], ordering=['AKAUFN DESC'],
                     joins=[('AAP00', 'AKAUFN', 'APAUFN')],
                     ua='husoftm2.auftraege.verspaetete_auftraege')
    return ["SO%s" % row[0] for row in rows]
    return _auftraege(conditions, addtables=['AAT00'], header_only=False)
Пример #8
0
def abgabepreise_kunde(artnrs, kundennr, auftragsdatum=None):
    """
    Verkaufspreis für einen oder mehrere Artikel in Abhängigkeit von kundennr und Auftragsdatum ermitteln.

    Der Rückgabewert ist ein dict mit den ArtNr. als  Schlüssel.
    Die Werte sind Preisinformationen als Tupel (Preis, Herkunft)
    oder None, falls zu der ArtNr. kein Preis ermittelt werden konnte.

    Die Logik funktioniert genau wie bei abgabepreis_kunde:
    Es werden zuerst kundenspezifische Preise, dann kundengruppen-spezifische Preise und als
    letztes Listenpreise ermittelt.
    """
    warnings.warn("use `cs.salesforce.preise` instead!", DeprecationWarning, stacklevel=2)
    if not auftragsdatum:
        auftragsdatum = datetime.date.today()

    artnrs = set(artnrs)
    kundennr = remove_prefix(kundennr, 'SC')
    date_str = sql_quote(date2softm(auftragsdatum))

    abgabepreise = {}

    # 1. Preise für Kunden hinterlegt?
    conditions = ["PNSANR=PRSANR",
                  "PRANW='A'",
                  "PRPRLK<>''"
                  "PRSTAT=' '",
                  "PNSTAT=' '",
                  "PRDTBI>=%s" % date_str,
                  "PRDTVO<=%s" % date_str,
                  ]
    condition_kunde = conditions + ["PRKDNR=%s" % sql_quote("%8s" % kundennr),
                                    "PRARTN IN (%s)" % ",".join([sql_quote(artnr) for artnr in artnrs])]
    rows = query(tables=['XPN00', 'XPR00'],
                 fields=['PRARTN', 'PNPRB'],
                 condition=' AND '.join(condition_kunde),
                 ordering='PRDTVO')
    for row in rows:
        if row['artnr'] in artnrs:
            artnrs.remove(row['artnr'])
        abgabepreise[row['artnr']] = (int(row['preis'] * 100), u'Kundenpreis')

    if not artnrs:
        return abgabepreise

    # 2. Preise aus Preislistennr. des Kunden ermitteln
    condition_gruppe = conditions + [
                            # "PRPRLK = %s" % sql_quote(kunde['kunden_gruppe']),
                            "PRARTN IN (%s)" % ",".join([sql_quote(artnr) for artnr in artnrs]),
                            "KZKDNR=%s" % pad('KZKDNR', kundennr),
                            "PRPRLK=KZPREL"
                       ]
    rows = query(tables=['XPN00', 'XPR00', 'AKZ00'],
                 fields=['PRARTN', 'PNPRB', 'PRPRLK'],
                 condition=' AND '.join(condition_gruppe),
                 ordering='PRDTVO')

    for row in rows:
        if row['artnr'] in artnrs:
            artnrs.remove(row['artnr'])
        abgabepreise[row['artnr']] = (int(row['preis'] * 100), u'Preisliste %s' % row['preisliste_kunde'])

    if not artnrs:
        return abgabepreise

    # 3. Listenpreis aus Artikelstammdaten
    for artnr, preis in listenpreise(artnrs).iteritems():
        if artnr in artnrs:
            artnrs.remove(artnr)
        abgabepreise[artnr] = (preis, u'Listenpreis')

    for artnr in artnrs:
        abgabepreise[artnr] = None

    return abgabepreise
Пример #9
0
def _auftraege(additional_conditions=None, addtables=None, mindate=None, maxdate=None, limit=None,
               header_only=False, canceled=False):
    """
    Alle Aufträge ermitteln
    `additional_conditions` kann eine Liste von SQL-Bedingungen enthalten, die die Auftragssuche
    einschränken.
    `mindate` & `maxdate` können den Anliefertermin einschränken.
    `limit` kann die Zahl der zurückgelieferten Aufträge einschraenken. Dabei werden groessere
    Auftragsnummern zuerst zurueck gegeben.
    `header_only` ruft nur Auftragsköpfe ab und ist bedeutend schneller
    `canceled` wenn True, werden auch stornierte Aufträge zurück gegeben.

    Rückgabewert sind dicts nach dem Lieferungprotokoll.
    Wenn header_only == True, werden nur Auftragsköpfe zurück gegeben, was deutlich schneller ist.
    """

    # Solange der Client das nciht gesondert verlangt, werden stornierte Aufträge ignoriert.
    if not canceled:
        conditions = ["AKSTAT<>'X'"]
    else:
        conditions = []
    # Anliefertermin ist ein Range
    if mindate and maxdate:
        conditions.append("AKDTER BETWEEN %s AND %s" % (date2softm(mindate), date2softm(maxdate)))
    # Anliefertermin ist nach unten begrenzt
    elif mindate:
        conditions.append("AKDTER > %s" % date2softm(mindate))
    # Anliefertermin ist nach oben begrenzt
    elif maxdate:
        conditions.append("AKDTER < %s" % date2softm(maxdate))
    # vom Aufrufer direkt angegebenen, weitere SQL Bedingungen zufügen. Diese werden mit `AND` verkettet.
    if additional_conditions:
        conditions.extend(additional_conditions)

    condition = " AND ".join(conditions)
    koepfe = {}
    kopftexte = {}

    auftragsnr_to_lieferadresse_kdnr = {}

    if addtables is None:
        addtables = []

    # Köpfe und Adressen einlesen
    for kopf in query(['AAK00'] + addtables, ordering=['AKAUFN DESC'], condition=condition,
                      joins=[('XKD00', 'AKKDNR', 'KDKDNR')],
                             limit=limit, ua='husoftm2.auftraege'):
        d = dict(kundennr="SC%s" % kopf['kundennr_warenempf'],
                 kundennr_rechnung="SC%s" % kopf['kundennr_rechnungsempf'],
                 name1=kopf['name1'],
                 name2=kopf['name2'],
                 name3=kopf['name3'],
                 strasse=kopf['strasse'],
                 land=husoftm2.tools.land2iso(kopf['laenderkennzeichen']),
                 plz=kopf['plz'],
                 ort=kopf['ort'],
                 auftragsnr="SO%s" % kopf['auftragsnr'],
                 auftragsnr_kunde=kopf['auftragsnr_kunde'],
                 erfassung=kopf.get('AAK_erfassung'),
                 aenderung=kopf.get('AAK_aenderung', None),
                 sachbearbeiter=husoftm2.sachbearbeiter.resolve(kopf['sachbearbeiter']),
                 anliefertermin=kopf['liefer_date'],
                 teillieferung_erlaubt=(kopf['teillieferung_erlaubt'] == 1),
                 # TODO: md: ich denke, "erledigt" ist ein Auftrag auch, wenn er storneirt wurde,
                 # oder wenn alle Positionen auf voll_ausgeliefert stehen.
                 erledigt=(kopf['voll_ausgeliefert'] == 1),
                 positionen=[],
                 art=kopf['art'],
                 storniert=(kopf['AAK_status'] == 'X'))
        koepfe[kopf['auftragsnr']] = d

        # Auftrag geht an die 'normale' Lieferadresse: Kein .\d\d\d-Suffix an die `lieferadresse.kundennr`
        if kopf['versandadressnr'] == 0:
            auftragsnr_to_lieferadresse_kdnr[kopf['auftragsnr']] = add_prefix(kopf['kundennr_warenempf'],
                                                                              'SC')
        # Auftrag geht an eine abweichende Lieferadresse: .00?-Suffix an die `lieferadresse.kundennr` hängen.
        else:
            lieferadresse_kdnr = add_prefix("%s.%03d" % (kopf['kundennr_warenempf'], kopf['versandadressnr']),
                                            "SC")
            auftragsnr_to_lieferadresse_kdnr[kopf['auftragsnr']] = lieferadresse_kdnr

    if header_only:
        return koepfe.values()

    allauftrnr = koepfe.keys()
    # Texte auslesen
    # Die dritte und vierte Position des Werts von txt_auslesen sind posdaten und kopfdaten.
    # Es handelt sich dabei wohl um Texte, die nicht angedruckt werden sollen.
    # Bis auf weiteres werden diese hier ignoriert.
    postexte, kopftexte, _, _ = txt_auslesen(allauftrnr)
    while allauftrnr:
        # In 50er Schritten Auftragspositionen lesen und den 50 Aufträgen zuordnen
        batch = allauftrnr[:50]
        allauftrnr = allauftrnr[50:]

        # Abweichende Lieferadressen
        for row in query(['XAD00'], ua='husoftm2.auftraege',
                         condition="ADAART=1 AND ADRGNR IN (%s)" % ','.join([str(x) for x in batch])):
            koepfe[row['nr']]['lieferadresse'] = dict(name1=row['name1'],
                                                      name2=row['name2'],
                                                      name3=row['name3'],
                                                      strasse=row['strasse'],
                                                      land=husoftm2.tools.land2iso(row['laenderkennzeichen']),
                                                      plz=row['plz'],
                                                      kundennr=auftragsnr_to_lieferadresse_kdnr[row['nr']],
                                                      ort=row['ort'])
        # Positionen einlesen
        if not canceled:
            poscondition = "APSTAT<>'X' AND APAUFN IN (%s)" % ','.join([str(x) for x in batch])
        else:
            poscondition = "APAUFN IN (%s)" % ','.join([str(x) for x in batch])
        for row in query(['AAP00'], condition=poscondition,
                         ua='husoftm2.auftraege'):
            d = dict(menge=int(row['bestellmenge']),
                     lager="LG%s" % row['lager'],
                     artnr=row['artnr'],
                     liefer_date=row['liefer_date'],
                     menge_offen=int(row['menge_offen']),
                     fakturierte_menge=int(row['fakturierte_menge']),
                     erledigt=(row['voll_ausgeliefert'] == 1),
                     storniert=(row['AAP_status'] == 'X'),
                     posnr=int(row['position']),
                     _aenderung=row.get('AAP_aenderung'),
                     _erfassung=row.get('AAP_erfassung'),
                     _zuteilung=row.get('AAP_zuteilung'),
                     # 'position': 2,
                     # 'teilzuteilungsverbot': u'0',
                     )

            # Preis einfügen
            if row.get('verkaufspreis'):
                d['preis'] = row['verkaufspreis']

            texte = postexte.get(row['auftragsnr'], {}).get(row['position'], [])
            texte, attrs = texte_trennen(texte)
            d['infotext_kunde'] = texte
            if 'guid' in attrs:
                d['guid'] = attrs['guid']
            koepfe[row['auftragsnr']]['positionen'].append(d)

        # Kopftexte zuordnen
        for auftragsnr, texte in kopftexte.items():
            texte, attrs = texte_trennen(texte)
            koepfe[remove_prefix(auftragsnr, 'SO')]['infotext_kunde'] = texte
            if 'guid' in attrs:
                koepfe[remove_prefix(auftragsnr, 'SO')]['guid'] = attrs['guid']

    return koepfe.values()
Пример #10
0
def _auftraege(additional_conditions=None, addtables=None, mindate=None, maxdate=None, limit=None,
               header_only=False):
    """
    Alle Aufträge ermitteln
    `additional_conditions` kann eine Liste von SQL-Bedingungen enthalten, die die Auftragssuche
    einschränken.
    `mindate` & `maxdate` können den Anliefertermin einschränken.
    `limit` kann die Zahl der zurückgelieferten Aufträge einschraenken. Dabei werden groessere
    Auftragsnummern zuerst zurueck gegeben.

    Rückgabewert sind dicts nach dem Lieferungprotokoll.
    Wenn header_only == True, werden nur Auftragsköpfe zurück gegeben, was deutlich schneller ist.
    """

    conditions = ["AKSTAT<>'X'"]
    if mindate and maxdate:
        conditions.append("AKDTER BETWEEN %s AND %s" % (date2softm(mindate), date2softm(maxdate)))
    elif mindate:
        conditions.append("AKDTER > %s" % date2softm(mindate))
    elif maxdate:
        conditions.append("AKDTER < %s" % date2softm(maxdate))
    if additional_conditions:
        conditions.extend(additional_conditions)

    condition = " AND ".join(conditions)
    koepfe = {}
    kopftexte = {}

    if addtables is None:
        addtables = []

    # Köpfe und Adressen einlesen
    for kopf in query(['AAK00'] + addtables, ordering=['AKAUFN DESC'], condition=condition,
                      joins=[('XKD00', 'AKKDNR', 'KDKDNR')],
                             limit=limit, ua='husoftm2.auftraege'):
        d = dict(kundennr="SC%s" % kopf['kundennr_warenempf'],
                 auftragsnr="SO%s" % kopf['auftragsnr'],
                 auftragsnr_kunde=kopf['auftragsnr_kunde'],
                 erfassung=kopf['AAK_erfassung_date'],
                 aenderung=kopf['AAK_aenderung_date'],
                 sachbearbeiter=husoftm2.sachbearbeiter.resolve(kopf['sachbearbeiter']),
                 anliefertermin=kopf['liefer_date'],
                 teillieferung_erlaubt=(kopf['teillieferung_erlaubt'] == 1),
                 erledigt=(kopf['voll_ausgeliefert'] == 1),
                 positionen=[],
                 # * *auftragsnr_kunde* - id of the order submitted by the customer
                 # * *info_kunde* - Freitext der für den Empfänger relevanz hat
                 )
        koepfe[kopf['auftragsnr']] = d

    if header_only:
        return koepfe.values()

    allauftrnr = koepfe.keys()
    # Texte auslesen
    # Die dritte und vierte Position des Werts von txt_auslesen sind posdaten und kopfdaten.
    # Es handelt sich dabei wohl um Texte, die nicht angedruckt werden sollen.
    # Bis auf weiteres werden diese hier ignoriert.
    postexte, kopftexte, _, _ = txt_auslesen(allauftrnr)
    while allauftrnr:
        # In 50er Schritten Auftragspositionen lesen und den 50 Aufträgen zuordnen
        batch = allauftrnr[:50]
        allauftrnr = allauftrnr[50:]

        # Abweichende Lieferadressen
        for row in query(['XAD00'], ua='husoftm2.lieferscheine',
                         condition="ADAART=1 AND ADRGNR IN (%s)" % ','.join([str(x) for x in batch])):
            koepfe[row['nr']]['lieferadresse'] = dict(name1=kopf['name1'],
                                    name2=kopf['name2'],
                                    name3=kopf['name3'],
                                    strasse=row['strasse'],
                                    land=husoftm2.tools.land2iso(row['laenderkennzeichen']),
                                    plz=row['plz'],
                                    ort=row['ort'])

        # Positionen einlesen
        for row in query(['AAP00'], condition="APSTAT<>'X' AND APAUFN IN (%s)" % ','.join([str(x)
                                                                                           for x in batch]),
                         ua='husoftm2.auftraege'):
            d = dict(menge=int(row['bestellmenge']),
                     artnr=row['artnr'],
                     liefer_date=row['liefer_date'],
                     menge_offen=int(row['menge_offen']),
                     fakturierte_menge=int(row['fakturierte_menge']),
                     erledigt=(row['voll_ausgeliefert'] == 1),
                     # 'position': 2,
                     # 'teilzuteilungsverbot': u'0',
                     )
            texte = postexte.get(row['auftragsnr'], {}).get(row['position'], [])
            texte, attrs = texte_trennen(texte)
            d['infotext_kunde'] = texte
            if 'guid' in attrs:
                d['guid'] = attrs['guid']
            koepfe[row['auftragsnr']]['positionen'].append(d)

        # Kopftexte zuordnen
        for auftragsnr, texte in kopftexte.items():
            texte, attrs = texte_trennen(texte)
            koepfe[remove_prefix(auftragsnr, 'SO')]['infotext_kunde'] = texte
            if 'guid' in attrs:
                koepfe[remove_prefix(auftragsnr, 'SO')]['guid'] = attrs['guid']

    return koepfe.values()