<?php
/**
 * phpaga
 *
 * Persons management functionality.
 *
 * This file contains the necessary routines to manage persons.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @version $Id$
 *
 * Copyright (c) 2002, Florian Lanthaler <florian@phpaga.net>
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in
 *      the documentation and/or other materials provided with the
 *      distribution.
 *
 *    * Neither the name of Florian Lanthaler nor the names of his
 *      contributors may be used to endorse or promote products derived
 *      from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


/**
 * Returns a list of person categories as the content of an array.
 *
 * @return  array  $pecatList  Array of person categories
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_getCategoryList()
{
    $pecatList = array();

    $h = phpaga_persons_getPecats();

    if (!PhPagaError::isError($h)) {
        foreach ($h as $p)
            $pecatList[$p['pecat_id']] = $p['pecat_title'];
    }

    return $pecatList;
}


/**
 * Checks if all fields of a person contain valid values.
 *
 * @param  array  $peInfo  Array containing the project's information.
 *
 * @return bool   $errors  True. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_validdata($peInfo)
{

    $errors = array();

    if (!is_array($peInfo))
        $peInfo = array($peInfo);

    if (isset($peInfo['pe_email']) && strlen($peInfo['pe_email'])
        && !phpaga_checkemailaddress($peInfo['pe_email']))
        $errors[] = PHPAGA_ERR_INVALID_EMAIL;

    if (!isset($peInfo['pe_lastname']) || !strlen($peInfo['pe_lastname']))
        $errors[] = PHPAGA_ERR_PERSON_INVALIDLASTNAME;

    if (isset($peInfo['ctr_id']) && !is_numeric($peInfo['ctr_id']))
        $errors[] = PHPAGA_ERR_COUNTRY_INVALIDID;

    if (isset($peInfo['pecat_id']) && strlen($peInfo['pecat_id']) && !is_numeric($peInfo['pecat_id']))
        $errors[] = PHPAGA_ERR_PECAT_INVALIDID;

    if (isset($peInfo['pe_hourlyrate']) && strlen($peInfo['pe_hourlyrate']) && !is_numeric($peInfo['pe_hourlyrate']))
        $errors[] = PHPAGA_ERR_INVALID_HOURLYRATE;

    if (count($errors))
        return PhPagaError::raiseErrorByCode($errors);

    return true;
}


/**
 * Adds a person to the database
 *
 * @param array   $peInfo   Array containing the person information
 *
 * @return int    $peID     ID of the freshly inserted person. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_add($peInfo)
{

    $peID = null;

    $result = phpaga_persons_validdata($peInfo);

    if (PhPagaError::isError($result))
        return $result;

    $d = new PhPagaDbData;

    $peID = $d->nextId('persons_pe_id');

    if (!isset($peInfo['pe_lastname']) || !strlen($peInfo['pe_lastname']))
        $peInfo['pe_lastname'] = NULL;

    if (!isset($peInfo['pe_firstname']) || !strlen($peInfo['pe_firstname']))
        $peInfo['pe_firstname'] = NULL;

    if (!isset($peInfo['pe_address']) || !strlen($peInfo['pe_address']))
        $peInfo['pe_address'] = NULL;

    if (!isset($peInfo['pe_address2']) || !strlen($peInfo['pe_address2']))
        $peInfo['pe_address2'] = NULL;

    if (!isset($peInfo['pe_city']) || !strlen($peInfo['pe_city']))
        $peInfo['pe_city'] = NULL;

    if (!isset($peInfo['pe_zip']) || !strlen($peInfo['pe_zip']))
        $peInfo['pe_zip'] = NULL;

    if (!isset($peInfo['pe_region']) || !strlen($peInfo['pe_region']))
        $peInfo['pe_region'] = NULL;

    if (!isset($peInfo['ctr_id']) || !is_numeric($peInfo['ctr_id']))
        $peInfo['ctr_id'] = NULL;

    if (!isset($peInfo['pecat_id']) || !is_numeric($peInfo['pecat_id']))
        $peInfo['pecat_id'] = NULL;

    if (!isset($peInfo['pe_taxnr']) || ! strlen($peInfo['pe_taxnr']))
        $peInfo['pe_taxnr'] = NULL;

    if (!isset($peInfo['pe_url']) || !strlen($peInfo['pe_url']))
        $peInfo['pe_url'] = NULL;

    if (!isset($peInfo['pe_phonework']) || !strlen($peInfo['pe_phonework']))
        $peInfo['pe_phonework'] = NULL;

    if (!isset($peInfo['pe_phonemobile']) || !strlen($peInfo['pe_phonemobile']))
        $peInfo['pe_phonemobile'] = NULL;

    if (!isset($peInfo['pe_phonehome']) || !strlen($peInfo['pe_phonehome']))
        $peInfo['pe_phonehome'] = NULL;

    if (!isset($peInfo['pe_fax']) || !strlen($peInfo['pe_fax']))
        $peInfo['pe_fax'] = NULL;

    if (!isset($peInfo['pe_email']) || !strlen($peInfo['pe_email']))
        $peInfo['pe_email'] = NULL;

    if (!isset($peInfo['cpn_id']) || !is_numeric($peInfo['cpn_id']))
        $peInfo['cpn_id'] = NULL;

    if (!isset($peInfo['jcat_id']) || !is_numeric($peInfo['jcat_id']))
        $peInfo['jcat_id'] = NULL;

    if (!isset($peInfo['pe_hourlyrate']) || !is_numeric($peInfo['pe_hourlyrate']))
        $peInfo['pe_hourlyrate'] = NULL;

    if (!isset($peInfo['pe_note']) || !strlen($peInfo['pe_note']))
        $peInfo['pe_note'] = NULL;

    if (!isset($peInfo['pe_pe_id']))
        $peInfo['pe_pe_id'] = $peID;

    $fields = array('pe_id' => $peID,
                    'pe_lastname' => $peInfo['pe_lastname'],
                    'pe_firstname' => $peInfo['pe_firstname'],
                    'pe_address' => $peInfo['pe_address'],
                    'pe_address2' => $peInfo['pe_address2'],
                    'pe_city' => $peInfo['pe_city'],
                    'pe_zip' => $peInfo['pe_zip'],
                    'pe_region' => $peInfo['pe_region'],
                    'ctr_id' => $peInfo['ctr_id'],
                    'pecat_id' => $peInfo['pecat_id'],
                    'pe_hourlyrate' => $peInfo['pe_hourlyrate'],
                    'pe_taxnr' => $peInfo['pe_taxnr'],
                    'pe_url' => $peInfo['pe_url'],
                    'pe_phonework' => $peInfo['pe_phonework'],
                    'pe_phonemobile' => $peInfo['pe_phonemobile'],
                    'pe_phonehome' => $peInfo['pe_phonehome'],
                    'pe_fax' => $peInfo['pe_fax'],
                    'pe_email' => $peInfo['pe_email'],
                    'cpn_id' => $peInfo['cpn_id'],
                    'jcat_id' => $peInfo['jcat_id'],
                    'pe_note' => $peInfo['pe_note'],
                    'pe_pe_id' => $peInfo['pe_pe_id']
        );

    $r = $d->insert('persons', $fields);

    if (PhPagaError::isError($r))
        return $r;

    return $peID;
}


/**
 * Updates a person
 *
 * @param array   $peInfo  Array containing the person information
 *
 * @return bool   $result  True. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_update($peInfo)
{

    $result = phpaga_persons_validdata($peInfo);

    if (PhPagaError::isError($result))
        return $result;

    if (!isset($peInfo['pe_lastname']) || !strlen($peInfo['pe_lastname']))
        $peInfo['pe_lastname'] = NULL;

    if (!isset($peInfo['pe_firstname']) || !strlen($peInfo['pe_firstname']))
        $peInfo['pe_firstname'] = NULL;

    if (!isset($peInfo['pe_address']) || !strlen($peInfo['pe_address']))
        $peInfo['pe_address'] = NULL;

    if (!isset($peInfo['pe_address2']) || !strlen($peInfo['pe_address2']))
        $peInfo['pe_address2'] = NULL;

    if (!isset($peInfo['pe_city']) || !strlen($peInfo['pe_city']))
        $peInfo['pe_city'] = NULL;

    if (!isset($peInfo['pe_zip']) || !strlen($peInfo['pe_zip']))
        $peInfo['pe_zip'] = NULL;

    if (!isset($peInfo['pe_region']) || !strlen($peInfo['pe_region']))
        $peInfo['pe_region'] = NULL;

    if (!isset($peInfo['ctr_id']) || !is_numeric($peInfo['ctr_id']))
        $peInfo['ctr_id'] = NULL;

    if (!isset($peInfo['pecat_id']) || !is_numeric($peInfo['pecat_id']))
        $peInfo['pecat_id'] = NULL;

    if (!isset($peInfo['pe_taxnr']) || ! strlen($peInfo['pe_taxnr']))
        $peInfo['pe_taxnr'] = NULL;

    if (!isset($peInfo['pe_url']) || !strlen($peInfo['pe_url']))
        $peInfo['pe_url'] = NULL;

    if (!isset($peInfo['pe_phonework']) || !strlen($peInfo['pe_phonework']))
        $peInfo['pe_phonework'] = NULL;

    if (!isset($peInfo['pe_phonemobile']) || !strlen($peInfo['pe_phonemobile']))
        $peInfo['pe_phonemobile'] = NULL;

    if (!isset($peInfo['pe_phonehome']) || !strlen($peInfo['pe_phonehome']))
        $peInfo['pe_phonehome'] = NULL;

    if (!isset($peInfo['pe_fax']) || !strlen($peInfo['pe_fax']))
        $peInfo['pe_fax'] = NULL;

    if (!isset($peInfo['pe_email']) || !strlen($peInfo['pe_email']))
        $peInfo['pe_email'] = NULL;

    if (!isset($peInfo['cpn_id']) || !is_numeric($peInfo['cpn_id']))
        $peInfo['cpn_id'] = NULL;

    if (!isset($peInfo['jcat_id']) || !is_numeric($peInfo['jcat_id']))
        $peInfo['jcat_id'] = NULL;

    if (!isset($peInfo['pe_hourlyrate']) || !is_numeric($peInfo['pe_hourlyrate']))
        $peInfo['pe_hourlyrate'] = NULL;

    if (!isset($peInfo['pe_note']) || !strlen($peInfo['pe_note']))
        $peInfo['pe_note'] = NULL;

    $fields = array('pe_id' => $peInfo['pe_id'],
                    'pe_lastname' => $peInfo['pe_lastname'],
                    'pe_firstname' => $peInfo['pe_firstname'],
                    'pe_address' => $peInfo['pe_address'],
                    'pe_address2' => $peInfo['pe_address2'],
                    'pe_city' => $peInfo['pe_city'],
                    'pe_zip' => $peInfo['pe_zip'],
                    'pe_region' => $peInfo['pe_region'],
                    'ctr_id' => $peInfo['ctr_id'],
                    'pecat_id' => $peInfo['pecat_id'],
                    'pe_hourlyrate' => $peInfo['pe_hourlyrate'],
                    'pe_taxnr' => $peInfo['pe_taxnr'],
                    'pe_url' => $peInfo['pe_url'],
                    'pe_phonework' => $peInfo['pe_phonework'],
                    'pe_phonemobile' => $peInfo['pe_phonemobile'],
                    'pe_phonehome' => $peInfo['pe_phonehome'],
                    'pe_fax' => $peInfo['pe_fax'],
                    'pe_email' => $peInfo['pe_email'],
                    'cpn_id' => $peInfo['cpn_id'],
                    'jcat_id' => $peInfo['jcat_id'],
                    'pe_note' => $peInfo['pe_note']
        );

    $d = new PhPagaDbData;
    $r = $d->update('persons', $fields, 'pe_id = ?', $peInfo['pe_id']);

    if (PhPagaError::isError($r))
        return $r;

    return true;
}


/**
 * Gets a list (array) of all persons of a certain category or company
 * (from $offset to $limit, if set)
 *
 * @param int    $count     Number of records matching the query
 * @param int    $pecat     person category
 * @param int    $pecpn     company
 * @param int    $offset    Offset
 * @param int    $limit     Limit
 *
 * @return array $rows      Array containing the results. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_list(&$count, $pecat, $pecpn, $offset = null, $limit = null)
{
    $rows = array();
    $count = 0;
    $where = array();
    $qparam = array();
    $count_pe = array();

    /* Total number of matching records */

    $countquery = 'SELECT COUNT(pe_id) AS count_pe FROM persons ';

    /* Select records from persons table */

    $query = 'SELECT persons.pe_id, pe_lastname, pe_firstname, pe_address, pe_address2,
        pe_city, pe_region, pe_zip, persons.ctr_id, ctr_name, ctr_code,
        persons.pecat_id, pecat_title, pe_taxnr, pe_url,
        pe_phonework, pe_phonemobile, pe_phonehome,
        pe_fax, pe_email, persons.jcat_id, persons.cpn_id,
        pe_note, cpn_name, jcat_title
        FROM persons
        LEFT JOIN personcat ON personcat.pecat_id = persons.pecat_id
        LEFT JOIN countries ON countries.ctr_id = persons.ctr_id
        LEFT JOIN companies ON companies.cpn_id = persons.cpn_id
        LEFT JOIN jobcat ON jobcat.jcat_id = persons.jcat_id ';

    if (strlen($pecat)) {
        $where[] = '(persons.pecat_id = ?)';
        $qparam[] = $pecat;
    }

    if (strlen($pecpn)) {
        $where[] = '(persons.cpn_id = ?)';
        $qparam[] = $pecpn;
    }

    if (count($where) > 0) {
        $h = sprintf('WHERE %s ', implode(' AND ', $where));
        $countquery .= $h;
        $query .= $h;
    }

    $query .= 'ORDER BY pe_lastname, pe_firstname, pe_city, ctr_name ';

    $d = new PhPagaDbData;

    /* First do the count stuff */

    $count_pe = $d->queryOneVal($countquery, $qparam);

    if (!PhPagaError::isError($count_pe))
        $count = $count_pe;

    /* Fetch person records */

    $persons = $d->queryAll($query, $qparam, $offset, $limit);

    if (PhPagaError::isError($persons))
        return $persons;

    foreach($persons as $qrow) {

        phpaga_sanitizearray($qrow);

        $qrow['pe_person'] = phpaga_persons_formatname ($qrow['pe_lastname'], $qrow['pe_firstname']);
        $qrow['pe_location'] = phpaga_getLocation ($qrow['pe_city'], $qrow['pe_region']);
        $qrow['pe_full_location'] = phpaga_getFullLocation ($qrow['pe_city'],
                                                            $qrow['pe_region'],
                                                            $qrow['pe_zip'],
                                                            $qrow['ctr_name']);

        $rows[] = $qrow;
    }

    phpaga_sanitizearray($rows);
    return $rows;
}


/**
 * Gets a person's information
 *
 * @param  int    $peID   person ID
 *
 * @return array  $peInfo Array containing the results. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_getInfo($peID)
{
    $peInfo = array();
    $persprj = array();
    $dummy = null;
    $pe_duration = null;

    if (!isset($peID) || !is_numeric($peID))
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_PERSON_INVALIDID);

    $query = 'SELECT p.pe_id, p.pe_lastname, p.pe_firstname,
		p.pe_address, p.pe_address2, p.pe_city, p.pe_zip, p.pe_region,
		p.ctr_id, ctr_name, ctr_code,
		p.pecat_id, pecat_title, p.pe_taxnr, p.pe_url,
        p.pe_phonework, p.pe_phonemobile, p.pe_phonehome,
        p.pe_fax, p.pe_email, p.jcat_id, p.cpn_id,
        p.pe_note, p.pe_hourlyrate, cpn_name, jcat_title,
        p.pe_pe_id, p2.pe_id as owner_pe_id, p2.pe_lastname as owner_pe_lastname,
        p2.pe_firstname as owner_pe_firstname
		FROM persons p
        LEFT JOIN personcat ON personcat.pecat_id = p.pecat_id
        LEFT JOIN countries ON countries.ctr_id = p.ctr_id
        LEFT JOIN companies ON companies.cpn_id = p.cpn_id
		LEFT JOIN jobcat ON jobcat.jcat_id = p.jcat_id
        LEFT JOIN persons p2 ON p2.pe_id = p.pe_pe_id
		WHERE p.pe_id = ?';

    $d = new PhPagaDbData;

    $peInfo = $d->queryRow($query, $peID);

    if (PhPagaError::isError($peInfo) || ($peInfo===false ) || !count($peInfo))
        return $peInfo;

    $peInfo['pe_person'] = phpaga_persons_formatname($peInfo['pe_lastname'], $peInfo['pe_firstname']);
    $peInfo['owner_pe_person'] = phpaga_persons_formatname($peInfo['owner_pe_lastname'], $peInfo['owner_pe_firstname']);
    $peInfo['pe_location'] = phpaga_getLocation($peInfo['pe_city'], $peInfo['pe_region']);
    $peInfo['pe_full_location'] = phpaga_getFullLocation($peInfo['pe_city'],
                                                         $peInfo['pe_region'],
                                                         $peInfo['pe_zip'],
                                                         $peInfo['ctr_name']);

    $peInfo['pe_duration'] = 0;
    $peInfo['projects'] = array();

    $d = new PhPagaDbData;
    $pedur = $d->queryAll('SELECT SUM(op_duration) AS duration FROM operations WHERE pe_id = ?', $peInfo['pe_id']);

    if (!PhPagaError::isError($pedur) && is_array($pedur) && count($pedur) && isset($pedur[0]['duration']))
        $peInfo['pe_duration'] = (int)$pedur[0]['duration'];

    $persprj = phpaga_projects_getpersonprojects($peID);

    if (!PhPagaError::isError($persprj))
        $peInfo['projects'] = $persprj;

    $files = phpaga_files_getAllRel(PHPAGA_RELTYPE_PERSON, $peID);

    if (!PhPagaError::isError($files))
        $peInfo['files'] = $files;

    phpaga_sanitizeArray($peInfo);

    return $peInfo;
}


/**
 * Loads all person categories into an array.
 *
 * @return array $result    Array containing the results. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_getPecats()
{
    $pecatInfo = null;

    $d = new PhPagaDbData;

    $pecatInfo = $d->queryAll('SELECT pecat_id, pecat_title FROM personcat ORDER BY pecat_title');

    return $pecatInfo;
}


/**
 * Add a new person category.
 *
 * @param  string  $pecat_title  Person category to be added
 *
 * @return int     $id           Category Id. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_addPecat($pecat_title)
{

    if (!isset($pecat_title) || !strlen($pecat_title))
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_PECAT_INVALIDTITLE);

    $d = new PhPagaDbData;
    $id = $d->nextId('personcat_pecat_id');

    $r = $d->insert('personcat', array('pecat_id'=> $id, 'pecat_title' => $pecat_title));

    if (PhPagaError::isError($r))
        return $r;

    return $id;
}


/**
 * Delete a person category.
 *
 * First check if this pecat is needed by any table relationship;
 * delete it only if it is not needed.
 *
 * @param  int     $pecat_id    Id of person category to be deleted
 *
 * @return bool    $result      True if successful. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_deletePecat($pecat_id)
{
    if (!isset($pecat_id) || !is_numeric($pecat_id))
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_PECAT_INVALIDID);

    $d = new PhPagaDbData;

    $n = $d->queryOneVal('SELECT COUNT(pe_id) FROM persons WHERE pecat_id = ?', $pecat_id);

    if (PhPagaError::isError($n))
        return $n;

    if ($n > 0)
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_DEL_DEPENDENCIES);

    $result = $d->execute('DELETE FROM personcat WHERE pecat_id = ?', $pecat_id);

    if (PhPagaError::isError($result))
        return $result;

    return true;
}


/**
 * Loads all job categories into an array.
 *
 * @return array $result  Array containing the results. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_getJobcats()
{
    $jcatInfo = null;

    $d = new PhPagaDbData;

    $jcatInfo = $d->queryAll('SELECT jcat_id, jcat_title FROM jobcat ORDER BY jcat_title');

    return $jcatInfo;
}


/**
 * Add a new job category.
 *
 * @param  string  $jcat_title   Job category title to be added
 *
 * @return int     $id           Category Id. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_addJobcat($jcat_title)
{

    if (!isset($jcat_title) || !strlen($jcat_title))
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_JOBCAT_INVALIDTITLE);

    $d = new PhPagaDbData;
    $id = $d->nextId('jobcat_jcat_id');

    $r = $d->insert('jobcat',  array('jcat_id'=> $id,
                                     'jcat_title' => $jcat_title));
    if (PhPagaError::isError($r))
        return $r;

    return $id;
}


/**
 * Delete a job category.
 *
 * First check if this jcat is needed by any table relationship;
 * delete it only if it is not needed.
 *
 * @param  int     $jcat_id     ID of job category to be deleted
 *
 * @return bool    $result      True if successfull, PhPagaError otherwise.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_deleteJobcat($jcat_id)
{
    if (!isset($jcat_id) || !is_numeric($jcat_id))
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_JOBCAT_INVALIDID);

    $d = new PhPagaDbData;

    $n = $d->queryOneVal('SELECT COUNT(pe_id) FROM persons WHERE jcat_id = ?', $jcat_id);

    if (PhPagaError::isError($n))
        return $n;

    if ($n > 0)
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_DEL_DEPENDENCIES);

    $n = $d->queryOneVal('SELECT COUNT(jcat_id) FROM project_members WHERE jcat_id = ?', $jcat_id);

    if (PhPagaError::isError($n))
        return $n;

    if ($n > 0)
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_DEL_DEPENDENCIES);

    $result = $d->execute('DELETE FROM jobcat WHERE jcat_id = ?', $jcat_id);

    if (PhPagaError::isError($result))
        return $result;

    return true;
}


/**
 * Return a person's name formatted according to PHPAGA_PENAME_FORMAT
 *
 * @param  string  $lastname   Person's last name
 * @param  string  $firstname  Person's first name
 *
 * @return string  $name       Formatted name
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1.2
 */

function phpaga_persons_formatname($lastname = '', $firstname = '')
{

    $formattedname = '';

    /* Format the name if both last and first name are defined,
     * otherwise just return whatever is available
     */

    if (strlen($lastname) && strlen($firstname))
    {
        $formattedname = str_replace("%LASTNAME", $lastname, PHPAGA_PENAME_FORMAT);
        $formattedname = str_replace("%FIRSTNAME", $firstname, $formattedname);
    }
    elseif (strlen($lastname))
        $formattedname = $lastname;
    elseif (strlen($firstname))
        $formattedname = $firstname;

    return $formattedname;
}


/**
 * Delete a person. A person can only be deleted if there are no dependencies
 * left (check companies, operations, persons (owner), projects,
 * users (user and owner)).
 *
 * @param  int     $pe_id       Person ID
 *
 * @return bool    $result      True. A PhPagaError on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_persons_delete($pe_id)
{

    if (!isset($pe_id) || !is_numeric($pe_id))
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_PERSON_INVALIDID);

    $d = new PhPagaDbData;

    /* Check if this person is needed by any table relationship if it
     * is not needed, then delete it, otherwise don't */

    $depqueries = array('SELECT COUNT(cpn_id) FROM companies WHERE pe_id = ?',
                        'SELECT COUNT(op_id) FROM operations WHERE pe_id = ?',
                        'SELECT COUNT(pe_id) FROM persons where pe_pe_id = ?',
                        'SELECT COUNT(prj_id) FROM projects WHERE pe_id = ?',
                        'SELECT COUNT(prj_id) FROM project_members WHERE pe_id = ?',
                        'SELECT COUNT(usr_id) FROM users WHERE pe_id = ?',
                        'SELECT COUNT(usr_id) FROM users WHERE usr_pe_id = ?');

    foreach ($depqueries as $dpq) {

        $n = $d->queryOneVal($dpq, $pe_id);

        if (PhPagaError::isError($n))
            return $n;

        if ($n > 0)
            return PhPagaError::raiseErrorByCode(PHPAGA_ERR_DEL_DEPENDENCIES);
    }

    /* It is safe to delete the person as it is not needed by any
     * relationship.
     */

    $result = $d->execute('DELETE FROM persons WHERE pe_id = ?', $pe_id);

    if (PhPagaError::isError($result))
        return $result;

    return true;
}


/**
 * Gets all persons matching the search parmeters
 * (from $offset to $limit, if set)
 *
 * @param int    $count     Number of records matching the query
 * @param array  $prsInfo   Person information
 * @param array  $prsIds    IDs of the resulting persons
 * @param int    $order     Specifies the order by field
 *
 * @param int    $offset    Offset (optional)
 * @param int    $limit     Limit (optional)
 *
 * @return array $rows      Array containing the results. A PhPagaError object on failure.
 *
 * @author Mark Parssey <markpa@users.sourceforge.net>
 * @since phpaga 0.3
 */

function phpaga_persons_search(&$count,
                               &$prsIds,
                               $prsInfo,
                               $order = 0,
                               $offset = null,
                               $limit = null)
{

    $count = 0;
    $rows = array();
    $prsIds = array();
    $where = array();
    $qparam = array();

    /* Total number of matching records? */

    $countquery = 'SELECT COUNT(persons.pe_id) AS count_pe  FROM persons ';

    /* Select records from persons table */

    $query = 'SELECT persons.pe_id, pe_lastname, pe_firstname, pe_address, pe_address2,
        pe_city, pe_region, pe_zip, persons.ctr_id, ctr_name, ctr_code,
        persons.pecat_id, pecat_title, pe_taxnr, pe_url,
        pe_phonework, pe_phonemobile, pe_phonehome,
        pe_fax, pe_email, persons.jcat_id, persons.cpn_id,
        pe_note, cpn_name, jcat_title, pe_pe_id
        FROM persons
        LEFT JOIN personcat ON personcat.pecat_id = persons.pecat_id
        LEFT JOIN countries ON countries.ctr_id = persons.ctr_id
        LEFT JOIN companies ON companies.cpn_id = persons.cpn_id
        LEFT JOIN jobcat ON jobcat.jcat_id = persons.jcat_id ';

    if (isset($prsInfo['pe_lastname']) && strlen($prsInfo['pe_lastname'])) {
        $where[] = 'persons.pe_lastname LIKE ?';
        $qparam[] = '%'.$prsInfo['pe_lastname'].'%';
    }

    if (isset($prsInfo['pe_firstname']) && strlen($prsInfo['pe_firstname'])) {
        $where[] = 'persons.pe_firstname LIKE ?';
        $qparam[] = '%'.$prsInfo['pe_firstname'].'%';
    }

    if (isset($prsInfo['pe_address']) && strlen($prsInfo['pe_address'])) {
        $where[] = 'persons.pe_address LIKE ?';
        $qparam[] = '%'.$prsInfo['pe_address'].'%';
    }

    if (isset($prsInfo['pe_address2']) && strlen($prsInfo['pe_address2'])) {
        $where[] = 'persons.pe_address2 LIKE ?';
        $qparam[] = '%'.$prsInfo['pe_address2'].'%';
    }

    if (isset($prsInfo['pe_city']) && strlen($prsInfo['pe_city'])) {
        $where[] = 'persons.pe_city LIKE ?';
        $qparam[] = '%'.$prsInfo['pe_city'].'%';
    }

    if (isset($prsInfo['pe_zip']) && strlen($prsInfo['pe_zip'])) {
        $where[] = 'persons.pe_zip LIKE ?';
        $qparam[] = '%'.$prsInfo['pe_zip'].'%';
    }

    if (isset($prsInfo['pe_region']) && strlen($prsInfo['pe_region'])) {
        $where[] = 'persons.pe_region LIKE ?';
        $qparam[] = '%'.$prsInfo['pe_region'].'%';
    }

    if (isset($prsInfo['ctr_id']) && is_numeric($prsInfo['ctr_id'])) {
        $where[] = 'persons.ctr_id = ?';
        $qparam[] = $prsInfo['ctr_id'];
    }

    if (isset($prsInfo['cpn_id']) && is_numeric($prsInfo['cpn_id'])) {
        $where[] = 'persons.cpn_id = ?';
        $qparam[] = $prsInfo['cpn_id'];
    }

    if (isset($prsInfo['pecat_id']) && is_numeric($prsInfo['pecat_id'])) {
        $where[] = 'persons.pecat_id = ?';
        $qparam[] = $prsInfo['pecat_id'];
    }

    if (isset($prsInfo['pe_email']) && strlen($prsInfo['pe_email'])) {
        $where[] = 'persons.pe_email LIKE ?';
        $qparam[] = '%'.$prsInfo['pe_email'].'%';
    }

    if (isset($prsInfo['jcat_id']) && is_numeric($prsInfo['jcat_id'])) {
        $where[] = 'persons.jcat_id = ?';
        $qparam[] = $prsInfo['jcat_id'];
    }

    if (count($where)) {
        $h = sprintf('WHERE %s ', implode(' AND ', $where));
        $countquery .= $h;
        $query .= $h;
    }

    $query .= ' ORDER BY ';

    if (($order == '') || ($order == 0) || !is_numeric($order))
        $order = ORDER_PE_NAME_ASC;

    switch ($order) {

        case ORDER_PE_NAME_ASC:
            $query .= 'pe_lastname ASC, pe_firstname ASC';
            break;
        case ORDER_PE_NAME_DESC:
            $query .= 'pe_lastname DESC, pe_firstname DESC';
            break;
        case ORDER_PE_ADDRESS_ASC:
            $query .= 'pe_address ASC';
            break;
        case ORDER_PE_ADDRESS_DESC:
            $query .= 'pe_address DESC';
            break;
        case ORDER_PE_CITY_ASC:
            $query .= 'pe_city ASC';
            break;
        case ORDER_PE_CITY_DESC:
            $query .= 'pe_city DESC';
            break;
        case ORDER_PE_COUNTRY_ASC:
            $query .= 'ctr_name ASC';
            break;
        case ORDER_PE_COUNTRY_DESC:
            $query .= 'ctr_name DESC';
            break;
        case ORDER_PE_COMPANY_ASC:
            $query .= 'cpn_name ASC';
            break;
        case ORDER_PE_COMPANY_DESC:
            $query .= 'cpn_name DESC';
            break;
        case ORDER_PE_EMAIL_ASC:
            $query .= 'pe_email ASC';
            break;
        case ORDER_PE_EMAIL_DESC:
            $query .= 'pe_email DESC';
            break;
        default:
            $query .= 'pe_lastname ASC, pe_firstname ASC';
            break;
    }

    $d = new PhPagaDbData;

    /* First do the count stuff */

    $count_pe = $d->queryOneVal($countquery, $qparam);

    if (!PhPagaError::isError($count_pe))
        $count = $count_pe;

    /* Fetch persons */

    $persons = $d->queryAll($query, $qparam, $offset, $limit);

    if (PhPagaError::isError($persons))
        return $persons;

    foreach($persons as $qrow) {

        phpaga_sanitizearray($qrow);

        $qrow['pe_person'] = phpaga_persons_formatname ($qrow['pe_lastname'], $qrow['pe_firstname']);
        $qrow['pe_location'] = phpaga_getLocation ($qrow['pe_city'], $qrow['pe_region']);
        $qrow['pe_full_location'] = phpaga_getFullLocation ($qrow['pe_city'],
                                                            $qrow['pe_region'],
                                                            $qrow['pe_zip'],
                                                            $qrow['ctr_name']);

        $rows[] = $qrow;
    }

    phpaga_sanitizearray($rows);
    return $rows;
}


/**
 * Determine whether some person is the owner of a person record. If
 * pe_pe_id is omitted, the current user's pe_id is used.
 *
 * @param int    $pe_id     Person ID of the record
 *
 * @param int    $pe_pe_id  Person ID  of the owner (optional, if omitted, the
 *                          current user's pe_id is used)
 *
 * @return bool             $isowner  Is pe_pe_id the owner of pe_id?
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_persons_isowner($pe_id, $pe_pe_id = null)
{

    $isowner = false;

    if (($pe_pe_id == null) && isset($_SESSION['auth_user']) && isset($_SESSION['auth_user']['pe_id'])
        && is_numeric($_SESSION['auth_user']['pe_id']))

        $pe_pe_id = $_SESSION['auth_user']['pe_id'];

    $d = new PhPagaDbData;
    $p = $d->queryAll('SELECT pe_pe_id FROM persons WHERE pe_id = ?', $pe_id);

    if (PhPagaError::isError($p) || !is_array($p) || !count($p) || !isset($p[0]['pe_pe_id']))
        return false;

    return ((int)$pe_pe_id == (int)$p[0]['pe_pe_id']);
}


/**
 * Get some manhours statistics for a specific person.
 *
 * @param int    $pe_id     Person ID
 *
 * @param int    $fy        Fiscal year (optional, default is current FY)
 *
 * @return array $stats     Array with manhours per FY, per current month and per current week.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.5.2
 */

function phpaga_persons_getBillableManhoursStats($pe_id, $fy=null) {

    if (strlen($fy) && !is_numeric($fy))
        return false;

    if (is_null($fy))
        $fy = phpaga_finances_getfiscalyear(date('Y-m-d'));

    $stats = array('fy' => $fy,
        'curr_fy' => null,
        'curr_month' => null,
        'curr_week' => null,
        'spark_month' => null,
        'op' => array('begin' => null, 'end' => null));

    $fydates = phpaga_finances_getfiscalyeardates($fy);

    $d = new PhPagaDbData;

    $op = $d->queryRow('select min(op_start) as begin_op, max(op_start) as end_op from operations where pe_id=?', $pe_id);

    if (PhPagaError::isError($op))
        return $op;

    $stats['op']['begin'] = phpaga_modifier_date_format($op['begin_op'], PHPAGA_DATEFORMAT_SHORT);
    $stats['op']['end'] = phpaga_modifier_date_format($op['end_op'], PHPAGA_DATEFORMAT_SHORT);

    $val = $d->queryOneVal('SELECT SUM(op_duration) FROM operations WHERE pe_id=? AND op_billabletype<>? AND op_start>=? and op_start<=?',
        array($pe_id, PHPAGA_BILLABLETYPE_NO, $fydates['start'], $fydates['end']));

    if (PhPagaError::isError($val))
        return $val;

    $stats['curr_fy'] = phpaga_minutes2duration($val);

    $val = $d->queryOneVal('SELECT SUM(op_duration) FROM operations 
        WHERE pe_id=? 
        AND op_billabletype<>?
        AND YEAR(op_start)=YEAR(CURRENT_TIMESTAMP) 
        AND MONTH(op_start)=MONTH(CURRENT_TIMESTAMP)',
        array($pe_id, PHPAGA_BILLABLETYPE_NO));

    if (PhPagaError::isError($val))
        return $val;

    $stats['curr_month'] = phpaga_minutes2duration($val);

    $query = 'SELECT YEAR(op_start) as y, MONTH(op_start) AS m, SUM(op_duration) as s 
        FROM operations 
        WHERE pe_id=?
        AND op_billabletype<>?
        group by year(op_start), month(op_start) order by year(op_start), month(op_start)';
    $val = $d->queryAll($query, array($pe_id, PHPAGA_BILLABLETYPE_NO));

    if (PhPagaError::isError($val))
        return $val;

    $stats['spark_month'] = array();
    $beginy = (int)date('Y', strtotime($op['begin_op']));
    $beginm = (int)date('m', strtotime($op['begin_op']));
    $endy = (int)date('Y', strtotime($op['end_op']));
    $endm = (int)date('m', strtotime($op['end_op']));

    $_s = array();
    $y = $beginy;
    $m = $beginm;

    while ($y <= $endy) {
        $_s[$y][$m] = 0;
        if (($y == $endy) && ($m == $endm))
            break;

        if ($m == 12) {
            $m = 1;
            $y++;
        } else
            $m++;
    }

    foreach ($val as $v)
        $_s[$v['y']][$v['m']] = $v['s'];

    $_sm = array();
    foreach ($_s as $y => $ya)
        foreach ($ya as $m => $v)
            $_sm[] = $v;

    $stats['spark_month'] = $_sm;

    $query = '';
    $param = array();

    switch(PHPAGA_DB_SYSTEM) {
        case 'pgsql':
            $query = "SELECT SUM(op_duration) FROM operations 
                WHERE pe_id=? 
                AND op_billabletype<>?
                AND YEAR(op_start)=YEAR(CURRENT_TIMESTAMP) 
                AND TO_CHAR(op_start, 'iw')=TO_CHAR(CURRENT_TIMESTAMP, 'iw')";
            $param = array($pe_id, PHPAGA_BILLABLETYPE_NO);
            break;
        case "mysql":
            $query = "SELECT SUM(op_duration) FROM operations 
                WHERE pe_id=? 
                AND op_billabletype<>?
                AND YEAR(op_start)=YEAR(CURRENT_TIMESTAMP) 
                AND DATE_FORMAT(op_start, '%v')=DATE_FORMAT(CURRENT_TIMESTAMP, '%v')";
            $param = array($pe_id, PHPAGA_BILLABLETYPE_NO);
            break;

        default:
            return PhPagaError::raiseError('Unsupported database system');
            break;
    }

    $val = $d->queryOneVal($query, $param);
    if (PhPagaError::isError($val))
        return $val;

    $stats['curr_week'] = phpaga_minutes2duration($val);

    return $stats;
}

?>
