// Implementation of the Lookup Service interface used to send information
// to the client. Very overcrowded. Probably should move SQL functions to new
// file.
// Authors:
// Cameron Hayes
// Mac Stephens
// Elias Sarraf
unit Lookup.ServiceImpl;

interface

uses
  XData.Server.Module,
  XData.Service.Common,
  Api.Database, Data.DB, frxClass, frxExportPDF, JS,
  Lookup.Service, System.Hash, System.JSON, Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, MemDS, DBAccess, Uni,
  hyiedefs, hyieutils, iexBitmaps, iesettings, iexLayers, iexRulers,
  iexToolbars, iexUserInteractions, imageenio, imageenproc, QuickRpt, QRCtrls,
  dbimageen, Vcl.ExtCtrls, ieview, imageenview, IdBaseComponent, IdComponent,
  IdTCPConnection, IdTCPClient, IdExplicitTLSClientServerBase, IdFTP,
  iexProcEffects, frxDBSet, frxExportBaseDialog, frCoreClasses, rOrderList, rOrderCorrugated, Common.Logging;


type
  [ServiceImplementation]
  TLookupService = class(TInterfacedObject, ILookupService)
  strict private
    ordersDB: TApiDatabase;
  private
    function GetItems(searchOptions: string): TItemList;
    function GetUsers(searchOptions: string): TUserList;
    function GetOrders(searchOptions: string): TOrderList;
    function GetOrder(orderInfo: string): TFullOrder;
    function GetCustomers(): TCustomerList;
    function GetCustomer(ID: string): TCustomerItem;
    function GetWebOrder(orderInfo: string): TWebOrder;
    function GetCuttingDieOrder(orderInfo: string): TCuttingDie;

    function EditUser(const editOptions: string): string;

    function GenerateOrderListPDF(searchOptions: string): string;
    function GenerateOrderCorrugatedPDF(orderID: string): string;
    function GenerateOrderWebPDF(orderID: string): string;
    function GenerateOrderCuttingPDF(orderID: string): string;

    function AddUser(userInfo: string): string;
    function AddItem(itemInfo: string): string;
    function DelUser(username: string): string;
    function AddCorrugatedOrder(orderInfo: string): TJSONObject;
    function AddWebOrder(orderInfo: string): TJSONObject;
    function AddCuttingDieOrder(orderInfo: string): TJSONObject;

    function GenerateSubQuery(currStatus: string): string;

    function AddStatusSchedule(StatusType: string; order: TJSONObject; ORDER_ID: integer): string;
    function GenerateOrdersSQL(searchOptions: string): TSQLQuery;
    function GetColorCount(colors: string): string;
    function GenerateStatusSelectSQL(statusTableShort, statusTableLong, startDate, endDate, statusType: string): string;
    function GenerateStatusWhereSQL(status: TStatusSearchInfo): string;
    function CreateStatusSearchInfo(params: TStringList; statusNum: string): TStatusSearchInfo;
    function SetStatus(statusOptions: string): string;
    procedure AddToOrdersTable(mode, ORDER_TYPE: string; JSONData: TJSONObject);
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  end;

implementation

uses
  System.Generics.Collections,
  XData.Sys.Exceptions, uLibrary, rOrderWeb, rOrderCutting;

procedure TLookupService.AfterConstruction;
begin
  inherited;
  ordersDB := TApiDatabase.Create(nil);

end;

procedure TLookupService.BeforeDestruction;
begin
  ordersDB.Free;
  inherited;
end;

function TLookupService.GetCustomers: TCustomerList;
// Retrieves a list of customers and sends it to the client in object form.
// The object contains the ID, Name, Short Name, and the shipping address.
var
  SQL: string;
  customer: TCustomerItem;
begin
  SQL := 'select NAME, CUSTOMER_ID, SHORT_NAME, BILL_ADDRESS, BILL_CITY, BILL_STATE, BILL_ZIP, START_DATE from customers';
  doQuery(ordersDB.UniQuery1, SQL);

  result := TCustomerList.Create;
  Result.data := TList<TCustomerItem>.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add( Result.data );
  result.count := 0;

  while not ordersDB.UniQuery1.Eof do
  begin
    customer := TCustomerItem.Create;
    TXDataOperationContext.Current.Handler.ManagedObjects.Add( customer );
    customer.NAME := ordersDB.UniQuery1.FieldByName('NAME').AsString;
    customer.ID := ordersDB.UniQuery1.FieldByName('CUSTOMER_ID').AsInteger;
    customer.SHORT_NAME := ordersDB.UniQuery1.FieldByName('SHORT_NAME').AsString;
    customer.staff_fields_invoice_to := ordersDB.UniQuery1.FieldByName('BILL_ADDRESS').AsString +
                        ', ' + ordersDB.UniQuery1.FieldByName('BILL_CITY').AsString +
                        ' ' + ordersDB.UniQuery1.FieldByName('BILL_ZIP').AsString;
    customer.START_DATE := ordersDB.UniQuery1.FieldByName('START_DATE').AsString;
    result.data.Add(customer);
    result.count := result.count + 1;
    ordersDB.UniQuery1.Next;
  end;
  ordersDB.UniQuery1.Close;
end;

function TLookupService.GetCustomer(ID: string): TCustomerItem;
// Gets one specific customer from the ID given by the client. This is used for
// the OrderEntry forms.
var
  SQL: string;
  ADDRESS: TAddressItem;
begin
  SQL := 'SELECT c.NAME, c.SHORT_NAME, c.BILL_ADDRESS_BLOCK, s.ship_block  FROM customers c JOIN customers_ship s ON c.CUSTOMER_ID = s.customer_id WHERE c.CUSTOMER_ID = ' + ID;
  doQuery(ordersDB.UniQuery1, SQL);
  result := TCustomerItem.Create;
  result.ADDRESS_LIST := TList<TAddressItem>.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add( Result.ADDRESS_LIST );

  result.NAME := ordersDB.UniQuery1.FieldByName('NAME').AsString;
  result.ID := StrToInt(ID);
  result.SHORT_NAME := ordersDB.UniQuery1.FieldByName('SHORT_NAME').AsString;
  result.staff_fields_invoice_to := ordersDB.UniQuery1.FieldByName('BILL_ADDRESS_BLOCK').AsString;

  while not ordersDB.UniQuery1.Eof do
  begin
    ADDRESS := TAddressItem.Create;
    TXDataOperationContext.Current.Handler.ManagedObjects.Add( ADDRESS );
    ADDRESS.ADDRESS := ordersDB.UniQuery1.FieldByName('ship_block').AsString;
    result.ADDRESS_LIST.Add(ADDRESS);
    ordersDB.UniQuery1.Next;
  end;

  ordersDB.UniQuery1.Close;

end;

function TLookupService.GenerateOrderListPDF(searchOptions: string): string;
// Generates a report pdf based on the last search (if any) Linked to rOrders
// file which does most of the work.
var
  SQL: string;
  rptOrderList: TrptOrderList; // Local instance of rptOrders
  CompanyID, CompanyName: string;
  params: TStringList;
begin
  rptOrderList := TrptOrderList.Create(nil);
  try
    params := TStringList.Create;
    params.StrictDelimiter := true;
    // parse the searchOptions
    params.Delimiter := '&';
    params.DelimitedText := searchOptions;
    companyID := params.Values['companyID'];
    if companyID <> '' then
    begin
      SQL := 'Select NAME from customers c where CUSTOMER_ID = ' + companyID;
      doQuery(ordersDB.UniQuery1, SQL);
      CompanyName := 'Company: ' + ordersDB.UniQuery1.FieldByName('NAME').AsString;
    end
    else
      CompanyName := '';

    SQL := GenerateOrdersSQL(searchOptions).SQL;
    result := rptOrderList.PrepareReport(SQL, CompanyName);

    //rptOrderList.GeneratePDF;

    // Optionally, log success
   Logger.log(5, 'PDF Report successfully generated for searchOptions: ' + searchOptions);
  finally
    rptOrderList.Free;
  end;
end;


function TLookupService.GenerateOrderCorrugatedPDF(orderID: string): string;
var
  SQL: string;
  rptOrderCorrugated: TrptOrderCorrugated; // Local instance of the report
begin
  rptOrderCorrugated := TrptOrderCorrugated.Create(nil);
  try
    // Generate SQL query for a single order
    SQL := 'SELECT * FROM corrugated_plate_orders WHERE ORDER_ID = ' + orderID;

    // Prepare the report with the query
    Result := rptOrderCorrugated.PrepareReport(SQL);

    // Optionally log success
    Logger.Log(5, 'PDF Report successfully generated for order ID: ' + orderID);
  finally
    rptOrderCorrugated.Free;
  end;
end;

function TLookupService.GenerateOrderWebPDF(orderID: string): string;
var
  SQL: string;
  rptOrderWeb: TrptOrderWeb; // Local instance of the report
begin
  rptOrderWeb := TrptOrderWeb.Create(nil);
  try
    // Generate SQL query for a single order
    //SQL := 'SELECT * FROM web_plate_orders w WHERE w.ORDER_ID = ' + orderID ;
    SQL := 'SELECT * FROM web_plate_orders w LEFT JOIN qb_sales_orders q ON w.ORDER_ID = q.ORDER_ID WHERE w.ORDER_ID = ' + orderID ;

    // Prepare the report with the query
    Result := rptOrderWeb.PrepareReport(SQL);

    // Optionally log success
    Logger.Log(5, 'PDF Report successfully generated for order ID: ' + orderID);
  finally
    rptOrderWeb.Free;
  end;
end;

function TLookupService.GenerateOrderCuttingPDF(orderID: string): string;
var
  SQL: string;
  rptOrderCutting: TrptOrderCutting; // Local instance of the report
begin
  rptOrderCutting := TrptOrderCutting.Create(nil);
  try
    // Generate SQL query for a single order
    SQL := 'SELECT * FROM cutting_die_orders WHERE ORDER_ID = ' + orderID;

    // Prepare the report with the query
    Result := rptOrderCutting.PrepareReport(SQL);

    // Optionally log success
    Logger.Log(5, 'PDF Report successfully generated for order ID: ' + orderID);
  finally
    rptOrderCutting.Free;
  end;
end;


function TLookupService.generateSubQuery(currStatus: string): string;
// Generates the subquery in order to retrieve all the status due/done dates
// This must be a subquery because there are at most 5 different entries which
// causes the query to return 5 different entries on a join. There is probably
// a work around but I couldn't get the SQL to work with it. Manually goes through
// all 5 statuses.
// currStatus: current status SQL is getting generated for.
begin
  result := '(select oss.STATUS_DATE from orders_status_schedule oss ' +
              'where oss.ORDER_ID = o.ORDER_ID and oss.ORDER_STATUS = ' + quotedStr(currStatus) + ') AS '+ currStatus + '_DUE,';

  result := result +'(select os.STATUS_TIMESTAMP from orders_status os where os.ORDER_ID = o.ORDER_ID and os.ORDER_STATUS = ' +
            quotedStr(currStatus) + ' order by os.STATUS_TIMESTAMP desc LIMIT 1) AS ' + currStatus + '_DONE, ';
end;

function TLookupService.generateStatusSelectSQL(statusTableShort: string; statusTableLong: string; startDate: string; endDate: string; statusType: string): string;
// Generates the SQL query to figure out whether or not an entry exists within
// a given time frame.
// statusTableShort: short name for the status tables (os or oss)
// statusTableLong: full name for the status table
// startDate/endDate: starting date/ending date for when the status is due/done.
begin
  result := 'exists ( select 1 from ' + statusTableLong + ' ' +
              statusTableShort + ' where ' + statusTableShort + '.ORDER_ID = ' +
              'o.ORDER_ID AND ' + statusTableShort + '.ORDER_STATUS = ' +
              quotedStr(statusType);

  if  startDate <> '' then
  begin
    result := result + ' AND ' + quotedStr(startDate) + ' <= STATUS_DATE';
  end;

  if ( ( endDate <> '1899/12/30' ) AND ( endDate <> '' ) ) then
  begin
    result := result + ' AND ' + quotedStr(endDate) + ' >= STATUS_DATE';
  end;

  result := result + ' AND STATUS_DATE IS NOT NULL)';
end;

function TLookupService.createStatusSearchInfo(params: TStringList; statusNum: string): TStatusSearchInfo;
// Takes all the status info recieved from the client and puts it into an object
// for convinence and to make it easier to expand. Returns said object.
// params: string list recieved from the client with all the search params
// statusNum: which status number we are on to make it easier to tell what info
// we want to take from client. I.E. differentiate between startDate1 and
// startDate2
var
  statusType: string;
begin
  result := TStatusSearchInfo.Create;
  result.startDate := params.Values['startDate' + statusNum];
  result.endDate := params.Values['endDate' + statusNum];
  if params.Values['null' + statusNum] <> '' then
    result.null := StrToBool(params.Values['null' + statusNum]);

  result.statusType := '';
  result.filterType := '';
  result.statusSuffix := '';
  if ( ( params.Values['filterType'  + statusNum] <> '' ) and ( params.Values['filterType'  + statusNum] <> 'NONE' ) ) then
  begin
    result.statusType := params.Values['filterType'  + statusNum].Split([' '])[0];
    result.statusSuffix := params.Values['filterType'  + statusNum].Split([' '])[1];
    result.filterType := result.statusType + '_' + result.statusSuffix;
  end;

  // Figure out what table the status belongs to
  if result.statusSuffix = 'DUE' then
  begin
    result.statusTableShort := 'oss';
    result.statusTableLong := 'orders_status_schedule';
    result.altStatusTableShort := 'os';
    result.altStatusTableLong := 'orders_status';
  end
  else
  begin
    result.statusTableShort := 'os';
    result.statusTableLong := 'orders_status';
    result.altStatusTableShort := 'oss';
    result.altStatusTableLong := 'orders_status_schedule';
  end;

end;

function TLookupService.generateStatusWhereSQL(status: TStatusSearchInfo): string;
// Generates the where SQL for each status to apply the filters used from the
// clients search.
// status - status information obtained from the client.
begin
  if status.filterType <> 'ORDER_DATE' then
    begin
    if status.null then
    begin
      result := result + ' AND not exists (select 1 from ' + status.statusTableLong +
                              ' ' + status.statusTableShort + ' where '
                              + status.statusTableShort +'.ORDER_ID = o.ORDER_ID ' +
                              'AND ' + status.statusTableShort + '.ORDER_STATUS = ' +
                              quotedStr(status.statusType) + ')';
      // out dated useage. uncomment if you want to return entries where dues are null, but dones exist or vice versa.
      //generateStatusSelectSQL(status.altStatusTableShort, status.altStatusTableLong, status.startDate, status.endDate, status.statusType);
    end
    else
      result := result + ' AND ' + generateStatusSelectSQL(status.statusTableShort, status.statusTableLong, status.startDate, status.endDate, status.statusType);
  end
  else
  begin
    if  status.startDate <> '' then
    begin
      result := result + ' AND ' + quotedStr(status.startDate) + ' <= o.ORDER_DATE';
    end;

    if ( ( status.endDate <> '1899/12/30' ) AND ( status.endDate <> '' ) ) then
    begin
      result := result + ' AND ' + quotedStr(status.endDate) + ' >= o.ORDER_DATE';
    end;
  end;
end;

function TLookupService.generateOrdersSQL(searchOptions: string): TSQLQuery;
// Generates the orderSQL to retrieve entries specified by the search recieved
// from the client.
// searchOptions: search information sent form client to be parsed.
var
  params: TStringList;
  PageNum: integer;
  PageSize: integer;
  OrderBy: string;
  offset: string;
  limit: string;
  SQL: string;
  whereSQL: string;
  orderBySQL: string;

  OrderID: string;
  CompanyID: string;
  JobName: string;
  orderType: string;

  // These variables apply to the first status sent from the client
  status1: TStatusSearchInfo;

  // These variables apply to the second status sent from the client
  status2: TStatusSearchInfo;

begin
  result := TSQLQuery.Create;
  params := TStringList.Create;
  params.StrictDelimiter := true;
  // parse the searchOptions
  params.Delimiter := '&';
  params.DelimitedText := searchOptions;

  orderBySQL := '';
  PageNum := StrToInt(params.Values['pagenumber']);
  PageSize := StrToInt(params.Values['pagesize']);
  OrderBy := params.Values['orderby'];
  orderType := params.Values['orderType'].ToLower();
  OrderID := params.Values['orderID'];
  companyID := params.Values['companyID'];
  jobName := params.Values['jobName'];

  // Search infomation for first status type
  status1 := createStatusSearchInfo(params, '1');

  // Search Information for second status type
  status2 := createStatusSearchInfo(params, '2');

  offset := IntToStr((PageNum - 1) * PageSize);
  limit := IntToStr(PageSize);

 { SQL := 'SELECT o.ORDER_ID, o.LOCATION AS Loc, c.NAME AS COMPANY_NAME, o.JOB_NAME, ' +
    'o.ORDER_DATE, o.ORDER_TYPE, oss_proof.STATUS_DATE AS PROOF_DUE, oss_art.STATUS_DATE AS ART_DUE, ' +
	 'oss_plate.STATUS_DATE AS PLATE_DUE, oss_mount.STATUS_DATE AS MOUNT_DUE, oss_ship.STATUS_DATE AS SHIP_DUE, ' +
   'os_proof.STATUS_DATE AS PROOF_DONE, os_art.STATUS_DATE AS ART_DONE, os_plate.STATUS_DATE AS PLATE_DONE, ' +
	 'os_mount.STATUS_DATE AS MOUNT_DONE, os_ship.STATUS_DATE AS SHIP_DONE, ' +
	 'o.PRICE, qb.QB_REF_NUM, coalesce( cpo.staff_fields_po_number, wpo.staff_fields_po_number, cdo.staff_fields_po_number ) AS po_number, ' +
	 'coalesce( cpo.staff_fields_quickbooks_item, wpo.staff_fields_quickbooks_item, cdo.staff_fields_quickbooks_item ) AS quickbooks_item ' +
  'FROM orders o JOIN customers c ON c.CUSTOMER_ID = o.COMPANY_ID LEFT JOIN qb_sales_orders qb ON qb.ORDER_ID = o.ORDER_ID ' +
    'LEFT JOIN ORDERS_status_schedule oss_proof ON o.ORDER_ID = oss_proof.Order_id AND oss_proof.order_status = ' + quotedStr('PROOF') +
    ' LEFT JOIN ORDERS_status_schedule oss_art ON o.ORDER_ID = oss_art.Order_id AND oss_art.order_status = ' + quotedStr('ART') +
    ' LEFT JOIN orders_status_schedule oss_plate ON o.ORDER_ID = oss_plate.Order_id AND oss_plate.order_status = ' + quotedStr('PLATE') +
	 ' LEFT JOIN orders_status_schedule oss_mount ON o.ORDER_ID = oss_mount.Order_id AND oss_mount.order_status = ' + quotedStr('MOUNT') +
	 ' LEFT JOIN orders_status_schedule oss_ship ON o.ORDER_ID = oss_ship.Order_id AND oss_ship.order_status = ' + quotedStr('SHIP') +
   'LEFT JOIN ORDERS_status os_proof ON o.ORDER_ID = os_proof.Order_id AND os_proof.order_status = ' + quotedStr('PROOF') +
    ' LEFT JOIN ORDERS_status os_art ON o.ORDER_ID = os_art.Order_id AND os_art.order_status = ' + quotedStr('ART') +
    ' LEFT JOIN orders_status os_plate ON o.ORDER_ID = os_plate.Order_id AND os_plate.order_status = ' + quotedStr('PLATE') +
	 ' LEFT JOIN orders_status os_mount ON o.ORDER_ID = os_mount.Order_id AND os_mount.order_status = ' + quotedStr('MOUNT') +
	 ' LEFT JOIN orders_status os_ship ON o.ORDER_ID = os_ship.Order_id AND os_ship.order_status = ' + quotedStr('SHIP') +
    ' LEFT JOIN corrugated_plate_orders cpo ON o.ORDER_ID = cpo.ORDER_ID ' +
    'LEFT JOIN web_plate_orders wpo ON o.ORDER_ID = wpo.ORDER_ID ' +
    'LEFT JOIN cutting_die_orders cdo ON o.ORDER_ID = cdo.ORDER_ID '; }

    SQL := 'SELECT o.ORDER_ID,  o.LOCATION AS Loc, c.NAME AS COMPANY_NAME, o.JOB_NAME, o.ORDER_DATE, o.ORDER_TYPE, ';

  // Generate the subqueries that get the Status Dates(Due and Done)
  SQL := SQL + generateSubquery('PROOF');
  SQL := SQL + generateSubquery('ART');
  SQL := SQL + generateSubquery('PLATE');
  SQL := SQL + generateSubquery('MOUNT');
  SQL := SQL + generateSubquery('SHIP');

  // Generate the Where SQL based on if there is a status filter applied or not
  whereSQL := 'from orders o join customers c on ' +
          'c.CUSTOMER_ID = o.COMPANY_ID left join qb_sales_orders qb on qb.ORDER_ID = o.ORDER_ID ' +
          ' LEFT JOIN corrugated_plate_orders cpo ON o.ORDER_ID = cpo.ORDER_ID ' +
          'LEFT JOIN web_plate_orders wpo ON o.ORDER_ID = wpo.ORDER_ID ' +
          'LEFT JOIN cutting_die_orders cdo ON o.ORDER_ID = cdo.ORDER_ID  where 0 = 0';

  if ( ( status1.filterType <> '' ) and ( status1.filterType <> 'NONE' ) ) then
    whereSQL := whereSQL + generateStatusWhereSQL(status1);

  if ( (status2.filterType <> '' ) and ( status2.filterType <> 'NONE' ) ) then
    whereSQL := whereSQL + generateStatusWhereSQL(status2);

  if ( ( orderType <> '' ) and ( orderType <> 'any' ) ) then
  begin
    if ( orderType <> 'cutting die') then
      whereSQL := whereSQL + ' AND o.ORDER_TYPE = ' + quotedStr(orderType + '_plate')
    else
      whereSQL := whereSQL + ' AND o.ORDER_TYPE = ' + quotedStr('cutting_die');
  end;

  if orderID <> '' then
    whereSQL := whereSQL + ' AND o.ORDER_ID = ' + orderID;

  if companyID <> '' then
    whereSQL := whereSQL + ' AND c.CUSTOMER_ID = ' + companyID;

  if jobName <> '' then
    whereSQL := whereSQL + ' AND o.JOB_NAME = ' + quotedStr(jobName);



  orderBySQL := 'ORDER BY ' + OrderBy;

  SQL := SQL + 'o.PRICE, qb.QB_REF_NUM, ' +
  'coalesce( cpo.staff_fields_po_number, wpo.staff_fields_po_number, cdo.staff_fields_po_number ) AS po_number, ' +
	 'coalesce( cpo.staff_fields_quickbooks_item, wpo.staff_fields_quickbooks_item, cdo.staff_fields_quickbooks_item ) AS quickbooks_item ' +
   whereSQL + ' ' + orderBySQL + ' LIMIT ' + limit + ' OFFSET ' + offset;


  // send the SQL and whereSQL back. whereSQL is needed to get the total count.
  // probably a way to getthe TotalCount in original SQL but it was giving me issues
  result.SQL := SQL;
  result.whereSQL := whereSQL;
end;

function TLookupService.getColorCount(colors: string): string;
// Colors are stored in a JSON in the database so this function parses the
// stringified JSON and returns the count of colors.
// colors: stringified JSON to be parsed.
var
  colorObject: TJSONObject;
  colorList: TJSONArray;
begin
  if colors = '' then
    Result := '0'
  else
  begin
    colorObject := TJSONObject.ParseJSONValue(colors) as TJSONObject;
    try
      if Assigned(colorObject) then
      begin
        colorList := colorObject.GetValue<TJSONArray>('items');
        if Assigned(colorList) then
          Result := IntToStr(colorList.Count)
        else
          Result := '0';
      end
      else
        Result := '0';
    finally
      colorObject.Free;
    end;
  end;
end;


function TLookupService.GetOrders(searchOptions: string): TOrderList;
var
  SQL: string;
  whereSQL: string;
  Order: TOrderItem;
  colors: string;
  ColorType: string;
  SQLQuery: TSQLQuery;
begin
  SQLQuery := generateOrdersSQL(searchOptions);
  TXDataOperationContext.Current.Handler.ManagedObjects.Add(SQLQuery); // Added SQLQuery to ManagedObjects

  Result := TOrderList.Create;
  try
    Result.data := TList<TOrderItem>.Create;
    TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result.data);

    SQL := SQLQuery.SQL;
    whereSQL := SQLQuery.whereSQL;

    doQuery(ordersDB.UniQuery1, SQL);

    while not ordersDB.UniQuery1.Eof do
    begin
      Order := TOrderItem.Create;
      TXDataOperationContext.Current.Handler.ManagedObjects.Add(Order);
      Result.data.Add(Order);

      Order.ID := ordersDB.UniQuery1.FieldByName('ORDER_ID').AsString;
      Order.companyName := ordersDB.UniQuery1.FieldByName('COMPANY_NAME').AsString;
      Order.jobName := ordersDB.UniQuery1.FieldByName('JOB_NAME').AsString;
      Order.orderDate := ordersDB.UniQuery1.FieldByName('ORDER_DATE').AsString;
      Order.proofDue := ordersDB.UniQuery1.FieldByName('PROOF_DUE').AsString;
      Order.proofDone := ordersDB.UniQuery1.FieldByName('PROOF_DONE').AsString;
      Order.artDue := ordersDB.UniQuery1.FieldByName('ART_DUE').AsString;
      Order.artDone := ordersDB.UniQuery1.FieldByName('ART_DONE').AsString;
      Order.plateDue := ordersDB.UniQuery1.FieldByName('PLATE_DUE').AsString;
      Order.plateDone := ordersDB.UniQuery1.FieldByName('PLATE_DONE').AsString;
      Order.mountDue := ordersDB.UniQuery1.FieldByName('MOUNT_DUE').AsString;
      Order.mountDone := ordersDB.UniQuery1.FieldByName('MOUNT_DONE').AsString;
      Order.shipDue := ordersDB.UniQuery1.FieldByName('SHIP_DUE').AsString;
      Order.shipDone := ordersDB.UniQuery1.FieldByName('SHIP_DONE').AsString;
      Order.price := ordersDB.UniQuery1.FieldByName('PRICE').AsString;
      Order.qbRefNum := ordersDB.UniQuery1.FieldByName('QB_REF_NUM').AsString;
      Order.orderType := ordersDB.UniQuery1.FieldByName('ORDER_TYPE').AsString.Replace('_', ' ');

      if ordersDB.UniQuery1.FieldByName('ORDER_TYPE').AsString = 'web_plate' then
      begin
        ColorType := 'quantity_and_colors_qty_colors';
        SQL := 'Select quantity_and_colors_qty_colors from web_plate_orders where order_id = ' + Order.ID;
      end
      else
      begin
        ColorType := 'colors_colors';
        SQL := 'Select colors_colors from corrugated_plate_orders where order_id = ' + Order.ID;
      end;

      doQuery(ordersDB.UniQuery2, SQL);
      colors := ordersDB.UniQuery2.FieldByName(ColorType).AsString;
      Order.colors := GetColorCount(colors);

      ordersDB.UniQuery1.Next;
    end;

    ordersDB.UniQuery1.Close;

    SQL := 'SELECT COUNT(*) AS total_count ' + whereSQL;
    doQuery(ordersDB.UniQuery1, SQL);
    Result.count := ordersDB.UniQuery1.FieldByName('total_count').AsInteger;

    ordersDB.UniQuery1.Close;
  except
    Result.Free; // Cleaned up memory in case of exceptions
  end;
end;


function TLookupService.GetOrder(orderInfo: string): TFullOrder;
// Gets on singular order from the database for the order entry page.
// orderInfo: the ORDER_ID.
// TODO seperate this into GetCorrugatedOrder and GetWebOrder.
var
  orderType: string;
  orderID: string;
  SQL: string;
  table: string;
  ADDRESS: TAddressItem;
begin
  orderID := orderInfo;
  SQL := 'select ORDER_TYPE from orders where ORDER_ID = ' + quotedStr(orderID);
  doQuery(ordersDB.UniQuery1, SQL);
  orderType := ordersDB.UniQuery1.FieldByName('ORDER_TYPE').AsString;
  if orderType = 'web_plate' then
     table := 'web_plate_orders'
  else
    table := 'corrugated_plate_orders';
  SQL := 'select * from ' + table + ' o JOIN customers c ON c.CUSTOMER_ID = o.COMPANY_ID where ORDER_ID = ' + quotedStr(orderID);
  doQuery(ordersDB.UniQuery1, SQL);

  result := TFullOrder.Create;

  // Company
  result.COMPANY_ID := ordersDB.UniQuery1.FieldByName('COMPANY_ID').AsInteger;
  result.NAME := ordersDB.UniQuery1.FieldByName('NAME').AsString;
  result.SHORT_NAME := ordersDB.UniQuery1.FieldByName('SHORT_NAME').AsString;
  result.inQuickBooks := '?';

  // Staff Fields
  result.staff_fields_order_date := ordersDB.UniQuery1.FieldByName('staff_fields_order_date').AsString;
  result.staff_fields_proof_date := ordersDB.UniQuery1.FieldByName('staff_fields_proof_date').AsString;
  result.staff_fields_ship_date := ordersDB.UniQuery1.FieldByName('staff_fields_ship_date').AsString;
  result.staff_fields_ship_via := ordersDB.UniQuery1.FieldByName('staff_fields_ship_via').AsString;
  result.staff_fields_quantity := ordersDB.UniQuery1.FieldByName('staff_fields_quantity').AsString;
  result.staff_fields_ship_to := ordersDB.UniQuery1.FieldByName('staff_fields_ship_to').AsString;
  result.staff_fields_po_number := ordersDB.UniQuery1.FieldByName('staff_fields_po_number').AsString;
  result.staff_fields_job_name := ordersDB.UniQuery1.FieldByName('staff_fields_job_name').AsString;
  result.staff_fields_quickbooks_item := ordersDB.UniQuery1.FieldByName('staff_fields_quickbooks_item').AsString;
  result.staff_fields_art_due := ordersDB.UniQuery1.FieldByName('staff_fields_art_due').AsString;
  result.staff_fields_plate_due := ordersDB.UniQuery1.FieldByName('staff_fields_plate_due').AsString;
  result.staff_fields_price := ordersDB.UniQuery1.FieldByName('staff_fields_price').AsString;
  result.staff_fields_mount_due := ordersDB.UniQuery1.FieldByName('staff_fields_mount_due').AsString;
  result.staff_fields_art_location := ordersDB.UniQuery1.FieldByName('staff_fields_art_location').AsString;
  result.staff_fields_invoice_to := ordersDB.UniQuery1.FieldByName('staff_fields_invoice_to').AsString;

  // Supplied by Customer
  if ordersDB.UniQuery1.FieldByName('supplied_by_customer_b_w_copy').AsString = 'T' then
    result.supplied_by_customer_b_w_copy := true
  else
    result.supplied_by_customer_b_w_copy := false;
  if ordersDB.UniQuery1.FieldByName('supplied_by_customer_color_copy').AsString = 'T' then
    result.supplied_by_customer_color_copy := true
  else
    result.supplied_by_customer_color_copy := false;
  if ordersDB.UniQuery1.FieldByName('supplied_by_customer_plates').AsString = 'T' then
    result.supplied_by_customer_plates := true
  else
    result.supplied_by_customer_plates := false;
  if ordersDB.UniQuery1.FieldByName('supplied_by_customer_sample_ca').AsString = 'T' then
    result.supplied_by_customer_sample_ca := true
  else
    result.supplied_by_customer_sample_ca := false;
  result.supplied_by_customer_dimension := ordersDB.UniQuery1.FieldByName('supplied_by_customer_dimension').AsString;

  if orderType = 'web_plate' then
  begin
    {if ordersDB.UniQuery1.FieldByName('supplied_by_customer_disk').AsString = 'T' then
      result.diskOrCD := true
    else
      result.diskOrCD := false;}
  end
  else
  begin
    if ordersDB.UniQuery1.FieldByName('supplied_by_customer_disk_or_cd').AsString = 'T' then
      result.supplied_by_customer_disk_or_cd := true
    else
      result.supplied_by_customer_disk_or_cd := false;
  end;
  result.supplied_by_customer_e_mail := ordersDB.UniQuery1.FieldByName('supplied_by_customer_e_mail').AsString;
  result.supplied_by_customer_ftp := ordersDB.UniQuery1.FieldByName('supplied_by_customer_ftp').AsString;
  result.supplied_by_customer_other := ordersDB.UniQuery1.FieldByName('supplied_by_customer_other').AsString;
  if orderType = 'corrugated_plate' then
  begin
    result.supplied_by_customer_existing_ := ordersDB.UniQuery1.FieldByName('supplied_by_customer_existing_').AsString;
    result.supplied_by_customer_ref_art_p := ordersDB.UniQuery1.FieldByName('supplied_by_customer_ref_art_p').AsString;
    result.supplied_by_customer_ref_art_a := ordersDB.UniQuery1.FieldByName('supplied_by_customer_ref_art_a').AsString;
  end;

  // Layout
  if orderType = 'corrugated_plate' then
  begin
    result.layout_rsc_l := ordersDB.UniQuery1.FieldByName('layout_rsc_l').AsString;
    result.layout_rcs_w := ordersDB.UniQuery1.FieldByName('layout_rcs_w').AsString;
    result.layout_rcs_d := ordersDB.UniQuery1.FieldByName('layout_rcs_d').AsString;
    result.layout_die_cut_no := ordersDB.UniQuery1.FieldByName('layout_die_cut_no').AsString;
    result.layout_accross_no := ordersDB.UniQuery1.FieldByName('layout_accross_no').AsString;
    result.layout_around_no := ordersDB.UniQuery1.FieldByName('layout_around_no').AsString;
    result.layout_cad_file := ordersDB.UniQuery1.FieldByName('layout_cad_file').AsString;
    if ordersDB.UniQuery1.FieldByName('layout_excalibur_die').AsString = 'T' then
      result.layout_excalibur_die := true
    else
      result.layout_excalibur_die := false;
    result.layout_rsc_style := ordersDB.UniQuery1.FieldByName('layout_rsc_style').AsString;
  end
  else
  begin
    //result.acrossNo := ordersDB.UniQuery1.FieldByName('layout_accross').AsString;
    //result.aroundNo := ordersDB.UniQuery1.FieldByName('layout_around').AsString;
  end;

  // Mounting & Colors & Proofing
  if orderType = 'corrugated_plate' then
  begin
    result.mounting_loose := ordersDB.UniQuery1.FieldByName('mounting_loose').AsString;
    if ordersDB.UniQuery1.FieldByName('mounting_sticky_bak').AsString = 'T' then
      result.mounting_sticky_bak := true
    else
      result.mounting_sticky_bak := false;
    if ordersDB.UniQuery1.FieldByName('mounting_full_mount').AsString = 'T' then
      result.mounting_full_mount := true
    else
      result.mounting_full_mount := false;
    result.mounting_strip_mount := ordersDB.UniQuery1.FieldByName('mounting_strip_mount').AsString;
    result.mounting_standard_setup := ordersDB.UniQuery1.FieldByName('mounting_standard_setup').AsString;
    result.mounting_custom_backing := ordersDB.UniQuery1.FieldByName('mounting_custom_backing').AsString;
    result.mounting_custom_adhesive := ordersDB.UniQuery1.FieldByName('mounting_custom_adhesive').AsString;

    result.colors_cylinder_size := ordersDB.UniQuery1.FieldByName('colors_cylinder_size').AsString;
    result.colors_machine_ident := ordersDB.UniQuery1.FieldByName('colors_machine_ident').AsString;
    result.colors_cross_hairs := ordersDB.UniQuery1.FieldByName('colors_cross_hairs').AsString;
    result.colors_clemson := ordersDB.UniQuery1.FieldByName('colors_clemson').AsString;
    result.colors_colors := ordersDB.UniQuery1.FieldByName('colors_colors').AsString;

    result.proofing_fax := ordersDB.UniQuery1.FieldByName('proofing_fax').AsString;
    result.proofing_fax_attn := ordersDB.UniQuery1.FieldByName('proofing_fax_attn').AsString;
    result.proofing_e_mail := ordersDB.UniQuery1.FieldByName('proofing_e_mail').AsString;
    result.proofing_e_mail_attn := ordersDB.UniQuery1.FieldByName('proofing_e_mail_attn').AsString;
    result.proofing_ship_to := ordersDB.UniQuery1.FieldByName('proofing_ship_to').AsString;
    if ordersDB.UniQuery1.FieldByName('proofing_full_size_panel').AsString = 'T' then
      result.proofing_full_size_panel := true
    else
      result.proofing_full_size_panel := false;
    if ordersDB.UniQuery1.FieldByName('proofing_print_card').AsString = 'T'then
      result.proofing_print_card := true
    else
      result.proofing_print_card := false;
    if ordersDB.UniQuery1.FieldByName('proofing_wide_format').AsString = 'T'then
      result.proofing_wide_format := true
    else
      result.proofing_wide_format := false;
    if ordersDB.UniQuery1.FieldByName('proofing_pdf_file').AsString = 'T'then
      result.proofing_pdf_file := true
    else
      result.proofing_pdf_file := false;
    result.proofing_other := ordersDB.UniQuery1.FieldByName('proofing_other').AsString;
    if ordersDB.UniQuery1.FieldByName('proofing_art_approved_as_is').AsString = 'T'then
      result.proofing_art_approved_as_is := true
    else
      result.proofing_art_approved_as_is := false;
    result.proofing_approved_date := ordersDB.UniQuery1.FieldByName('proofing_approved_date').AsString;
  end
  else
  begin
    {result.colors := ordersDB.UniQuery1.FieldByName('quatity_and_colors_qty_colors').AsString;
    if ordersDB.UniQuery1.FieldByName('proofing_pdf').AsString = 'T'then
      result.pdfFile := true
    else
      result.pdfFile := false;}
  end;

  // Plates
  result.plates_thickness := ordersDB.UniQuery1.FieldByName('plates_thickness').AsString;
  result.plates_plate_material := ordersDB.UniQuery1.FieldByName('plates_plate_material').AsString;
  result.plates_job_number := ordersDB.UniQuery1.FieldByName('plates_job_number').AsString;

  // General
  if orderType = 'corrugated_plate' then
    result.general_special_instructions := ordersDB.UniQuery1.FieldByName('general_special_instructions').AsString
  else
    //result.specialInstructions := ordersDB.UniQuery1.FieldByName('general_comments').AsString

  ordersDB.UniQuery1.Close;

  SQL := 'SELECT s.ship_block FROM customers c JOIN customers_ship s ON c.CUSTOMER_ID = s.customer_id WHERE c.CUSTOMER_ID = ' + IntToStr(result.COMPANY_ID);
  doQuery(ordersDB.UniQuery1, SQL);
  result.ADDRESS_LIST := TList<TAddressItem>.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add( Result.ADDRESS_LIST );

  while not ordersDB.UniQuery1.Eof do
  begin
    ADDRESS := TAddressItem.Create;
    TXDataOperationContext.Current.Handler.ManagedObjects.Add( ADDRESS );
    ADDRESS.ADDRESS := ordersDB.UniQuery1.FieldByName('ship_block').AsString;
    result.ADDRESS_LIST.Add(ADDRESS);
    ordersDB.UniQuery1.Next;
  end;

  ordersDB.UniQuery1.Close;

  result.ITEMS := GetItems('');
end;

function TLookupService.GetWebOrder(orderInfo: string): TWebOrder;
var
  orderType: string;
  orderID: string;
  SQL: string;
  ADDRESS: TAddressItem;
begin
  orderID := orderInfo;
  SQL := 'select * from web_plate_orders o JOIN customers c ON c.CUSTOMER_ID = o.COMPANY_ID where ORDER_ID = ' + quotedStr(orderID);
  doQuery(ordersDB.UniQuery1, SQL);

  result := TWebOrder.Create;

  // Company
  result.COMPANY_ID := ordersDB.UniQuery1.FieldByName('COMPANY_ID').AsInteger;
  result.NAME := ordersDB.UniQuery1.FieldByName('NAME').AsString;
  result.SHORT_NAME := ordersDB.UniQuery1.FieldByName('SHORT_NAME').AsString;
  result.inQuickBooks := '?';

  // Staff Fields
  result.staff_fields_order_date := ordersDB.UniQuery1.FieldByName('staff_fields_order_date').AsString;
  result.staff_fields_proof_date := ordersDB.UniQuery1.FieldByName('staff_fields_proof_date').AsString;
  result.staff_fields_ship_date := ordersDB.UniQuery1.FieldByName('staff_fields_ship_date').AsString;
  result.staff_fields_ship_via := ordersDB.UniQuery1.FieldByName('staff_fields_ship_via').AsString;
  result.staff_fields_quantity := ordersDB.UniQuery1.FieldByName('staff_fields_quantity').AsString;
  result.staff_fields_ship_to := ordersDB.UniQuery1.FieldByName('staff_fields_ship_to').AsString;
  result.staff_fields_po_number := ordersDB.UniQuery1.FieldByName('staff_fields_po_number').AsString;
  result.staff_fields_job_name := ordersDB.UniQuery1.FieldByName('staff_fields_job_name').AsString;
  result.staff_fields_quickbooks_item := ordersDB.UniQuery1.FieldByName('staff_fields_quickbooks_item').AsString;
  result.staff_fields_art_due := ordersDB.UniQuery1.FieldByName('staff_fields_art_due').AsString;
  result.staff_fields_plate_due := ordersDB.UniQuery1.FieldByName('staff_fields_plate_due').AsString;
  result.staff_fields_price := ordersDB.UniQuery1.FieldByName('staff_fields_price').AsString;
  result.staff_fields_art_location := ordersDB.UniQuery1.FieldByName('staff_fields_art_location').AsString;
  result.staff_fields_invoice_to := ordersDB.UniQuery1.FieldByName('staff_fields_invoice_to').AsString;

  // Supplied by Customer
  result.supplied_by_customer_b_w_or_co := ordersDB.UniQuery1.FieldByName('supplied_by_customer_b_w_or_co').AsString;
  result.supplied_by_customer_plates := ordersDB.UniQuery1.FieldByName('supplied_by_customer_plates').AsString;
  result.supplied_by_customer_sample := ordersDB.UniQuery1.FieldByName('supplied_by_customer_sample').AsString;
  result.supplied_by_customer_dimension := ordersDB.UniQuery1.FieldByName('supplied_by_customer_dimension').AsString;
  result.supplied_by_customer_other := ordersDB.UniQuery1.FieldByName('supplied_by_customer_other').AsString;
  result.supplied_by_customer_disk := ordersDB.UniQuery1.FieldByName('supplied_by_customer_disk').AsString;
  result.supplied_by_customer_e_mail := ordersDB.UniQuery1.FieldByName('supplied_by_customer_e_mail').AsString;
  result.supplied_by_customer_ftp := ordersDB.UniQuery1.FieldByName('supplied_by_customer_ftp').AsString;
  result.supplied_by_customer_total_inc := ordersDB.UniQuery1.FieldByName('supplied_by_customer_total_inc').AsString;
  result.supplied_by_customer_sheets_us := ordersDB.UniQuery1.FieldByName('supplied_by_customer_sheets_us').AsString;
  result.supplied_by_customer_initials := ordersDB.UniQuery1.FieldByName('supplied_by_customer_initials').AsString;

  // Proofing
  result.proofing_pdf := ordersDB.UniQuery1.FieldByName('proofing_pdf').AsBoolean;
  result.proofing_pdf_to := ordersDB.UniQuery1.FieldByName('proofing_pdf_to').AsString;
  result.proofing_pdf_date_1 := ordersDB.UniQuery1.FieldByName('proofing_pdf_date_1').AsString;
  result.proofing_pdf_date_2 := ordersDB.UniQuery1.FieldByName('proofing_pdf_date_2').AsString;
  result.proofing_pdf_date_3 := ordersDB.UniQuery1.FieldByName('proofing_pdf_date_3').AsString;
  result.proofing_full_size_ink_jet_for := ordersDB.UniQuery1.FieldByName('proofing_full_size_ink_jet_for').AsBoolean;
  result.proofing_ink_jet_to := ordersDB.UniQuery1.FieldByName('proofing_ink_jet_to').AsString;
  result.proofing_ink_jet_to_2 := ordersDB.UniQuery1.FieldByName('proofing_ink_jet_to').AsString;
  result.proofing_ink_jet_date_1 := ordersDB.UniQuery1.FieldByName('proofing_ink_jet_date_1').AsString;
  result.proofing_ink_jet_date_2 := ordersDB.UniQuery1.FieldByName('proofing_ink_jet_date_2').AsString;
  result.proofing_color_contract := ordersDB.UniQuery1.FieldByName('proofing_color_contract').AsString;
  result.proofing_color_contrac_to := ordersDB.UniQuery1.FieldByName('proofing_color_contrac_to').AsString;
  result.proofing_color_contrac_date_1 := ordersDB.UniQuery1.FieldByName('proofing_color_contrac_date_1').AsString;
  result.proofing_color_contrac_date_2 := ordersDB.UniQuery1.FieldByName('proofing_color_contrac_date_2').AsString;
  result.proofing_digital_color_key := ordersDB.UniQuery1.FieldByName('proofing_digital_color_key').AsString;
  result.proofing_digital_color_to := ordersDB.UniQuery1.FieldByName('proofing_digital_color_to').AsString;
  result.proofing_digital_color_date_1 := ordersDB.UniQuery1.FieldByName('proofing_digital_color_date_1').AsString;

  // Colors
  result.quantity_and_colors_press_name := ordersDB.UniQuery1.FieldByName('quantity_and_colors_press_name').AsString;
  result.quantity_and_colors_anilox_info := ordersDB.UniQuery1.FieldByName('quantity_and_colors_anilox_info').AsString;
  result.quantity_and_colors_qty_colors := ordersDB.UniQuery1.FieldByName('quantity_and_colors_qty_colors').AsString;

  // Plate Marks
  result.plate_marks_microdots := ordersDB.UniQuery1.FieldByName('plate_marks_microdots').AsString;
  result.plate_marks_microdots_comments := ordersDB.UniQuery1.FieldByName('plate_marks_microdots_comments').AsString;
  result.plate_marks_crosshairs := ordersDB.UniQuery1.FieldByName('plate_marks_crosshairs').AsString;
  result.plate_marks_crosshairs_comments := ordersDB.UniQuery1.FieldByName('plate_marks_crosshairs').AsString;
  result.plate_marks_color_bars := ordersDB.UniQuery1.FieldByName('plate_marks_color_bars').AsString;
  result.plate_marks_color_bars_comments := ordersDB.UniQuery1.FieldByName('plate_marks_color_bars_comments').AsString;
  result.plate_marks_other := ordersDB.UniQuery1.FieldByName('plate_marks_other').AsString;
  result.plate_marks_other_comments := ordersDB.UniQuery1.FieldByName('plate_marks_other_comments').AsString;

  // Print Orientation
  result.print_orientation_print_orient := ordersDB.UniQuery1.FieldByName('print_orientation_print_orient').AsString;

  // Plate
  result.plates_plate_material := ordersDB.UniQuery1.FieldByName('plates_plate_material').AsString;
  result.plates_thickness := ordersDB.UniQuery1.FieldByName('plates_thickness').AsString;
  result.plates_job_number := ordersDB.UniQuery1.FieldByName('plates_job_number').AsString;

  // Layout
  result.layout_around := ordersDB.UniQuery1.FieldByName('layout_around').AsString;
  result.layout_accross := ordersDB.UniQuery1.FieldByName('layout_accross').AsString;
  result.layout_surface_print := ordersDB.UniQuery1.FieldByName('layout_surface_print').AsString;
  result.layout_reverse_print := ordersDB.UniQuery1.FieldByName('layout_reverse_print').AsString;
  result.layout_cylinder_repeat := ordersDB.UniQuery1.FieldByName('layout_cylinder_repeat').AsString;
  result.layout_cutoff_dimension := ordersDB.UniQuery1.FieldByName('layout_cutoff_dimension').AsString;
  result.layout_pitch := ordersDB.UniQuery1.FieldByName('layout_pitch').AsString;
  result.layout_teeth := ordersDB.UniQuery1.FieldByName('layout_teeth').AsString;
  result.layout_bleed := ordersDB.UniQuery1.FieldByName('layout_bleed').AsString;
  result.layout_cutback := ordersDB.UniQuery1.FieldByName('layout_cutback').AsString;
  result.layout_minimum_trap_dim := ordersDB.UniQuery1.FieldByName('layout_minimum_trap_dim').AsString;
  result.layout_maximum_trap_dim := ordersDB.UniQuery1.FieldByName('layout_maximum_trap_dim').AsString;

  // UPC
  result.upc_size := ordersDB.UniQuery1.FieldByName('upc_size').AsString;
  result.upc_bar_width_reduction := ordersDB.UniQuery1.FieldByName('upc_bar_width_reduction').AsString;
  result.upc_distortion_percent := ordersDB.UniQuery1.FieldByName('upc_distortion_percent').AsString;
  result.upc_distortion_amount := ordersDB.UniQuery1.FieldByName('upc_distortion_amount').AsString;


  // General
  result.general_comments := ordersDB.UniQuery1.FieldByName('general_comments').AsString;

  ordersDB.UniQuery1.Close;

  SQL := 'SELECT s.ship_block FROM customers c JOIN customers_ship s ON c.CUSTOMER_ID = s.customer_id WHERE c.CUSTOMER_ID = ' + IntToStr(result.COMPANY_ID);
  doQuery(ordersDB.UniQuery1, SQL);
  result.ADDRESS_LIST := TList<TAddressItem>.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add( Result.ADDRESS_LIST );

  while not ordersDB.UniQuery1.Eof do
  begin
    ADDRESS := TAddressItem.Create;
    TXDataOperationContext.Current.Handler.ManagedObjects.Add( ADDRESS );
    ADDRESS.ADDRESS := ordersDB.UniQuery1.FieldByName('ship_block').AsString;
    result.ADDRESS_LIST.Add(ADDRESS);
    ordersDB.UniQuery1.Next;
  end;

  ordersDB.UniQuery1.Close;

  result.ITEMS := GetItems('');


end;

function TLookupService.GetCuttingDieOrder(orderInfo: string): TCuttingDie;
var
  orderType: string;
  orderID: string;
  SQL: string;
  ADDRESS: TAddressItem;
begin
  orderID := orderInfo;
  SQL := 'select * from cutting_die_orders o JOIN customers c ON c.CUSTOMER_ID = o.COMPANY_ID where ORDER_ID = ' + quotedStr(orderID);
  doQuery(ordersDB.UniQuery1, SQL);

  result := TCuttingDie.Create;

  // Company
  result.COMPANY_ID := ordersDB.UniQuery1.FieldByName('COMPANY_ID').AsInteger;
  result.NAME := ordersDB.UniQuery1.FieldByName('NAME').AsString;
  result.SHORT_NAME := ordersDB.UniQuery1.FieldByName('SHORT_NAME').AsString;
  result.inQuickBooks := '?';

  // Staff Fields
  result.staff_fields_order_date := ordersDB.UniQuery1.FieldByName('staff_fields_order_date').AsString;
  result.staff_fields_proof_date := ordersDB.UniQuery1.FieldByName('staff_fields_proof_date').AsString;
  result.staff_fields_ship_date := ordersDB.UniQuery1.FieldByName('staff_fields_ship_date').AsString;
  result.staff_fields_ship_via := ordersDB.UniQuery1.FieldByName('staff_fields_ship_via').AsString;
  result.staff_fields_quantity := ordersDB.UniQuery1.FieldByName('staff_fields_quantity').AsString;
  result.staff_fields_ship_to := ordersDB.UniQuery1.FieldByName('staff_fields_ship_to').AsString;
  result.staff_fields_po_number := ordersDB.UniQuery1.FieldByName('staff_fields_po_number').AsString;
  result.staff_fields_job_name := ordersDB.UniQuery1.FieldByName('staff_fields_job_name').AsString;
  result.staff_fields_quickbooks_item := ordersDB.UniQuery1.FieldByName('staff_fields_quickbooks_item').AsString;
  result.staff_fields_price := ordersDB.UniQuery1.FieldByName('staff_fields_price').AsString;
  result.staff_fields_invoice_to := ordersDB.UniQuery1.FieldByName('staff_fields_invoice_to').AsString;

  result.general_special_instructions := ordersDB.UniQuery1.FieldByName('general_special_instructions').AsString;

  ordersDB.UniQuery1.Close;

  SQL := 'SELECT s.ship_block FROM customers c JOIN customers_ship s ON c.CUSTOMER_ID = s.customer_id WHERE c.CUSTOMER_ID = ' + IntToStr(result.COMPANY_ID);
  doQuery(ordersDB.UniQuery1, SQL);
  result.ADDRESS_LIST := TList<TAddressItem>.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add( Result.ADDRESS_LIST );

  while not ordersDB.UniQuery1.Eof do
  begin
    ADDRESS := TAddressItem.Create;
    TXDataOperationContext.Current.Handler.ManagedObjects.Add( ADDRESS );
    ADDRESS.ADDRESS := ordersDB.UniQuery1.FieldByName('ship_block').AsString;
    result.ADDRESS_LIST.Add(ADDRESS);
    ordersDB.UniQuery1.Next;
  end;

  ordersDB.UniQuery1.Close;

  result.ITEMS := GetItems('');
end;

function TLookupService.GetItems(searchOptions: string): TItemList;
// retrueves all the quickbooks items for the items page on client.
// searchOptions: probably not needed but adds limits to the page to prevent
// table on client side from getting too long. This table currently has about 27
// entries so probably not needed.
var
  params: TStringList;
  PageNum: integer;
  PageSize: integer;
  OrderBy: string;
  offset: string;
  limit: string;
  SQL: string;
  item: TItemItem;
begin
  params := TStringList.Create;
  params.StrictDelimiter := true;
  // parse the searchOptions
  params.Delimiter := '&';
  params.DelimitedText := searchOptions;

  SQL := 'select * from qb_items';

  if (  ( params.Values['pagenumber'] <> '' ) and ( params.Values['pagesize'] <> '' ) ) then
  begin
    pageNum := StrToInt(params.Values['pagenumber']);
    PageSize := StrToInt(params.Values['pagesize']);
    OrderBy := params.Values['orderby'];


    limit := IntToStr(PageSize);
    offset := IntToStr((PageNum - 1) * PageSize);
    SQL := SQL + ' limit ' + limit + ' offset ' + offset;
  end;


  doQuery(ordersDB.UniQuery1, SQL);

  Result:= TItemList.Create;
  Result.data := TList<TItemItem>.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add( Result.data );

  while not ordersDB.UniQuery1.Eof do
  begin
    item := TItemItem.Create;
    TXDataOperationContext.Current.Handler.ManagedObjects.Add( item );
    Result.data.Add( item );
    item.ID := ordersDB.UniQuery1.FieldByName('qb_items_id').AsString;
    item.name := ordersDB.UniQuery1.FieldByName('qb_item_name').AsString;
    item.description := ordersDB.UniQuery1.FieldByName('item_desc').AsString;
    item.status := ordersDB.UniQuery1.FieldByName('status').AsString;

    ordersDB.UniQuery1.Next;
  end;
  ordersDB.UniQuery1.Close;
  SQL:= 'select count(*) as total_count from qb_items';
  doQuery(ordersDB.UniQuery1, SQL);
  Result.count := ordersDB.UniQuery1.FieldByName('total_count').AsInteger;
  ordersDB.UniQuery1.Close;
end;

function TLookupService.GetUsers(searchOptions: string): TUserList;
// Gets a list of all users for the user edit page.
// searchOptions: username of a specific user otherwise empty.
// TODO: change SQL to hide passwords instead of storing them in plain text.
var
  SQL: string;
  user: TUserItem;
begin
  if searchOptions = '' then
    SQL := 'select * from users order by NAME ASC'
  else
    SQL := 'select * from users where username=' + quotedStr(searchOptions);
  doQuery(ordersDB.UniQuery1, SQL);

  Result:= TUserList.Create;
  Result.data := TList<TUserItem>.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add( Result.data );

  while not ordersDB.UniQuery1.Eof do
    begin
      user := TUserItem.Create;
      TXDataOperationContext.Current.Handler.ManagedObjects.Add( user );
      Result.data.Add( user );
      user.userID := ordersDB.UniQuery1.FieldByName('USER_ID').AsString;
      user.username := ordersDB.UniQuery1.FieldByName('USER_NAME').AsString;
      user.password := ordersDB.UniQuery1.FieldByName('PASSWORD').AsString;
      user.full_name := ordersDB.UniQuery1.FieldByName('NAME').AsString;
      user.status := ordersDB.UniQuery1.FieldByName('STATUS').AsString;
      user.email_address := ordersDB.UniQuery1.FieldByName('EMAIL').AsString;
      user.AType := ordersDB.UniQuery1.FieldByName('ACCESS_TYPE').AsString;
      user.rights := ordersDB.UniQuery1.FieldByName('SYSTEM_RIGHTS').AsInteger;
      user.perspectiveID := ordersDB.UniQuery1.FieldByName('PERSPECTIVE_ID').AsString;
      user.QBID := ordersDB.UniQuery1.FieldByName('QB_ID').AsString;
      ordersDB.UniQuery1.Next;
    end;
    ordersDB.UniQuery1.Close;
    SQL:= 'select count(*) as total_count from users';
    doQuery(ordersDB.UniQuery1, SQL);
    Result.count := ordersDB.UniQuery1.FieldByName('total_count').AsInteger;
    ordersDB.UniQuery1.Close;
end;

function TLookupService.EditUser(const editOptions: string): string;
// Edits the user.
// editOptions: all user information that will be changed.
var
  params: TStringList;
  user: string;
  password: string;
  full_name: string;
  status: string;
  email: string;
  access: string;
  rights: string;
  perspective: string;
  QB: string;
  SQL: string;
  newUser: string;
  hashString: string;
  hashPW: string;
begin
  params := TStringList.Create;
  // parse the searchOptions
  params.Delimiter := '&';
  params.StrictDelimiter := true;
  params.DelimitedText := editOptions;
  user := params.Values['username'];
  password := params.Values['password'];
  full_name := params.Values['fullname'];
  status := params.Values['status'];
  email := params.Values['email'];
  access := params.Values['access'];
  rights := params.Values['rights'];
  perspective := params.Values['perspective'];
  QB := params.Values['QB'];
  newUser := params.Values['newuser'];

  SQL := 'select * from users where USER_NAME = ' + QuotedStr(user);
  doQuery(ordersDB.UniQuery1, SQL);

  if ordersDB.UniQuery1.IsEmpty then
    Result := 'Failure:No such user found'
  else
  begin
    ordersDB.UniQuery1.Edit;

    //user.password := ordersDB.UniQuery1.FieldByName('PASSWORD').AsString;

    if not newUser.IsEmpty then
      ordersDB.UniQuery1.FieldByName('USER_NAME').AsString := newUser;

    if not full_name.IsEmpty then
      ordersDB.UniQuery1.FieldByName('NAME').AsString := full_name;

    if not status.IsEmpty then
    begin
      if StrToBool(status) then
        ordersDB.UniQuery1.FieldByName('STATUS').AsString := 'ACTIVE'
      else
        ordersDB.UniQuery1.FieldByName('STATUS').AsString := 'INACTIVE'
    end;

    if not email.IsEmpty then
      ordersDB.UniQuery1.FieldByName('EMAIL').AsString := email;

    if not access.IsEmpty then
      ordersDB.UniQuery1.FieldByName('ACCESS_TYPE').AsString := Access;

    if not rights.IsEmpty then
      ordersDB.UniQuery1.FieldByName('SYSTEM_RIGHTS').AsInteger := StrToInt(rights);

    if not perspective.IsEmpty then
      ordersDB.UniQuery1.FieldByName('PERSPECTIVE_ID').AsString := perspective;

    if not QB.IsEmpty then
      ordersDB.UniQuery1.FieldByName('QB_ID').AsString := QB;

    {if((not (Password = 'hidden')) and (not (Password.IsEmpty))) then
    begin
      hashString :=  ordersDB.UniQuery1.FieldByName('date_created').AsString + password;
      hashPW := THashSHA2.GetHashString(hashString, THashSHA2.TSHA2Version.SHA512).ToUpper;
      ordersDB.UniQuery1.FieldByName('password').AsString := hashPW;
    end;}

    ordersDB.UniQuery1.Post;
    Result := 'Success:Edit Successful';
  end;
  ordersDB.UniQuery1.Close;
end;

procedure TLookupService.AddToOrdersTable(mode, ORDER_TYPE: string; JSONData: TJSONObject);
var
SQL: string;
begin
  if mode = 'ADD' then
    begin
      SQL := 'select * from orders where ORDER_ID = 0 and ORDER_ID <> 0';
      doQuery(ordersDB.UniQuery1, SQL);
      ordersDB.UniQuery1.Insert;
    end
    else
    begin
      SQL := 'select * from orders where ORDER_ID = ' + JSONData.GetValue<string>('ORDER_ID');
      doQuery(ordersDB.UniQuery1, SQL);
      ordersDB.UniQuery1.Edit;
    end;

    ordersDB.UniQuery1.FieldByName('COMPANY_ID').AsString := JSONData.GetValue<string>('COMPANY_ID');
    ordersDB.UniQuery1.FieldByName('ORDER_TYPE').AsString := ORDER_TYPE;

    if mode = 'ADD' then
      ordersDB.UniQuery1.FieldByName('ORDER_DATE').AsDateTime := Now
    else
    begin
      // No idea why I need this line but without it an error gets thrown
      //ordersDB.UniQuery1.FieldByName('ORDER_DATE').AsDateTime := ordersDB.UniQuery1.FieldByName('ORDER_DATE').AsDateTime;
    end;

    if JSONData.GetValue<string>('staff_fields_price') = '' then
      ordersDB.UniQuery1.FieldByName('PRICE').AsString := '0'
    else
      ordersDB.UniQuery1.FieldByName('PRICE').AsString := JSONData.GetValue<string>('staff_fields_price');

    ordersDB.UniQuery1.FieldByName('JOB_NAME').AsString := JSONData.GetValue<string>('staff_fields_job_name');
    ordersDB.UniQuery1.FieldByName('USER_ID').AsString := JSONData.GetValue<string>('USER_ID');
    ordersDB.UniQuery1.FieldByName('LOCATION').AsString := '';//JSONData.GetValue<string>('staff_fields_art_location');

    ordersDB.UniQuery1.Post;

    ordersDB.UniQuery1.Close;
end;

function TLookupService.AddCorrugatedOrder(orderInfo: string): TJSONObject;
// Adds corrugated order to the database. This process is done in 3 different
// tables so if any changes are made make sure to check orders, corrugated_plate_orders
// and orders_status_schedule. This also functions as an edit function.
// orderInfo - all the inputted order information from client side.
var
  JSONData: TJSONObject;
  SQL: string;
  Pair: TJSONPair;
  Field: TField;
  DateFormat: TFormatSettings;
  ORDER_ID: integer;
  mode: string;
begin
  DateFormat := TFormatSettings.Create;
  DateFormat.ShortDateFormat := 'yyyy-mm-dd';
  DateFormat.DateSeparator := '-';
  JSONData := TJSONObject.ParseJSONValue(orderInfo) as TJSONObject;
  if JSONData = nil then
    raise Exception.Create('Invalid JSON format');  // If parsing fails, raise an exception
  mode := JSONData.GetValue<string>('mode');

  AddToOrdersTable(mode, 'corrugated_plate', JSONData);

  if mode = 'ADD' then
  begin
    ordersDB.UniQuery1.SQL.Text := 'SELECT LAST_INSERT_ID() AS OrderID'; // Use database's method to get the last inserted ID
    ordersDB.UniQuery1.Open;
    ORDER_ID := ordersDB.UniQuery1.FieldByName('OrderID').AsInteger;
  end;

  if mode = 'ADD' then
    SQL := 'select * from corrugated_plate_orders where ORDER_ID = 0 and ORDER_ID <> 0'
  else
  begin
    ORDER_ID := JSONData.GetValue<integer>('ORDER_ID');
    SQL := 'select * from corrugated_plate_orders where ORDER_ID = ' + IntToStr(ORDER_ID);
  end;
  doQuery(ordersDB.UniQuery1, SQL);
  try
    if mode = 'ADD' then
      ordersDB.UniQuery1.Insert
    else
      ordersDB.UniQuery1.Edit;

    for Pair in JSONData do
    begin
      Field := ordersDB.UniQuery1.FindField(Pair.JsonString.Value); // Checks if the field exists in the dataset
      if Assigned(Field) then
      begin
      // handles any dates or datetimes
        if (Field is TDateTimeField) and (Pair.JsonValue.Value <> '') then
          TDateTimeField(Field).AsDateTime := StrToDate(Pair.JsonValue.Value)
        else if Pair.JsonValue.Value <> '' then
          Field.AsString := Pair.JsonValue.Value;
      end;
    end;

    ordersDB.UniQuery1.FieldByName('ORDER_ID').AsInteger := ORDER_ID;

    // Post the record to the database
    ordersDB.UniQuery1.Post;

    if JSONData.GetValue<string>('staff_fields_proof_date') <> '' then
      AddStatusSchedule('PROOF', JSONData, ORDER_ID);
    if JSONData.GetValue<string>('staff_fields_ship_date') <> '' then
      AddStatusSchedule('SHIP', JSONData, ORDER_ID);
    if JSONData.GetValue<string>('staff_fields_art_due') <> '' then
      AddStatusSchedule('ART', JSONData, ORDER_ID);
    if JSONData.GetValue<string>('staff_fields_plate_due') <> '' then
      AddStatusSchedule('PLATE', JSONData, ORDER_ID);

    Result := TJSONObject.Create.AddPair('status', 'success');
    TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
    except
    on E: Exception do
    begin
      Result := TJSONObject.Create.AddPair('error', E.Message);
    end;
  end;
end;



function TLookupService.AddStatusSchedule(StatusType: string; order: TJSONObject; ORDER_ID: integer): string;
// Adds/edits orders_status_schedule table.
// StatusType: name of the status getting added to the schedule.
// Order: JSON object containing all of the order info sent from client
// ORDER_ID: order id sent from client if we are in edit mode empty if in add mode
var
  SQL: string;
  mode: string;
  change: boolean;
begin
  mode := order.GetValue<string>('mode');
  if mode = 'ADD' then
  begin
    SQL := 'select * from orders_status_schedule where ORDER_ID = 0 and ORDER_ID <> 0';
    doQuery(ordersDB.UniQuery1, SQL);
    ordersDB.UniQuery1.Insert;
  end
  else
  begin
    SQL := 'select * from orders_status_schedule where ORDER_ID = ' + IntToStr(ORDER_ID) + ' AND ORDER_STATUS = ' + quotedStr(StatusType);
    doQuery(ordersDB.UniQuery1, SQL);
    ordersDB.UniQuery1.Edit;
  end;

  ordersDB.UniQuery1.FieldByName('ORDER_ID').AsInteger := ORDER_ID;
  ordersDB.UniQuery1.FieldByName('ORDER_STATUS').AsString := StatusType;

  if( (StatusType = 'PROOF') or (StatusType = 'SHIP') ) then
  begin
    change := ordersDB.UniQuery1.FieldByName('STATUS_DATE').AsString <> order.GetValue<string>('staff_fields_'+ StatusType.ToLower +'_date');
    ordersDB.UniQuery1.FieldByName('STATUS_DATE').AsString := order.GetValue<string>('staff_fields_'+ StatusType.ToLower +'_date');
    if mode <> 'EDIT' then
      ordersDB.UniQuery1.FieldByName('ORIGINAL_STATUS_DATE').AsString := order.GetValue<string>('staff_fields_'+ StatusType.ToLower +'_date');
  end
  else
  begin
    change := ordersDB.UniQuery1.FieldByName('STATUS_DATE').AsDateTime <> StrToDateTime(order.GetValue<string>('staff_fields_'+ StatusType.ToLower +'_due'));
    ordersDB.UniQuery1.FieldByName('STATUS_DATE').AsDateTime := StrToDateTime(order.GetValue<string>('staff_fields_'+ StatusType.ToLower +'_due'));
    if mode <> 'EDIT' then
      ordersDB.UniQuery1.FieldByName('ORIGINAL_STATUS_DATE').AsDateTime := StrToDateTime(order.GetValue<string>('staff_fields_'+ StatusType.ToLower +'_due'));
  end;
  ordersDB.UniQuery1.FieldByName('USER_ID').AsString := order.GetValue<string>('USER_ID');

  if mode = 'ADD' then
    ordersDB.UniQuery1.FieldByName('ORDER_REVISION').AsInteger := 1
  else
  begin
    if change then
      ordersDB.UniQuery1.FieldByName('ORDER_REVISION').AsInteger := ordersDB.UniQuery1.FieldByName('ORDER_REVISION').AsInteger + 1;
  end;

  ordersDB.UniQuery1.Post;

  Result := 'success';
end;

function TLookupService.SetStatus(statusOptions: string): string;
var
  params: TStringList;
  StatusInfo: TJSONObject;
  ORDER_ID: integer;
  Date: String;
  Status: string;
  UserID: string;
  SQL: string;
  temp: string;
begin
  try
    logger.Log(1, 'Set Status Hit');
    StatusInfo := TJSONObject.ParseJSONValue(statusOptions) as TJSONObject;
    params := TStringList.Create;
    // parse the statusOptions
    params.Delimiter := '&';
    params.StrictDelimiter := true;
    params.DelimitedText := statusOptions;
    ORDER_ID := StatusInfo.GetValue<integer>('ORDER_ID');
    Date := StatusInfo.GetValue<string>('date');
    Status := StatusInfo.GetValue<string>('status');
    UserID := StatusInfo.GetValue<string>('USER_ID');

    SQL := 'select * from orders_status where ORDER_ID = ' + IntToStr(ORDER_ID) + ' AND ' +
            'ORDER_STATUS = ' + quotedStr(Status);

    logger.Log(1, 'Trying to Set Status');
    doQuery(ordersDB.UniQuery1, SQL);

    if ordersDB.UniQuery1.IsEmpty then
    // Add Status
    begin
      ordersDB.UniQuery1.Insert;
      ordersDB.UniQuery1.FieldByName('ORDER_ID').AsString := IntToStr(ORDER_ID);
      ordersDB.UniQuery1.FieldByName('ORDER_STATUS').AsString := Status;
      ordersDB.UniQuery1.FieldByName('STATUS_DATE').AsDateTime := StrToDateTime(Date);
      ordersDB.UniQuery1.FieldByName('STATUS_TIMESTAMP').AsDateTime := Now;
      ordersDB.UniQuery1.FieldByName('USER_ID').AsString := UserID;
      ordersDB.UniQuery1.FieldByName('ORDER_REVISION').AsInteger := 1;
    end
    else
    // Edit Status
    begin
      ordersDB.UniQuery1.Edit;
      ordersDB.UniQuery1.FieldByName('STATUS_DATE').AsDateTime := StrToDateTime(Date);
      ordersDB.UniQuery1.FieldByName('STATUS_TIMESTAMP').AsDateTime := Now;
      ordersDB.UniQuery1.FieldByName('ORDER_REVISION').AsInteger := ordersDB.UniQuery1.FieldByName('ORDER_REVISION').AsInteger + 1;
    end;

    ordersDB.UniQuery1.Post;

    if StatusInfo.GetValue<string>('staff_fields_ship_date') <> '12/30/1899' then
      AddStatusSchedule('SHIP', StatusInfo, ORDER_ID);
    if StatusInfo.GetValue<string>('staff_fields_art_due') <> '12/30/1899' then
      AddStatusSchedule('ART', StatusInfo, ORDER_ID);
    if StatusInfo.GetValue<string>('staff_fields_plate_due') <> '12/30/1899' then
      AddStatusSchedule('PLATE', StatusInfo, ORDER_ID);
    if StatusInfo.GetValue<string>('staff_fields_mount_due') <> '12/30/1899' then
      AddStatusSchedule('MOUNT', StatusInfo, ORDER_ID);

    logger.Log(1, 'Status Successfully set');
    result := 'success';
  except
  on E: Exception do
    // This block will catch any exception derived from the Exception class
    logger.Log(5, 'An error occurred when setting status: ' + E.Message);
  end;

end;

function TLookupService.AddUser(userInfo:string): string;
// Adds a user to the database
// userInfo - user information being added
var
  user: string;
  password: string;
  full_name: string;
  status: string;
  email: string;
  access: string;
  rights: string;
  perspective: string;
  QB: string;
  SQL: string;
  dateCreated: TDateTime;
  hashString: string;
  hashPW: string;
  params: TStringList;
begin
  params := TStringList.Create;
  params.StrictDelimiter := true;
  // parse the searchOptions
  params.Delimiter := '&';
  params.DelimitedText := userInfo;
  dateCreated := now;
  user := params.Values['username'];
  password := params.Values['password'];
  full_name := params.Values['fullname'];
  status := params.Values['status'];
  email := params.Values['email'];
  access := params.Values['access'];
  rights := params.Values['rights'];
  perspective := params.Values['perspective'];
  QB := params.Values['QB'];
  //newUser := params.Values['newuser'];
  //hashString :=  DateTimeToStr(dateCreated) + params.Values['password'];
  //hashPW := THashSHA2.GetHashString(hashString, THashSHA2.TSHA2Version.SHA512).ToUpper;
  SQL := 'select * from users where USER_NAME = ' + QuotedStr(params.Values['username'].toLower);
  ordersDB.UniQuery1.Close;
  ordersDB.UniQuery1.SQL.Text := SQL;
  ordersDB.UniQuery1.Open;
  if ordersDB.UniQuery1.IsEmpty then
  begin
    ordersDB.UniQuery1.Insert;

    ordersDB.UniQuery1.FieldByName('USER_NAME').AsString := user;
    ordersDB.UniQuery1.FieldByName('PASSWORD').AsString := password;
    //THashSHA2.GetHashString(hashString, THashSHA2.TSHA2Version.SHA512).ToUpper;
    //ordersDB.UniQuery1.FieldByName('date_created').AsString := DateTimeToStr(dateCreated);
    ordersDB.UniQuery1.FieldByName('NAME').AsString := full_name;

    if( StrToBool(status) ) then
      ordersDB.UniQuery1.FieldByName('STATUS').AsString := 'ACTIVE'
    else
      ordersDB.UniQuery1.FieldByName('STATUS').AsString := 'INACTIVE';

    ordersDB.UniQuery1.FieldByName('EMAIL').AsString := email;
    ordersDB.UniQuery1.FieldByName('ACCESS_TYPE').AsString := Access;
    ordersDB.UniQuery1.FieldByName('SYSTEM_RIGHTS').AsInteger := StrToInt(rights);
    ordersDB.UniQuery1.FieldByName('PERSPECTIVE_ID').AsString := perspective;
    ordersDB.UniQuery1.FieldByName('QB_ID').AsString := QB;

    ordersDB.UniQuery1.Post;
    Result := 'Success:User successfully added';
  end
  else
    Result := 'Failure:Username already taken';
end;

function TLookupService.AddItem(itemInfo: string): string;
// Adds an item to the database
// itemInfo: item info to add to database
var
  params: TStringList;
  Name: string;
  Description: string;
  Status: boolean;
  SQL: string;
begin
  params := TStringList.Create;
  params.StrictDelimiter := true;
  // parse the searchOptions
  params.Delimiter := '&';
  params.DelimitedText := itemInfo;

  Name := params.Values['name'];
  Description := params.Values['description'];
  Status := StrToBool(params.Values['status']);

  SQL := 'select * from qb_items where qb_item_name = ' + QuotedStr(Name);

  doQuery(ordersDB.UniQuery1, SQL);
  if ordersDB.UniQuery1.IsEmpty then
  begin
    ordersDB.UniQuery1.Insert;

    ordersDB.UniQuery1.FieldByName('qb_item_name').AsString := Name;
    ordersDB.UniQuery1.FieldByName('item_desc').AsString := Description;
    if Status then
      ordersDB.UniQuery1.FieldByName('status').AsString := 'ACTIVE'
    else
      ordersDB.UniQuery1.FieldByName('status').AsString := 'INACTIVE';

    ordersDB.UniQuery1.Post;
    Result := 'Success:Item successfully added';
  end
  else
    Result := 'Failure: Item already exists';
end;

function TLookupService.DelUser(username: string): string;
// deletes a user. not currently implemented definitely needs touching up to avoid
// deleting users prematurely.
// username: username to be deleted.
var
SQL: string;
params: TStringList;
begin
  SQL := 'select * from users where username = ' + QuotedStr(username.toLower);
  ordersDB.UniQuery1.Close;
  ordersDB.UniQuery1.SQL.Text := SQL;
  ordersDB.UniQuery1.Open;
  if ordersDB.UniQuery1.IsEmpty then
  begin
    Result := 'Failure:User does not exist';
  end
  else
  begin
    SQL:= 'DELETE FROM users where username = ' +  QuotedStr(username.toLower);
    ordersDB.UniQuery1.SQL.Text := SQL;
    ordersDB.UniQuery1.ExecSQL;
    Result := 'Success:User deleted';
  end;
end;


function TLookupService.AddWebOrder(orderInfo: string): TJSONObject;
// Adds corrugated order to the database. This process is done in 3 different
// tables so if any changes are made make sure to check orders, corrugated_plate_orders
// and orders_status_schedule. This also functions as an edit function.
// orderInfo - all the inputted order information from client side.
var
  JSONData, ResponseData: TJSONObject;
  SQL: string;
  Pair: TJSONPair;
  Field: TField;
  DateFormat: TFormatSettings;
  CurrDate: TDateTime;
  ORDER_ID: integer;
  mode: string;
  temp: string;
begin
  DateFormat := TFormatSettings.Create;
  DateFormat.ShortDateFormat := 'yyyy-mm-dd';
  DateFormat.DateSeparator := '-';
  JSONData := TJSONObject.ParseJSONValue(orderInfo) as TJSONObject;
  if JSONData = nil then
    raise Exception.Create('Invalid JSON format');  // If parsing fails, raise an exception
  mode := JSONData.GetValue<string>('mode');

  AddToOrdersTable(mode, 'web_plate', JSONData);

  if mode = 'ADD' then
  begin
    ordersDB.UniQuery1.SQL.Text := 'SELECT LAST_INSERT_ID() AS OrderID'; // Use database's method to get the last inserted ID
    ordersDB.UniQuery1.Open;
    ORDER_ID := ordersDB.UniQuery1.FieldByName('OrderID').AsInteger;
  end;

  if mode = 'ADD' then
    SQL := 'select * from web_plate_orders where ORDER_ID = 0 and ORDER_ID <> 0'
  else
  begin
    ORDER_ID := JSONData.GetValue<integer>('ORDER_ID');
    SQL := 'select * from web_plate_orders where ORDER_ID = ' + IntToStr(ORDER_ID);
  end;
  doQuery(ordersDB.UniQuery1, SQL);
  try
    if mode = 'ADD' then
      ordersDB.UniQuery1.Insert
    else
      ordersDB.UniQuery1.Edit;

    for Pair in JSONData do
    begin
      Field := ordersDB.UniQuery1.FindField(Pair.JsonString.Value); // Checks if the field exists in the dataset
      if Assigned(Field) then
      begin
      // handles any dates or datetimes
        if (Field is TDateTimeField) and (Pair.JsonValue.Value <> '') then
          TDateTimeField(Field).AsDateTime := StrToDate(Pair.JsonValue.Value)
        else if Pair.JsonValue.Value <> '' then
          Field.AsString := Pair.JsonValue.Value;
      end;
    end;

    ordersDB.UniQuery1.FieldByName('ORDER_ID').AsInteger := ORDER_ID;

    // Post the record to the database
    ordersDB.UniQuery1.Post;

    if JSONData.GetValue<string>('staff_fields_proof_date') <> '' then
      AddStatusSchedule('PROOF', JSONData, ORDER_ID);
    if JSONData.GetValue<string>('staff_fields_ship_date') <> '' then
      AddStatusSchedule('SHIP', JSONData, ORDER_ID);
    if JSONData.GetValue<string>('staff_fields_art_due') <> '' then
      AddStatusSchedule('ART', JSONData, ORDER_ID);
    if JSONData.GetValue<string>('staff_fields_plate_due') <> '' then
      AddStatusSchedule('PLATE', JSONData, ORDER_ID);

    Result := TJSONObject.Create.AddPair('status', 'success');
    TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
    except
    on E: Exception do
    begin
      Result := TJSONObject.Create.AddPair('error', E.Message);
    end;
  end;
end;

function TLookupService.AddCuttingDieOrder(orderInfo: string): TJSONObject;
var
  JSONData, ResponseData: TJSONObject;
  SQL: string;
  Pair: TJSONPair;
  Field: TField;
  DateFormat: TFormatSettings;
  CurrDate: TDateTime;
  ORDER_ID: integer;
  mode: string;
  temp: string;
begin
  DateFormat := TFormatSettings.Create;
  DateFormat.ShortDateFormat := 'yyyy-mm-dd';
  DateFormat.DateSeparator := '-';
  JSONData := TJSONObject.ParseJSONValue(orderInfo) as TJSONObject;
  if JSONData = nil then
    raise Exception.Create('Invalid JSON format');  // If parsing fails, raise an exception
  mode := JSONData.GetValue<string>('mode');
  ORDER_ID := JSONData.GetValue<integer>('ORDER_ID');

  AddToOrdersTable(mode, 'cutting_die', JSONData);

  if mode = 'ADD' then
  begin
    ordersDB.UniQuery1.SQL.Text := 'SELECT LAST_INSERT_ID() AS OrderID'; // Use database's method to get the last inserted ID
    ordersDB.UniQuery1.Open;
    ORDER_ID := ordersDB.UniQuery1.FieldByName('OrderID').AsInteger;
  end;

  if mode = 'ADD' then
    SQL := 'select * from cutting_die_orders where ORDER_ID = 0 and ORDER_ID <> 0'
  else
  begin
    ORDER_ID := JSONData.GetValue<integer>('ORDER_ID');
    SQL := 'select * from cutting_die_orders where ORDER_ID = ' + IntToStr(ORDER_ID);
  end;
  doQuery(ordersDB.UniQuery1, SQL);
  try
    if mode = 'ADD' then
      ordersDB.UniQuery1.Insert
    else
      ordersDB.UniQuery1.Edit;

    for Pair in JSONData do
    begin
      Field := ordersDB.UniQuery1.FindField(Pair.JsonString.Value); // Checks if the field exists in the dataset
      if Assigned(Field) then
      begin
      // handles any dates or datetimes
        if (Field is TDateTimeField) and (Pair.JsonValue.Value <> '') then
          TDateTimeField(Field).AsDateTime := StrToDate(Pair.JsonValue.Value)
        else if Pair.JsonValue.Value <> '' then
          Field.AsString := Pair.JsonValue.Value;
      end;
    end;

    ordersDB.UniQuery1.FieldByName('ORDER_ID').AsInteger := ORDER_ID;

    // Post the record to the database
    ordersDB.UniQuery1.Post;

    if JSONData.GetValue<string>('staff_fields_proof_date') <> '' then
      AddStatusSchedule('PROOF', JSONData, ORDER_ID);
    if JSONData.GetValue<string>('staff_fields_ship_date') <> '' then
      AddStatusSchedule('SHIP', JSONData, ORDER_ID);

    Result := TJSONObject.Create.AddPair('status', 'success');
    TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
    except
    on E: Exception do
    begin
      Result := TJSONObject.Create.AddPair('error', E.Message);
    end;
  end;
end;

initialization
  RegisterServiceType(TLookupService);

end.

