<?php
/**
* Copyright OpenCraft | http://www.open-craft.com
* ------------------------------------------------------------------
*
* Contrack Order Management System
*
* Details and latest version at:
* http://contrack.sourceforge.net
*
* $Id: $
*
* This software is distributed under the GNU General Public License.
* Read the entire license text here: http://www.gnu.org/licenses/gpl.html
*/
?>
<?php
/******************************************************************************************
Incomplete:
~~~~~~~~~~~
Printing the invoice
Invoice association logic
Invoice page
This page will handle creating/editing/sending/closing/crediting of invoices.
The *page* has the following states:
a) "NEW" to create a new invoice
b) "EDIT" to edit an existing invoice
c) "VIEW" to view an existing invoice
d) "PROCESS". The form is in this state while it's being processed after any of the
following actions has been performed:
- Edit (Only allowed when invoice is in DRAFT status)
- Save (Only allowed when invoice is in DRAFT status)
- Send (Only allowed when invoice is in DRAFT status)
- Close (Only allowed when invoice is in PENDING status)
- Credit (Only allowed when invoice is in PENDING status)
- Delete (Only allowed when invoice is in DRAFT status)
- Print
The page will never be viewable in the "PROCESS" state. (Transient state)
Page states are different yet linked to Invoice statuses.
An Invoice has one of the following states:
- DRAFT
- PENDING
- CLOSED
- CREDITED
The following table illustrates the different actions that are allowed on the invoice
State transitions are as follows:
Page State Invoice Status Allowed Actions ==> Resulting Invoice Status, Page State
---------- ------------- -------------------------------------------------------
NEW NONE SAVE ==> DRAFT, VIEW
NEW NONE CANCEL ==> NONE, history.back()
VIEW DRAFT EDIT ==> DRAFT, EDIT
VIEW DRAFT SEND ==> PENDING, VIEW
VIEW DRAFT DELETE ==> Invoice is deleted from the system.
User is redirected to View All Invoices page
EDIT DRAFT SAVE ==> DRAFT, VIEW
EDIT DRAFT CANCEL ==> DRAFT, VIEW
VIEW PENDING CREDIT ==> CREDITED, VIEW
VIEW PENDING CLOSE ==> CLOSED, VIEW
VIEW ANY STATE PRINT ==> ANY STATE, VIEW. Open new browser window with print format
Notes: - EDIT and NEW are similar states except that in EDIT the controls
are pre-populated with the attributes of the entity being edited
whereas in NEW the controls are blank.
- In EDIT and VIEW state, the primary key exists (PARTY_ID), in NEW state
it has the value -1.
- Deleting sub-entities (line items) works by resubmitting the form
with the relevant opcode and sub-entity name
- "Print" and "Credit" Buttons are currently not shown until associated
business rules are defined.
******************************************************************************************/
require_once("db_utils.php");
require_once("general_utils.php");
require_once("invoice_utils.php");
require_once("patUser-2.2.3/prepend.php" );
require_once("permissions.php");
init_page();
process_state();
/****************************************************
Page Initialisation
- Authenticate
- Get all parameters passed to the page
- Validate permissions for current user
- Check for any errors passed to the page
- Check page state and branch accordingly
****************************************************/
function init_page()
{
global $user, $line_items_exist, $line_items_have_milestone;
// Authenticate
if (! ($user->isAuthenticated())){
deny_access();
}
get_page_params ();
validate_permissions();
check_error();
$line_items_exist = do_line_items_exist();
$line_items_have_milestone = is_any_line_item_milestone();
}
/*
Get key parameters passed to this page and format display string
*/
function get_page_params()
{
global $state, $invoice_status, $invoice_id, $invoice_action, $sub_entity_op,
$sub_entity_name, $sub_entity_id, $page_name;
// Get key parameters passed to the page
$state = isset($_GET['state']) ? $_GET['state'] : (isset($_POST['state']) ? $_POST['state'] : state_VIEW);
$invoice_id = isset($_GET['invoice_id']) ? $_GET['invoice_id'] : (isset($_POST['INVOICE_ID']) ? $_POST['INVOICE_ID'] : -1);
$invoice_action = isset($_POST['invoice_action']) ? $_POST['invoice_action'] : -1;
if (($invoice_id == -1) && ($state != state_PROCESS)) {
$state = state_NEW;
}
// Current sub entity is line items
$sub_entity_op = isset($_GET['sub_entity_op']) ? $_GET['sub_entity_op'] : (isset($_POST['sub_entity_op']) ? $_POST['sub_entity_op'] : -1);
$sub_entity_name = isset($_GET['sub_entity_name']) ? $_GET['sub_entity_name'] : (isset($_POST['sub_entity_name']) ? $_POST['sub_entity_name'] : -1);
$sub_entity_id = isset($_GET['sub_entity_id']) ? $_GET['sub_entity_id'] : (isset($_POST['sub_entity_id']) ? $_POST['sub_entity_id'] : -1);
// This could be the general way to redirect all pages that incorrect or missing params
if ( ($invoice_id == -1) && ( ($state != state_NEW) && ($state != state_PROCESS) ) ){
$location_url = "Location: search_invoice.php"; //redirect to view all invoices
header($location_url);
exit;
}
// Set page name for use in fetching permissions from DB
$page_name = "invoice";
}
/****************************************************
Permissions and security functions
****************************************************/
/*
~~
~~ Move to permissions.php?
~~ pass $page name and $user paramaters
Validates that the user has the required permissions to view the page in this state
- Get User permissions
- Set global flags
- Get minimum permissions required
- Evaluate the user permissions vis-a-vis the required permissions
*/
function validate_permissions()
{
global $user,$page_name;
$user_perms = get_user_permissions ($user, $page_name);
//var_dump ($user_perms);
//echo "<br>";
set_user_perms($user_perms);
set_required_perms(is_associated());
if (! is_authorized()) {
do_unauthorized();
}
}
/*
~~~ INCOMPLETE ~~~
Determine whether the currently logged in user is associated with the invoice
being viewed.
Association logic:
Agent: Invoice belongs to a SO belonging to a GO featuring Agent |
Buyer: Buyer is payor, or payor contact (or invoice belongs to a GO featuring this buyer?) |
Supplier: Supplier is payor, or payor contact (or invoice belongs to a SO featuring this supplier?)
*/
function is_associated()
{
global $user, $invoice_id;
// Get associated party_id
$logged_in_party_id = get_party_id_for_user($user->getSessionValue('userid'));
/* Get group id and based on it determine which function to call to get the
needed party id value to determine association.
*/
//$buyer_id = get_buyer_id_for_single_order($single_order_id);
return true;
}
/*
Based on state of page, go to appropriate function
*/
function process_state()
{
global $state;
// Check state
if (empty($state)) { /* if $state is empty go to VIEW state*/
view_form();
} elseif ( $state == state_VIEW ) {
view_form();
} elseif ( $state == state_EDIT ) {
edit_form();
} elseif ( $state == state_PROCESS ) {
process_form();
} elseif ( $state == state_NEW ) {
new_form();
} else { // might be redundant but just in case...
view_form();
}
}
/****************************************
Root functions
*****************************************/
function new_form()
{
init_form();
}
function edit_form()
{
init_form();
get_record();
}
function view_form()
{
init_form();
get_record();
}
/*
Intitializes form
*/
function init_form()
{
global $glb_arr_values, $state, $invoice_id;
if ( $state != state_NEW ) {
$glb_arr_values['INVOICE_ID'] = $invoice_id;
}
else { //NEW
new_values();
}
}
/*
Function: process_form()
Desc:
This function is called upon submission of the form.
If this is a sub entity operation (contact or attachment delete) perform it otherwise:
Validates values posted.
Checks whether this is a NEW state or an EDIT state and acts accordingly.
Params:
None
Returns:
Nothing
*/
function process_form()
{
global $glb_arr_values, $state, $invoice_id, $invoice_action,
$sub_entity_op, $sub_entity_name, $sub_entity_id;
// Are we deleting the invoice
if ($invoice_action == opcode_DELETE) {
delete_invoice();
$location_url = "Location: search_invoice.php"; //redirect to view all invoices
header($location_url);
exit;
}
// Get values from submitted form
if ($invoice_action == opcode_SAVE) post_values();
// If we're deleting a sub-entity
if ( $sub_entity_op == opcode_DELETE ) {
if ( strcasecmp ('LINE_ITEM', $sub_entity_name ) == 0) {
delete_invoice_line_item($invoice_id, $sub_entity_id);
// Update Billed amount in invoice table
$sql = generate_update_invoice_billed_amount($invoice_id);
$result = mysql_query($sql) or die(mysql_error());
if (!$result) return mysql_error();
}
// Redirect while setting state back to "EDIT" after having deleted successfully
$location_url = "Location: invoice.php?invoice_id=".$invoice_id."&state=".state_EDIT;
header($location_url);
exit;
}
else {
if ($invoice_action == opcode_SAVE) { // Are we saving
if ( validate_form() ) { // and validated
if ( -1 == $glb_arr_values['INVOICE_ID'] ) { // Creating New
$sql = generate_insert_invoice_sql() ;
//echo "SQL Generated: " . $sql . "<br>";
$result = mysql_query($sql) or die(mysql_error());
if (!$result) return mysql_error();
$glb_arr_values['INVOICE_ID'] = mysql_insert_id();
$invoice_id = $glb_arr_values['INVOICE_ID'];
} else { // Or just Editing
$sql = generate_update_invoice_sql();
//echo "SQL Generated: " . $sql . "<br>";
$result = mysql_query($sql) or die(mysql_error());
if (!$result) return mysql_error();
}
// Redirect while setting state back to "VIEW" after having saved successfully
$location_url = "Location: invoice.php?invoice_id=".$glb_arr_values['INVOICE_ID']."&state=".state_VIEW;
header($location_url);
exit;
}
else { // validation unsuccessful
if ( -1 != $glb_arr_values['INVOICE_ID'] ) { // Go back to EDIT state
$state = state_EDIT;
$glb_arr_values['DATE_CREATED'] = get_date_created_for_entity(entity_INVOICE, $glb_arr_values['INVOICE_ID']);
} else { // Go back to NEW state
$state = state_NEW;
}
}
}
else { // opcode != Save
$sql = generate_update_invoice_status_sql($invoice_id);
$result = mysql_query($sql) or die(mysql_error());
if (!$result) return mysql_error();
// Redirect while setting state back to "VIEW" after having updated the status successfully
$location_url = "Location: invoice.php?invoice_id=".$invoice_id."&state=".state_VIEW;
header($location_url);
exit;
}
} // end big else
}
/*
Validate form values
*/
function validate_form()
{
global $glb_arr_values, $errors;
// Verify that currency had been selected
//if ($glb_arr_values['CURRENCY_ID'] == 0) $errors['CURRENCY_ID'] = 'Please select a currency for this invoice';
//if ($glb_arr_values['PAYOR_PARTY_ID'] == 0) $errors['PAYOR_PARTY_ID'] = 'Please select a payor for this invoice';
if (count($errors) > 0) {
$errors['error'] = "There was a problem with the information entered. Please review the form and make necessary corrections.";
}
return count($errors) == 0;
}
/*
Populate global values array with defaults when the page is in 'NEW' state
*/
function new_values()
{
global $glb_arr_values;
$glb_arr_values['INVOICE_ID'] = -1;
$glb_arr_values['NUMBER'] = 'New';
$glb_arr_values['STATUS_ID'] = 3;
$glb_arr_values['STATUS'] = 'Draft';
$glb_arr_values['CURRENCY_ID'] = -1;
$glb_arr_values['DATE'] ='';
$glb_arr_values['MESSAGE'] = '';
$glb_arr_values['DESCRIPTION'] = '';
$glb_arr_values['PAYOR_PARTY_ID'] = -1;
$glb_arr_values['PAYOR_CONTACT_ID'] = -1;
$glb_arr_values['PAYEE_CONTACT_ID'] = -1;
$glb_arr_values['PAYMENT_TERMS'] = '';
}
/*
Get values from submitted form and populate global values array
*/
function post_values()
{
global $glb_arr_values, $invoice_id;
$glb_arr_values['INVOICE_ID'] = $invoice_id ;
$glb_arr_values['NUMBER'] = isset($_POST['NUMBER']) ? $_POST['NUMBER'] : "";
$glb_arr_values['STATUS_ID'] = isset($_POST['STATUS_ID']) ? $_POST['STATUS_ID'] : 3;
$glb_arr_values['STATUS'] = isset($_POST['STATUS']) ? $_POST['STATUS'] : 'Draft';
$glb_arr_values['CURRENCY_ID'] = isset($_POST['CURRENCY_ID']) ? $_POST['CURRENCY_ID'] : '';
$glb_arr_values['DATE'] = isset($_POST['DATE']) ? $_POST['DATE'] : '';
$glb_arr_values['MESSAGE'] = isset($_POST['MESSAGE']) ? $_POST['MESSAGE'] : '';
$glb_arr_values['DESCRIPTION'] = isset($_POST['DESCRIPTION']) ? $_POST['DESCRIPTION'] : '';
$glb_arr_values['PAYOR_PARTY_ID'] = isset($_POST['PAYOR_PARTY_ID']) ? $_POST['PAYOR_PARTY_ID'] : '';
$glb_arr_values['PAYOR_CONTACT_ID'] = isset($_POST['PAYOR_CONTACT_ID']) ? $_POST['PAYOR_CONTACT_ID'] : '';
$glb_arr_values['PAYEE_CONTACT_ID'] = isset($_POST['PAYEE_CONTACT_ID']) ? $_POST['PAYEE_CONTACT_ID'] : '';
$glb_arr_values['PAYMENT_TERMS'] = isset($_POST['PAYMENT_TERMS']) ? $_POST['PAYMENT_TERMS'] : '';
}
/*
Function: get_record()
Desc:
If invoice_id < 1 then this is a new record. Populate with defaults.
If invoice_id > 1 get record from database and populate values global array
Params:
None
Returns:
Nothing
*/
function get_record()
{
global $glb_arr_values, $invoice_id;
if (1 > $glb_arr_values['INVOICE_ID']) {
new_values();
return;
}
$sql = generate_select_invoice_sql($invoice_id);
$result = mysql_query($sql) or die(mysql_error());
if (!$result) return mysql_error();
$row = mysql_fetch_array($result);
// Populate values array
$glb_arr_values['NUMBER'] = mysql_decode($row['NUMBER']);
$glb_arr_values['STATUS_ID'] = mysql_decode($row['STATUS_ID']);
$glb_arr_values['STATUS'] = mysql_decode($row['STATUS']);
$glb_arr_values['CURRENCY_ID'] = mysql_decode($row['CURRENCY_ID']);
$glb_arr_values['CURRENCY_NAME'] = mysql_decode($row['CURRENCY_NAME']);
$glb_arr_values['CURRENCY_DESC'] = mysql_decode($row['CURRENCY_DESC']);
$glb_arr_values['DATE'] = mysql_decode($row['DATE'] );
$glb_arr_values['MESSAGE'] = mysql_decode($row['MESSAGE']);
$glb_arr_values['DESCRIPTION'] = mysql_decode($row['DESCRIPTION'] );
$glb_arr_values['PAYOR_PARTY_ID'] = mysql_decode($row['PAYOR_PARTY_ID']);
$glb_arr_values['PAYOR_NAME'] = mysql_decode($row['PAYOR_NAME']);
$glb_arr_values['PAYOR_TYPE'] = mysql_decode($row['PAYOR_TYPE']);
$glb_arr_values['PAYOR_CONTACT_ID'] = mysql_decode($row['PAYOR_CONTACT_ID']);
$glb_arr_values['PAYOR_CONTACT_NAME'] = mysql_decode($row['PAYOR_CONTACT_NAME']);
$glb_arr_values['PAYEE_CONTACT_ID'] = mysql_decode($row['PAYEE_CONTACT_ID']);
$glb_arr_values['PAYEE_CONTACT_NAME'] = mysql_decode($row['PAYEE_CONTACT_NAME']);
$glb_arr_values['PAYMENT_TERMS'] = mysql_decode($row['PAYMENT_TERMS']);
mysql_free_result($result);
}
/****************************************
SQL Generation functions
*****************************************/
/*
Generates insert SQL for Invoice
*/
function generate_insert_invoice_sql()
{
global $glb_arr_values;
$insert_sql = "INSERT INTO invoice
( STATE , NUMBER , STATUS , DATE , MESSAGE , DESCRIPTION , PAYOR_PARTY_ID ,
PAYOR_CONTACT_ID , PAYEE_CONTACT_ID , PAYMENT_TERMS , CURRENCY_ID , DATE_CREATED )
VALUES
('ACTIVE',
'".mysql_encode(get_new_code('INVOICE'))."',
'Draft',
'".format_date($glb_arr_values['DATE'])."',
'".mysql_encode($glb_arr_values['MESSAGE'])."',
'".mysql_encode($glb_arr_values['DESCRIPTION'])."',
".$glb_arr_values['PAYOR_PARTY_ID'].",
".$glb_arr_values['PAYOR_CONTACT_ID'].",
".$glb_arr_values['PAYEE_CONTACT_ID'].",
'".mysql_encode($glb_arr_values['PAYMENT_TERMS'])."',
".$glb_arr_values['CURRENCY_ID'].",
NOW())";
return $insert_sql;
}
/*
Generates update SQL for Invoice
No need to update the STATUS column since it's updated based on the actions only
*/
function generate_update_invoice_sql()
{
global $glb_arr_values;
//STATUS = " .mysql_encode( $glb_arr_values['STATUS']).",
$update_sql = "UPDATE invoice SET
DATE = '". format_date($glb_arr_values['DATE'])."',
MESSAGE = '" .mysql_encode( $glb_arr_values['MESSAGE'])."',
DESCRIPTION = '" .mysql_encode( $glb_arr_values['DESCRIPTION'])."',
PAYOR_PARTY_ID = " .$glb_arr_values['PAYOR_PARTY_ID'].",
PAYOR_CONTACT_ID = ".$glb_arr_values['PAYOR_CONTACT_ID'].",
PAYEE_CONTACT_ID = " .$glb_arr_values['PAYEE_CONTACT_ID'].",
PAYMENT_TERMS = '" .mysql_encode( $glb_arr_values['PAYMENT_TERMS'])."',
CURRENCY_ID = " .$glb_arr_values['CURRENCY_ID']."
WHERE INVOICE_ID=".$glb_arr_values['INVOICE_ID'];
return $update_sql;
}
/*
Generates update status SQL for the Invoice based on action performed
*/
function generate_update_invoice_status_sql ($invoice_id)
{
global $invoice_action, $glb_arr_values;
switch ($invoice_action){
case opcode_SEND:
$new_status = "Pending";
break;
case opcode_CREDIT:
$new_status = "Credited";
break;
case opcode_CLOSE:
$new_status = "Closed";
break;
default:
$new_status = $glb_arr_values['STATUS']; // New Status = Old status
}
$update_sql = "UPDATE invoice SET
STATUS = '".$new_status."'
WHERE INVOICE_ID=".$invoice_id;
return $update_sql;
}
/*
Cannot send multiple SQL statements in one batch to MySQL.
This is a documented PHP/MySQL bug
*/
function delete_invoice()
{
global $invoice_id;
$sql = "DELETE FROM invoice WHERE INVOICE_ID =". $invoice_id . "; ";
$result = mysql_query($sql) or die(mysql_error());
$sql = "DELETE FROM invoice_line_item WHERE INVOICE_ID =". $invoice_id . "; ";
$result = mysql_query($sql) or die(mysql_error());
$sql = "DELETE FROM invoice_rel_order WHERE INVOICE_ID =". $invoice_id . "; ";
$result = mysql_query($sql) or die(mysql_error());
$sql = "DELETE FROM invoice_term WHERE INVOICE_ID =". $invoice_id . "; ";
$result = mysql_query($sql) or die(mysql_error());
$sql = "DELETE FROM invoice_history WHERE INVOICE_ID =". $invoice_id . "; ";
$result = mysql_query($sql) or die(mysql_error());
$sql = "DELETE FROM payment WHERE INVOICE_ID =". $invoice_id . "; ";
$result = mysql_query($sql) or die(mysql_error());
}
/*
Generates select invoice line items
*/
function generate_select_invoice_line_items_sql ()
{
global $invoice_id;
$select_sql = "SELECT lineitem.INVOICE_LINE_ITEM_ID, lineitem.INVOICE_ID, lineitem.TYPE,
lineitem.TYPE+0 AS TYPE_ID,
single_order.SINGLE_ORDER_ID, single_order.FRIENDLY_NAME, single_order.CODE,
single_order.CLIENT_ORDER_ID,
IF( lineitem.TYPE='Milestone', milestone.DESCRIPTION, lineitem.DESCRIPTION) AS DESCRIPTION,
IF( lineitem.TYPE='Milestone', milestone.AMOUNT, lineitem.AMOUNT) AS AMOUNT,
IF( lineitem.TYPE='Milestone', milestone.DATE, lineitem.DATE) AS DATE
FROM invoice_line_item AS lineitem
LEFT JOIN milestone ON lineitem.MILESTONE_ID = milestone.MILESTONE_ID
LEFT JOIN single_order ON milestone.SINGLE_ORDER_ID = single_order.SINGLE_ORDER_ID
WHERE INVOICE_ID = ".$invoice_id." AND lineitem.STATE='ACTIVE'
ORDER BY DATE";
return $select_sql;
}
/*
Delete invoice line item from inoice_line_item table and from invoice_rel_order table
*/
function delete_invoice_line_item($invoice_id, $line_item_id)
{
if ($line_item_id > 0 ) {
$sql = "DELETE FROM invoice_line_item
WHERE INVOICE_LINE_ITEM_ID =". $line_item_id." AND INVOICE_ID = ".$invoice_id;
$result = mysql_query($sql) or die(mysql_error());
$sql = "DELETE FROM invoice_rel_order
WHERE INVOICE_LINE_ITEM_ID =". $line_item_id." AND INVOICE_ID = ".$invoice_id;
$result = mysql_query($sql) or die(mysql_error());
}
}
/*
Generates select milestones line items SQL
*/
function generate_select_milestones_line_items_sql ()
{
global $invoice_id;
$select_sql = "SELECT lineitem.INVOICE_LINE_ITEM_ID
FROM invoice_line_item AS lineitem
WHERE lineitem.INVOICE_ID = ".$invoice_id." AND lineitem.STATE='ACTIVE' AND
lineitem.TYPE = 'Milestone' ";
return $select_sql;
}
/*
Get all contacts for all buyers and suppliers from database and populate array
to be used to populate dropdown values
*/
function populate_payor_contacts_array()
{
global $arr_payors_contacts;
$sql = "SELECT contact.PARTY_ID, contact.NAME, contact.EMPLOYER_ID
FROM party as contact INNER JOIN party AS employer ON contact.EMPLOYER_ID = employer.PARTY_ID
WHERE (contact.EMPLOYER_ID <> 0) AND (employer.TYPE = 'BUYER' OR employer.TYPE='SUPPLIER')
ORDER BY contact.EMPLOYER_ID, contact.NAME";
$result = mysql_query($sql) or die(mysql_error());
if (mysql_num_rows($result)) {
$i = 0;
while ($row = mysql_fetch_array($result)) {
$arr_payors_contacts[$i]['PARTY_ID'] = $row['PARTY_ID'];
$arr_payors_contacts[$i]['NAME'] = $row['NAME'];
$arr_payors_contacts[$i]['EMPLOYER_ID'] = $row['EMPLOYER_ID'];
$i++;
}
}
mysql_free_result($result);
}
/****************************************
XHTML Generation functions
*****************************************/
/*
Based on page state and invoice status, show appropriate buttons
Notes: "Print" and "Credit" Buttons are currently not shown until associated
business rules are defined.
*/
function show_invoice_buttons()
{
global $glb_arr_values, $state, $allow_modify_all, $allow_modify_own;
$int_invoice_status = $glb_arr_values['STATUS_ID'];
if ( ($state == state_VIEW) && ($int_invoice_status == invoice_DRAFT) ) {
if ($allow_modify_all || $allow_modify_own) {
echo generate_button('button','Edit','entityEdit()');
echo ' ';
echo generate_button('button','Send','entityDoAction('.opcode_SEND.')');
echo ' ';
echo generate_button('button','Delete','entityDoAction('.opcode_DELETE.')');
//echo ' ';
//echo generate_button('button','Print','entityDoAction('.opcode_PRINT.')');
}
}
elseif ((($state == state_EDIT) && ($int_invoice_status == invoice_DRAFT)) || ($state == state_NEW)) {
if ($allow_modify_all ) {
echo generate_button('button','Save','entityDoAction('.opcode_SAVE.')');
echo ' ';
echo generate_button('button','Cancel','entityCancel()');
}
}
elseif ( ($state == state_VIEW) && ($int_invoice_status == invoice_PENDING) ) {
if ($allow_modify_all || $allow_modify_own) {
//echo generate_button('button','Credit','entityDoAction('.opcode_CREDIT.')');
//echo ' ';
echo generate_button('button','Close','entityDoAction('.opcode_CLOSE.')');
//echo ' ';
//echo generate_button('button','Print','entityDoAction('.opcode_PRINT.')');
}
}
else {
//echo generate_button('button','Print','entityDoAction('.opcode_PRINT.')');
}
}
/*
Generate line items block
*/
function show_line_items()
{
global $glb_arr_values, $state;
// Get line items from DB
$sql = generate_select_invoice_line_items_sql();
$result = mysql_query($sql) or die(mysql_error());
// Contacts Header
$markup = "<tr><td width=\"15%\" valign=\"top\"><b>Invoice Details:</b></td>";
// If there are no line items AND we're not in EDIT state
if ( (! mysql_num_rows($result)) && ($state != state_EDIT) ) {
$markup .= "<td colspan =\"3\"><i>No Line Items</td></i></tr>\r\n";
} else {
$markup .= "<td colspan =\"3\">
<table>
<tr class=\"lineitems_header\"> ";
// Are we in EDIT ?
if ($state == state_EDIT) {
$markup .= "<td class=\"small_text\" width=\"5%\" align=\"center\"><a href=\"javascript:
void open_popup('invoice_line_item_popup.php?invoice_id=".$glb_arr_values['INVOICE_ID'].
"&state=".state_NEW."&line_item_type=".line_item_MILESTONE.
"&payor_id=".$glb_arr_values['PAYOR_PARTY_ID'].
"¤cy_id=".$glb_arr_values['CURRENCY_ID']."',
'invoiceLineItem','menubar=no,toolbar=no,scrollbars=no',360,640);\">
<img src=\"images/b.add.png\" alt=\"Add\" title=\"Add\"/></a></td>\r\n" ;
}
$markup .= "<td>Type</td>
<td>Single Order</td>
<td>Description</td>
<td>Amount</td>
<td>Date</td>" ;
$total_amount = 0.00;
$i=0;
while ($row = mysql_fetch_array($result)) {
$pk = $row['INVOICE_LINE_ITEM_ID'];
$line_item_type_id = $row['TYPE_ID'];
$order_id = $row['SINGLE_ORDER_ID'];
$order_name = ($order_id == NULL) ? '' : $row['FRIENDLY_NAME']."(".$row['CODE']."/".$row['CLIENT_ORDER_ID'].")";
$markup .= "<tr class=\"lineitems_line" .($i%2). "\">";
if ($state == state_EDIT ) {
$markup .= "<td class=\"small_text\" align=\"center\">";
if ($line_item_type_id != line_item_MILESTONE) {
$markup .= "<a href=\"javascript:
void open_popup('invoice_line_item_popup.php?invoice_id=".
$glb_arr_values['INVOICE_ID']."&state=".state_EDIT."&line_item_id=".$pk.
"&line_item_type=".$line_item_type_id."&payor_id=".$glb_arr_values['PAYOR_PARTY_ID'].
"¤cy_id=".$glb_arr_values['CURRENCY_ID'] ."',
'invoiceLineItem','menubar=no,toolbar=no,scrollbars=no',360,640);\">
<img src=\"images/b.edit.png\" alt=\"Edit\" title=\"Edit\"/></a>";
}
$markup .= "<a href=\"javascript:void subEntityDelete('LINE_ITEM',". $pk .");\">
<img src=\"images/b.delete.png\" alt=\"Delete\" title=\"Delete\"/></a></td>\r\n";
}
$markup .= "<td>". $row['TYPE'] . "</td>";
$markup .= "<td>";
$markup .= ($order_id == NULL) ? "N/A" : "<a href=\"single_order_milestones.php?single_order_id=".$order_id."\">".$order_name."</a>";
$markup .= "</td>";
$markup .= "<td>". $row['DESCRIPTION'] . "</td>";
$markup .= "<td>". $glb_arr_values['CURRENCY_NAME']. ' '. number_format($row['AMOUNT'], NUM_DEC_PLACES, '.',',') . "</td>";
$markup .= "<td>". format_date_reverse($row['DATE']) . "</td></tr>";
$total_amount += $row['AMOUNT'];
++$i;
} // end while
if (mysql_num_rows($result)) {
$colspan = ($state == state_VIEW ? 3 : 4 );
$markup .= '<tr><td colspan="'.$colspan.'"></td><td class="total_amount">'.$glb_arr_values['CURRENCY_NAME'].' '.
number_format($total_amount, NUM_DEC_PLACES, '.', ',').'</td></tr>';
}
// Lineitems table footer
$markup .= "</table></td></tr>\r\n" ;
mysql_free_result($result);
} // end else
return $markup;
}
/*
Check if any line items exist for this invoice
*/
function do_line_items_exist()
{
$sql = generate_select_invoice_line_items_sql();
$result = mysql_query($sql) or die(mysql_error());
$existence = mysql_num_rows($result) > 0 ;
mysql_free_result($result);
return $existence;
}
/*
Check if any line items of this invoice are of type milestones
*/
function is_any_line_item_milestone()
{
$existence = false;
if (do_line_items_exist()) {
$sql = generate_select_milestones_line_items_sql();
$result = mysql_query($sql) or die(mysql_error());
$existence = mysql_num_rows($result) > 0 ;
mysql_free_result($result);
}
return $existence;
}
/*
Generates standard form header for invoice
*/
function generate_form_header()
{
global $glb_arr_values, $state, $invoice_action, $sub_entity_op, $sub_entity_name, $sub_entity_id;
$str_frm_header = "<form action=\"". $_SERVER['PHP_SELF'] . "\" method=\"post\" name=\"frm_invoice\">\r\n" ;
$str_frm_header .= "<input type=\"hidden\" id=\"state\" name=\"state\" value=\"" . $state . "\"/>\r\n";
$str_frm_header .= "<input type=\"hidden\" id=\"INVOICE_ID\" name=\"INVOICE_ID\" value=\"" . $glb_arr_values["INVOICE_ID"] . "\"/>\r\n";
$str_frm_header .= "<input type=\"hidden\" id=\"invoice_action\" name=\"invoice_action\" value=\"".$invoice_action."\"/>\r\n";
$str_frm_header .= "<input type=\"hidden\" id=\"NUMBER\" name=\"NUMBER\" value=\"" . $glb_arr_values["NUMBER"] . "\"/>\r\n";
$str_frm_header .= "<input type=\"hidden\" id=\"sub_entity_op\" name=\"sub_entity_op\" value=\"" . $sub_entity_op . "\"/>\r\n";
$str_frm_header .= "<input type=\"hidden\" id=\"sub_entity_name\" name=\"sub_entity_name\" value=\"" . $sub_entity_name . "\"/>\r\n";
$str_frm_header .= "<input type=\"hidden\" id=\"sub_entity_id\" name=\"sub_entity_id\" value=\"" . $sub_entity_id . "\"/>\r\n";
$str_frm_header .= "<input type=\"hidden\" id=\"CURRENCY_ID_HIDDEN\" name=\"CURRENCY_ID_HIDDEN\" value=\"" . $glb_arr_values["CURRENCY_ID"] . "\"/>\r\n";
$str_frm_header .= "<input type=\"hidden\" id=\"PAYOR_PARTY_ID_HIDDEN\" name=\"PAYOR_PARTY_ID_HIDDEN\" value=\"" . $glb_arr_values["PAYOR_PARTY_ID"] . "\"/>\r\n";
return $str_frm_header;
}
/*
Generate the javascript code fragment which declares and populates the payors' contacts array
*/
function generate_js_payors_contacts_arr()
{
global $arr_payors_contacts;
populate_payor_contacts_array();
$js_arr = '';
if (!empty($arr_payors_contacts)) {
foreach ($arr_payors_contacts as $row_id => $party_values) {
$js_arr .= 'arr_payors_contacts['.$row_id.'] = new Array();'."\r\n" ;
foreach ($party_values as $column_id => $value) {
$js_arr .= 'arr_payors_contacts['.$row_id.']["'.$column_id.'"] =';
if ($column_id == 'NAME'){
$js_arr .= '"'.$value.'";'."\r\n" ;
}
else {
$js_arr .= $value.';'."\r\n" ;
}
}
}
}
return $js_arr;
}
// Set page title and include body header
if ($state != state_NEW ) {
$page_title = $glb_arr_values['NUMBER'] . " :: Profile";
}
else {
$page_title = "Invoice :: New";
}
require_once("body_header.php");
?>
<!-- JavaScript Section -->
<script type="text/javascript"><!--
var lineItemsExist = <?= $line_items_exist ? 1 : 0 ?>;
var lineItemsHaveMilestone = <?= $line_items_have_milestone ? 1 : 0 ?>;
var invoiceCurrencyID = <?= $glb_arr_values['CURRENCY_ID'] ?>;
var payorPartyID = <?= $glb_arr_values['PAYOR_PARTY_ID'] ?>;
var arr_payors_contacts = new Array();
// Initialize and populate array
<?= generate_js_payors_contacts_arr(); ?>
function currencyChange()
{
if (lineItemsExist) {
alert ("This invoice contains line items using this currency.\nPlease remove the line items before changing the currency.");
document.frm_invoice.CURRENCY_ID.value = invoiceCurrencyID;
return;
}
}
function hasMilestones()
{
if (lineItemsHaveMilestone) {
alert ("This invoice contains line items as milestones associated with this payor.\nPlease remove those line items before changing the payor.");
document.frm_invoice.PAYOR_PARTY_ID.value = payorPartyID;
return true;
}
else {
return false;
}
}
/*
Note: There is hack in this function to add options to the contacts dropdowns.
The method, HTMLSelectElement.add(), is a Document Object Model (DOM)
method. It expects two arguments. The first is a reference to the new
HTMLOptionElement to insert, while the second is where to insert it.
According to the DOM 1 HTML Specification, the second argument should
either be a reference to the element that the new OPTION is inserted
before, or null for the end of the list. According to Microsoft, it is the
*index* of the element that the new OPTION will be inserted before. The two
are completely incompatible (one is an object, the other is a number).
The only way around it -I think- is to catch the exception that will be thrown
when the method is called with the wrong type of argument and act accordingly.
*/
function populateDropDown(rootDropdown, dropdownToPopulate, selectedPayorContactID)
{
var payorContactOption;
if (hasMilestones()) return;
// Empty the dropdown to be populated of any choices
for (var i = dropdownToPopulate.options.length; i >= 0; i--) {
dropdownToPopulate.options[i] = null;
}
// Add Default Choice in Payor Contacts dropdown
payorContactOption = document.createElement("option") ;
if ( payorContactOption ) {
// Once the option has been created, set the display text for it
payorContactOption.text = 'Select...';
payorContactOption.value = 0 ;
// Attempt to use Microsoft's incorrect method to add the
// element to the end of the list
try {
dropdownToPopulate.add( payorContactOption, 1 );
}
catch( e ) { // If it fails, use the correct DOM approach
dropdownToPopulate.add( payorContactOption, null );
}
}
// Now loop through the array of contacts
// Any contact containing the same employer ID as the party ID of the root DD is added to the second dropdown box
for (i = 0 ; i < arr_payors_contacts.length ; i++ ) {
if ( arr_payors_contacts[i]["EMPLOYER_ID"] == rootDropdown.value ) {
payorContactOption = document.createElement("option") ;
payorContactOption.value = arr_payors_contacts[i]["PARTY_ID"] ;
payorContactOption.text = arr_payors_contacts[i]["NAME"] ;
try {
dropdownToPopulate.add( payorContactOption, i );
} catch( e ) { // If it fails, use the correct DOM approach
dropdownToPopulate.add( payorContactOption, null );
}
}
}
// Set the default value if any
for (i = 0; i < dropdownToPopulate.options.length; i++) {
if (dropdownToPopulate.options[i].value == selectedPayorContactID) {
dropdownToPopulate.options[i].selected = true;
}
}
}
function entityDoAction(intOpcode)
{
var frm = document.frm_invoice;
switch (intOpcode) {
case opcodeDELETE:
strAction = "Delete";
break;
case opcodeSAVE:
strAction = "Save";
if (frm.DATE.value.length < 1) {
alert ("You must enter a date for this Invoice");
return;
}
if (frm.PAYOR_PARTY_ID.value <= 0 ) {
alert ("You must associate a payor with this Invoice");
return;
}
break;
case opcodeSEND:
strAction = "Send";
if (frm.CURRENCY_ID_HIDDEN.value <= 0) {
alert ("You must associate a currency with this Invoice");
return;
}
if (frm.PAYOR_PARTY_ID_HIDDEN.value <= 0 ) {
alert ("You must associate a payor with this Invoice");
return;
}
if (!lineItemsExist) {
alert ("Please associate a value with this invoice by filling out the Invoice Details");
return;
}
break;
case opcodeCREDIT:
strAction = "Credit";
break;
case opcodeCLOSE:
strAction = "Close";
break;
case opcodePRINT:
strAction = "Print";
break;
default: // do nothing
return;
}
if (confirm("Are you sure you want to " + strAction + " this invoice?")) {
frm.state.value = statePROCESS;
frm.invoice_action.value = intOpcode;
frm.submit();
}
}
function entityEdit()
{
frm = document.frm_invoice;
frm.state.value = stateEDIT;//stateEDIT
frm.submit();
}
/*
history.go(-) and history.back() don't work in the following sequence:
1- Save record -> you're back in View mode
2- Click Edit -> you're in Edit mode
3- Click Cancel to go back to View mode. This doesn't work because all the form
information is lost. We need to resubmit.
*/
function entityCancel()
{
frm = document.frm_invoice;
if (frm.INVOICE_ID.value == -1) {
//frm.state.value = stateNEW;
history.back();
}
else {
frm.state.value = stateVIEW;
frm.submit();
}
}
function subEntityDelete(strName, intID)
{
if (confirm ('Are you sure you want to delete this record?') ) {
frm = document.frm_invoice;
frm.state.value = statePROCESS;
frm.sub_entity_op.value = opcodeDELETE;
frm.sub_entity_name.value = strName;
frm.sub_entity_id.value = intID;
frm.submit();
}
}
function resubmitForm()
{
var frm = document.frm_invoice;
frm.state.value = stateEDIT;
setTimeout("submitForm();",1000);
}
function submitForm()
{
frm = document.frm_invoice;
frm.submit();
}
--></script>
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td colspan="3">
<h4>
<?php
if ($state != state_NEW ) {
echo "INVOICE :: ". $glb_arr_values["NUMBER"];
}
else {
echo "INVOICE :: New";
}
?>
</h4>
</td>
</tr>
<!-- code to display informational/error message goes here -->
<tr> <!-- Tabs -->
<td class = "tabs" colspan="3">
<table border="0" cellspacing="0">
<tr>
<?php
/* Tab Generation
Pass the ordinal of the tab to be activated
*/
echo generate_invoice_tabs(1, $invoice_id, $state);
?>
</tr>
</table>
</td>
</tr> <!-- End of Tabs -->
<tr>
<td colspan = "3">
<table border="0" class="tab_content">
<tr>
<td colspan="4">
<table border="0">
<tr><td>
<?php if (isset($errors['error'])) echo "<em class=\"error_block\">" . $errors['error'] . "</em>"; ?>
</td></tr>
<tr class="actions_header" >
<td>Actions</td>
</tr>
<tr>
<td>
<?php
echo generate_form_header();
echo show_invoice_buttons();
?>
</td>
</tr>
<tr></tr>
</table>
</td>
</tr>
<tr><td width="15%"><b>Status:</b></td>
<td >
<?php
echo $glb_arr_values['STATUS'];
?>
</td>
</tr>
<tr>
<td ><b>Date:</b><?= generate_required_superscript($state);?></td>
<td>
<?php
if ($state == state_VIEW ) {
echo format_date_reverse($glb_arr_values['DATE']);
}
else{
echo generate_text_input ("date_box", "DATE", format_date_reverse($glb_arr_values['DATE']));
}
?>
</td>
</tr>
<tr>
<td><b>Currency:</b></td>
<td>
<?php
if ($state == state_VIEW ) {
echo $glb_arr_values['CURRENCY_DESC']. "(". $glb_arr_values['CURRENCY_NAME'].")";
}
else{
echo generate_dd_currency_input($glb_arr_values['CURRENCY_ID'],'currencyChange()');
}
?>
<?php if (isset($errors['CURRENCY_ID'])) echo "<em class=\"error\">" . $errors['CURRENCY_ID'] . "</em>"; ?>
</td>
</tr>
<tr><td valign="top"><b>Description:</b></td>
<td colspan="1">
<?php
if ( $state == state_VIEW ) {
echo '<span class="contrack_notes">';
echo $glb_arr_values['DESCRIPTION'];
echo '</span>';
}
else{
echo generate_textarea_input ("DESCRIPTION", 5, 70, $glb_arr_values['DESCRIPTION']);
}
?>
</td></tr>
<tr><td valign="top"><b>Message:</b></td>
<td colspan="1">
<?php
if ( $state == state_VIEW ) {
echo '<span class="contrack_notes">';
echo $glb_arr_values['MESSAGE'];
echo '</span>';
}
else{
echo generate_textarea_input ("MESSAGE", 5, 70, $glb_arr_values['MESSAGE']);
}
?>
</td>
</tr>
<tr>
<td><b>Payor :</b><?= generate_required_superscript($state);?></td>
<td>
<?php
if ($state == state_VIEW ) {
$entity = ($glb_arr_values['PAYOR_TYPE'] == entity_BUYER ? entity_GEN_ORDER : entity_SINGLE_ORDER);
echo "<a href=\"search_order.php?entity=".$entity."&referrer=".$glb_arr_values['PAYOR_TYPE']."&referrer_id=".$glb_arr_values['PAYOR_PARTY_ID']."\">";
echo $glb_arr_values['PAYOR_NAME'];
echo "</a>";
}
else{
echo '<select id="PAYOR_PARTY_ID" name="PAYOR_PARTY_ID" onChange="populateDropDown(this,document.getElementById(\'PAYOR_CONTACT_ID\'),'.$glb_arr_values['PAYOR_CONTACT_ID'].');">';
echo populate_payor_dropdown($glb_arr_values['PAYOR_PARTY_ID']);
echo '</select>';
}
?>
<?php if (isset($errors['PAYOR_PARTY_ID'])) echo "<em class=\"error\">" . $errors['PAYOR_PARTY_ID'] . "</em>"; ?>
</td>
</tr>
<tr>
<td><b>Payor Contact:</b></td>
<td>
<?php
if ($state == state_VIEW ) {
echo $glb_arr_values['PAYOR_CONTACT_NAME'];
}
else{
echo generate_dd_payor_contacts_input($glb_arr_values['PAYOR_CONTACT_ID'], $glb_arr_values['PAYOR_PARTY_ID']);
}
?>
</td>
</tr>
<tr>
<td><b>Internal Contact:</b></td>
<td>
<?php
if ($state == state_VIEW ) {
echo $glb_arr_values['PAYEE_CONTACT_NAME'];
}
else{
echo generate_dd_org_contacts_input($glb_arr_values['PAYEE_CONTACT_ID']);
}
?>
</td>
</tr>
<tr>
<td ><b>Payment Terms:</b></td>
<td>
<?php
if ($state == state_VIEW ) {
echo $glb_arr_values['PAYMENT_TERMS'];
}
else{
echo generate_text_input ("formtext_longwidth", "PAYMENT_TERMS", $glb_arr_values['PAYMENT_TERMS']);
}
?>
</td>
</tr>
</form>
<?php
echo show_line_items();
?>
</table>
</tr>
</table>
<?php
include("body_footer.php");
?>