0

我正在为租赁发票生成创建一些数据库模型。 发票由N个预订时间范围组成。DB架构:版本化的价格模型与发票相关的数据

每个预订都属于价格模式。价格模型是一组确定最终价格的规则(基准价格+季节价格+数量减少+ ...)。

这意味着发票内N张预订的最终价格可能是一个复杂的计算,当然我想跟踪最终价格计算的每个方面以便以后查看发票。

问题是,价格模型可能在未来发生变化。因此,在发票生成时,有两种可能性:

  • (a)绝对不要更改价格模型。通过对其进行版本控制,使其不可变,并从发票中引用具体版本。 (b)将所有价格信息,折扣和额外费用存入发票中。这意味着很多数据,因为发票包含N份预订,部分预订可能在季节价格的范围内。 基本上,我会分解每个预订到它的日子,每天我会有N行计算基本价格,折扣和额外费用。

可能表型号:

Invoice 
    id: int 

InvoiceBooking   # Each booking. One invoice has N bookings 
    id: int 
    invoiceId: int 
    (other data, e.g. guest information) 

InvoiceBookingDay  # Days of a booking. Each booking has N days 
    id: int 
    invoiceBookingId: id 
    date: date 

InvoiceBookingDayPriceItem # Concrete discounts, etc. One days has many items 
    id: int 
    invoiceBookingDayId: int 
    price: decimal 
    title: string 

我的问题是,我应该喜欢,为什么它的方式。

我考虑:

  • 随着溶液(a)中,发票将每一个数据被看作时间使用价格模型信息重新计算。我不喜欢这个,因为算法可以改变。发票的“只读”性质并不自然。 此外,价格模型的版本处理并不是一项简单的任务,用户需要了解版本概念,这增加了应用程序的复杂性。

  • 有了解决方案(b),我生成了一堆嵌套的数据,它增加了架构的复杂性。

您更喜欢哪种方式?我错过了什么吗?

谢谢

回答

1

怎么样 B'

重新计算发票的任何组件并不是最佳实践,特别是在打印组件时。发票和发票细节应该是不可变的,并且您应该能够重新生成它而不用重新计算。

如果您在确定如何达到一定数量时遇到问题,或者如果程序中存在错误,您会很高兴获得详细信息,特别是在计算复杂时。

此外,保留定价模型的历史记录是一个不错的主意,因此您可以验证如何达到一定的价格。你可以简化你的用户。他们不必查看历史记录 - 但是您应该将其更改记录在历史记录中。

2

我推荐了第三种选择。我称之为暂时(时间)版本化,表格的布局非常简单。你没有描述你的定价数据,所以我只是展示一个简单的例子。

Table: DailyPricing 
ID EffDate  Price ... 
A 01/01/2015 17.50 ... 
B 01/01/2015 20.00 ... 
C 01/01/2015 22.50 ... 
B 01/01/2016 19.50 ... 
C 07/01/2016 24.00 ... 

这表明,所有三个价格明细表(A,B和C的任何方法,您使用的价格水平来区分只是代表)分别获得了价格上的1月1日,2015年在2016年1月1日,价格的计划B减少了。 7月份,C计划的价格上涨。

要获得计划的当前价格,查询是这样的:

select dp.Price 
from DailyPricing dp 
where dp.ID = 'A' 
    and dp.Effdate =(
     select Max(dp2.EffDate) 
     from DailyPricing dp2 
     where dp2.ID = dp.ID 
      and dp2.EffDate >= :DateOfInterest); 

DateOfInterest变量将被加载当前的日期/时间。该查询返回当前有效的一个价格。在这种情况下,2015年1月1日设定的价格自从生效以来从未改变过。如果搜索是针对B计划的,则2016年1月1日设定的价格将被退回,C计划则是2016年7月1日设定的价格。这些是针对每个计划设定的最新价格;也就是目前的价格。

这样的查询更有可能在与发票表的连接中,因此您可以执行价格计算。

select ... 
from Invoices i 
join DailyPricing dp 
    on dp.ID = i.ID 
    and dp.Effdate =(
     select Max(dp2.EffDate) 
     from DailyPricing dp2 
     where dp2.ID = dp.ID 
      and dp2.EffDate >= i.InvoiceDate) 
where i.ID = 1234; 

这比简单的查询稍微复杂一点,但您要求更复杂的数据(或者更复杂的数据视图)。但是,此计算可能只会执行一次,并将最终价格存回发票数据或其他地方。

只有在客户进行了一些更改或您正在审核审核后重新检查计算的准确性时,才会再次进行计算。

然而,注意到一些微妙但非常重要的东西。如果上面的查询是针对刚刚创建的发票执行的,那么InvoiceDate将是当前日期,并且返回的价格将是当前价格。但是,如果查询是作为两年前发票上的验证运行的,则发票日期为两年前,且退货价格将为两年前生效的价格。

换句话说,返回当前数据的查询和返回过去数据的查询是相同的查询。

这是因为当前数据和过去的数据保持在同一个表中,只有数据生效日期不同。我认为,这是关于你想要做的最简单的解决方案。