<?php
/**
 * phpaga
 *
 * quotations functionality.
 *
 * This file contains the necessary routines for quotations management.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @version $Id: quotations.php 878 2007-10-31 22:11:00Z florian $
 *
 * 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.
 */


/**
 * Checks if the quotation information is valid.
 *
 * @param  array   $quotInfo    Array containing billoing information
 * @return array   $valid       True. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_quotations_checkInfo($quotInfo)
{

    $errors = array();

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

    if (!isset($quotInfo['quot_number']) || !strlen($quotInfo['quot_number']))
        $errors[] = PHPAGA_ERR_QUOTATION_INVALID_NUMBER;

    if (!isset($quotInfo['quot_date'])
        || !phpaga_checkdate ($quotInfo['quot_date'], false))

        $errors[] = PHPAGA_ERR_QUOTATION_INVALID_DATE;

    if (!isset($quotInfo['cpn_id']) || !is_numeric($quotInfo['cpn_id']))
        $errors[] = PHPAGA_ERR_QUOTATION_INVALID_CPNID;

    if (!isset($quotInfo['bmt_id']) || !is_numeric($quotInfo['bmt_id']))
        $errors[] = PHPAGA_ERR_QUOTATION_INVALID_BMTID;

    if (!isset($quotInfo['curr_id']) || !is_numeric($quotInfo['curr_id']))
        $errors[] = PHPAGA_ERR_QUOTATION_INVALID_CURRID;

    if (!isset($quotInfo['quot_startsum'])
        || !is_numeric($quotInfo['quot_startsum']))

        $errors[] = PHPAGA_ERR_QUOTATION_INVALID_STARTSUM;

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

    return true;
}


/**
 * Creates and inserts a new quotation.
 *
 * @param  array  $quotInfo    Array containing billing information
 *
 * @return int    $result      ID of the freshly inserted quotation. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_quotations_add($quotInfo)
{

    $quotID = null;

    $result = phpaga_quotations_checkInfo($quotInfo);

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

    if (!isset($quotInfo['quot_payterm']) || !strlen($quotInfo['quot_payterm']))
        $quotInfo['quot_payterm'] = null;

    if (!isset($quotInfo['pe_id_recipient']) || !is_numeric($quotInfo['pe_id_recipient']))
        $quotInfo['pe_id_recipient'] = null;

    $d = new PhPagaDbData;

    $quotID = $d->nextId('quotations_quot_id');

    $fields = array('quot_id' => $quotID,
                    'quot_number' => $quotInfo['quot_number'],
                    'quot_date' => $quotInfo['quot_date'],
                    'quot_payterm' => $quotInfo['quot_payterm'],
                    'cpn_id' => $quotInfo['cpn_id'],
                    'pe_id_recipient' => $quotInfo['pe_id_recipient'],
                    'quot_sent' => PHPAGA_NO,
                    'quot_startsum' => $quotInfo['quot_startsum'],
                    'quot_endsum' => $quotInfo['quot_endsum'],
                    'bmt_id' => $quotInfo['bmt_id'],
                    'curr_id' => $quotInfo['curr_id']
        );

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

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

    /* insert line items */
    $fields = array('quot_id',
                    'lit_id',
                    'lit_prodcode',
                    'lit_desc',
                    'lit_qty',
                    'lit_netprice');

    $val = array();

    foreach ($quotInfo['lineitems'] as $lit) {

        $val[] = array($quotID,
                       $d->nextId('lineitems_lit_id'),
                       $lit['lit_prodcode'],
                       $lit['lit_desc'],
                       $lit['lit_qty'],
                       $lit['lit_netprice']);
    }

    $r = $d->insertMultiple('lineitems', $fields, $val);

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

    return $quotID;
}


/**
 * Generates a new quotation number (numeric or alphanumeric) for the
 * current year.
 *
 * @return string $quotationNumber  A quotation number. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.1
 */

function phpaga_quotations_getNewQuotationNumber()
{
    $quotationNumber = null;
    $quotnums = array();

    $d = new PhPagaDbData;
    $r = $d->queryAll('SELECT quot_number FROM quotations WHERE YEAR(quot_date) = YEAR(NOW())');

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

    if (!count($r))
        $quotationNumber = 1;
    else {

        foreach($r as $t)
            $quotnums[] = $t['quot_number'];

        natsort($quotnums);
        $quotationNumber = array_pop($quotnums);

        /* This also works with alphanumeric strings */
        $quotationNumber++;
    }

    return $quotationNumber;
}


/**
 * Search quotations matching search criteria.
 *
 * @param int    $count     Number of records matching the query
 * @param array  $quotInfo  Quotation information
 * @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_quotations_search(&$count, $quotInfo, $offset = null, $limit = null)
{
    $count = 0;
    $rows = array();
    $where = array();
    $where_clause = null;
    $qparam = array();

    $countquery = 'SELECT COUNT(quot_id) AS count_quote FROM quotations ';

    $query = 'SELECT quot_id, quot_number, quot_date,
        quotations.cpn_id, cpn_name, quot_sent,
        quot_startsum, quot_endsum, quot_payterm,
        quotations.bmt_id, quotations.curr_id,
        curr_name, bmt_description, pe_id_recipient, pe_lastname, pe_firstname
        FROM quotations
        LEFT JOIN companies ON companies.cpn_id = quotations.cpn_id
        LEFT JOIN currencies ON currencies.curr_id = quotations.curr_id
        LEFT JOIN billing_methods ON billing_methods.bmt_id = quotations.bmt_id
        LEFT JOIN persons ON persons.pe_id = pe_id_recipient ';

    if (isset($quotInfo['quot_id']) && is_numeric($quotInfo['quot_id'])) {
        $where[] = 'quotations.quot_id = ?';
        $qparam[] = $quotInfo['quot_id'];
    }

    if (isset($quotInfo['curr_id']) && is_numeric($quotInfo['curr_id'])) {
        $where[] = 'quotations.curr_id = ?';
        $qparam[] = $quotInfo['curr_id'];
    }

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

    if (isset($quotInfo['pe_id_recipient']) && is_numeric($quotInfo['pe_id_recipient'])) {
        $where[] = 'quotations.pe_id_recipient = ?';
        $qparam[] = $quotInfo['pe_id_recipient'];
    }

    if (isset($quotInfo['bmt_id']) && is_numeric($quotInfo['bmt_id'])) {
        $where[] = 'quotations.bmt_id = ?';
        $qparam[] = $quotInfo['bmt_id'];
    }

    if (isset($quotInfo['quot_sent']) && is_numeric($quotInfo['quot_sent'])) {

        switch ($quotInfo['quot_sent']) {

            case PHPAGA_QUOTATION_NOTSENT:
                $where[] = 'quotations.quot_sent IS NULL';
                break;

            case PHPAGA_QUOTATION_SENT:
                $where[] = 'quotations.quot_sent IS NOT NULL';
                break;

            default:
                break;
        }
    }

    if (isset($quotInfo['quot_number']) && strlen($quotInfo['quot_number'])) {
        $where[] = 'quotations.quot_number = ?';
        $qparam[] = $quotInfo['quot_number'];
    }

    if (isset($quotInfo['quot_datefrom']) && phpaga_checkdate($quotInfo['quot_datefrom'], false)) {
        $where[] = 'quotations.quot_date >= ?';
        $qparam[] = $quotInfo['quot_datefrom'];
    }

    if (isset($quotInfo['quot_dateto']) && phpaga_checkdate($quotInfo['quot_dateto'], false)) {
        $where[] = 'quotations.quot_date <= ?';
        $qparam[] = $quotInfo['quot_dateto'];
    }

    if (isset($quotInfo['quot_year']) && is_numeric($quotInfo['quot_year'])) {
        $where[] = 'YEAR(quotations.quot_date) = ?';
        $qparam[] = $quotInfo['quot_year'];
    }

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

    $query .= ' ORDER BY quot_date DESC, quot_number DESC ';

    $d = new PhPagaDbData;

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

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


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

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

    foreach ($quotations as $qrow) {
        phpaga_sanitizearray($qrow);
        $qrow['quot_person_recipient'] = phpaga_persons_formatname($qrow['pe_lastname'], $qrow['pe_firstname']);
        $rows[] = $qrow;
    }

    phpaga_sanitizearray($rows);
    return $rows;
}


/**
 * Gets an quotation's information
 *
 * @param int    $quot_id   Invoice ID
 *
 * @return array $quotInfo  Array containing the results. A PhPagaError object on failure.
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_quotations_getinfo($quot_id)
{

    $quotInfo = array();

    if (!isset($quot_id) || !is_numeric($quot_id))
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_INVALID_QUOTATIONID);

    $query = 'SELECT quot_id, quot_number, quot_date,
        quotations.cpn_id, cpn_name, quot_sent, quot_payterm,
        quot_startsum, quot_endsum, quotations.bmt_id,
        quotations.curr_id, curr_name, bmt_description, pe_id_recipient, pe_lastname, pe_firstname
        FROM quotations
        LEFT JOIN companies ON companies.cpn_id = quotations.cpn_id
        LEFT JOIN currencies ON currencies.curr_id = quotations.curr_id
        LEFT JOIN billing_methods ON billing_methods.bmt_id = quotations.bmt_id
        LEFT JOIN persons ON persons.pe_id = quotations.pe_id_recipient
        WHERE quot_id = ?';

    $d = new PhPagaDbData;

    $quotInfo = $d->queryRow($query, $quot_id);

    if (PhPagaError::isError($quotInfo) || !count($quotInfo))
        return $quotInfo;

    $quotInfo['quot_person_recipient'] = phpaga_persons_formatname($quotInfo['pe_lastname'], $quotInfo['pe_firstname']);

    /* line items */

    $query = 'SELECT lit_id, NULL as op_id, lit_prodcode, lit_desc, lit_qty, lit_netprice
            FROM lineitems
            WHERE quot_id = ?
            ORDER BY lit_id';

    $l = $d->queryAll($query, $quot_id);

    if (PhPagaError::isError($l) || !count($l))
        return $l;

    $quotInfo['lineitems'] = array();

    foreach ($l as $qrow) {
        $qrow['lit_netprice_format'] = phpaga_formatamount((float)$qrow['lit_netprice']);
        $quotInfo['lineitems'][] = $qrow;
    }

    /* calculation detail */

    $quotInfo['quot_details'] = array();

    $plres = phpaga_bills_includebmtPlugin($quotInfo['bmt_id']);

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

    $endsum = 0;

    phpaga_bill_calculate($quotInfo['quot_startsum'], $quotInfo['curr_id'], $endsum, $details);

    $quotInfo['quot_details'] = $details->details;

    return $quotInfo;
}


/**
 * Deletes a quotation. This is only possible if no depending records
 * exists.
 *
 * @param  int     $quot_id     ID of the quotation
 *
 * @return bool    $result      True if successfull, PhPagaError otherwise.
 *
 *
 * @author Florian Lanthaler <florian@phpaga.net>
 * @since phpaga 0.3
 */

function phpaga_quotations_delete($quot_id)
{

    if (!isset($quot_id) || !is_numeric($quot_id))
        return PhPagaError::raiseErrorByCode(PHPAGA_ERR_INVALID_QUOTATIONID);

    $d = new PhPagaDbData;

    $n = $d->queryOneVal('SELECT COUNT(prj_id) FROM projects WHERE quot_id = ?', $quot_id);

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

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

    $r = $d->beginTransaction();
    if (PhPagaError::isError($r))
        return $r;

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

    if (PhPagaError::isError($result)) {
        $d->rollback();
        return $r;
    }

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

    if (PhPagaError::isError($result)) {
        $d->rollback();
        return $r;
    }

    $r = $d->commit();

    return $r;
}

?>