3.9.5. The Invoice Details

Next, we move on to the details of an invoice. For the qryInvoiceLine dataset, we set the MasterSource property to the datasource that is linked to qryInvoice and the MasterFields property to INVOICE_ID. We specify the following query in the SQL property:

  1. SELECT
  2. invoice_line.invoice_line_id AS invoice_line_id,
  3. invoice_line.invoice_id AS invoice_id,
  4. invoice_line.product_id AS product_id,
  5. product.name AS productname,
  6. invoice_line.quantity AS quantity,
  7. invoice_line.sale_price AS sale_price,
  8. invoice_line.quantity * invoice_line.sale_price AS total
  9. FROM
  10. invoice_line
  11. JOIN product ON product.product_id = invoice_line.product_id
  12. WHERE invoice_line.invoice_id = :invoice_id

As with the invoice header, we will use stored procedures to perform all modifications. Let’s examine the query strings in the CommandText property of the commands that call the stored procedures.

qryAddInvoiceLine.CommandText

  1. EXECUTE PROCEDURE sp_add_invoice_line(
  2. :invoice_id,
  3. :product_id,
  4. :quantity
  5. )

qryEditInvoiceLine.CommandText

  1. EXECUTE PROCEDURE sp_edit_invoice_line(
  2. :invoice_line_id,
  3. :quantity
  4. )

qryDeleteInvoiceLine.CommandText

  1. EXECUTE PROCEDURE sp_delete_invoice_line(
  2. :invoice_line_id
  3. )

As with the header, the form for adding a new record and editing an existing one does not use data-aware visual components. To select a product, we use the TButtonedEdit component again. The code for the on-click handler for the button on the TButtonedEdit object is as follows:

  1. procedure TEditInvoiceLineForm.edtProductRightButtonClick(Sender: TObject);
  2. var
  3. xSelectForm: TGoodsForm;
  4. begin
  5. if FEditMode = emInvoiceLineEdit then
  6. Exit;
  7. xSelectForm := TGoodsForm.Create(Self);
  8. try
  9. xSelectForm.Visible := False;
  10. if xSelectForm.ShowModal = mrOK then
  11. begin
  12. FProductId := xSelectForm.Goods.Product.PRODUCT_ID.Value;
  13. edtProduct.Text := xSelectForm.Goods.Product.NAME.Value;
  14. edtPrice.Text := xSelectForm.Goods.Product.PRICE.AsString;
  15. end;
  16. finally
  17. xSelectForm.Free;
  18. end;
  19. end;

Since we are not using data-aware visual components, again we will need to initialize the product code and name and its price for displaying on the edit form.

  1. procedure TInvoiceForm.actEditInvoiceLineExecute(Sender: TObject);
  2. var
  3. xEditorForm: TEditInvoiceLineForm;
  4. begin
  5. xEditorForm := TEditInvoiceLineForm.Create(Self);
  6. try
  7. xEditorForm.EditMode := emInvoiceLineEdit;
  8. xEditorForm.OnClose := EditInvoiceLineEditorClose;
  9. xEditorForm.Caption := 'Edit invoice line';
  10. xEditorForm.InvoiceLineId := Invoices.InvoiceLine.INVOICE_LINE_ID.Value;
  11. xEditorForm.SetProduct(
  12. Invoices.InvoiceLine.PRODUCT_ID.Value,
  13. Invoices.InvoiceLine.PRODUCTNAME.Value,
  14. Invoices.InvoiceLine.SALE_PRICE.AsCurrency);
  15. xEditorForm.Quantity := Invoices.InvoiceLine.QUANTITY.Value;
  16. xEditorForm.ShowModal;
  17. finally
  18. xEditorForm.Free;
  19. end;
  20. end;
  21. procedure TEditInvoiceLineForm.SetProduct(AProductId: Integer;
  22. AProductName: string; APrice: Currency);
  23. begin
  24. FProductId := AProductId;
  25. edtProduct.Text := AProductName;
  26. edtPrice.Text := CurrToStr(APrice);
  27. end;

We handle adding a new item and editing an existing one in the Close event of the modal form.

  1. procedure TInvoiceForm.actAddInvoiceLineExecute(Sender: TObject);
  2. var
  3. xEditorForm: TEditInvoiceLineForm;
  4. begin
  5. xEditorForm := TEditInvoiceLineForm.Create(Self);
  6. try
  7. xEditorForm.EditMode := emInvoiceLineAdd;
  8. xEditorForm.OnClose := AddInvoiceLineEditorClose;
  9. xEditorForm.Caption := 'Add invoice line';
  10. xEditorForm.Quantity := 1;
  11. xEditorForm.InvoiceId := Invoices.Invoice.INVOICE_ID.Value;
  12. xEditorForm.ShowModal;
  13. finally
  14. xEditorForm.Free;
  15. end;
  16. end;
  17. procedure TInvoiceForm.actEditInvoiceLineExecute(Sender: TObject);
  18. var
  19. xEditorForm: TEditInvoiceLineForm;
  20. begin
  21. xEditorForm := TEditInvoiceLineForm.Create(Self);
  22. try
  23. xEditorForm.EditMode := emInvoiceLineEdit;
  24. xEditorForm.OnClose := EditInvoiceLineEditorClose;
  25. xEditorForm.Caption := 'Edit invoice line';
  26. xEditorForm.InvoiceLineId := Invoices.InvoiceLine.INVOICE_LINE_ID.Value;
  27. xEditorForm.SetProduct(
  28. Invoices.InvoiceLine.PRODUCT_ID.Value,
  29. Invoices.InvoiceLine.PRODUCTNAME.Value,
  30. Invoices.InvoiceLine.SALE_PRICE.AsCurrency);
  31. xEditorForm.Quantity := Invoices.InvoiceLine.QUANTITY.Value;
  32. xEditorForm.ShowModal;
  33. finally
  34. xEditorForm.Free;
  35. end;
  36. end;
  37. procedure TInvoiceForm.AddInvoiceLineEditorClose(Sender: TObject;
  38. var Action: TCloseAction);
  39. var
  40. xEditorForm: TEditInvoiceLineForm;
  41. xCustomerId: Integer;
  42. begin
  43. xEditorForm := TEditInvoiceLineForm(Sender);
  44. if xEditorForm.ModalResult <> mrOK then
  45. begin
  46. Action := caFree;
  47. Exit;
  48. end;
  49. try
  50. Invoices.AddInvoiceLine(xEditorForm.ProductId, xEditorForm.Quantity);
  51. Action := caFree;
  52. except
  53. on E: Exception do
  54. begin
  55. Application.ShowException(E);
  56. // It does not close the window give the user correct the error
  57. Action := caNone;
  58. end;
  59. end;
  60. end;
  61. procedure TInvoiceForm.EditInvoiceLineEditorClose(Sender: TObject;
  62. var Action: TCloseAction);
  63. var
  64. xCustomerId: Integer;
  65. xEditorForm: TEditInvoiceLineForm;
  66. begin
  67. xEditorForm := TEditInvoiceLineForm(Sender);
  68. if xEditorForm.ModalResult <> mrOK then
  69. begin
  70. Action := caFree;
  71. Exit;
  72. end;
  73. try
  74. Invoices.EditInvoiceLine(xEditorForm.Quantity);
  75. Action := caFree;
  76. except
  77. on E: Exception do
  78. begin
  79. Application.ShowException(E);
  80. // It does not close the window give the user correct the error
  81. Action := caNone;
  82. end;
  83. end;
  84. end;

Now let’s take a look at the code for the AddInvoiceLine and EditInvoiceLine procedures of the dmInvoice data module:

  1. procedure TdmInvoice.AddInvoiceLine(AProductId: Integer; AQuantity: Integer);
  2. begin
  3. // We do everything in a short transaction
  4. trWrite.StartTransaction;
  5. try
  6. qryAddInvoiceLine.ParamByName('INVOICE_ID').AsInteger :=
  7. Invoice.INVOICE_ID.Value;
  8. if AProductId = 0 then
  9. raise Exception.Create('Not selected product');
  10. qryAddInvoiceLine.ParamByName('PRODUCT_ID').AsInteger := AProductId;
  11. qryAddInvoiceLine.ParamByName('QUANTITY').AsInteger := AQuantity;
  12. qryAddInvoiceLine.Execute();
  13. trWrite.Commit;
  14. qryInvoice.Refresh;
  15. qryInvoiceLine.Refresh;
  16. except
  17. on E: Exception do
  18. begin
  19. if trWrite.Active then
  20. trWrite.Rollback;
  21. raise;
  22. end;
  23. end;
  24. end;
  25. procedure TdmInvoice.EditInvoiceLine(AQuantity: Integer);
  26. begin
  27. // We do everything in a short transaction
  28. trWrite.StartTransaction;
  29. try
  30. qryEditInvoiceLine.ParamByName('INVOICE_LINE_ID').AsInteger :=
  31. InvoiceLine.INVOICE_LINE_ID.Value;
  32. qryEditInvoiceLine.ParamByName('QUANTITY').AsInteger := AQuantity;
  33. qryEditInvoiceLine.Execute();
  34. trWrite.Commit;
  35. qryInvoice.Refresh;
  36. qryInvoiceLine.Refresh;
  37. except
  38. on E: Exception do
  39. begin
  40. if trWrite.Active then
  41. trWrite.Rollback;
  42. raise;
  43. end;
  44. end;
  45. end;