Мой совет от 4-х лет работы с бэкэндом системы выставления счетов, который кто-то еще разработал: не иметь статуса "в ожидании" на счетах. Это сведет вас с ума.
Проблема с хранением отложенных счетов-фактур в виде обычных счетов-фактур (с флагом / статусом «ожидание») состоит в том, что будут сотни операций / отчетов, которые должны учитывать только опубликованных счетов, которые буквально означает каждый статус за исключением для ожидания. Это означает, что этот статус должен проверяться каждый. не замужем. время. И кто-то забудет. И пройдут недели, прежде чем кто-нибудь это поймет.
Вы можете создать ActiveInvoices
представление со встроенным фильтром ожидания, но это только снимает проблему; кто-то забудет использовать представление вместо таблицы.
Ожидающий счет не является счетом. Это правильно указано в комментариях к вопросу как черновик (или приказ, запрос и т. Д., Все та же концепция). Необходимость изменения этих проектов понятна, безусловно. Итак, вот моя рекомендация.
Сначала создайте черновую таблицу (назовем ее Orders
):
CREATE TABLE Orders
(
OrderID int NOT NULL IDENTITY(1, 1)
CONSTRAINT PK_Orders PRIMARY KEY CLUSTERED,
OrderDate datetime NOT NULL
CONSTRAINT DF_Orders_OrderDate DEFAULT GETDATE(),
OrderStatus tinyint NOT NULL, -- 0 = Active, 1 = Canceled, 2 = Invoiced
...
)
CREATE TABLE OrderDetails
(
-- Optional, if individual details need to be referenced
OrderDetailID int NOT NULL IDENTITY(1, 1)
CONSTRAINT PK_OrderDetails PRIMARY KEY CLUSTERED,
OrderID int NOT NULL
CONSTRAINT FK_OrderDetails_Orders FOREIGN KEY
REFERENCES Orders (OrderID)
ON UPDATE CASCADE
ON DELETE CASCADE,
...
)
CREATE INDEX IX_OrderDetails
ON OrderDetails (OrderID)
INCLUDE (...)
Это ваши основные "черновые" таблицы. Они могут быть изменены. Для отслеживания изменений необходимо создать таблицы истории, в которых есть все столбцы в исходных таблицах Orders
и OrderDetails
, а также столбцы аудита для последнего измененного пользователя, даты и типа изменения (вставка, обновление, или удалить).
Как упоминает Кейд, вы можете использовать AutoAudit для автоматизации большей части этого процесса.
Вам также понадобится триггер, предотвращающий обновления черновиков, которые больше не активны (особенно те, которые были опубликованы и стали счетами). Важно сохранить эти данные непротиворечивыми:
CREATE TRIGGER tr_Orders_ActiveUpdatesOnly
ON Orders
FOR UPDATE, DELETE
AS
IF EXISTS
(
SELECT 1
FROM deleted
WHERE OrderStatus <> 0
)
BEGIN
RAISERROR('Cannot modify a posted/canceled order.', 16, 1)
ROLLBACK
END
Поскольку счета-фактуры представляют собой двухуровневую иерархию, вам необходим аналогичный и несколько более сложный триггер для деталей:
CREATE TRIGGER tr_OrderDetails_ActiveUpdatesOnly
ON OrderDetails
FOR INSERT, UPDATE, DELETE
AS
IF EXISTS
(
SELECT 1
FROM
(
SELECT OrderID FROM deleted
UNION ALL
SELECT OrderID FROM inserted
) d
INNER JOIN Orders o
ON o.OrderID = d.OrderID
WHERE o.OrderStatus <> 0
)
BEGIN
RAISERROR('Cannot change details for a posted/canceled order.', 16, 1)
ROLLBACK
END
Это может показаться большой работой, но теперь вы можете сделать это:
CREATE TABLE Invoices
(
InvoiceID int NOT NULL IDENTITY(1, 1)
CONSTRAINT PK_Invoices PRIMARY KEY CLUSTERED,
OrderID int NOT NULL
CONSTRAINT FK_Invoices_Orders FOREIGN KEY
REFERENCES Orders (OrderID),
InvoiceDate datetime NOT NULL
CONSTRAINT DF_Invoices_Date DEFAULT GETDATE(),
IsPaid bit NOT NULL
CONSTRAINT DF_Invoices_IsPaid DEFAULT 0,
...
)
Видишь, что я здесь сделал? Наши счета-фактуры являются нетронутыми, священными объектами, незапятнанными произвольными изменениями со стороны какого-то первого сотрудника службы поддержки клиентов. Здесь нет риска облажаться. Но, если нам нужно, мы все равно можем узнать всю «историю» счета-фактуры, потому что он ссылается на свой первоначальный Order
- который, если вы помните, мы не разрешаем вносить изменения после того, как он покидает активный состояние.
Это правильно отражает то, что происходит в реальном мире. После того, как счет отправлен / отправлен, он не может быть возвращен. Это там. Если вы хотите отменить его, вы должны опубликовать аннулирование либо в A / R (если ваша система поддерживает такие вещи), либо в качестве отрицательного счета-фактуры, чтобы удовлетворить вашу финансовую отчетность. И если это будет сделано, вы сможете увидеть, что произошло , не копаясь в истории аудита для каждого счета; вам просто нужно посмотреть на сами счета.
Есть еще проблема, которую разработчики должны помнить, чтобы изменить статус заказа после того, как он был выставлен как счет, но мы можем исправить это с помощью триггера:
CREATE TRIGGER tr_Invoices_UpdateOrderStatus
ON Invoices
FOR INSERT
AS
UPDATE Orders
SET OrderStatus = 2
WHERE OrderID IN (SELECT OrderID FROM inserted)
Теперь ваши данные защищены от неосторожных пользователей и даже неосторожных разработчиков. И счета больше не являются двусмысленными; Вам не нужно беспокоиться о появлении ошибок, потому что кто-то забыл проверить статус счета, потому что там нет статуса .
Итак, просто для того, чтобы перефразировать и перефразировать кое-что из этого: Почему я пошел на все эти неприятности только для некоторой истории счета?
Поскольку счета, которые еще не были опубликованы , не являются реальными транзакциями . Это транзакция "состояние" - транзакции в процессе. Они не относятся к вашим транзакционным данным. Держа их отдельно, вы решите много потенциальных будущих проблем.
Отказ от ответственности: Все это говорит из моего личного опыта, и я не видел каждой системы выставления счетов в мире. Я не могу гарантировать со 100% уверенностью, что это подходит для вашего конкретного применения. Я могу только повторить гнездо проблем шершня, которое я видел в результате представления «ожидающих» счетов, из-за смешивания данных состояния с данными транзакций.
Как и в случае с любым другим дизайном, который вы найдете в Интернете, вы должны изучить этот вариант как один из возможных вариантов и оценить, действительно ли он может работать для вас.