2.2.4. Stored Procedures

Some parts of the business logic will be implemented by means of stored procedures.

Adding a new invoice

The procedure for adding a new invoice is quite simple:

  1. SET TERM ^;
  2. CREATE OR ALTER PROCEDURE SP_ADD_INVOICE (
  3. INVOICE_ID INTEGER,
  4. CUSTOMER_ID INTEGER,
  5. INVOICE_DATE TIMESTAMP = CURRENT_TIMESTAMP)
  6. AS
  7. BEGIN
  8. INSERT INTO INVOICE (
  9. INVOICE_ID,
  10. CUSTOMER_ID,
  11. INVOICE_DATE,
  12. TOTAL_SALE,
  13. PAID
  14. )
  15. VALUES (
  16. :INVOICE_ID,
  17. :CUSTOMER_ID,
  18. :INVOICE_DATE,
  19. 0,
  20. 0
  21. );
  22. END
  23. ^
  24. SET TERM ;^
  25. COMMENT ON PROCEDURE SP_ADD_INVOICE IS
  26. 'Adding Invoice';
  27. COMMENT ON PARAMETER SP_ADD_INVOICE.INVOICE_ID IS
  28. 'Invoice number';
  29. COMMENT ON PARAMETER SP_ADD_INVOICE.CUSTOMER_ID IS
  30. 'Customer Id';
  31. COMMENT ON PARAMETER SP_ADD_INVOICE.INVOICE_DATE IS
  32. 'Date';
  33. GRANT INSERT ON INVOICE TO PROCEDURE SP_ADD_INVOICE;

Editing an invoice

The procedure for editing an invoice is a bit more complicated. We will include a rule to block further editing of an invoice once it is paid. We will create an exception that will be raised if an attempt is made to modify a paid invoice.

  1. CREATE EXCEPTION E_INVOICE_ALREADY_PAYED 'Change is impossible, invoice paid.';
  2. -- The stored procedure for editing an invoice:
  3. SET TERM ^;
  4. CREATE OR ALTER PROCEDURE SP_EDIT_INVOICE (
  5. INVOICE_ID INTEGER,
  6. CUSTOMER_ID INTEGER,
  7. INVOICE_DATE TIMESTAMP)
  8. AS
  9. BEGIN
  10. IF (EXISTS(SELECT *
  11. FROM INVOICE
  12. WHERE INVOICE_ID = :INVOICE_ID
  13. AND PAID = 1)) THEN
  14. EXCEPTION E_INVOICE_ALREADY_PAYED;
  15. UPDATE INVOICE
  16. SET CUSTOMER_ID = :CUSTOMER_ID,
  17. INVOICE_DATE = :INVOICE_DATE
  18. WHERE INVOICE_ID = :INVOICE_ID;
  19. END
  20. ^
  21. SET TERM ;^
  22. COMMENT ON PROCEDURE SP_EDIT_INVOICE IS
  23. 'Editing invoice';
  24. COMMENT ON PARAMETER SP_EDIT_INVOICE.INVOICE_ID IS
  25. 'Invoice number';
  26. COMMENT ON PARAMETER SP_EDIT_INVOICE.CUSTOMER_ID IS
  27. 'Customer Id';
  28. COMMENT ON PARAMETER SP_EDIT_INVOICE.INVOICE_DATE IS
  29. 'Date';
  30. GRANT SELECT,UPDATE ON INVOICE TO PROCEDURE SP_EDIT_INVOICE;

In Firebird 3.0, the USAGE privilege is required for exceptions, so we need to add the following line:

  1. GRANT USAGE ON EXCEPTION E_INVOICE_ALREADY_PAYED TO PROCEDURE SP_EDIT_INVOICE;

Deleting an invoice

The procedure SP_DELETE_INVOICE procedure checks whether the invoice is paid and raises an exception if it is:

  1. SET TERM ^ ;
  2. CREATE OR ALTER PROCEDURE SP_DELETE_INVOICE (
  3. INVOICE_ID INTEGER)
  4. AS
  5. BEGIN
  6. IF (EXISTS(SELECT * FROM INVOICE
  7. WHERE INVOICE_ID = :INVOICE_ID
  8. AND PAID = 1)) THEN
  9. EXCEPTION E_INVOICE_ALREADY_PAYED;
  10. DELETE FROM INVOICE WHERE INVOICE_ID = :INVOICE_ID;
  11. END
  12. ^
  13. SET TERM ;^
  14. COMMENT ON PROCEDURE SP_DELETE_INVOICE IS
  15. 'Deleting invoices';
  16. GRANT SELECT,DELETE ON INVOICE TO PROCEDURE SP_DELETE_INVOICE;

In Firebird 3.0, the USAGE privilege is required for exceptions, so we need to add the following line:

  1. GRANT USAGE ON EXCEPTION E_INVOICE_ALREADY_PAYED TO PROCEDURE SP_DELETE_INVOICE;

Paying an invoice

We will add one more procedure for paying an invoice:

  1. SET TERM ^;
  2. CREATE OR ALTER PROCEDURE SP_PAY_FOR_INVOICE (
  3. INVOICE_ID INTEGER)
  4. AS
  5. BEGIN
  6. IF (EXISTS(SELECT *
  7. FROM INVOICE
  8. WHERE INVOICE_ID = :INVOICE_ID
  9. AND PAID = 1)) THEN
  10. EXCEPTION E_INVOICE_ALREADY_PAYED;
  11. UPDATE INVOICE
  12. SET PAID = 1
  13. WHERE INVOICE_ID = :INVOICE_ID;
  14. END
  15. ^
  16. SET TERM ;^
  17. COMMENT ON PROCEDURE SP_PAY_FOR_INVOICE IS
  18. 'Payment of invoices';
  19. COMMENT ON PARAMETER SP_PAY_FOR_INVOICE.INVOICE_ID IS
  20. 'Invoice number';
  21. GRANT SELECT,UPDATE ON INVOICE TO PROCEDURE SP_PAY_FOR_INVOICE;

In Firebird 3.0, the USAGE privilege is required for exceptions, so we need to add the following line:

  1. GRANT USAGE ON EXCEPTION E_INVOICE_ALREADY_PAYED TO PROCEDURE SP_PAY_FOR_INVOICE;

Invoice Line Items

Procedures for managing invoice items will check whether the invoice is paid and block any attempt to alter the line items of paid invoices. They will also correct the invoice total according to the amount of the product sold and its price.

Adding line items

The procedure for adding a line item to an invoice:

  1. SET TERM ^;
  2. CREATE OR ALTER PROCEDURE SP_ADD_INVOICE_LINE (
  3. INVOICE_ID INTEGER,
  4. PRODUCT_ID INTEGER,
  5. QUANTITY INTEGER)
  6. AS
  7. DECLARE sale_price D_MONEY;
  8. DECLARE paid D_BOOLEAN;
  9. BEGIN
  10. SELECT
  11. paid
  12. FROM
  13. invoice
  14. WHERE
  15. invoice_id = :invoice_id
  16. INTO :paid;
  17. -- It does not allow you to edit already paid invoice.
  18. IF (paid = 1) THEN
  19. EXCEPTION E_INVOICE_ALREADY_PAYED;
  20. SELECT
  21. price
  22. FROM
  23. product
  24. WHERE
  25. product_id = :product_id
  26. INTO :sale_price;
  27. INSERT INTO invoice_line (
  28. invoice_line_id,
  29. invoice_id,
  30. product_id,
  31. quantity,
  32. sale_price)
  33. VALUES (
  34. NEXT VALUE FOR gen_invoice_line_id,
  35. :invoice_id,
  36. :product_id,
  37. :quantity,
  38. :sale_price);
  39. -- Increase the amount of the account.
  40. UPDATE invoice
  41. SET total_sale = COALESCE(total_sale, 0) + :sale_price * :quantity
  42. WHERE invoice_id = :invoice_id;
  43. END
  44. ^
  45. SET TERM ;^
  46. COMMENT ON PROCEDURE SP_ADD_INVOICE_LINE IS
  47. 'Adding line invoices';
  48. COMMENT ON PARAMETER SP_ADD_INVOICE_LINE.INVOICE_ID IS
  49. 'Invoice number';
  50. COMMENT ON PARAMETER SP_ADD_INVOICE_LINE.PRODUCT_ID IS
  51. 'Product Id';
  52. COMMENT ON PARAMETER SP_ADD_INVOICE_LINE.QUANTITY IS
  53. 'Quantity';
  54. GRANT SELECT, UPDATE ON INVOICE TO PROCEDURE SP_ADD_INVOICE_LINE;
  55. GRANT SELECT ON PRODUCT TO PROCEDURE SP_ADD_INVOICE_LINE;
  56. GRANT INSERT ON INVOICE_LINE TO PROCEDURE SP_ADD_INVOICE_LINE;
  57. -- only Firebird 3.0 and above
  58. GRANT USAGE ON EXCEPTION E_INVOICE_ALREADY_PAYED TO PROCEDURE SP_ADD_INVOICE_LINE;
  59. GRANT USAGE ON SEQUENCE GEN_INVOICE_LINE_ID TO PROCEDURE SP_ADD_INVOICE_LINE;

Editing line items

The procedure for editing an invoice line item:

  1. SET TERM ^;
  2. CREATE OR ALTER PROCEDURE SP_EDIT_INVOICE_LINE (
  3. INVOICE_LINE_ID INTEGER,
  4. QUANTITY INTEGER)
  5. AS
  6. DECLARE invoice_id INT;
  7. DECLARE price D_MONEY;
  8. DECLARE paid D_BOOLEAN;
  9. BEGIN
  10. SELECT
  11. product.price,
  12. invoice.invoice_id,
  13. invoice.paid
  14. FROM
  15. invoice_line
  16. JOIN invoice ON invoice.invoice_id = invoice_line.invoice_id
  17. JOIN product ON product.product_id = invoice_line.product_id
  18. WHERE
  19. invoice_line.invoice_line_id = :invoice_line_id
  20. INTO
  21. :price,
  22. :invoice_id,
  23. :paid;
  24. -- It does not allow you to edit an already paid invoice.
  25. IF (paid = 1) THEN
  26. EXCEPTION E_INVOICE_ALREADY_PAYED;
  27. -- Update price and quantity.
  28. UPDATE invoice_line
  29. SET sale_price = :price,
  30. quantity = :quantity
  31. WHERE invoice_line_id = :invoice_line_id;
  32. -- Now update the amount of the account.
  33. MERGE INTO invoice
  34. USING (
  35. SELECT
  36. invoice_id,
  37. SUM(sale_price * quantity) AS total_sale
  38. FROM invoice_line
  39. WHERE invoice_id = :invoice_id
  40. GROUP BY invoice_id) L
  41. ON invoice.invoice_id = L.invoice_id
  42. WHEN MATCHED THEN
  43. UPDATE SET total_sale = L.total_sale;
  44. END
  45. ^
  46. SET TERM ;^
  47. COMMENT ON PROCEDURE SP_EDIT_INVOICE_LINE IS
  48. 'Editing invoice line';
  49. COMMENT ON PARAMETER SP_EDIT_INVOICE_LINE.INVOICE_LINE_ID IS
  50. 'Invoice line id';
  51. COMMENT ON PARAMETER SP_EDIT_INVOICE_LINE.QUANTITY IS
  52. 'Quantity';
  53. GRANT SELECT,UPDATE ON INVOICE_LINE TO PROCEDURE SP_EDIT_INVOICE_LINE;
  54. GRANT SELECT,UPDATE ON INVOICE TO PROCEDURE SP_EDIT_INVOICE_LINE;
  55. GRANT SELECT ON PRODUCT TO PROCEDURE SP_EDIT_INVOICE_LINE;
  56. -- only Firebird 3.0 and above
  57. GRANT USAGE ON EXCEPTION E_INVOICE_ALREADY_PAYED TO PROCEDURE SP_EDIT_INVOICE_LINE;

Deleting line items

The procedure for deleting an invoice line item from an invoice:

  1. SET TERM ^;
  2. CREATE OR ALTER PROCEDURE SP_DELETE_INVOICE_LINE (
  3. INVOICE_LINE_ID INTEGER)
  4. AS
  5. DECLARE invoice_id INT;
  6. DECLARE price D_MONEY;
  7. DECLARE quantity INT;
  8. BEGIN
  9. IF (EXISTS(SELECT *
  10. FROM invoice_line
  11. JOIN invoice ON invoice.invoice_id = invoice_line.invoice_id
  12. WHERE invoice.paid = 1
  13. AND invoice_line.invoice_line_id = :invoice_line_id)) THEN
  14. EXCEPTION E_INVOICE_ALREADY_PAYED;
  15. DELETE FROM invoice_line
  16. WHERE invoice_line.invoice_line_id = :invoice_line_id
  17. RETURNING invoice_id, quantity, sale_price
  18. INTO invoice_id, quantity, price;
  19. -- Reduce the amount of the account.
  20. UPDATE invoice
  21. SET total_sale = total_sale - :quantity * :price
  22. WHERE invoice_id = :invoice_id;
  23. END
  24. ^
  25. SET TERM ;^
  26. COMMENT ON PROCEDURE SP_DELETE_INVOICE_LINE IS
  27. 'Deleting invoice item';
  28. COMMENT ON PARAMETER SP_DELETE_INVOICE_LINE.INVOICE_LINE_ID IS
  29. 'Code invoice item';
  30. Privileges for Procedures
  31. GRANT SELECT,DELETE ON INVOICE_LINE TO PROCEDURE SP_DELETE_INVOICE_LINE;
  32. GRANT SELECT,UPDATE ON INVOICE TO PROCEDURE SP_DELETE_INVOICE_LINE;
  33. -- only Firebird 3.0 and above
  34. GRANT USAGE ON EXCEPTION E_INVOICE_ALREADY_PAYED TO PROCEDURE SP_DELETE_INVOICE_LINE;