我在将付款分配到发票行时遇到问题。
数据如下:
发票行表(销售):
lineId invoiceId value
1 1 100
2 1 -50
3 1 40
4 2 500
付款表(付款):
paymentId invoiceId amount
1 1 50
2 1 40
3 2 300
现在,我想知道每个发票行的付款明细。付款应首先分配到最小值(即第2行-50)
输出应如下所示:
lineId invoiceId value paymentId valuePaid valueUnpaid
2 1 -50 1 -50 0
3 1 40 1 40 0
1 1 100 1 60 40
1 1 100 2 40 0
4 2 500 3 300 200
问题在下面的帖子中得到了解决,但是如果发票值为负或必须将发票行分为两笔付款,则该解决方案将不起作用。
https://dba.stackexchange.com/questions/58474/how-can-i-use-running-total-aggregates-in-a-query-to-output-financial-accumulati/219925?noredirect=1#comment431486_219925
根据以上文章,这是我到目前为止所做的:
drop table dbo.#sales
drop table dbo.#payments
CREATE TABLE dbo.#sales
( lineId int primary key, -- unique line id
invoiceId int not null , -- InvoiceId foreign key
itemValue money not null ) -- value of invoice line.
CREATE TABLE dbo.#payments
( paymentId int primary key, -- Unique payment id
InvoiceId int not null, -- InvoiceId foreign key
PayAmount money not null
)
-- Example invoice, id #1, with 3 lines, total ammount = 90; id #2, with one line, value 500
INSERT dbo.#sales VALUES
(1, 1, 100),
(2, 1, -50),
(3, 1, 40),
(4, 2, 500) ;
-- Two payments paid towards invoice id#1, 50+40 = 90
-- One payment paid towards invoice id#2, 300
INSERT dbo.#Payments
VALUES (1, 1, 50),
(2, 1, 40),
(3, 2, 300);
-- Expected output should be as follows, for reporting purposes.
/* lineId, invoiceId, value, paymentId, valuePaid, valueUnpaid
2, 1, -50, 1, -50, 0
3, 1, 40, 1, 40, 0
1, 1, 100, 1, 60, 40
1, 1, 100, 2, 40, 0
4, 2, 500, 3, 300, 200 */
WITH inv AS
( SELECT lineId, invoiceId,
itemValue,
SumItemValue = SUM(itemValue) OVER
(PARTITION BY InvoiceId
ORDER BY ItemValue Asc
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
FROM dbo.#Sales
)
, pay AS
( SELECT
PaymentId, InvoiceId, PayAmount as PayAmt,
SumPayAmt = SUM(PayAmount) OVER
(PARTITION BY InvoiceId
ORDER BY PaymentId
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
FROM dbo.#payments
)
SELECT
inv.lineId,
inv.InvoiceId,
inv.itemValue,
pay.PaymentId,
PaymentAllocated =
CASE WHEN SumPayAmt <= SumItemValue - itemValue
OR SumItemValue <= SumPayAmt - PayAmt
THEN 0
ELSE
CASE WHEN SumPayAmt <= SumItemValue THEN SumPayAmt
ELSE SumItemValue END
- CASE WHEN SumPayAmt-PayAmt <= SumItemValue-itemValue
THEN SumItemValue-itemValue
ELSE SumPayAmt-PayAmt END
END
FROM inv JOIN pay
ON inv.InvoiceId = pay.InvoiceId
ORDER BY
inv.InvoiceId,
pay.PaymentId;
当前输出为:
lineId InvoiceId itemValue PaymentId PaymentAllocated
2 1 -50.00 1 0.00
3 1 40.00 1 0.00
1 1 100.00 1 50.00
2 1 -50.00 2 0.00
3 1 40.00 2 0.00
1 1 100.00 2 40.00
4 2 500.00 3 300.00
任何方向将不胜感激。谢谢。
有关分配规则的更多信息:
答案 0 :(得分:0)
最后,我找到了一个非常简单自然的解决方案-根据每次付款在发票总值中所占的百分比来分配付款。
drop table dbo.#sales
drop table dbo.#payments
CREATE TABLE dbo.#sales
( lineId int primary key, -- unique line id
invoiceId int not null , -- InvoiceId foreign key
itemValue money not null ) -- value of invoice line.
CREATE TABLE dbo.#payments
( paymentId int primary key, -- Unique payment id
InvoiceId int not null, -- InvoiceId foreign key
PayAmount money not null
)
-- Example invoice, id #1, with 3 lines, total ammount = 90; id #2, with one line, value 500
INSERT dbo.#sales VALUES
(1, 1, 100),
(2, 1, -50),
(3, 1, 40),
(4, 2, 500) ;
-- Two payments paid towards invoice id#1, 50+40 = 90
-- One payment paid towards invoice id#2, 300
INSERT dbo.#Payments
VALUES (1, 1, 50),
(2, 1, 40),
(3, 2, 300);
SELECT
s.lineId,
s.InvoiceId,
s.itemValue,
p.PayAmount,
p.PaymentId,
round(p.PayAmount / ts.SumItemValue,3) as PaymentPercent,
s.ItemValue * round(p.PayAmount / ts.SumItemValue,3) as AllocatedPayment
FROM dbo.#sales s
LEFT JOIN dbo.#payments p
ON s.InvoiceId = p.InvoiceId
LEFT JOIN (SELECT invoiceId, sum(itemValue) as SumItemValue FROM dbo.#sales GROUP BY invoiceId) ts
ON s.invoiceId = ts.invoiceId
ORDER BY
s.InvoiceId,
p.PaymentId;
重新启动看起来像这样:
lineId InvoiceId itemValue PayAmount PaymentId PaymentPercent AllocatedPayment
1 1 100.00 50.00 1 0.556 55.60
2 1 -50.00 50.00 1 0.556 -27.80
3 1 40.00 50.00 1 0.556 22.24
3 1 40.00 40.00 2 0.444 17.76
2 1 -50.00 40.00 2 0.444 -22.20
1 1 100.00 40.00 2 0.444 44.40
4 2 500.00 300.00 3 0.60 300.00