Example #1
0
def get_ls_kb_data(conditions, additional_conditions=None, limit=None, header_only=False,
                   is_lieferschein=True):
    """Lieferscheindaten oder Kommsissionierbelegdaten entsprechend dem Lieferungsprotokoll.

    Wenn is_lieferschein = False, dann werden Kommiauftragsdaten zurückgebeben (Kommimengen)

    """

    cachingtime = 60 * 60 * 12

    if additional_conditions:
        conditions.extend(additional_conditions)
    condition = " AND ".join(conditions)
    koepfe = {}
    auftragsnr2satznr = {}
    satznr2auftragsnr = {}

    # Lieferscheinkopf JOIN Kundenadresse um die Anzahl der Queries zu minimieren
    # JOIN Lieferadresse geht nicht, weil wir "ADAART=1" mit DB2/400 nicht klappt
    for row in query(['ALK00'], ordering=['LKSANK DESC'], condition=condition, limit=limit,
                      joins=[('XKD00', 'LKKDNR', 'KDKDNR'), ('AAK00', 'LKAUFS', 'AKAUFN')],
                      cachingtime=cachingtime, ua='husoftm2.lieferscheine'):
        kopf = dict(positionen=[],
                    auftragsnr="SO%s" % row['auftragsnr'],
                    auftragsnr_kunde=row['auftragsnr_kunde'],
                    erfassung=row['ALK_erfassung'],
                    aenderung=row.get('ALK_aenderung'),
                    anliefer_date=row['anliefer_date'],
                    kundennr="SC%s" % row['rechnungsempfaenger'],
                    lieferadresse=dict(kundennr="SC%s" % row['warenempfaenger']),
                    anlieferdatum=row['anliefer_date'],
                    lager="LG%03d" % int(row['lager']),
                    kommiauftragnr="KA%s" % row['kommibelegnr'],
                    kommiauftrag_datum=row['kommibeleg_date'],
                    lieferscheinnr="SL%s" % row['lieferscheinnr'],
                    name1=row.get('name1', ''),
                    name2=row.get('name2', ''),
                    name3=row.get('name3', ''),
                    strasse=row.get('strasse', ''),
                    land=husoftm2.tools.land2iso(row['laenderkennzeichen']),
                    plz=row.get('plz', ''),
                    ort=row.get('ort', ''),
                    tel=row.get('tel', ''),
                    fax=row.get('fax', ''),
                    art=row.get('art', ''),
                    softm_created_at=row.get('ALK_lieferschein'),
                    )
        # Wir hatten erhebliche Probleme, weil datumsfelder mal befüllt waren
        # und mal nicht (race condition) - wir gehen deswegen recht großzügig
        # bei der suceh nach einem geeigenten feld vor.
        for fieldname in ['ALK_lieferschein', 'ALK_aenderung']:
            if row.get(fieldname):
                kopf['datum'] = row.get(fieldname)
                break
        if not kopf.get('datum'):
            raise RuntimeError("Konnte kein Datum ermitteln %r", row)

        pos_key = remove_prefix((row['satznr']), 'SO')
        if row.get('bezogener_kopf'):
            pos_key = remove_prefix(row['bezogener_kopf'], 'SO')
        auftragsnr2satznr[remove_prefix(row['auftragsnr'], 'SO')] = pos_key
        satznr2auftragsnr[pos_key] = remove_prefix(row['auftragsnr'], 'SO')
        if row['ALK00_dfsl']:
            # Basis dieses Codes:
            # [LH #721] LS mit 0-mengen vermeiden
            # Wir sehen im Produktiv-Betrieb immer wieder Lieferscheine mit der Menge "0"
            # erzeugt werden. Wir vermuten hier eine race Condition, bei der die
            # ALK00 schon mit der Lieferscheinnummer geupdated ist, die ALN00 aber noch
            # nicht mit der Lieferscheinmenge.
            # Eine weitere Vermutung ist, dass wenn in der ALN00 die Menge noch cniht eingetragen
            # ist, dort auch noch die Lieferscheinnummer fehlt. Das versuchen wir hier abzufangen.
            # Lieber ein Crash, als ein Lieferschein mit (unbegründerter) 0-menge.
            raise RuntimeError("Dateiführungsschlüssel in ALK00: %r" % kopf)
        koepfe[pos_key] = kopf

    if header_only:
        return koepfe.values()

    satznr = koepfe.keys()
    allauftrnr = koepfe.keys()
    # Alle texte einlesen
    postexte, kopftexte, posdaten, kopfdaten = txt_auslesen([satznr2auftragsnr[x] for x in allauftrnr])
    while satznr:
        # In 50er Schritten Auftragspositionen & Texte lesen und den 50 Aufträgen zuordnen
        batch = satznr[:50]
        satznr = satznr[50:]

        # Abweichende Lieferadressen
        condition = "ADAART=1 AND ADRGNR IN (%s) AND ADRGNR=AKAUFN" % ','.join([str(satznr2auftragsnr[x])
                                                                                for x in batch])
        for row in query(['XAD00', 'AAK00'], cachingtime=cachingtime, ua='husoftm2.lieferscheine',
                         condition=condition):
            aktsatznr = auftragsnr2satznr[row['nr']]
            koepfe[aktsatznr]['lieferadresse'].update(dict(name1=row['name1'],
                            name2=row['name2'],
                            name3=row['name3'],
                            strasse=row['strasse'],
                            land=husoftm2.tools.land2iso(row['laenderkennzeichen']),
                            plz=row['plz'],
                            ort=row['ort']))
            versandadressnr = row['versandadressnr']
            warenempfaenger = koepfe[aktsatznr]['lieferadresse']['kundennr']
            if versandadressnr:
                warenempfaenger = "%s.%03d" % (warenempfaenger, versandadressnr)
            koepfe[aktsatznr]['lieferadresse']['warenempfaenger'] = warenempfaenger

        # Positionen & Positionstexte zuordnen
        for row in query(['ALN00'], condition="LNSTAT<>'X' AND LNSANK IN (%s)" % ','.join([str(x)
                                                                                           for x in batch]),
                         cachingtime=cachingtime, ua='husoftm2.lieferscheine'):
            if is_lieferschein == True:
                lsmenge = int(row['menge'])
                if row['ALN00_dfsl']:
                    # Basis dieses Codes:
                    # [LH #721] LS mit 0-mengen vermeiden
                    # Wir sehen im Produktiv-Betrieb immer wieder Lieferscheine mit der Menge "0"
                    # erzeugt werden. Wir vermuten hier eine race Condition, bei der die
                    # ALK00 schon mit der Lieferscheinnummer geupdated ist, die ALN00 aber noch
                    # nicht mit der Lieferscheinmenge.
                    # Eine weitere Vermutung ist, dass wenn in der ALN00 die Menge noch cniht eingetragen
                    # ist, dort auch noch die Lieferscheinnummer fehlt. Das versuchen wir hier abzufangen.
                    # Lieber ein Crash, als ein Lieferschein mit (unbegründerter) 0-menge.
                    raise RuntimeError("Dateiführungsschlüssel in ALN00: %r" % row)
            else:
                lsmenge = int(row['menge_komissionierbeleg'])
            pos = dict(artnr=row['artnr'],
                       guid='%s-%03d-%03d' % (row['kommibelegnr'], row['auftrags_position'],
                                              row['kommibeleg_position']),
                       menge=lsmenge)
            texte = postexte.get(remove_prefix(row['auftragsnr'], 'SO'),
                                               {}).get(row['auftrags_position'], [])
            pos['infotext_kunde'] = texte
            if 'guid' in posdaten.get(remove_prefix(row['auftragsnr'], 'SO'), {}):
                pos['auftragpos_guid'] = posdaten.get(remove_prefix(row['auftragsnr'], 'SO'), {}).get('guid')
            else:
                pos['auftragpos_guid'] = "%s-%03d" % (row['auftragsnr'], row['auftrags_position'])

            lieferung = koepfe[remove_prefix(row['satznr_kopf'], 'SO')]
            lieferung['positionen'].append(pos)
            # *Sachbearbeiter* ist der, der den Vorgang tatsächlich bearbeitet hat. *Betreuer* ist
            # die (oder der), die für den Kunden zusändig ist.
            lieferung['sachbearbeiter'] = husoftm2.sachbearbeiter.resolve(row['sachbearbeiter_bearbeitung'])

        # Kopftexte zuordnen
        for auftragsnr, texte in kopftexte.items():
            pos_key = auftragsnr2satznr[remove_prefix(auftragsnr, 'SO')]
            koepfe[pos_key]['infotext_kunde'] = texte
        for auftragsnr, werte in kopfdaten.items():
            if 'guid' in werte:
                pos_key = auftragsnr2satznr[remove_prefix(auftragsnr, 'SO')]
                koepfe[pos_key]['guid_auftrag'] = werte['guid']

        for aktsatznr in koepfe.keys():
            # Entfernt Konstrukte wie das:
            #     "kundennr": "SC19971",
            #      "lieferadresse": {
            #       "kundennr": "SC19971"
            #      }
            if len(koepfe[aktsatznr]['lieferadresse']) == 1:
                if koepfe[aktsatznr]['lieferadresse']['kundennr'] == koepfe[aktsatznr]['kundennr']:
                    del(koepfe[aktsatznr]['lieferadresse'])

    return koepfe.values()
Example #2
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()
Example #3
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()