Ответ 1
Мой совет около 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% уверенностью, что это подходит для вашего конкретного приложения. Я могу только повторить гнездо шершней проблем, которые я видел в результате представления "ожидающих" счетов, от смешивания данных состояния с транзакционными данными.
Как и в любом другом дизайне, который вы найдете в Интернете, вы должны изучить это как один из возможных вариантов и оценить, действительно ли он может работать для вас.