// Implementation of the Lookup Service interface used to send call information
// to the client.
// Authors:
// Cameron Hayes
// Mac ...
// 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, rOrders, 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 EditUser(const editOptions: string): string;
    function Search(phoneNum: string): TOrderList;
    function GenerateReportPDF(searchOptions: string): string;
    function AddUser(userInfo: string): string;
    function AddItem(itemInfo: string): string;
    function DelUser(username: string): string;
    function GenerateSelectSQL(filterType, startDate, endDate: string): string;
    function GenerateSubQuery(currStatus: string): string;
    function AddCorrugatedOrder(orderInfo: string): TJSONObject;
    function AddStatusSchedule(StatusType: string; order: TJSONObject; ORDER_ID: integer): string;
    function EditStatusSchedule(StatusType: string; order: TJSONObject; ORDER_ID: string): 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;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  end;

implementation

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

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

end;

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

function TLookupService.GetCustomers: TCustomerList;
var
  SQL: string;
  customer: TCustomerItem;
begin
  SQL := 'select NAME, CUSTOMER_ID, SHORT_NAME, BILL_ADDRESS, BILL_CITY, BILL_STATE, BILL_ZIP 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;
    result.data.Add(customer);
    result.count := result.count + 1;
    ordersDB.UniQuery1.Next;
  end;
  ordersDB.UniQuery1.Close;
end;

function TLookupService.GetCustomer(ID: string): TCustomerItem;
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.Search(phoneNum: string): TOrderList;
// Searchs the database for a specific from phone number then returns a list of
// calls from said number
// SQL: String of the sql query used to retrieve info from the database
// call: object representing the call information we want to send to the client
// Return: A list of calls
var
SQL: string;
order: TOrderItem;
begin
  SQL := 'select * ' +
          'from calls inner join recordings on calls.sid = recordings.call_sid ' +
         'where from_formatted = ' + QuotedStr(PhoneNum)  + ' order by calls.date_created desc';

  doQuery(ordersDB.UniQuery1, SQL);

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

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

  ordersDB.UniQuery1.Close;
  SQL:= '';

  doQuery(ordersDB.UniQuery1, SQL);

  Result.count := ordersDB.UniQuery1.FieldByName('total_count').AsInteger;
end;


function TLookupService.GenerateReportPDF(searchOptions: string): string;
var
  SQL: string;
  rptOrders: TrptOrders; // Local instance of rptOrders
begin
  // Create instance of rptOrders
  rptOrders := TrptOrders.Create(nil);
  try
    // Generate SQL dynamically using the existing GetOrders logic
    SQL := GenerateOrdersSQL(searchOptions).SQL;

    // Prepare the report dataset
    result := rptOrders.PrepareReport(SQL);

    // Generate the PDF report
    //rptOrders.GeneratePDF;

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


function TLookupService.generateSelectSQL(filterType, startDate, endDate: string): string;
begin


end;

function TLookupService.generateSubQuery(currStatus: string): string;
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;
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(endDate) + ' <= 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;
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;
begin
  result := ' AND ';
  if status.null then
  begin
    result := result + '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) + ') AND ' +
    generateStatusSelectSQL(status.altStatusTableShort, status.altStatusTableLong, status.startDate, status.endDate, status.statusType);
  end
  else
    result := result + generateStatusSelectSQL(status.statusTableShort, status.statusTableLong, status.startDate, status.endDate, status.statusType);
end;

function TLookupService.generateOrdersSQL(searchOptions: string): TSQLQuery;
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, ';

  // 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 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
    whereSQL := whereSQL + ' AND o.ORDER_TYPE = ' + quotedStr(orderType + '_plate');

  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 ' + 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;
var
  colorObject: TJSONObject;
  colorList: TJSONArray;
  temp: string;
  temp2: string;
begin
  if colors = '' then
    result := '0'
  else
  begin
    colorObject := TJSONObject.ParseJSONValue(colors) as TJSONObject;
    colorList := TJSONArray(colorObject.GetValue('items'));
    //temp := colorList.toString;
    result := IntToStr(colorList.Count);
  end;
end;

function TLookupService.GetOrders(searchOptions: string): TOrderList;
var
  SQL: string;
  whereSQL: string;
  Order: TOrderItem;
  colors: string;
  ColorType: string;
  SQLArray: TArray<string>;
  SQLQuery: TSQLQuery;
begin
  SQLQuery := generateOrdersSQL(searchOptions);

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

  doQuery(ordersDB.UniQuery1, SQL);

  Result:= TOrderList.Create;
  Result.data := TList<TOrderItem>.Create;
  TXDataOperationContext.Current.Handler.ManagedObjects.Add( Result.data );
  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;
end;

function TLookupService.GetOrder(orderInfo: string): TFullOrder;
var
  orderType: string;
  orderID: string;
  SQL: string;
  table: string;
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.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
end;

function TLookupService.GetItems(searchOptions: string): TItemList;
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;

  PageNum := StrToInt(params.Values['pagenumber']);
  PageSize := StrToInt(params.Values['pagesize']);
  OrderBy := params.Values['orderby'];

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

  SQL := 'select * from qb_items ' + 'limit ' + limit + ' offset ' + offset;

  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;
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;
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;

function TLookupService.AddCorrugatedOrder(orderInfo: string): TJSONObject;
var
  JSONData, ResponseData: TJSONObject;
  SQL: string;
  Pair: TJSONPair;
  Field: TField;
  DateFormat: TFormatSettings;
  CurrDate: TDateTime;
  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');
  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;

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

    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 = ' + IntToStr(ORDER_ID);
      doQuery(ordersDB.UniQuery1, SQL);
    end;

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

    if mode = 'ADD' then
      ordersDB.UniQuery1.FieldByName('ORDER_DATE').AsDateTime := Now;

    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;
    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 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);
    if JSONData.GetValue<string>('staff_fields_mount_due') <> '' then
      AddStatusSchedule('MOUNT', 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;
var
  SQL: string;
  mode: string;
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);
    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
    ordersDB.UniQuery1.FieldByName('STATUS_DATE').AsString := order.GetValue<string>('staff_fields_'+ StatusType.ToLower +'_date');
    ordersDB.UniQuery1.FieldByName('ORIGINAL_STATUS_DATE').AsString := order.GetValue<string>('staff_fields_'+ StatusType.ToLower +'_date');
  end
  else
  begin
    ordersDB.UniQuery1.FieldByName('STATUS_DATE').AsDateTime := StrToDateTime(order.GetValue<string>('staff_fields_'+ StatusType.ToLower +'_due'));
    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
    ordersDB.UniQuery1.FieldByName('ORDER_REVISION').AsInteger := ordersDB.UniQuery1.FieldByName('ORDER_REVISION').AsInteger + 1;

  ordersDB.UniQuery1.Post;

  Result := 'success';
end;

function TLookupService.EditStatusSchedule(StatusType: string; order: TJSONObject; ORDER_ID: string): string;
var
  SQL: string;
begin
end;

function TLookupService.AddUser(userInfo:string): string;
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;
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;
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;


initialization
  RegisterServiceType(TLookupService);

end.

