home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Runimage / Delphi50 / Demos / Db / IbMastApp / DATAMOD.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-08-11  |  23.1 KB  |  761 lines

  1. unit DataMod;
  2.  
  3. { See the comments in MAIN.PAS for information about this project }
  4.  
  5. interface
  6.  
  7. uses
  8.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  9.   DB, IBQuery, IBCustomDataSet, IBTable, IBDatabase, IB;
  10.  
  11. type
  12.   TMastData = class(TDataModule)
  13.     Database: TIBDatabase;
  14.     NextCust: TIBTable;
  15.     NextCustNewCust: TFloatField;
  16.     Parts: TIBTable;
  17.     PartsPartNo: TFloatField;
  18.     PartsDescription: TIBStringField;
  19.     PartsOnHand: TFloatField;
  20.     PartsOnOrder: TFloatField;
  21.     PartsQuery: TIBQuery;
  22.     PartsQueryPartNo: TFloatField;
  23.     PartsQueryDescription: TIBStringField;
  24.     PartsQueryOnHand: TFloatField;
  25.     PartsQueryOnOrder: TFloatField;
  26.     Vendors: TIBTable;
  27.     PartsVendorNo: TFloatField;
  28.     PartsCost: TCurrencyField;
  29.     PartsListPrice: TCurrencyField;
  30.     PartsBackOrd: TBooleanField;
  31.     PartsQueryVendorNo: TFloatField;
  32.     PartsQueryCost: TCurrencyField;
  33.     PartsQueryListPrice: TCurrencyField;
  34.     PartsQueryBackOrd: TBooleanField;
  35.     Orders: TIBTable;
  36.     OrdersOrderNo: TFloatField;
  37.     OrdersCustNo: TFloatField;
  38.     OrdersSaleDate: TDateTimeField;
  39.     OrdersShipDate: TDateTimeField;
  40.     OrdersShipToContact: TIBStringField;
  41.     OrdersShipToAddr1: TIBStringField;
  42.     OrdersShipToAddr2: TIBStringField;
  43.     OrdersShipToCity: TIBStringField;
  44.     OrdersShipToState: TIBStringField;
  45.     OrdersShipToZip: TIBStringField;
  46.     OrdersShipToCountry: TIBStringField;
  47.     OrdersShipToPhone: TIBStringField;
  48.     OrdersShipVIA: TIBStringField;
  49.     OrdersPO: TIBStringField;
  50.     OrdersEmpNo: TIntegerField;
  51.     OrdersTerms: TIBStringField;
  52.     OrdersPaymentMethod: TIBStringField;
  53.     OrdersItemsTotal: TCurrencyField;
  54.     OrdersTaxRate: TFloatField;
  55.     OrdersTaxTotal: TCurrencyField;
  56.     OrdersFreight: TCurrencyField;
  57.     OrdersAmountPaid: TCurrencyField;
  58.     OrdersAmountDue: TCurrencyField;
  59.     CustByOrd: TIBTable;
  60.     CustByOrdCustNo: TFloatField;
  61.     CustByOrdCompany: TIBStringField;
  62.     CustByOrdAddr1: TIBStringField;
  63.     CustByOrdAddr2: TIBStringField;
  64.     CustByOrdCity: TIBStringField;
  65.     CustByOrdState: TIBStringField;
  66.     CustByOrdZip: TIBStringField;
  67.     CustByOrdCountry: TIBStringField;
  68.     CustByOrdPhone: TIBStringField;
  69.     CustByOrdFAX: TIBStringField;
  70.     CustByOrdTaxRate: TFloatField;
  71.     CustByOrdContact: TIBStringField;
  72.     CustByOrdLastInvoiceDate: TDateTimeField;
  73.     Items: TIBTable;
  74.     ItemsItemNo: TFloatField;
  75.     ItemsOrderNo: TFloatField;
  76.     ItemsDescription: TIBStringField;
  77.     ItemsSellPrice: TCurrencyField;
  78.     ItemsQty: TIntegerField;
  79.     ItemsDiscount: TFloatField;
  80.     ItemsExtPrice: TCurrencyField;
  81.     NextOrd: TIBTable;
  82.     NextOrdNewKey: TFloatField;
  83.     Emps: TIBTable;
  84.     EmpsEmpNo: TIntegerField;
  85.     EmpsFullName: TIBStringField;
  86.     EmpsLastName: TIBStringField;
  87.     EmpsFirstName: TIBStringField;
  88.     EmpsPhoneExt: TIBStringField;
  89.     EmpsHireDate: TDateTimeField;
  90.     EmpsSalary: TFloatField;
  91.     LastItemQuery: TIBQuery;
  92.     Cust: TIBTable;
  93.     CustCustNo: TFloatField;
  94.     CustCompany: TIBStringField;
  95.     CustPhone: TIBStringField;
  96.     CustLastInvoiceDate: TDateTimeField;
  97.     CustQuery: TIBQuery;
  98.     CustQueryCustNo: TFloatField;
  99.     CustQueryCompany: TIBStringField;
  100.     CustQueryPhone: TIBStringField;
  101.     CustQueryLastInvoiceDate: TDateTimeField;
  102.     OrdByCust: TIBTable;
  103.     OrdByCustOrderNo: TFloatField;
  104.     OrdByCustCustNo: TFloatField;
  105.     OrdByCustSaleDate: TDateTimeField;
  106.     OrdByCustShipDate: TDateTimeField;
  107.     OrdByCustItemsTotal: TCurrencyField;
  108.     OrdByCustTaxRate: TFloatField;
  109.     OrdByCustFreight: TCurrencyField;
  110.     OrdByCustAmountPaid: TCurrencyField;
  111.     OrdByCustAmountDue: TCurrencyField;
  112.     ItemsPartNo: TFloatField;
  113.     CustAddr1: TIBStringField;
  114.     CustAddr2: TIBStringField;
  115.     CustCity: TIBStringField;
  116.     CustState: TIBStringField;
  117.     CustZip: TIBStringField;
  118.     CustCountry: TIBStringField;
  119.     CustFAX: TIBStringField;
  120.     CustTaxRate: TFloatField;
  121.     CustContact: TIBStringField;
  122.     CustByComp: TIBTable;
  123.     CustByLastInvQuery: TIBQuery;
  124.     CustByLastInvQueryCustNo: TFloatField;
  125.     CustByLastInvQueryCompany: TIBStringField;
  126.     CustByLastInvQueryAddr1: TIBStringField;
  127.     CustByLastInvQueryAddr2: TIBStringField;
  128.     CustByLastInvQueryCity: TIBStringField;
  129.     CustByLastInvQueryState: TIBStringField;
  130.     CustByLastInvQueryZip: TIBStringField;
  131.     CustByLastInvQueryCountry: TIBStringField;
  132.     CustByLastInvQueryPhone: TIBStringField;
  133.     CustByLastInvQueryFAX: TIBStringField;
  134.     CustByLastInvQueryTaxRate: TFloatField;
  135.     CustByLastInvQueryContact: TIBStringField;
  136.     CustByLastInvQueryLastInvoiceDate: TDateTimeField;
  137.     OrdersByDateQuery: TIBQuery;
  138.     OrdersSalesPerson: TIBStringField;
  139.     OrdersByDateQueryOrderNo: TFloatField;
  140.     OrdersByDateQueryCustNo: TFloatField;
  141.     OrdersByDateQuerySaleDate: TDateTimeField;
  142.     OrdersByDateQueryShipDate: TDateTimeField;
  143.     OrdersByDateQueryEmpNo: TIntegerField;
  144.     OrdersByDateQueryShipToContact: TIBStringField;
  145.     OrdersByDateQueryShipToAddr1: TIBStringField;
  146.     OrdersByDateQueryShipToAddr2: TIBStringField;
  147.     OrdersByDateQueryShipToCity: TIBStringField;
  148.     OrdersByDateQueryShipToState: TIBStringField;
  149.     OrdersByDateQueryShipToZip: TIBStringField;
  150.     OrdersByDateQueryShipToCountry: TIBStringField;
  151.     OrdersByDateQueryShipToPhone: TIBStringField;
  152.     OrdersByDateQueryShipVIA: TIBStringField;
  153.     OrdersByDateQueryPO: TIBStringField;
  154.     OrdersByDateQueryTerms: TIBStringField;
  155.     OrdersByDateQueryPaymentMethod: TIBStringField;
  156.     OrdersByDateQueryItemsTotal: TCurrencyField;
  157.     OrdersByDateQueryTaxRate: TFloatField;
  158.     OrdersByDateQueryFreight: TCurrencyField;
  159.     OrdersByDateQueryAmountPaid: TCurrencyField;
  160.     OrdersByDateQueryCompany: TIBStringField;
  161.     Transaction: TIBTransaction;
  162.     OpenDialog: TOpenDialog;
  163.     OrdersSource: TDataSource;
  164.     CustByOrdSrc: TDataSource;
  165.     ItemsSource: TDataSource;
  166.     PartsSource: TDataSource;
  167.     EmpsSource: TDataSource;
  168.     VendorSource: TDataSource;
  169.     CustSource: TDataSource;
  170.     CustMasterSrc: TDataSource;
  171.     OrdByCustSrc: TDataSource;
  172.     CustByCompSrc: TDataSource;
  173.     procedure PartsBeforeOpen(DataSet: TDataSet);
  174.     procedure PartsCalcFields(DataSet: TDataSet);
  175.     procedure PartsQueryCalcFields(DataSet: TDataSet);
  176.     procedure OrdersAfterCancel(DataSet: TDataSet);
  177.     procedure OrdersAfterPost(DataSet: TDataSet);
  178.     procedure OrdersBeforeCancel(DataSet: TDataSet);
  179.     procedure OrdersBeforeClose(DataSet: TDataSet);
  180.     procedure OrdersBeforeDelete(DataSet: TDataSet);
  181.     procedure OrdersBeforeInsert(DataSet: TDataSet);
  182.     procedure OrdersBeforeOpen(DataSet: TDataSet);
  183.     procedure OrdersCalcFields(DataSet: TDataSet);
  184.     procedure OrdersNewRecord(DataSet: TDataSet);
  185.     procedure ItemsAfterDelete(DataSet: TDataSet);
  186.     procedure ItemsAfterPost(DataSet: TDataSet);
  187.     procedure EnsureOrdersEdit(DataSet: TDataSet);
  188.     procedure ItemsBeforeEdit(DataSet: TDataSet);
  189.     procedure ItemsBeforeOpen(DataSet: TDataSet);
  190.     procedure ItemsBeforePost(DataSet: TDataSet);
  191.     procedure ItemsCalcFields(DataSet: TDataSet);
  192.     procedure ItemsNewRecord(DataSet: TDataSet);
  193.     procedure EmpsCalcFields(DataSet: TDataSet);
  194.     procedure OrdersCustNoChange(Sender: TField);
  195.     procedure ItemsQtyValidate(Sender: TField);
  196.     procedure OrdersFreightValidate(Sender: TField);
  197.     procedure ItemsPartNoValidate(Sender: TField);
  198.     procedure OrdersSaleDateValidate(Sender: TField);
  199.     procedure CustBeforeOpen(DataSet: TDataSet);
  200.     procedure OrdByCustCalcFields(DataSet: TDataSet);
  201.     procedure CustBeforePost(DataSet: TDataSet);
  202.     procedure OrdersAfterDelete(DataSet: TDataSet);
  203.     procedure OrdersBeforeEdit(DataSet: TDataSet);
  204.     procedure EditUpdateError(DataSet: TIBDataSet; E: EIBInterbaseError;
  205.       UpdateKind: TUpdateKind; var UpdateAction: TIBUpdateAction);
  206.     procedure MastDataCreate(Sender: TObject);
  207.   private
  208.     PrevPartNo: Double;       { remembers Item's previous part# }
  209.     PrevQty: Longint;         { remembers Item's previous qty }
  210.     DeletingItems: Boolean;   { suppress totals calc. if deleting items }
  211.     FItemNo: Integer;
  212.     function DataDirectory: string;
  213.     //procedure SetDatabaseAlias(AliasName: string);
  214.     procedure UpdateTotals;
  215.     procedure DeleteItems;
  216.   public
  217.     //procedure UseLocalData;
  218.     //procedure UseRemoteData;
  219.     function DataSetApplyUpdates(DataSet: TDataSet; Apply: Boolean): Boolean;
  220.   end;
  221.  
  222. function Confirm(Msg: string): Boolean;
  223.  
  224. var
  225.   MastData: TMastData;
  226.  
  227. implementation
  228.  
  229. {$R *.DFM}
  230.  
  231. { Utility Functions }
  232.  
  233. function Confirm(Msg: string): Boolean;
  234. begin
  235.   Result := MessageDlg(Msg, mtConfirmation, mbYesNoCancel, 0) = mrYes;
  236. end;
  237.  
  238. function TMastData.DataDirectory: string;
  239. begin
  240.   { Assume data is in ..\..\..\..\..\Common Files\Borland Shared\DATA\data relative to where we are }
  241.   Result := ExtractFilePath(ParamStr(0));
  242.   //Result := ExpandFileName(Result + '..\..\DATA\');
  243.   Result := ExpandFileName(Result + '..\..\..\..\..\Common Files\Borland Shared\DATA\');
  244. end;
  245.  
  246. { This function switches the database to a different alias }
  247.  
  248. //Shouldn't need this function any more
  249. {
  250. procedure TMastIBData.SetDatabaseAlias(AliasName: string);
  251. begin
  252.   Screen.Cursor := crHourGlass;
  253.   try
  254.     Database.Close;
  255.     Database.AliasName := AliasName;
  256.     Database.Open;
  257.   finally
  258.     Screen.Cursor := crDefault;
  259.   end;
  260. end;
  261. }
  262. { Create an alias for the local data if needed, then swith the Database
  263.   to use it }
  264. (*
  265. procedure TMastIBData.UseLocalData;
  266. var
  267.   DataDir: string;
  268. begin
  269.   { See if the target alias exists, if not then add it. }
  270.   if not Session.IsAlias('DBDEMOS') then
  271.   begin
  272.     DataDir := DataDirectory;
  273.     if not FileExists(DataDir+'ORDERS.DB') then
  274.       raise Exception.Create('Cannot locate Paradox data files');
  275.     Session.AddStandardAlias('DBDEMOS', DataDir, 'PARADOX');
  276.   end;
  277.   //Old Code
  278.   //SetDatabaseAlias('DBDEMOS');
  279.   //Change all DataSources to point to Paradox Tables
  280. end;
  281. *)
  282. { Create an alias to point to the MastSQL.GDB file if needed }
  283. (*
  284. procedure TMastData.UseRemoteData;
  285. var
  286.   //Params: TStringList;
  287.   DataFile: string;
  288.   iCounter: integer;
  289. begin
  290.   { See if the alias exists.  if not then add it. }
  291.   if not Session.IsAlias('MASTSQL') then
  292.   begin
  293.     DataFile := DataDirectory + 'MASTSQL.GDB';
  294.     if not FileExists(DataFile) then
  295.       raise Exception.Create('Cannot locate Interbase data file: MASTSQL.GDB');
  296.     Params := TStringList.create;
  297.     try
  298.       Params.Values['SERVER NAME'] := DataFile;
  299.       Params.Values['USER NAME'] := 'SYSDBA';
  300.       Session.AddAlias('MASTSQL', 'INTRBASE', Params);
  301.     finally
  302.        Params.Free;
  303.     end;
  304.   end;
  305.   SetDatabaseAlias('MASTSQL');
  306.   //if the Database is open, then remote is already in use and we need to do nothing
  307.   if Database.Connected then
  308.     exit;
  309.   DataFile := DataDirectory + 'MASTSQL.GDB';
  310.   if not FileExists(DataFile) then
  311.       raise Exception.Create('Cannot locate Interbase data file: MASTSQL.GDB');
  312.   Database.DatabaseName := DataFile;
  313.   Database.Params.Clear;
  314.   Database.Params.Add('user_name=SYSDBA');
  315.   Database.Params.Add('Password=MASTERKEY');
  316.   //Set the DataSet for all TDataSources to point to Interbase table
  317.   for iCounter := 0 to ComponentCount -1 do
  318.   begin
  319.     if (Components[iCounter] is TDataSource) then
  320.       //Components[iCounter].DataSource =
  321.   end;
  322.   MastData.Database.Close;
  323.   Database.Open;
  324. end;
  325. *)
  326. { Event Handlers }
  327.  
  328. procedure TMastData.PartsBeforeOpen(DataSet: TDataSet);
  329. begin
  330.   Vendors.Open;
  331. end;
  332.  
  333. procedure TMastData.PartsCalcFields(DataSet: TDataSet);
  334. begin
  335.   PartsBackOrd.Value := PartsOnOrder.Value > PartsOnHand.Value;
  336. end;
  337.  
  338. procedure TMastData.PartsQueryCalcFields(DataSet: TDataSet);
  339. begin
  340.   PartsQueryBackOrd.Value := PartsOnOrder.Value > PartsOnHand.Value;
  341. end;
  342.  
  343. { If user cancels the updates to the orders table, cancel the updates to
  344.   the line items as well }
  345.  
  346. procedure TMastData.OrdersAfterCancel(DataSet: TDataSet);
  347. begin
  348.   Cust.CancelUpdates;
  349.   Parts.CancelUpdates;
  350.   Items.CancelUpdates;
  351.   Orders.CancelUpdates;
  352. end;
  353.  
  354. procedure TMastData.OrdersAfterDelete(DataSet: TDataSet);
  355. begin
  356.   Database.ApplyUpdates([Cust, Parts, Items, Orders]);
  357.   Transaction.CommitRetaining;
  358. end;
  359.  
  360. { Order Entry }
  361.  
  362. { Post new LastInvoiceDate to CUST table. }
  363.  
  364. procedure TMastData.OrdersAfterPost(DataSet: TDataSet);
  365.  
  366. begin
  367.   if Cust.Locate('CustNo', OrdersCustNo.Value, []) and
  368.     (CustLastInvoiceDate.Value < OrdersShipDate.Value) then
  369.   begin
  370.     Cust.Edit;
  371.     CustLastInvoiceDate.Value := OrdersShipDate.Value;
  372.     Cust.Post;
  373.   end;
  374.   Database.ApplyUpdates([Orders, Items, Parts, Cust]);
  375.   Transaction.CommitRetaining;
  376. end;
  377.  
  378. procedure TMastData.OrdersBeforeCancel(DataSet: TDataSet);
  379. begin
  380.   if (Orders.State = dsInsert) and not (Items.BOF and Items.EOF) then
  381.     if not Confirm('Cancel order being inserted and delete all line items?') then
  382.       Abort;
  383. end;
  384.  
  385. procedure TMastData.OrdersBeforeClose(DataSet: TDataSet);
  386. begin
  387.   Items.Close;
  388.   Emps.Close;
  389.   CustByOrd.Close;
  390. end;
  391.  
  392. procedure TMastData.OrdersBeforeDelete(DataSet: TDataSet);
  393. begin
  394.   if not Confirm('Delete order and line items?') then
  395.     Abort
  396.   else
  397.     DeleteItems;
  398. end;
  399.  
  400. procedure TMastData.OrdersBeforeInsert(DataSet: TDataSet);
  401. begin
  402.   if Orders.State in dsEditModes then
  403.   begin
  404.     if Confirm('An order is being processed.  Save changes and start a new one?') then
  405.       Orders.Post
  406.     else
  407.       Abort;
  408.   end;
  409.   FItemNo := 1;
  410. end;
  411.  
  412. procedure TMastData.OrdersBeforeOpen(DataSet: TDataSet);
  413. begin
  414.   CustByComp.Open;
  415.   CustByOrd.Open;
  416.   Cust.Open;
  417.   Emps.Open;
  418.   Items.Open;
  419. end;
  420.  
  421. { Calculate the order's tax totals and amount due }
  422.  
  423. procedure TMastData.OrdersCalcFields(DataSet: TDataSet);
  424. begin
  425.   OrdersTaxTotal.Value := OrdersItemsTotal.Value * (OrdersTaxRate.Value / 100);
  426.   OrdersAmountDue.Value := OrdersItemsTotal.Value + OrdersTaxTotal.Value +
  427.     OrdersFreight.Value - OrdersAmountPaid.Value;
  428. end;
  429.  
  430. { Inititializes the record values as a result of an Orders.Insert. }
  431.  
  432. procedure TMastData.OrdersNewRecord(DataSet: TDataSet);
  433. begin
  434.  
  435.   { Get the Next Order Value from the NextOrd Table }
  436.  
  437.   with NextOrd do
  438.   begin
  439.     Open;
  440.     try
  441.       Edit;
  442.       OrdersOrderNo.Value := NextOrdNewKey.Value;
  443.       NextOrdNewKey.Value := NextOrdNewKey.Value + 1;
  444.       Post;
  445.     finally
  446.       Close;
  447.     end;
  448.   end;
  449.   OrdersSaleDate.Value := Date;
  450.   OrdersShipVia.Value := 'UPS';
  451.   OrdersTerms.Value := 'net 30';
  452.   OrdersPaymentMethod.Value := 'Check';
  453.   OrdersItemsTotal.Value := 0;
  454.   OrdersTaxRate.Value := 0;
  455.   OrdersFreight.Value := 0;
  456.   OrdersAmountPaid.Value := 0;
  457. end;
  458.  
  459. procedure TMastData.ItemsAfterDelete(DataSet: TDataSet);
  460. begin
  461.   UpdateTotals;
  462. end;
  463.  
  464. { Update the order totals and the Parts table }
  465.  
  466. procedure TMastData.ItemsAfterPost(DataSet: TDataSet);
  467.  
  468.   { Reduce/increase Parts table's OnOrder field }
  469.  
  470.   procedure UpdateParts(PartNo: Double; Qty : Longint);
  471.   begin
  472.     if (PartNo > 0) and (Qty <> 0) then
  473.     try
  474.       if not Parts.Locate('PartNo', PartNo, []) then Abort;
  475.       Parts.Edit;
  476.       PartsOnOrder.Value := PartsOnOrder.Value + Qty;
  477.       Parts.Post;
  478.     except
  479.       on E: Exception do
  480.         ShowMessage(Format('Error updating parts table for PartNo: %d', [PartNo]));
  481.     end;
  482.   end;
  483.  
  484. begin
  485.   { Maintain next available item number }
  486.   Inc(FItemNo);
  487.   UpdateTotals;
  488.   if not ((PrevPartNo = ItemsPartNo.Value) and (PrevQty = ItemsQty.Value)) then
  489.   begin
  490.    { Reduce previous Part#'s OnOrder field by previous Qty }
  491.     UpdateParts(PrevPartNo, -PrevQty);
  492.    { Increase new Part#'s OnOrder field by previous Qty }
  493.     UpdateParts(ItemsPartNo.Value, ItemsQty.Value);
  494.   end;
  495. end;
  496.  
  497. {  When a change to the detail table affects a field in the master, always make
  498.   sure the master (orders) table is in edit or insert mode before allowing the
  499.   detail table to be modified. }
  500.  
  501. procedure TMastData.EnsureOrdersEdit(DataSet: TDataSet);
  502. begin
  503.   Orders.Edit;
  504. end;
  505.  
  506. { Remember previous PartNo and Qty for updating Parts.OnOrder after post.
  507.   When a change to the detail table affects a field in the master, always make
  508.   sure the master table is in edit or insert mode before allowing the
  509.   detail table to be modified. }
  510.  
  511. procedure TMastData.ItemsBeforeEdit(DataSet: TDataSet);
  512. begin
  513.   Orders.Edit;
  514.   PrevPartNo := ItemsPartNo.Value;
  515.   PrevQty := ItemsQty.Value;
  516. end;
  517.  
  518. { Make sure the Parts table opens before the Items table, since there are
  519.   lookups which depend on it. }
  520.  
  521. procedure TMastData.ItemsBeforeOpen(DataSet: TDataSet);
  522. begin
  523.   Parts.Open;
  524. end;
  525.  
  526. { Complete the item's key by initializing its NextItemNo field }
  527.  
  528. procedure TMastData.ItemsBeforePost(DataSet: TDataSet);
  529. begin
  530.   ItemsItemNo.Value := FItemNo;
  531. end;
  532.  
  533. { Lookup PartNo info for the item; calculate its extended price }
  534.  
  535. procedure TMastData.ItemsCalcFields(DataSet: TDataSet);
  536. begin
  537.   ItemsExtPrice.Value := ItemsQty.Value *
  538.     ItemsSellPrice.Value * (100 - ItemsDiscount.Value) / 100;
  539. end;
  540.  
  541. { New item. Zero the "prev" buckets, initialize the key }
  542.  
  543. procedure TMastData.ItemsNewRecord(DataSet: TDataSet);
  544. begin
  545.   PrevPartNo := 0;
  546.   PrevQty := 0;
  547.   ItemsOrderNo.Value := OrdersOrderNo.Value;
  548.   ItemsQty.Value := 1;
  549.   ItemsDiscount.Value := 0;
  550. end;
  551.  
  552. { Concatenate last name + first name for the order's SoldBy DBLookupCombo }
  553.  
  554. procedure TMastData.EmpsCalcFields(DataSet: TDataSet);
  555. begin
  556.   EmpsFullName.Value := Format('%s, %s', [EmpsLastName.Value, EmpsFirstName.Value]);
  557. end;
  558.  
  559. procedure TMastData.DeleteItems;
  560. begin
  561.   DeletingItems := True;    { suppress recalc of totals during delete }
  562.   Items.DisableControls;    { for faster table traversal }
  563.   try
  564.     Items.First;
  565.     while not Items.EOF do Items.Delete;
  566.   finally
  567.     DeletingItems := False;
  568.     Items.EnableControls;   { always re-enable controls after disabling }
  569.   end;
  570. end;
  571.  
  572. { Steps through Items and gathers sum of ExtPrice. After OrdersItemsTotal
  573.   is calculated, OrdersCalcFields is automatically called (which
  574.   updates other calculated fields. }
  575.   
  576. procedure TMastData.UpdateTotals;
  577. var
  578.   TempTotal: Extended;
  579.   PrevRecord: TBookmark;
  580. begin
  581.   if DeletingItems then Exit;        { don't calculate if deleting all items }
  582.   PrevRecord := Items.GetBookmark;    { returns nil if table is empty }
  583.   try
  584.     Items.DisableControls;
  585.     Items.First;
  586.     TempTotal := 0;            { use temp for efficiency }
  587.     while not Items.EOF do
  588.     begin
  589.       TempTotal := TempTotal + ItemsExtPrice.Value;
  590.       Items.Next;
  591.     end;
  592.     OrdersItemsTotal.Value := TempTotal;
  593.   finally
  594.      Items.EnableControls;
  595.      if PrevRecord <> nil then
  596.      begin
  597.        Items.GoToBookmark(PrevRecord);
  598.        Items.FreeBookmark(PrevRecord);
  599.      end;
  600.   end;
  601. end;
  602.  
  603. procedure TMastData.OrdersCustNoChange(Sender: TField);
  604. var
  605.   TaxRate: Variant;
  606. begin
  607.   OrdersShipToContact.Value := '';
  608.   OrdersShipToPhone.Value := '';
  609.   OrdersShipToAddr1.Value := '';
  610.   OrdersShipToAddr2.Value := '';
  611.   OrdersShipToCity.Value := '';
  612.   OrdersShipToState.Value := '';
  613.   OrdersShipToZip.Value := '';
  614.   OrdersShipToCountry.Value := '';
  615.   TaxRate := Cust.Lookup('CustNo', OrdersCustNo.Value, 'TaxRate');
  616.   if not VarIsNull(TaxRate) then
  617.     OrdersTaxRate.Value := TaxRate;
  618. end;
  619.  
  620. { Alternatively, could set the Qty field's Min and Max values in code
  621.   or in the Object Inspector. }
  622.  
  623. procedure TMastData.ItemsQtyValidate(Sender: TField);
  624. begin
  625.   if ItemsQty.Value < 1 then
  626.     raise Exception.Create('Must specify quantity');
  627. end;
  628.  
  629. { Alternatively, could set the Freight field's Min and Max values in code
  630.   or in the Object Inspector. }
  631.  
  632. procedure TMastData.OrdersFreightValidate(Sender: TField);
  633. begin
  634.   if OrdersFreight.Value < 0 then
  635.     raise Exception.Create('Freight cannot be less than zero');
  636. end;
  637.  
  638. procedure TMastData.ItemsPartNoValidate(Sender: TField);
  639. begin
  640.   if not Parts.Locate('PartNo', ItemsPartNo.Value, []) then
  641.     raise Exception.Create('You must specify a valid PartNo');
  642. end;
  643.  
  644. procedure TMastData.OrdersSaleDateValidate(Sender: TField);
  645. begin
  646.   if OrdersSaleDate.Value > Now then
  647.     raise Exception.Create('Cannot enter a future date');
  648. end;
  649.  
  650. { Browse Customers }
  651.  
  652. procedure TMastData.CustBeforeOpen(DataSet: TDataSet);
  653. begin
  654.   OrdByCust.Open;
  655. end;
  656.  
  657. procedure TMastData.OrdByCustCalcFields(DataSet: TDataSet);
  658. begin
  659.   OrdByCustAmountDue.Value := OrdByCustItemsTotal.Value +
  660.     OrdByCustItemsTotal.Value * OrdByCustTaxRate.Value / 100 +
  661.     OrdByCustFreight.Value - OrdByCustAmountPaid.Value;
  662. end;
  663.  
  664. { Get the next available customer number from the NextCust table }
  665.  
  666. procedure TMastData.CustBeforePost(DataSet: TDataSet);
  667. begin
  668.   if Cust.State = dsInsert then
  669.     with NextCust do
  670.     begin
  671.       Open;
  672.       try
  673.         Edit;
  674.         CustCustNo.Value := NextCustNewCust.Value;
  675.         NextCustNewCust.Value := NextCustNewCust.Value + 1;
  676.         Post;
  677.       finally
  678.         Close;
  679.       end;
  680.     end;
  681. end;
  682.  
  683. function TMastData.DataSetApplyUpdates(DataSet: TDataSet; Apply: Boolean): Boolean;
  684. begin
  685.   Result := True;
  686.   with TIBCustomDataSet(DataSet) do
  687.   begin
  688.     if (State in dsEditModes) or UpdatesPending then
  689.     begin
  690.       if Apply then
  691.       begin
  692.         Database.ApplyUpdates([DataSet as TIBCustomDataSet]);
  693.         Transaction.CommitRetaining;
  694.        { Always call CancelUpdates to remove any discard changes }
  695.         CancelUpdates;
  696.       end
  697.       else
  698.       begin
  699.         if (MessageDlg('Unsaved changes, exit anyway?', mtConfirmation,
  700.           [mbYes, mbCancel], 0) = mrYes) then
  701.           CancelUpdates
  702.         else
  703.           Result := False;
  704.       end;
  705.     end;
  706.   end;
  707. end;
  708.  
  709. { Determine the next available ItemNo for this order }
  710.  
  711. procedure TMastData.OrdersBeforeEdit(DataSet: TDataSet);
  712. begin
  713.   LastItemQuery.Close;
  714.   LastItemQuery.Open;
  715.   { SQL servers return Null for some aggregates if no items are present }
  716.   with LastItemQuery.Fields[0] do
  717.     if IsNull then FItemNo := 1
  718.     else FItemNo := AsInteger + 1;
  719. end;
  720.  
  721. procedure TMastData.EditUpdateError(DataSet: TIBDataSet; E: EIBInterbaseError;
  722.   UpdateKind: TUpdateKind; var UpdateAction: TIBUpdateAction);
  723. var
  724.   Key: Variant;
  725. const
  726.   UpdErrMsg = '%s.'#13#10'Discard the edits to %S %S and continue updating?';
  727. begin
  728.   if UpdateKind = ukDelete then
  729.     Key := Dataset.Fields[0].OldValue else
  730.     Key := Dataset.Fields[0].NewValue;
  731.   if MessageDlg(Format(UpdErrMsg, [E.Message, DataSet.Fields[0].DisplayLabel, Key]),
  732.     mtConfirmation, [mbYes, mbCancel], 0) = mrYes then
  733.     UpdateAction := uaSkip else
  734.     UpdateAction := uaAbort;
  735. end;
  736.  
  737. procedure TMastData.MastDataCreate(Sender: TObject);
  738. var
  739.   DataFile: string;
  740. begin
  741.   DataFile := DataDirectory + 'MASTSQL.GDB';
  742.   if not FileExists(DataFile) then
  743.     if MessageDlg('Could not locate MASTSQL.GDB.  Would you like to locate the file?',
  744.     mtError, [mbYes, mbNo], 0) = mrYes then
  745.       if OpenDialog.Execute then
  746.       begin
  747.         if UpperCase(ExtractFileName(OpenDialog.FileName)) = 'MASTSQL.GDB' then
  748.           DataFile := OpenDialog.FileName
  749.         else
  750.           raise Exception.Create('Invalid File: ' + OpenDialog.FileName);
  751.       end
  752.       else
  753.         raise Exception.Create('Cannot locate Interbase data file: MASTSQL.GDB');
  754.   Database.DatabaseName := DataFile;
  755.   Database.Open;
  756.   Transaction.StartTransaction;
  757. end;
  758.  
  759. end.
  760.  
  761.