将多笔付款分配给发票行
Distribute multiple payments to invoice lines
我在为发票行分配付款时遇到问题。
数据如下所示:
发票行table(销售):
lineId invoiceId value
1 1 100
2 1 -50
3 1 40
4 2 500
付款table(付款):
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
问题已在下面的 post 中解决,但如果您的发票值为负数或者您必须将一个发票行拆分为两次付款,则该解决方案不起作用。
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
如有任何指示,我们将不胜感激。谢谢你。
关于分配规则的更多信息:
- 将第一笔付款分配给最小的销售额(即 -50)只是一个
确保所有销售线都能得到付款的惯例。如果我分配
任意或使用另一个规则,第 1 行(值 100)将获得
第一次付款,我会用这条线的所有付款和其余的
发票将保持未分配状态。
- 正如我所说,这只是一个惯例。如果其他人带着
不同的规则有效,没关系。其实结构是
与生产相比简化了 tables:付款也有一个
付款日期、类型……以及正确的分配应该告诉我们什么
发票行在每次付款时支付。
- 付款受系统逻辑限制要小
发票行的总和。好吧,这可能是付款时的情况
较大:发票总额为负(即:-100)。在这种情况下
我们可以在付款 table 范围内插入 -100: 0 的金额
总付款限制为 -100
最后我找到了一个非常简单自然的解决方案——根据每笔付款占发票总价值的百分比来分配付款。
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
我在为发票行分配付款时遇到问题。
数据如下所示:
发票行table(销售):
lineId invoiceId value
1 1 100
2 1 -50
3 1 40
4 2 500
付款table(付款):
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
问题已在下面的 post 中解决,但如果您的发票值为负数或者您必须将一个发票行拆分为两次付款,则该解决方案不起作用。
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
如有任何指示,我们将不胜感激。谢谢你。
关于分配规则的更多信息:
- 将第一笔付款分配给最小的销售额(即 -50)只是一个 确保所有销售线都能得到付款的惯例。如果我分配 任意或使用另一个规则,第 1 行(值 100)将获得 第一次付款,我会用这条线的所有付款和其余的 发票将保持未分配状态。
- 正如我所说,这只是一个惯例。如果其他人带着 不同的规则有效,没关系。其实结构是 与生产相比简化了 tables:付款也有一个 付款日期、类型……以及正确的分配应该告诉我们什么 发票行在每次付款时支付。
- 付款受系统逻辑限制要小 发票行的总和。好吧,这可能是付款时的情况 较大:发票总额为负(即:-100)。在这种情况下 我们可以在付款 table 范围内插入 -100: 0 的金额 总付款限制为 -100
最后我找到了一个非常简单自然的解决方案——根据每笔付款占发票总价值的百分比来分配付款。
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