#!/usr/bin/perl

#use utf8;
use strict;
use CGI;

use Time::localtime;
use Opals::Context;
use Opals::Template_ajax qw(
  tmpl_read
  tmpl_write
  tmpl_preference
);

use Opals::User qw(
  user_getInformationById
  user_isUserBc
  user_getUserCircInfo
);
use Opals::Circulation qw(
  circ_getUserCircStatus
  circ_getItemStatus
  circ_getItemInfo
  circ_getRecID
  circ_getActiveLoanId
  circ_processLoan
  circ_placeHold
  circ_fillHold
  circ_setHoldIdLoan
  circ_cancelHold
  circ_placeReserve
  circ_fillReserve
  circ_resetReserve
  circ_setAvailable
  circ_processRenew
  Circ_validateFollettBarcode
  Circ_validateSpectrumBarcode
  Circ_validateLeadingZeroBc
  Circ_validateSagebrushBarcode
  circ_record_ODL
  circ_processReturn
  circ_getBookingUserList
  circ_isReserve4booking
  circ_record_ODL_ext
  circ_fillReserveById
);
use Opals::Date qw(
  date_getDeadLineDate
  date_deltaWorkDay
  date_deltaWorkDayHour
  date_parse
  dateTime_parse
  date_today
  date_text
  date_DHM_text
  date_now
  date_getDeadLineDateTime
);

use Opals::Transaction qw(
  trans_doReverseLost
  trans_getBalance
);
use Opals::BarcodeMgmt qw(
  bcm_validateBc
);
use Opals::Constant;
use JSON;
my $dbh = Opals::Context->dbh();
END { $dbh->disconnect(); }

# Process loan code
use constant L_ITEM_NOTEXIST   => '00';
use constant L_ITEM_UNAVAIL    => '01';
use constant L_ITEM_RESTRICTED => '02';
use constant L_ITEM_LOST       => '03';
use constant L_ITEM_DAMAGED    => '04';

use constant L_ITEM_HOLD_4_CUR_USER              => '05';
use constant L_ITEM_HOLD_4_OTHER_USR_IN_RSVLIST  => '06';
use constant L_ITEM_HOLD_4_OTHER_USR_NOT_RSVLIST => '07';

use constant L_ITEM_INLIB_USR_TOP_RSVLIST       => '08';
use constant L_ITEM_INLIB_USR_NOT_TOP_RSVLIST   => '09';
use constant L_ITEM_INLIB_USR_NOT_RSVLIST       => '10';
use constant L_ITEM_ONLOAN_2_CUR_USER_RSV4OTHER => '11';
use constant L_ITEM_ONLOAN_USR_TOP_RSVLIST      => '12';
use constant L_ITEM_ONLOAN_USR_IN_RSVLIST       => '13'; # user in reservation but with a lower priority. This item is also on loan to other
use constant L_ITEM_LOAN_TO_OTHER__RSV4OTHER => '14';    #
use constant L_ITEM_ONLOAN_TO_CUR_USER       => '15';
use constant L_ITEM_ONLOAN_TO_OTHER          => '16';
use constant L_ITEM_FULL_AVAILABLE           => '17';
use constant L_ITEM_MISSING                  => '18';

use constant L_ITEM_IN_PROCESSING    => '20';
use constant L_ITEM_IN_REPAIR        => '21';
use constant L_ITEM_CLAIM_RETURN     => '22';
use constant L_ITEM_CLAIM_NEVER_LOAN => '23';
use constant L_ITEM_ON_ORDER         => '24';
use constant L_ITEM_WEED             => '25';

my $cgi            = CGI->new;
my $input          = $cgi->Vars();
my $uid            = $input->{"uid"};
my $barcode        = $input->{"barcode"};
my $dueDate        = $input->{"duedate"};
my $cmdCode        = $input->{"command"};
my $loanType       = $input->{"loanType"};
my $requestedBySIP = ($input->{"sip_sid"}) ? 1 : 0;

if (!defined $loanType || $loanType ne 'hourly') {
  $loanType = 'daily';
}
my ($status, $errorCode, $errorMsg);
my ($permission, $cookie, $template) = tmpl_read(
  {
    dbh           => $dbh,
    cgi           => $cgi,
    tmplFile      => 'ajax/circ/loan.tmpl',
    reqPermission => 'circ_loan',
  }
);
my $syspref    = tmpl_preference($dbh);
my $chargeFine = $syspref->{'charge_overdue'};
my $loginuid   = $ENV{'curUserId'};              #$template->param('curUserId');

my $todayStr   = date_now();
my $dLast      = Opals::Context->preference('dateLast');
my $lang       = $template->param('curLang');
my @cookieList = (@$cookie);
if ($permission && $permission->{'circ_loan'}) {
  my ($userInfo, $guardian) = user_getInformationById($dbh, $uid);
  if (!$userInfo->{'uid'}) {                     # doing nothing???
  }
  my $status        = $userInfo->{'status'};
  my $loanOverride  = $cgi->cookie('loanOverride');
  my $OSBOverride   = $cgi->cookie('OSBOverride');
  my $maxITOverride = $cgi->cookie('maxITOverride');

  my $validateBc       = $syspref->{'validateBarcode'};
  my $barcodeType      = $syspref->{'barcodeType'};
  my $circulationSound = $syspref->{'circulationSound'};

  # Load borrower type (or permissions) --- quick user entry

  my $itemCategory = $input->{'itemCategory'};
  if (!$itemCategory || $itemCategory !~ m/Library|Textbook/gi) {
    $itemCategory = "Library";
  }

  push @cookieList,
    $cgi->cookie(
    -name  => 'borrower',
    -value => $uid
    );
  push @cookieList,
    $cgi->cookie(
    -name  => 'loanOverride',
    -value => $loanOverride
    );
  $barcode =~ s/^\s+|\s+$//g;
  $barcode = bcm_validateBc($dbh, $barcode, $barcodeType)
    if ($validateBc eq '1');
  my ($processCode, $userMsgCode, $itemMsgCode, $msgCode) = ('', '', '');
  my ($return, $hold, $reserve, $preLoanStatus) = ({}, {}, {}, {});
  my $loanId = 0;

  if ($barcode ne '') {
    my $userStatus = circ_getUserCircStatus($dbh, $uid, $barcode);
    $userStatus->{'balance'} = trans_getBalance($dbh, $uid);
    my $itemCircStatus = circ_getItemStatus($dbh, $barcode, $uid);
    my $itemMsgCode = getMsgCode_item($itemCircStatus, $uid);
    my $userMsgCode = getMsgCode_user($userStatus);
    my $rid         = circ_getRecID($dbh, $barcode);
    my $itemInfo    = circ_getItemInfo($dbh, $barcode, $uid);

    if ($itemMsgCode eq '03' || $itemMsgCode eq '22' || $itemMsgCode eq '23') {
      $preLoanStatus = getPreLoanStatusInfo($dbh, $barcode);
    }
    if ($itemCircStatus->{'status'} == IT_STAT_NOEXIST) {
      my $tmpUid = user_isUserBc($dbh, $barcode);
      if ($tmpUid > 0) {
        $itemMsgCode = '19';
        my $tmpUser=user_getUserCircInfo($dbh,$tmpUid);
        $template->param(userInfo=>to_json($tmpUser));
      }
    }

    elsif (($itemMsgCode ne '00') && ($itemInfo->{'loanPeriod'} == 0)) {
      $itemMsgCode = '02';
    }
    my $loanPeriod =
      ($itemInfo->{'loanPeriod'}) ? $itemInfo->{'loanPeriod'} : 0;
    $loanType = $itemInfo->{'loanUnit'} eq 'hour' ? 'hourly' : 'daily';

    $dueDate = validateDueDate($dueDate, $loanPeriod, $itemCategory);
    my $holdList = undef;
    $holdList = $itemCircStatus->{'holdList'}
      if ($itemCircStatus->{'holdList'});
    my $reserveList = undef;
    $reserveList = $itemCircStatus->{'reserveList'}
      if ($itemCircStatus->{'reserveList'});

    my $questionOSBalance   = 0;
    my $questionMaxItemType = 0;
    my $maxLoanBit          = substr($userMsgCode, 2, 1);
    my $overdueBit          = substr($userMsgCode, 1, 1);
    my $maxItemTypeLoan     = $userStatus->{"maxItemTypeLoan"};
    my $allowOD             = $syspref->{'allowOverdue'};
    my $allowOSB            = $syspref->{'allowOutstandingBalance'};
    my $accBalThreshold=$userInfo->{'accBalThreshold'}||0.0;
    $allowOSB = 2 if (!($userStatus->{"balance"} > $accBalThreshold));

    if ($OSBOverride && $OSBOverride == 1 && $allowOSB == 1) {
      $allowOSB = 2;
    }
    if (
      $allowOD == 1
      && ($overdueBit eq '0'
        || (substr($loanOverride, 1, 1) eq '1' && $overdueBit eq '1'))
      )
    {
      $allowOD = 2;
    }
    my $allowMaxLoan = ($maxLoanBit eq '0'
        || (substr($loanOverride, 2, 1) eq '1' && $maxLoanBit eq '1'));
    my $allowMaxLoanType =
      (    $maxItemTypeLoan eq '0'
        || !defined $maxItemTypeLoan
        || ($maxItemTypeLoan > 0 && $maxITOverride && $maxITOverride eq '1'));

    #//START: Wed, Apr 28, 2010 @ 09:17:11 EDT
    if ($itemMsgCode eq '05') {
      my $holdInfo = getHoldInfo($uid, $itemCircStatus->{'holdList'});
      if ($holdInfo->{'idReserve'}
        && circ_isReserve4booking($dbh, $holdInfo->{'idReserve'}))
      {
        if (defined $holdInfo->{'dateExpiry'}
          && $holdInfo->{'dateExpiry'} gt $dueDate)
        {
          $dueDate = $holdInfo->{'dateExpiry'};
        }
      }
    }

    #//END:  Wed, Apr 28, 2010 @ 09:17:22 EDT
    #
    if (
      (
           $itemMsgCode eq '05'
        || $itemMsgCode eq '08'
        || $itemMsgCode eq '12'
        || $itemMsgCode eq '16'
        || $itemMsgCode eq '17'
        || $itemMsgCode eq '20'
        || $itemMsgCode eq '21'
        || $itemMsgCode eq '22'
        || $itemMsgCode eq '23'
        || $itemMsgCode eq '24'
        || $itemMsgCode eq '25'
      )
      && (
        (
             substr($userMsgCode, 0, 3) eq '100'
          && $userStatus->{"maxItemTypeLoan"} == 0
          && !($userStatus->{"balance"} > 0)
        )
        || ( $allowOSB == 2
          && $allowOD == 2
          && $allowMaxLoan
          && $allowMaxLoanType)

      )
      )
    {
      ($processCode, $loanId, $return, $reserve, $hold) =
        processCmd($dbh, $uid, $barcode, $itemInfo, $itemCircStatus,
        $itemMsgCode, $dueDate, $itemCategory);
    }
    elsif ($cmdCode && $cmdCode ne '') {
      ($processCode, $loanId, $return, $reserve, $hold) =
        processCmd($dbh, $uid, $barcode, $itemInfo, $itemCircStatus, $cmdCode,
        $dueDate, $itemCategory);
    }
    elsif ($itemCircStatus->{'status'} != IT_STAT_NOEXIST) {
      if (($overdueBit eq '1' || $maxLoanBit eq '1') && $allowOD == 0) {
        $processCode = 'no_override';

      }
      elsif ($userStatus->{"balance"} > 0 && $allowOSB == 0) {
        $processCode = 'no_overrideOSB';
      }
      elsif ($userStatus->{"balance"} > 0 && $allowOSB == 1) {
        $questionOSBalance = 1;
      }
      elsif ($userStatus->{"maxItemTypeLoan"} > 0) {
        $questionMaxItemType = 1;
      }
      else {
        $msgCode = $itemMsgCode . "-" . $userMsgCode;
      }
    }

    #my $dateLoanStr = date_text($todayStr, 0, $lang);
    my $dateLoanStr = substr($todayStr,0,10);
    my $dueDateStr =
        ($requestedBySIP) ? $dueDate
      : ($loanType eq 'daily') ? substr($dueDate,0,10):$dueDate;

    if ($processCode =~ m/^p1/) {
      my $curLidList = "";
      if ($cgi->cookie('borrower_curLidList') ne "") {
        $curLidList = $cgi->cookie('borrower_curLidList') . ',';
      }
      $curLidList .= $loanId;

      push @cookieList,
        $cgi->cookie(
        -name  => 'borrower_curLidList',
        -value => $curLidList
        );
    }

    $template->param(
      uid                 => $uid,
      processCode         => $processCode,
      userMsgCode         => $userMsgCode,
      itemMsgCode         => $itemMsgCode,
      msgCode             => $msgCode,
      itemCategory        => $itemCategory,
      duedate             => $dueDateStr,
      dateloan            => $dateLoanStr,
      maxItemTypeLoan     => $userStatus->{"maxItemTypeLoan"},
      balance             => $userStatus->{"balance"},
      itemType            => $itemInfo->{'typeId'},
      questionOSBalance   => $questionOSBalance,
      questionMaxItemType => $questionMaxItemType,
    );
    if ($return->{'lid'} && $return->{'lid'} > 0) {
      $template->param(
        return_lid          => $return->{'lid'},
        return_loanType     => $return->{'loanType'},
        return_daysOverdue  => $return->{'overdue'}->{'day'},
        return_hoursOverdue => $return->{'overdue'}->{'hour'},
        return_odl_id       => $return->{'odl_id'},
        return_firstname    => $return->{'l_firstname'},
        return_lastname     => $return->{'l_lastname'},
      );
    }
    if ($reserve->{'reserveId'}) {
      $template->param(
        r_reserveId   => $reserve->{'reserveId'},
        r_uid         => $reserve->{'uid'},
        r_firstName   => $reserve->{'firstName'},
        r_lastName    => $reserve->{'lastName'},
        r_dateReserve => $dateLoanStr,
        r_expiryDate  => date_text($reserve->{'expiryDate'}, 0, $lang),
      );
    }
    if ($hold->{'reserveId'}) {
      $template->param(
        h_reserveId   => $hold->{'reserveId'},
        h_uid         => $hold->{'uid'},
        h_firstName   => $hold->{'firstName'},
        h_lastName    => $hold->{'lastName'},
        h_dateReserve => $dateLoanStr,
        h_expiryDate  => date_text($hold->{'expiryDate'}, 0, $lang)
      );
    }
    if ($preLoanStatus->{'status'}) {
      $template->param(
        preLoan_status    => $preLoanStatus->{'status'},
        preLoan_uid       => $preLoanStatus->{'uid'},
        preLoan_firstname => $preLoanStatus->{'firstname'},
        preLoan_lastname  => $preLoanStatus->{'lastname'},
        preLoan_idloan    => $preLoanStatus->{'idloan'}
      );
    }

    $template->param(barcode     => $barcode);
    $template->param(holdList    => $holdList) if ($holdList);
    $template->param(reserveList => $reserveList) if ($reserveList);
    if ($itemCircStatus->{'status'} != IT_STAT_NOEXIST) {
      foreach my $k (qw(title author pubDate price callNumber)) {
        $itemInfo->{$k} =~ s/</&lt;/g;
        $itemInfo->{$k} =~ s/>/&gt;/g;
      }
      $template->param(
        lid          => $loanId,
        rid          => $itemInfo->{'rid'},
        l_title      => $itemInfo->{'title'},
        l_author     => $itemInfo->{'author'},
        l_pubName    => $itemInfo->{'pubName'},
        l_pubDate    => $itemInfo->{'pubDate'},
        l_price      => $itemInfo->{'price'},
        l_itemType   => $itemInfo->{'typeId'},
        l_callNumber => $itemInfo->{'callNumber'},
        l_maxRenewal => $itemInfo->{'maxRenewal'},
        isTempILL    => $itemCircStatus->{'isTempILL'},
      );
    }

  }

}
tmpl_write($dbh, $cgi, \@cookieList, $template, 'application/json');

#-------------------------------------------------------------------------------------------
sub getMsgCode_item {
  my ($itemCircStatus, $uid) = @_;
  my $retCode     = "";
  my $reserveList = $itemCircStatus->{'reserveList'};
  my $holdList    = $itemCircStatus->{'holdList'};
  if ($itemCircStatus->{'status'} == IT_STAT_NOEXIST) {
    $retCode = L_ITEM_NOTEXIST;    #00
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_UNAVAIL) {
    $retCode = L_ITEM_UNAVAIL;     #01
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_RESTRICTED) {
    $retCode = L_ITEM_RESTRICTED;    #02
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_LOST) {
    $retCode = L_ITEM_LOST;          #03
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_MISSING) {
    $retCode = L_ITEM_MISSING;       #18
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_ON_ORDER) {
    $retCode = L_ITEM_ON_ORDER;      #19
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_IN_PROCESSING) {
    $retCode = L_ITEM_IN_PROCESSING;    #20
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_IN_REPAIR) {
    $retCode = L_ITEM_IN_REPAIR;        #21
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_CLAIM_RETURN) {
    $retCode = L_ITEM_CLAIM_RETURN;     #22
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_CLAIM_NEVER_LOAN) {
    $retCode = L_ITEM_CLAIM_NEVER_LOAN;    #23
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_WEED) {
    $retCode = L_ITEM_WEED;                #24
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_DAMAGED) {
    $retCode = L_ITEM_DAMAGED;             #04
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_ONHOLD) {
    if (getReserveId($uid, $holdList) > 0) {
      $retCode = L_ITEM_HOLD_4_CUR_USER;    #05
    }
    elsif (getReserveId($uid, $reserveList) > 0) {
      $retCode = L_ITEM_HOLD_4_OTHER_USR_IN_RSVLIST;    #06
    }
    else {
      $retCode = L_ITEM_HOLD_4_OTHER_USR_NOT_RSVLIST;    #07
    }
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_FULL_AVAIL
    && getReserveId($uid, $holdList) > 0)
  {
    $retCode = L_ITEM_HOLD_4_CUR_USER;                   #05
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_HAVE_RSVR) {
    if (scalar(@$reserveList)) {
      my $topReserveUID = @$reserveList[0]->{'uid'};
      if ($itemCircStatus->{'inLibrary'}) {
        if ($uid == $topReserveUID) {
          $retCode = L_ITEM_INLIB_USR_TOP_RSVLIST;       #08
        }
        elsif (getReserveId($uid, $reserveList) > 0) {
          $retCode = L_ITEM_INLIB_USR_NOT_TOP_RSVLIST;    #09
        }
        else {
          $retCode = L_ITEM_INLIB_USR_NOT_RSVLIST;        #10
        }
      }
      else {
        if ($uid == $itemCircStatus->{'l_uid'}) {
          $retCode = L_ITEM_ONLOAN_2_CUR_USER_RSV4OTHER;    #11
        }
        elsif ($uid == $topReserveUID) {
          $retCode = L_ITEM_ONLOAN_USR_TOP_RSVLIST;         #12
        }
        elsif (getReserveId($uid, $reserveList) > 0) {
          $retCode = L_ITEM_ONLOAN_USR_IN_RSVLIST;          #13
        }
        else {
          $retCode = L_ITEM_LOAN_TO_OTHER__RSV4OTHER;       #14
        }
      }
    }
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_ONLOAN
    && $uid eq $itemCircStatus->{'l_uid'})
  {
    $retCode = L_ITEM_ONLOAN_TO_CUR_USER;                   #15
  }
  elsif ($itemCircStatus->{'status'} == IT_STAT_ONLOAN
    && $uid ne $itemCircStatus->{'l_uid'})
  {
    $retCode = L_ITEM_ONLOAN_TO_OTHER;                      #16
  }
  else {                                                    # fully available
    $retCode = L_ITEM_FULL_AVAILABLE;                       #17
  }

  return $retCode;
}

#-------------------------------------------------------------------------------------------
sub getPreLoanStatusInfo {
  my ($dbh, $barcode) = @_;
  my $sql = "select u.uid,u.firstname,u.lastname ,l.idloan ,s.status   
            from opl_loan l inner join opl_user u using(uid) inner join opl_itemstatus s using(barcode) 
            where l.barcode=?  order by l.idloan desc , s.id desc limit 1";
  my $preLoanStatus = $dbh->selectrow_hashref($sql, undef, $barcode);
  return $preLoanStatus;
}

#-------------------------------------------------------------------------------------------
sub processCmd {
  my ($dbh, $uid, $barcode, $itemInfo, $itemCircStatus, $cmdCode, $dueDate,
    $type)
    = @_;
  my $dateReturn = $todayStr;
  my $loanId     = 0;
  my $return     = {};
  my $reserve    = {};
  my $hold       = {};
  my $retVal;

  if ($cmdCode eq '03') {
    circ_setAvailable($dbh, $barcode, IT_STAT_LOST);
    trans_doReverseLost($dbh, $barcode, $loginuid);
  }
  if ($cmdCode eq '18') {
    circ_setAvailable($dbh, $barcode, IT_STAT_MISSING);
  }
  elsif ($cmdCode eq '20') {
    circ_setAvailable($dbh, $barcode, IT_STAT_IN_PROCESSING);
  }
  elsif ($cmdCode eq '21') {
    circ_setAvailable($dbh, $barcode, IT_STAT_IN_REPAIR);
  }
  elsif ($cmdCode eq '22') {
    circ_setAvailable($dbh, $barcode, IT_STAT_CLAIM_RETURN);
  }
  elsif ($cmdCode eq '23') {
    circ_setAvailable($dbh, $barcode, IT_STAT_CLAIM_NEVER_LOAN);
  }
  elsif ($cmdCode eq '24') {
    circ_setAvailable($dbh, $barcode, IT_STAT_ON_ORDER);
  }
  elsif ($cmdCode eq '04') {
    circ_setAvailable($dbh, $barcode, IT_STAT_DAMAGED);
  }
  elsif ($cmdCode eq '05') {
    my $idReserve = getReserveId($uid, $itemCircStatus->{'holdList'});
    my $fillHold = circ_fillHold($dbh, $idReserve, $barcode);
  }
  elsif ($cmdCode eq '06') {
    my $idReserve = getReserveId($uid, $itemCircStatus->{'reserveList'});

    #my $fillReserve   = circ_fillReserve($dbh,$uid ,$itemCircStatus->{'rid'});
    my $fillReserve  = circ_fillReserveById($dbh, $idReserve);
    my $idReserve    = getLastReserveId($itemCircStatus->{'holdList'});
    my $cancelHold   = circ_cancelHold($dbh, $idReserve);
    my $resetReserve = circ_resetReserve($dbh, $idReserve);
  }
  elsif ($cmdCode eq '07') {
    my $idReserve    = getLastReserveId($itemCircStatus->{'holdList'});
    my $cancelHold   = circ_cancelHold($dbh, $idReserve);
    my $resetReserve = circ_resetReserve($dbh, $idReserve);
  }
  elsif ($cmdCode eq '07_R') {
    my $dateExpiry =
      date_getDeadLineDate($itemInfo->{'reservePeriod'}, $todayStr);
    my $idReserve = circ_placeReserve($dbh, $uid, $itemCircStatus->{'rid'},
      1, $todayStr, $dateExpiry);
    $retVal = "p1_07_R";
    my ($userInfo, $guardian) = user_getInformationById($dbh, $uid);
    $reserve = {
      reserveId  => $idReserve,
      uid        => $uid,
      firstName  => $userInfo->{'firstname'},
      lastName   => $userInfo->{'lastname'},
      expiryDate => $dateExpiry
    };
    return ($retVal, $loanId, $return, $reserve, $hold);

  }
  elsif ($cmdCode eq '08' || $cmdCode eq '09') {
    my $reserveList = $itemCircStatus->{'reserveList'};
    my $idReserve   = @$reserveList[0]->{'idReserve'};

    #my $fillReserve  = circ_fillReserve($dbh,$uid ,$itemCircStatus->{'rid'});
    circ_fillReserveById($dbh, $idReserve);
  }
  elsif ($cmdCode eq '09_H') {
    my $reserveList   = $itemCircStatus->{'reserveList'};
    my $idReserve     = @$reserveList[0]->{'idReserve'};
    my $uidReserve    = @$reserveList[0]->{'uid'};
    my $reserveExpiry = @$reserveList[0]->{'dateExpiry'};
    my $dateExpiry = date_getDeadLineDate($itemInfo->{'holdPeriod'}, $todayStr);
    if ($dateExpiry > $reserveExpiry) {
      $dateExpiry = $reserveExpiry;
    }

    #circ_fillReserve($dbh,$uidReserve ,$itemCircStatus->{'rid'});
    circ_fillReserveById($dbh, $idReserve);
    my $putOnHold = circ_placeHold($dbh, $idReserve, $todayStr, $dateExpiry);
    $retVal = "p1_09_H";
    my ($userInfo, $guardian) = user_getInformationById($dbh, $uidReserve);
    $hold = {
      reserveId  => $idReserve,
      uid        => $uidReserve,
      firstName  => $userInfo->{'firstname'},
      lastName   => $userInfo->{'lastname'},
      expiryDate => $dateExpiry
    };

    return ($retVal, $loanId, $return, $reserve, $hold);
  }
  elsif ($cmdCode eq '10_H') {
    my $reserveList   = $itemCircStatus->{'reserveList'};
    my $idReserve     = @$reserveList[0]->{'idReserve'};
    my $uidReserve    = @$reserveList[0]->{'uid'};
    my $reserveExpiry = @$reserveList[0]->{'dateExpiry'};
    my $dateExpiry = date_getDeadLineDate($itemInfo->{'holdPeriod'}, $todayStr);
    if ($dateExpiry > $reserveExpiry) {
      $dateExpiry = $reserveExpiry;
    }

    #circ_fillReserve($dbh,$uidReserve ,$itemCircStatus->{'rid'});
    circ_fillReserveById($dbh, $idReserve);
    my $putOnHold = circ_placeHold($dbh, $idReserve, $todayStr, $dateExpiry);
    $retVal = "p1_10_H";
    my ($userInfo, $guardian) = user_getInformationById($dbh, $uidReserve);
    $hold = {
      reserveId  => $idReserve,
      uid        => $uidReserve,
      firstName  => $userInfo->{'firstname'},
      lastName   => $userInfo->{'lastname'},
      expiryDate => $dateExpiry
    };

    return ($retVal, $loanId, $return, $reserve, $hold);
  }
  elsif ($cmdCode eq '10_HR') {
    my $reserveList   = $itemCircStatus->{'reserveList'};
    my $idReserve     = @$reserveList[0]->{'idReserve'};
    my $uidReserve    = @$reserveList[0]->{'uid'};
    my $reserveExpiry = @$reserveList[0]->{'dateExpiry'};
    my $dateExpiry = date_getDeadLineDate($itemInfo->{'holdPeriod'}, $todayStr);
    if ($dateExpiry > $reserveExpiry) {
      $dateExpiry = $reserveExpiry;
    }

    #circ_fillReserve($dbh,$uidReserve ,$itemCircStatus->{'rid'});
    circ_fillReserveById($dbh, $idReserve);
    my $putOnHold = circ_placeHold($dbh, $idReserve, $todayStr, $dateExpiry);
    my ($userInfo, $guardian) = user_getInformationById($dbh, $uidReserve);
    $hold = {
      reserveId  => $idReserve,
      uid        => $uidReserve,
      firstName  => $userInfo->{'firstname'},
      lastName   => $userInfo->{'lastname'},
      expiryDate => $dateExpiry
    };
    $dateExpiry = date_getDeadLineDate($itemInfo->{'reservePeriod'}, $todayStr);
    $idReserve = circ_placeReserve($dbh, $uid, $itemCircStatus->{'rid'},
      1, $todayStr, $dateExpiry);
    ($userInfo, $guardian) = user_getInformationById($dbh, $uid);
    $reserve = {
      reserveId  => $idReserve,
      uid        => $uid,
      firstName  => $userInfo->{'firstname'},
      lastName   => $userInfo->{'lastname'},
      expiryDate => $dateExpiry
    };
    $retVal = "p1_10_HR";
    return ($retVal, $loanId, $return, $reserve, $hold);
  }
  elsif ($cmdCode eq '11' || $cmdCode eq '15') {
    my $renewal = circ_processRenew($dbh, $uid, $barcode, $dueDate);
    $retVal = "p" . $renewal->{'renewal_ok'} . "_" . $cmdCode;
    $loanId = circ_getActiveLoanId($dbh, $barcode, $uid);
    return ($retVal, $loanId, $return, $reserve, $hold);
  }
  elsif ($cmdCode eq '11_H') {
    $return = doReturn($dbh, $itemCircStatus->{'l_uid'}, $barcode, $dateReturn);
    my $reserveList   = $itemCircStatus->{'reserveList'};
    my $idReserve     = @$reserveList[0]->{'idReserve'};
    my $uidReserve    = @$reserveList[0]->{'uid'};
    my $reserveExpiry = @$reserveList[0]->{'dateExpiry'};
    my $dateExpiry = date_getDeadLineDate($itemInfo->{'holdPeriod'}, $todayStr);
    if ($dateExpiry > $reserveExpiry) {
      $dateExpiry = $reserveExpiry;
    }

    #circ_fillReserve($dbh,$uidReserve ,$itemCircStatus->{'rid'});
    circ_fillReserveById($dbh, $idReserve);
    my $putOnHold = circ_placeHold($dbh, $idReserve, $todayStr, $dateExpiry);
    $retVal = "p1_11_H";
    my ($userInfo, $guardian) = user_getInformationById($dbh, $uidReserve);
    $hold = {
      reserveId  => $idReserve,
      uid        => $uidReserve,
      firstName  => $userInfo->{'firstname'},
      lastName   => $userInfo->{'lastname'},
      expiryDate => $dateExpiry
    };
    return ($retVal, $loanId, $return, $reserve, $hold);
  }
  elsif ($cmdCode eq '12' || $cmdCode eq '13') {
    $return = doReturn($dbh, $itemCircStatus->{'l_uid'}, $barcode, $dateReturn);
    my $reserveList = $itemCircStatus->{'reserveList'};
    my $idReserve   = @$reserveList[0]->{'idReserve'};

    #my $fillReserve  = circ_fillReserve($dbh,$uid ,$itemCircStatus->{'rid'});
    circ_fillReserveById($dbh, $idReserve);
  }
  elsif ($cmdCode eq '13_H') {
    $return = doReturn($dbh, $itemCircStatus->{'l_uid'}, $barcode, $dateReturn);
    my $reserveList   = $itemCircStatus->{'reserveList'};
    my $idReserve     = @$reserveList[0]->{'idReserve'};
    my $uidReserve    = @$reserveList[0]->{'uid'};
    my $reserveExpiry = @$reserveList[0]->{'dateExpiry'};
    my $dateExpiry = date_getDeadLineDate($itemInfo->{'holdPeriod'}, $todayStr);
    if ($dateExpiry > $reserveExpiry) {
      $dateExpiry = $reserveExpiry;
    }

    #circ_fillReserve($dbh,$uidReserve ,$itemCircStatus->{'rid'});
    circ_fillReserveById($dbh, $idReserve);
    my $putOnHold = circ_placeHold($dbh, $idReserve, $todayStr, $dateExpiry);
    $retVal = "p1_13_H";
    my ($userInfo, $guardian) = user_getInformationById($dbh, $uidReserve);
    $hold = {
      reserveId  => $idReserve,
      uid        => $uidReserve,
      firstName  => $userInfo->{'firstname'},
      lastName   => $userInfo->{'lastname'},
      expiryDate => $dateExpiry
    };

    return ($retVal, $loanId, $return, $reserve, $hold);
  }
  elsif ($cmdCode eq '14' || $cmdCode eq '16') {
    $return = doReturn($dbh, $itemCircStatus->{'l_uid'}, $barcode, $dateReturn);
  }
  elsif ($cmdCode eq '14_H') {
    $return = doReturn($dbh, $itemCircStatus->{'l_uid'}, $barcode, $dateReturn);
    my $reserveList   = $itemCircStatus->{'reserveList'};
    my $idReserve     = @$reserveList[0]->{'idReserve'};
    my $uidReserve    = @$reserveList[0]->{'uid'};
    my $reserveExpiry = @$reserveList[0]->{'dateExpiry'};
    my $dateExpiry = date_getDeadLineDate($itemInfo->{'holdPeriod'}, $todayStr);
    if ($dateExpiry > $reserveExpiry) {
      $dateExpiry = $reserveExpiry;
    }

    #circ_fillReserve($dbh,$uidReserve ,$itemCircStatus->{'rid'});
    circ_fillReserveById($dbh, $idReserve);
    my $putOnHold = circ_placeHold($dbh, $idReserve, $todayStr, $dateExpiry);
    $retVal = "p1_14_H";
    my ($userInfo, $guardian) = user_getInformationById($dbh, $uidReserve);
    $hold = {
      reserveId  => $idReserve,
      uid        => $uidReserve,
      firstName  => $userInfo->{'firstname'},
      lastName   => $userInfo->{'lastname'},
      expiryDate => $dateExpiry
    };

    return ($retVal, $loanId, $return, $reserve, $hold);
  }

  elsif ($cmdCode eq '14_HR') {
    $return = doReturn($dbh, $itemCircStatus->{'l_uid'}, $barcode, $dateReturn);
    my $reserveList   = $itemCircStatus->{'reserveList'};
    my $idReserve     = @$reserveList[0]->{'idReserve'};
    my $uidReserve    = @$reserveList[0]->{'uid'};
    my $reserveExpiry = @$reserveList[0]->{'dateExpiry'};
    my $dateExpiry = date_getDeadLineDate($itemInfo->{'holdPeriod'}, $todayStr);
    if ($dateExpiry > $reserveExpiry) {
      $dateExpiry = $reserveExpiry;
    }
    circ_fillReserveById($dbh, $idReserve);

    #circ_fillReserve($dbh,$uidReserve ,$itemCircStatus->{'rid'});
    my $putOnHold = circ_placeHold($dbh, $idReserve, $todayStr, $dateExpiry);
    my ($userInfo, $guardian) = user_getInformationById($dbh, $uidReserve);
    $hold = {
      reserveId  => $idReserve,
      uid        => $uidReserve,
      firstName  => $userInfo->{'firstname'},
      lastName   => $userInfo->{'lastname'},
      expiryDate => $dateExpiry
    };
    $dateExpiry = date_getDeadLineDate($itemInfo->{'reservePeriod'}, $todayStr);
    $idReserve = circ_placeReserve($dbh, $uid, $itemCircStatus->{'rid'},
      1, $todayStr, $dateExpiry);
    $retVal = "p1_14_HR";
    ($userInfo, $guardian) = user_getInformationById($dbh, $uidReserve);
    $reserve = {
      reserveId  => $idReserve,
      uid        => $uid,
      firstName  => $userInfo->{'firstname'},
      lastName   => $userInfo->{'lastname'},
      expiryDate => $dateExpiry
    };

    return ($retVal, $loanId, $return, $reserve, $hold);
  }

  #   2006-09-28
  #   fill hold if item is availbale and on hold for the same patron
  #   BEGIN
  my $idReserve = getReserveId($uid, $itemCircStatus->{'holdList'});
  if ($idReserve && ($cmdCode eq '16' || $cmdCode eq 17)) {
    my $fillHold = circ_fillHold($dbh, $idReserve, $barcode);
  }

  #   END
  my $loan = circ_processLoan($dbh, $uid, $barcode, $todayStr, $dueDate, $type);
  $loanId = circ_getActiveLoanId($dbh, $barcode, $uid);
  circ_setHoldIdLoan($dbh, $idReserve, $barcode, $loanId);

  $retVal = "p" . $loan . "_" . $cmdCode;
  return ($retVal, $loanId, $return, $reserve, $hold);

}

#-------------------------------------------------------------------------------------------
sub getMsgCode_user {
  my ($userStatus) = @_;
  return
      $userStatus->{'status'}
    . $userStatus->{'overdue'}
    . $userStatus->{'maxLoan'}
    . $userStatus->{'maxRenew'}
    . $userStatus->{'maxReserve'};
}

#----------------------------------------------------------------------------------

sub getHoldInfo {
  my ($uid, $list) = @_;
  my $ret = {};
  if (defined $list) {
    for (my $i = 0 ; $i < scalar(@$list) ; $i++) {
      if (@$list[$i]->{'uid'} == $uid) {
        $ret = @$list[$i];
      }
    }
  }
  return $ret;
}

#----------------------------------------------------------------------------------
#       this function is mainly used to check if a given uid is in the
#       reserve list or hold list and return its idReserve.
#----------------------------------------------------------------------------------

sub getReserveId {
  my ($uid, $list) = @_;
  if (!$list) {
    return 0;
  }
  for (my $i = 0 ; $i < scalar(@$list) ; $i++) {
    if (@$list[$i]->{'uid'} == $uid) {
      return @$list[$i]->{'idReserve'};
    }
    else {
      my $b4List = circ_getBookingUserList($dbh, @$list[$i]->{'idReserve'});
      foreach my $bUser (@$b4List) {
        if ($bUser->{'uid'} == $uid) {
          return @$list[$i]->{'idReserve'};
        }
      }
    }
  }
  return 0;
}

#----------------------------------------------------------------------------------
sub getTopReserveId {
  my ($list) = @_;
  if (!$list) {
    return 0;
  }
  return @$list[0]->{'idReserve'};
}

#----------------------------------------------------------------------------------
sub getLastReserveId {
  my ($list) = @_;
  if (!$list) {
    return 0;
  }
  my $i = scalar(@$list) - 1;
  return @$list[$i]->{'idReserve'};
}

#-------------------------------------------------------------------------------------------
sub validateBc {
  my ($dbh, $barcode, $barcodeType) = @_;
  if ($barcodeType eq '1') {    #follett
    $barcode = Circ_validateFollettBarcode($dbh, $barcode);
  }
  elsif ($barcodeType eq '2') {    #spectrum
    $barcode = Circ_validateSpectrumBarcode($dbh, $barcode);
  }
  elsif ($barcodeType eq '3') {    #leading zero
    $barcode = Circ_validateLeadingZeroBc($dbh, $barcode);
  }
  elsif ($barcodeType eq '4') {    #SagebrushBarcode
    $barcode = Circ_validateSagebrushBarcode($dbh, $barcode);
  }

  #else{#follett
  #    $barcode  = Circ_validateFollettBarcode($dbh,$barcode );
  #}
  return $barcode;
}

#-------------------------------------------------------------------------------------------
sub validateDueDate {
  my ($dueDate, $loanPeriod, $itemCategory) = @_;
  my $time = " 23:59:59";
  $dueDate="$dueDate$time" if($dueDate =~ m/^\d\d\d\d-\d\d-\d\d$/); 
  if ($dueDate =~ m/(\d\d\d\d-\d\d-\d\d) (\d\d:\d\d:\d\d)/g) {
    $dueDate = $1;
    if ($loanType eq 'hourly') {
      $time = " $2";
    }
    $dueDate .= $time;
  }
  if ($dueDate && $dueDate ne '') {
    my $today = date_today();
    my $dd    = date_parse($dueDate);
    if ($dd && $dd < $today) {
      $dueDate = "";
    }
  }
  if (!$dueDate || $dueDate eq '') {
    if ($loanType eq 'daily') {
      $dueDate = date_getDeadLineDate($loanPeriod, $todayStr);
    }
    else {
      $dueDate = date_getDeadLineDateTime($loanPeriod, $todayStr);
    }
  }
  return $dueDate;

}

#-------------------------------------------------------------------------------
sub doReturn {
  my ($dbh, $uid, $barcode, $dateReturn) = @_;
  my $loanType = 'daily';
  my $sth      = $dbh->prepare(
"select l.idloan, l.dateDue ,u.firstname,u.lastname from opl_loan l inner join opl_user u using (uid) where l.uid=? && barcode=? && dateReturn is null"
  );
  $sth->execute($uid, $barcode);
  my $rec     = $sth->fetchrow_hashref;
  my $idloan  = $rec->{'idloan'};
  my $dateDue = dateTime_parse($rec->{'dateDue'});
  my $dateRet = dateTime_parse($dateReturn);
  my $timeDd  = "23:59:59";

  if (
    $rec->{'dateDue'} =~ m/(\d{4}-\d{1,2}-\d{1,2}) (\d{1,2}:\d{1,2}:\d{1,2})/g)
  {
    $timeDd = $2;
  }
  if ($timeDd ne "23:59:59") {    #Case: Hourly Circ
    $loanType = 'hourly';
  }

  my ($mumOfDaysOD, $mumOfHoursOD) = date_deltaWorkDayHour($dateDue, $dateRet);

  $mumOfDaysOD = 0 if ($mumOfDaysOD <= 0);
  if ($loanType ne 'hourly') {
    $mumOfDaysOD += 1 if ($mumOfHoursOD > 0);
    $mumOfHoursOD = 0;
  }
  $mumOfHoursOD = 0 if ($mumOfHoursOD <= 0);

  #      $mumOfDaysOD = 0 if ($mumOfDaysOD<=0);
  #      $mumOfHoursOD= 0 if ($mumOfHoursOD<=0);

  my $odl_id = 0;

  # Even overduefine = 0, it is still recorded to keep track how many times
  # user make overdue
  if ($mumOfDaysOD > 0 || $mumOfHoursOD > 0) {
    $odl_id = circ_record_ODL_ext($dbh, $idloan, 'overdue', $mumOfDaysOD,
      $mumOfHoursOD);
  }
  my $return = {
    lid         => $idloan,
    l_firstname => $rec->{'firstname'},
    l_lastname  => $rec->{'lastname'},
    loanType    => $loanType,
    overdue     => { day => $mumOfDaysOD, hour => $mumOfHoursOD },
    odl_id      => $odl_id
  };
  circ_processReturn($dbh, $barcode, $dateReturn);
  return $return;
}

