Domanda

Ho bisogno di un consiglio per affrontare una query. Posso gestire questo in un'applicazione front-end, però, grazie al design, devo inplement questo nel back-end. Ho il seguente


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)

La tabella di cui sopra hanno tutte le fatture aperte per un cliente. Ho bisogno di applicare un pagamento a queste fatture a partire dalla più antica fattura (colonna daysOpen nella tabella). Quindi, se ho un pagamento $ 550.00, io prima applicarlo alla fattura con 123 daysOld, che è $ 491,96 - $ 500 (che lascia $ 8.04 da applicare alla prossima fattura ... e così via), quindi aggiornare il record (equilibrio colonna in tabella) per 0,00 e passare alla successiva e applicare il restante. Che sarebbe $ 4911,37 - $ 8.04, che lascerebbe $ 4903,33. Poiché non c'è equilibrio lasciata da applicare, il ciclo termina.

La colonna equilibrio dovrebbe leggere

0
4903.33
3457.69

Nota: Ho bisogno di fare questo per tutti i clienti in una tabella (circa 10.000). Un cliente ha una media di circa 20 fatture aperte.

Grazie

È stato utile?

Soluzione

Si consideri il seguente:

un pagamento o si applica integralmente a un equilibrio, si applica in parte ad un equilibrio, o overpays un equilibrio.

Ora, immaginiamo che siamo riusciti a trovare, per qualsiasi equilibrio, il saldo complessivo delle fatture fino ad oggi. Piuttosto che immaginare che, facciamolo:

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;

Ora possiamo trovare il primo saldo complessivo inferiore o uguale al pagamento, per qualsiasi id e tipo, e negozio che e daysOpen, e l'equilibrio complessivo nelle variabili del server.

Poi modificare tutte openItems con quel ID e tipo, dove daysOpen <= il valore abbiamo ottenuto, impostando tutti gli equilibri a zero.

Poi troviamo la prima bilancia non nulla di quel id e tipo, e impostare il suo equilibrio di essere è l'equilibrio - (pagamento - il saldo cumulato abbiamo memorizzato). se c'è un pagamento in eccesso, questo equilibrio sarà correttamente negativo.

Con la query corretta, sarete in grado di fare la ricerca e primo aggiornamento in un comunicato.

Ci sono due problemi. Uno è che non è possibile determinare, di due o più alances con lo stesso ID e tipo e daysOpen, che dovrebbe essere pagato prima. L'aggiunta di un Unique id al vostro tavolo potrebbe servire come un tie-breaker per questi casi.

In secondo luogo è la necessità di salvare il saldo complessivo di utilizzarlo nella query per il secondo aggiornamento. se è stato progettato il tavolo in modo corretto, con una colonna per INVOICE_AMOUNT che non è stato aggiornato da pagamenti, e una colonna di pagamento che è stato, questo sarebbe risolvere il problema.

Un refactoring ancora migliore sarebbe quella di avere due tabelle, una per le fatture e uno per il pagamento:. Poi una vista potrebbe semplicemente fare tutto il lavoro, mettendo a confronto i saldi cumulati ai pagamenti cumulativi, producendo un elenco di saldi non pagati o pagamenti in eccesso

In realtà, ho progettato solo un tale sistema per una grande azienda garanzia ipotecaria con le iniziali FM. E 'stato un po' più complicato di quello che hai, in quanto i saldi sono stati calcolati da un certo numero di formule di importi e le percentuali, e contribuenti più (in realtà, gli assicuratori, questo è stato per i mutui che erano andati in default) doveva essere fatturati in un per prescritto secondo altre regole, per mutuo defauted.

Tutto questo è stato fatto in vista, con una breve (100 linea o giù di lì) procedura che sostanzialmente ha fatto quello che ho descritto sopra memorizzato: utilizzato una vista che ha ordinato la fatturazione delle fatture da queste regole, pagamenti applicata (in vista), calcolando cosa pagamenti supplementari per fatturare in quale data alla quale assicuratore. La procedura memorizzata quindi fatture appena generati per la data attuale (che potrebbe essere impostato data corrente, sempre utilizzando una vista, a qualsiasi data a scopo di test).

(L'ironia è che avevo preso il lavoro onteh promessa che avrei avuto di scrivere C ++; l'unico C ++ che ho scritto usato le API di Oracle e Sybase C per trasferire i dati dal sistema Oracle per Sybase uno.)

Altri suggerimenti

Questo dovrebbe farlo. Ho dichiarato una variabile locale, ma si potrebbe fare che un parametro da una stored procedure. Ho anche aggiunto un invoice_id al tavolo per identificare in modo univoco le fatture dal id e la data non sembrano essere unico.

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

A meno che questo è uno sforzo una volta ...

Sembra che questa è la logica buisness e appartiene nel vostro livello di business della vostra applicazione.

Si dovrà utilizzare un paio di cursori e due cicli annidati. (Questo può essere un po 'lento)

Una di leggere tutti i pagamenti - presumo Cliente, Importo

Poi, per ogni cliente di creare un altro cursore per le partite aperte.

Il primo ciclo leggerà i pagamenti fino al fatto

In quel ciclo aprire un nuovo cursore per partite aperte del cliente ordinati per prima i più vecchi.

Loop attraverso ogni elemento aperto e applicare i pagamenti come hai descritto

Quindi ottenere il pagamento successivo.

Ripetere fino a quando non più pagamenti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top