Frage

Ich benötige Ratschläge zur Bearbeitung einer Anfrage.Ich kann dies in einer Front-End-Anwendung handhaben, muss dies jedoch aus Designgründen im Backend implementieren.Ich habe Folgendes


CREATE TABLE [dbo].[openitems](
    [id] [varchar](8) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [type] [char](5) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [date] [smalldatetime] NULL,
    [amount] [decimal](9, 2) NULL,
    [daysOpen] [smallint] NULL,
    [balance] [decimal](9, 2) NULL
) ON [PRIMARY]




insert into openitems values('A12399','INV','2008-12-05',491.96,123)

insert into openitems values('A12399','INV','2008-12-12',4911.37,116)

insert into openitems values('A12399','INV','2008-12-05',3457.69,109)

Die obige Tabelle enthält alle offenen Rechnungen für einen Kunden.Ich muss eine Zahlung auf diese Rechnungen anwenden, beginnend mit der ältesten Rechnung (Spalte „daysOpen“ in der Tabelle).Wenn ich also eine Zahlung in Höhe von 550,00 $ habe, wende ich sie zuerst auf die Rechnung an, die 123 Tage alt ist, also 491,96 $ - 500 $ (wodurch 8,04 $ übrig bleiben, die auf die nächste Rechnung angewendet werden müssen ...)und so weiter), aktualisieren Sie dann diesen Datensatz (Saldenspalte in der Tabelle) auf 0,00, gehen Sie zum nächsten über und wenden Sie den Rest an.Das wären 4.911,37 bis 8,04 US-Dollar, womit 4.903,33 US-Dollar übrig blieben.Da kein Restbetrag mehr anzuwenden ist, wird die Schleife beendet.

Die Saldospalte sollte jetzt lauten

0
4903.33
3457.69

Notiz:Ich muss dies für alle Kunden in einer Tabelle tun (ca. 10.000).Ein Kunde hat durchschnittlich etwa 20 offene Rechnungen.

Danke

War es hilfreich?

Lösung

Folgendes berücksichtigen:

Eine Zahlung gilt entweder vollständig für einen Restbetrag, gilt teilweise für einen Restbetrag oder führt zu einer Überzahlung eines Restbetrags.

Stellen Sie sich nun vor, wir könnten für jeden Saldo den kumulierten Saldo der bisherigen Rechnungen ermitteln.Anstatt uns das vorzustellen, lasst es uns tun:

create view cumulative_balance as
select a.*, 
  (select sum( balance ) 
  from openitems b 
  where b.id = a.id and b.type = a.type and a.daysOpen >= a.daysOpen)
  as cumulative_balance
from openitems a;

Jetzt können wir für jede ID und jeden Typ den ersten kumulierten Saldo ermitteln, der kleiner oder gleich der Zahlung ist, und diesen sowie die Tage und den kumulierten Saldo in Servervariablen speichern.

Dann aktualisieren wir alle openItems mit dieser ID und diesem Typ, wobei „daysOpen“ <= der Wert ist, den wir erhalten haben, und setzen alle diese Salden auf Null.

Dann finden wir den ersten Saldo ungleich Null dieser ID und dieses Typs und setzen seinen Saldo auf seinen Saldo – (Zahlung – den kumulierten Saldo, den wir gespeichert haben).Bei einer Überzahlung ist dieser Saldo korrekterweise negativ.

Mit der richtigen Abfrage können Sie die Suche und die erste Aktualisierung in einer Anweisung durchführen.

Es gibt zwei Probleme.Einer davon ist, dass Sie nicht bestimmen können, ob bei zwei oder mehr Tarifen mit der gleichen ID, dem gleichen Typ und den gleichen Öffnungstagen zuerst bezahlt werden soll.Hinzufügen eines einzigartig id an Ihren Tisch würde in diesen Fällen als Entscheidungshilfe dienen.

Zweitens muss der kumulierte Saldo gespeichert werden, um ihn in der Abfrage für die zweite Aktualisierung zu verwenden.Wenn Sie Ihre Tabelle richtig gestaltet hätten, mit einer Spalte für Rechnungsbetrag, die nicht durch Zahlungen aktualisiert wurde, und einer Zahlungsspalte, die aktualisiert wurde, würde dies Ihr Problem lösen.

Eine noch bessere Umgestaltung wäre es, zwei Tabellen zu haben, eine für Rechnungen und eine für Zahlungen:dann könnte eine Ansicht einfach die ganze Arbeit erledigen, indem sie die kumulierten Salden mit den kumulierten Zahlungen vergleicht und eine Liste der unbezahlten Salden oder Überzahlungen erstellt.

Tatsächlich habe ich ein solches System für ein großes Hypothekenbürgschaftsunternehmen mit den Initialen FM entworfen.Es war etwas komplizierter als das, was Sie haben, da die Salden anhand einer Reihe von Formeln für Beträge und Prozentsätze berechnet wurden und mehrere Zahler (eigentlich Versicherer, dies galt für Hypotheken, die in Verzug geraten waren) in Rechnung gestellt werden mussten vorgeschriebene Reihenfolge nach anderen Regeln, pro fällige Hypothek.

All dies wurde in Ansichten mit einer kurzen (ca. 100 Zeilen) gespeicherten Prozedur durchgeführt, die im Wesentlichen das tat, was ich oben beschrieben habe:Verwendete eine Ansicht, die die Abrechnung von Rechnungen nach diesen Regeln ordnete, wendete Zahlungen (in der Ansicht) an und berechnete, welche zusätzlichen Zahlungen zu welchem ​​Datum welchem ​​Versicherer in Rechnung gestellt werden sollten.Die gespeicherte Prozedur generierte dann einfach Rechnungen für das aktuelle Datum (das aktuelle Datum konnte wiederum mithilfe einer Ansicht zu Testzwecken auf ein beliebiges Datum festgelegt werden).

(Die Ironie ist, dass ich den Job angenommen hatte, weil ich versprochen hatte, C++ schreiben zu dürfen;Das einzige C++, das ich geschrieben habe, verwendete die Oracle- und Sybase-C-APIs, um Daten vom Oracle-System zum Sybase-System zu übertragen.)

Andere Tipps

Das sollte es tun. Ich habe eine lokale Variable deklariert, aber Sie, dass ein Parameter aus einer gespeicherten Prozedur machen könnten. Außerdem habe ich eine invoice_id die Tabelle Rechnungen eindeutig zu identifizieren, da id und Datum scheinen nicht eindeutig zu sein.

DECLARE
    @paid_amount DECIMAL(9, 2)

SET @paid_amount = 500

UPDATE
    OI
SET
    balance =
            CASE
                WHEN @paid_amount - SQ.running_total > balance THEN 0
                ELSE balance - (@paid_amount - SQ.running_total)
            END
FROM
    dbo.OpenItems OI
INNER JOIN (
    SELECT
        I1.id,
        I1.invoice_id,
        I1.date,
        ISNULL(SUM(I2.amount), 0) AS running_total
    FROM
        OpenItems I1
    LEFT OUTER JOIN OpenItems I2 ON
        I2.id = I1.id AND
        I2.type = 'INV' AND
        I2.daysopen > I1.daysopen
    GROUP BY
        I1.id,
        I1.invoice_id,
        I1.date
) AS SQ ON
    SQ.id = OI.id AND
    SQ.invoice_id = OI.invoice_id
WHERE
    @paid_amount > SQ.running_total

Es sei denn, dies ist eine einmalige Anstrengung ...

Es klingt wie diese Buisness-Logik und gehört in Ihrer Business-Schicht Ihrer Anwendung.

würden Sie haben ein paar Cursor verwenden und zwei verschachtelte Schleifen. (Dies kann ein wenig langsam)

Ein all Zahlungen lesen - ich nehme an Kunden, Betrag

Dann ist für jeden Kunden einen weiteren Cursor für die offenen Posten erstellen.

Die erste Schleife wird die Zahlungen lesen, bis getan

Innerhalb dieser Schleife öffnet einen neuen Cursor für den ersten nach alt sortierten offene Posten des Kunden.

Schleife durch jeden offenen Posten und gelten die Zahlungen, wie Sie beschrieben

Dann nächste Zahlung erhalten.

Wiederholen, bis keine Zahlungen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top