This repository has been archived on 2025-06-21. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
suitedesk/modules/storm/storminvoice/storminvoice.module

1170 lines
41 KiB
Text

<?php
/**
* @file
*
* 1: Hooks (help, perm, init, menu, theme, node_info)
* 2: Access functions
* 3: Load organization and project details
* 4: Invoice create / edit form
* 5: Invoice node manipulation functions
* 6: Admin settings
* 7: Auto_add handler
* 8: Views hook
* 9: Storminvoiceitem legacy functions
*/
// HOOKS
function storminvoice_help($path, $arg) {
$o = '';
switch ($path) {
case "admin/help#storminvoice":
$o = '<p>'. t("Provides invoice support for SuiteDesk") .'</p>';
break;
}
return $o;
}
function storminvoice_perm() {
return array(
'Storm invoice: access',
'Storm invoice: add',
'Storm invoice: delete all',
'Storm invoice: delete own',
'Storm invoice: delete of user organization',
'Storm invoice: edit all',
'Storm invoice: edit own',
'Storm invoice: edit of user organization',
'Storm invoice: view all',
'Storm invoice: view own',
'Storm invoice: view of user organization',
);
}
function storminvoice_init() {
$settings = array(
'storm' => array(
'project_invoices_url' => url('storm/project_invoices_js/')
),
);
drupal_add_js($settings, 'setting');
drupal_add_js(drupal_get_path('module', 'storminvoice') .'/storminvoice.js', 'module', 'header', FALSE);
}
function storminvoice_menu() {
$items = array();
$items['storm/project_invoices_js/%/%'] = array(
'title' => 'Tasks',
'page callback' => '_storminvoice_project_invoices_js',
'page arguments' => array(2, 3),
'access callback' => TRUE,
'file' => 'storminvoice.admin.inc',
'type' => MENU_CALLBACK,
'weight' => 8,
);
$items['invoices'] = array(
'title' => 'Invoices',
'description' => 'SuiteDesk invoices',
'page callback' => 'storminvoice_list',
'access arguments' => array('Storm invoice: access'),
'type' => MENU_NORMAL_ITEM,
'file' => 'storminvoice.admin.inc',
);
$items['invoice/auto_add/new/%node'] = array(
'page callback' => 'storminvoice_auto_add',
'page arguments' => array(3),
'access arguments' => array('Storm invoice: add'),
'type' => MENU_CALLBACK,
'file' => 'storminvoice.auto_add.inc',
);
$items['invoice/auto_add/existing/%'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array('storminvoice_auto_add_select'),
'access arguments' => array('Storm invoice: add'),
'type' => MENU_CALLBACK,
'file' => 'storminvoice.auto_add.inc',
);
$items['invoice/report/%node/%/%'] = array(
'title' => 'Invoice',
'page arguments' => array(2, 3, 4),
'description' => 'SuiteDesk Invoice',
'page callback' => 'storminvoice_report',
'access arguments' => array('Storm invoice: access'),
'type' => MENU_CALLBACK,
'file' => 'storminvoice.admin.inc',
);
$items['invoice/report/%node/email/%'] = array(
'title' => 'Send Invoice by Email',
'page arguments' => array(2, 4),
'description' => 'SuiteDesk Invoice',
'page callback' => 'storminvoice_send_page',
'access arguments' => array('Storm invoice: access'),
'type' => MENU_CALLBACK,
'file' => 'storminvoice.admin.inc',
);
$items['admin/settings/suitedesk/invoice'] = array(
'title' => 'Invoices',
'description' => 'SuiteDesk Invoice Administration Page',
'page callback' => 'drupal_get_form',
'page arguments' => array('storminvoice_admin_settings'),
'access arguments' => array('Storm: access administration pages'),
'type' => MENU_LOCAL_TASK,
'weight' => 120,
);
return $items;
}
function storminvoice_theme() {
return array(
'storminvoice_list' => array(
'file' => 'storminvoice.theme.inc',
'arguments' => array('header', 'invoices', 'itemsperpage', 'totals_topay', 'totals_paid', 'totals'),
),
'storminvoice_view' => array(
'file' => 'storminvoice.theme.inc',
'arguments' => array('node', 'teaser', 'page'),
),
'storminvoice_report' => array(
'file' => 'storminvoice.theme.inc',
'arguments' => array('node', 'report', 'language'),
),
'storminvoice_report_html' => array(
'file' => 'storminvoice.theme.inc',
'arguments' => array('node', 'language'),
),
'storminvoice_report_pdf' => array(
'file' => 'storminvoice.theme.inc',
'arguments' => array('node', 'language', array('output' => 'screen')),
),
'storminvoice_autoadd_links' => array(
'file' => 'storminvoice.theme.inc',
'arguments' => array('nid', 'billable', 'billed'),
),
);
}
function storminvoice_node_info() {
return array(
'storminvoice' => array(
'name' => t('Invoice'),
'module' => 'storminvoice',
'description' => t("An invoice for SuiteDesk."),
'title_label' => t("Description"),
'has_body' => FALSE,
),
);
}
function storminvoice_content_extra_fields($type_name) {
if ($type_name == 'storminvoice') {
return array(
'group1' => array('label' => 'Number', 'weight' => -20),
'group2' => array('label' => 'Organization/Project/Reference Group', 'weight' => -19),
'group3' => array('label' => 'Date Group', 'weight' => -18),
// Title (description) - weight -17
'group4' => array('label' => 'Invoice Items', 'weight' => -16),
'group5' => array('label' => 'Price Group', 'weight' => -15),
);
}
}
function storminvoice_access($op, $node, $account = NULL) {
if (empty($account)) {
global $user;
$account = $user;
}
if ($op == 'create') {
return user_access('Storm invoice: add');
}
if (is_numeric($node)) {
$node = node_load($node);
}
if ($op == 'delete') {
if (user_access('Storm invoice: delete all')) {
return TRUE;
}
elseif (user_access('Storm invoice: delete own') && ($account->uid == $node->uid)) {
return TRUE;
}
elseif (user_access('Storm invoice: delete of user organization') && ($account->stormorganization_nid == $node->organization_nid)) {
return TRUE;
}
}
if ($op == 'update') {
if (user_access('Storm invoice: edit all')) {
return TRUE;
}
elseif (user_access('Storm invoice: edit own') && ($account->uid == $node->uid)) {
return TRUE;
}
elseif (user_access('Storm invoice: edit of user organization') && ($account->stormorganization_nid == $node->organization_nid)) {
return TRUE;
}
}
if ($op == 'view') {
if (user_access('Storm invoice: view all')) {
return TRUE;
}
elseif (user_access('Storm invoice: view own') && ($account->uid == $node->uid)) {
return TRUE;
}
elseif (user_access('Storm invoice: view of user organization') && ($account->stormorganization_nid == $node->organization_nid)) {
return TRUE;
}
}
return FALSE;
}
function storminvoice_access_sql($sql, $where = array()) {
if (!user_access('Storm invoice: view all')) {
global $user;
$cond = '';
if (user_access('Storm invoice: view own')) {
$cond .= 'n.uid = ' . $user->uid;
}
if (user_access('Storm invoice: view of user organization')) {
$cond .= !empty($cond) ? ' OR ' : '';
$cond .= 'sin.organization_nid = ' . $user->stormorganization_nid;
}
$where[] = empty($cond) ? '0 = 1' : $cond;
}
$where[] = "'storm_access' = 'storm_access'";
return storm_rewrite_sql($sql, $where);
}
function storminvoice_storm_rewrite_where_sql($query, $primary_table, $account) {
static $conds = array();
if (isset($conds[$primary_table][$account->uid])) {
return $conds[$primary_table][$account->uid];
}
$cond = '';
if (!preg_match("/'storm_access' = 'storm_access'/", $query)) {
if (user_access('Storm invoice: view all', $account)) {
return '';
}
if (user_access('Storm invoice: view own', $account)) {
$cond .= "${primary_table}.uid = " . $account->uid;
}
if (user_access('Storm invoice: view of user organization', $account)) {
$cond .= !empty($cond) ? ' OR ' : '';
# If function is called without viewing an organization, this variable
# may not be set. These lines check for that and set the organization
# node id to zero if not otherwise set.
if (!isset($account->stormorganization_nid)) {
$account->stormorganization_nid = 0;
}
$cond .= ' sin1.organization_nid = ' . $account->stormorganization_nid;
}
if ($cond) {
$cond = " WHEN 'storminvoice' THEN (SELECT IF($cond, 1, 0) FROM {storminvoice} sin1 WHERE sin1.vid = ${primary_table}.vid) ";
}
else {
$cond = " WHEN 'storminvoice' THEN 0 ";
}
}
$conds[$primary_table][$account->uid] = $cond;
return $cond;
}
// IMPLEMENT STORM HOOKS - ACCOUNT FOR CHANGES IN OTHER STORM NODES
function storminvoice_stormorganization_change($organization_nid, $organization_title) {
$s = "UPDATE {storminvoice} SET organization_title='%s' WHERE organization_nid=%d AND organization_title <> '%s'";
db_query($s, $organization_title, $organization_nid, $organization_title);
}
function storminvoice_stormproject_change($project_nid, $project_title) {
$s = "UPDATE {storminvoice} SET project_title='%s' WHERE project_nid=%d AND project_title <> '%s'";
db_query($s, $project_title, $project_nid, $project_title);
}
function storminvoice_stormproject_change_hierarchy($project_nid, $organization_nid, $organization_title) {
$s = "UPDATE {storminvoice} SET organization_nid=%d, organization_title='%s' WHERE project_nid=%d";
db_query($s, $organization_nid, $organization_title, $project_nid);
}
// INVOICE CREATE/EDIT FORM
function storminvoice_form(&$node) {
$breadcrumb = array();
$breadcrumb[] = l(t('Home'), '<front>');
$breadcrumb[] = l(t('Invoices'), 'invoices');
drupal_set_breadcrumb($breadcrumb);
if (arg(1)=='add') {
$node->requestdate = time();
$node->duedate = $node->requestdate + (variable_get('storminvoice_payment_days', 30) * 86400);
$node->number = storminvoice_get_invoice_number($node->requestdate);
$node->paymentdate = NULL;
if (array_key_exists('organization_nid', $_GET) && !$node->organization_nid) {
$node->organization_nid = $_GET['organization_nid'];
}
if (array_key_exists('project_nid', $_GET) && !$node->project_nid) {
$node->project_nid = $_GET['project_nid'];
$p = node_load($node->project_nid);
$node->organization_nid = $p->organization_nid;
if (!stormorganization_access('view', $node->organization_nid)) {
drupal_set_message(t("You cannot add an invoice for this project, as you do not have access to view the organization's profile"));
drupal_goto('node/'. $node->project_nid);
}
}
if (!empty($_SESSION['storminvoice_list_filter']['organization_nid']) && !$node->organization_nid) {
$node->organization_nid = $_SESSION['storminvoice_list_filter']['organization_nid'];
}
if (!empty($_SESSION['storminvoice_list_filter']['project_nid']) && !$node->project_nid) {
$node->project_nid = $_SESSION['storminvoice_list_filter']['project_nid'];
}
$s_org = "SELECT n.nid, n.title FROM {stormorganization} so INNER JOIN {node} n
ON so.nid=n.nid WHERE n.status=1 AND so.isactive=1 AND n.type='stormorganization' ORDER BY n.title";
}
else {
$s_org = "SELECT n.nid, n.title FROM {stormorganization} so INNER JOIN {node} n
ON so.nid=n.nid WHERE n.status=1 AND n.type='stormorganization' ORDER BY n.title";
}
$type = node_get_types('type', $node);
$form['#attributes']['class'] = 'stormcomponent_node_form';
$form['group1'] = array(
'#type' => 'markup',
'#theme' => 'storm_form_group',
'#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'group1') : -20,
);
$form['group1']['number'] = array(
'#type' => 'textfield',
'#title' => t('Number'),
'#required' => TRUE,
'#size' => 10,
'#default_value' => $node->number,
);
$form['title'] = array(
'#type' => 'textfield',
'#title' => check_plain($type->title_label),
'#required' => TRUE,
'#default_value' => $node->title,
'#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'title') : -17,
);
$form['group2'] = array(
'#type' => 'markup',
'#theme' => 'storm_form_group',
'#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'group2') : -19,
);
$s_org = stormorganization_access_sql($s_org);
$s_org = db_rewrite_sql($s_org);
$r = db_query($s_org);
$organizations = array();
while ($organization = db_fetch_object($r)) {
$organizations[$organization->nid] = $organization->title;
if (!isset($node->organization_nid)) $node->organization_nid = $organization->nid;
}
$form['group2']['organization_nid'] = array(
'#type' => 'select',
'#title' => t('Organization'),
'#default_value' => $node->organization_nid,
'#options' => $organizations,
'#required' => TRUE,
'#attributes' => array('onchange' => "stormproject_organization_projects(this, 'edit-project-nid', true, '-')"),
);
$s = "SELECT n.nid, n.title FROM {node} AS n INNER JOIN {stormproject} AS spr ON spr.vid=n.vid WHERE spr.organization_nid=%d AND n.status=1 AND n.type='stormproject' ORDER BY n.title";
$s = stormproject_access_sql($s);
$s = db_rewrite_sql($s);
$r = db_query($s, $node->organization_nid);
$projects = array();
while ($project = db_fetch_object($r)) {
$projects[$project->nid] = $project->title;
}
$projects = array(0 => '-') + $projects;
$form['group2']['project_nid'] = array(
'#type' => 'select',
'#title' => t('Project'),
'#default_value' => isset($node->project_nid) ? $node->project_nid : NULL,
'#options' => $projects,
'#process' => array('storm_dependent_select_process'),
);
$form['group2']['reference'] = array(
'#type' => 'textfield',
'#title' => t('Reference'),
'#default_value' => isset($node->reference) ? $node->reference : NULL,
'#size' => 40,
);
$form['group3'] = array(
'#type' => 'markup',
'#theme' => 'storm_form_group',
'#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'group3') : -18,
);
$form['group3']['requestdate'] = array(
'#type' => 'dateext',
'#withnull' => 'true',
'#title' => t('Request date'),
'#default_value' => $node->requestdate,
);
$form['group3']['duedate'] = array(
'#type' => 'dateext',
'#withnull' => 'true',
'#title' => t('Due date'),
'#default_value' => $node->duedate,
);
$form['group3']['paymentdate'] = array(
'#type' => 'dateext',
'#withnull' => 'true',
'#title' => t('Payment date'),
'#default_value' => $node->paymentdate,
);
$form['group4'] = array(
'#type' => 'markup',
'#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'group4') : -16,
);
$count = (isset($node->items)) ? count($node->items) : 0;
for ($k = $count; $k <= $count + 2; $k++) {
$node->items[$k] = new stdClass();
$node->items[$k]->tax1app = variable_get('storm_tax1_app', 1);
$node->items[$k]->tax1percent = variable_get('storm_tax1_percent', 20);
$node->items[$k]->tax2app = variable_get('storm_tax2_app', 0);
$node->items[$k]->tax2percent = variable_get('storm_tax2_percent', 20);
$node->items[$k]->new_item = TRUE;
}
$i = 0;
foreach ($node->items as $item) {
$form['group4'][$i] = array(
'#type' => 'fieldset',
'#title' => isset($node->items[$i]->new_item) ? t('New item') : 'Item '. ($i + 1),
'#collapsible' => TRUE,
'#collapsed' => isset($node->items[$i]->new_item) ? TRUE : FALSE,
'#weight' => $i,
);
$form['group4'][$i]['first'] = array(
'#type' => 'markup',
'#theme' => 'storm_form_group',
'#weight' => 1,
);
$form['group4'][$i]['first']['items_'. $i .'_description'] = array(
'#type' => 'textfield',
'#title' => t('Item description'),
'#default_value' => isset($node->items[$i]->description) ? $node->items[$i]->description : NULL,
'#size' => 80,
);
$form['group4'][$i]['first']['items_'. $i .'_amount'] = array(
'#type' => 'textfield',
'#withnull' => 'true',
'#title' => t('Amount'),
'#size' => 15,
'#default_value' => isset($node->items[$i]->amount) ? $node->items[$i]->amount : NULL,
);
$form['group4'][$i]['first']['items_'. $i .'_weight'] = array(
'#type' => 'textfield',
'#title' => t('Weight'),
'#size' => 3,
'#maxlength' => 3,
'#default_value' => isset($node->items[$i]->weight) ? $node->items[$i]->weight : NULL,
);
$form['group4'][$i]['tax1'] = array(
'#type' => 'markup',
'#theme' => 'storm_form_group',
'#weight' => 2,
);
$form['group4'][$i]['tax1']['items_'. $i .'_tax1app'] = array(
'#type' => 'select',
'#title' => t('@tax1 Application', array('@tax1' => variable_get('storm_tax1_name', 'Tax 1'))),
'#options' => array(
1 => t('Apply to item amount'),
0 => t('Do not apply tax'),
),
'#default_value' => $node->items[$i]->tax1app,
);
$form['group4'][$i]['tax1']['items_'. $i .'_tax1percent'] = array(
'#type' => 'textfield',
'#title' => t('@tax1 Percentage', array('@tax1' => variable_get('storm_tax1_name', 'Tax 1'))),
'#default_value' => $node->items[$i]->tax1percent,
'#size' => 20,
);
$form['group4'][$i]['tax2'] = array(
'#type' => 'markup',
'#theme' => 'storm_form_group',
'#weight' => 3,
);
$form['group4'][$i]['tax2']['items_'. $i .'_tax2app'] = array(
'#type' => 'select',
'#title' => t('@tax2 Application', array('@tax2' => variable_get('storm_tax2_name', 'Tax 2'))),
'#options' => array(
2 => t('Apply to total of item amount plus previous tax'),
1 => t('Apply to item amount'),
0 => t('Do not apply tax'),
),
'#default_value' => $node->items[$i]->tax2app,
);
$form['group4'][$i]['tax2']['items_'. $i .'_tax2percent'] = array(
'#type' => 'textfield',
'#title' => t('@tax2 Percentage', array('@tax2' => variable_get('storm_tax2_name', 'Tax 2'))),
'#default_value' => $node->items[$i]->tax2percent,
'#size' => 20,
);
$form['group4'][$i]['first']['items_'. $i .'_src_nid'] = array(
'#type' => 'hidden',
'#default_value' => isset($node->items[$i]->src_nid) ? $node->items[$i]->src_nid : NULL,
);
$form['group4'][$i]['first']['items_'. $i .'_src_vid'] = array(
'#type' => 'hidden',
'#default_value' => isset($node->items[$i]->src_vid) ? $node->items[$i]->src_vid : NULL,
);
if (!variable_get('storm_tax_display', TRUE)) {
$form['group4'][$i]['tax1']['#type'] = 'hidden';
$form['group4'][$i]['tax2']['#type'] = 'hidden';
}
if (!variable_get('storm_tax2_display', TRUE)) {
$form['group4'][$i]['tax2']['#type'] = 'hidden';
}
$form['group4'][$i]['items_'. $i .'_total'] = array(
'#type' => 'hidden',
'#default_value' => isset($node->items[$i]->total) ? $node->items[$i]->total : NULL,
);
$i++;
} // foreach
$form['group5'] = array(
'#type' => 'markup',
'#theme' => 'storm_form_group',
'#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'group5') : -15,
);
$form['group5']['amount'] = array(
'#type' => 'textfield',
'#title' => t('Amount'),
/* '#attributes' => array('readonly' => 'readonly'), */ // See #450778
'#size' => 15,
'#default_value' => isset($node->amount) ? $node->amount : NULL,
);
$form['group5']['tax1'] = array(
'#type' => 'textfield',
'#title' => variable_get('storm_tax1_name', 'Tax 1'),
/* '#attributes' => array('readonly' => 'readonly'), */ // See #450778
'#size' => 15,
'#default_value' => isset($node->tax1) ? $node->tax1 : NULL,
);
$form['group5']['tax2'] = array(
'#type' => 'textfield',
'#title' => variable_get('storm_tax2_name', 'Tax 2'),
/* '#attributes' => array('readonly' => 'readonly'), */ // See #450778
'#size' => 15,
'#default_value' => isset($node->tax2) ? $node->tax2 : NULL,
);
$form['group5']['total'] = array(
'#type' => 'textfield',
'#title' => t('Total'),
/* '#attributes' => array('readonly' => 'readonly'), */ // See #450778
'#size' => 15,
'#default_value' => isset($node->total) ? $node->total : NULL,
);
if (!variable_get('storm_tax_display', TRUE)) {
$form['group5']['tax1']['#type'] = 'hidden';
$form['group5']['tax2']['#type'] = 'hidden';
$form['group5']['total']['#type'] = 'hidden';
}
if (!variable_get('storm_tax2_display', TRUE)) {
$form['group5']['tax2']['#type'] = 'hidden';
}
$form['group5']['totalcustomercurr'] = array(
'#type' => 'textfield',
'#title' => t('Total in customer currency'),
'#size' => 15,
'#default_value' => isset($node->totalcustomercurr) ? $node->totalcustomercurr : NULL,
);
return $form;
}
// INVOICE NODE MANIPULATION FUNCTIONS
function storminvoice_insert($node) {
_storminvoice_beforesave($node);
$node->taxexempt = (isset($node->taxexempt)) ? $node->taxexempt : NULL;
$node->src_nid = (isset($node->src_nid)) ? $node->src_nid : NULL;
$node->src_vid = (isset($node->src_vid)) ? $node->src_vid : NULL;
db_query("INSERT INTO {storminvoice}
(vid, nid, number, reference, organization_nid, organization_title,
project_nid, project_title, amount, tax1, tax2,
total, totalcustomercurr, requestdate, duedate, paymentdate,
taxexempt, src_nid, src_vid) VALUES
(%d, %d, '%s', '%s', %d, '%s',
%d, '%s', %f, %f, %f,
%f, %f, %d, %d, %d,
%d, %d, %d)",
$node->vid, $node->nid, $node->number, $node->reference, $node->organization_nid, $node->organization_title,
$node->project_nid, $node->project_title, $node->amount, $node->tax1, $node->tax2,
$node->total, $node->totalcustomercurr, $node->requestdate, $node->duedate, $node->paymentdate,
$node->taxexempt, $node->src_nid, $node->src_vid);
// Insert invoice items
_storminvoice_insert_items($node);
_storminvoice_aftersave($node);
}
function storminvoice_update($node) {
_storminvoice_beforesave($node);
if ($node->revision) {
storminvoice_insert($node);
}
else {
db_query("UPDATE {storminvoice} SET
number='%s', reference='%s', organization_nid=%d, organization_title='%s', project_nid=%d,
project_title='%s', amount=%f, tax1=%f, tax2=%f, total=%f,
totalcustomercurr=%f, requestdate=%d, duedate=%d, paymentdate=%d, taxexempt=%d WHERE vid = %d",
$node->number, $node->reference, $node->organization_nid, $node->organization_title, $node->project_nid,
$node->project_title, $node->amount, $node->tax1, $node->tax2, $node->total,
$node->totalcustomercurr, $node->requestdate, $node->duedate, $node->paymentdate, $node->taxexempt, $node->vid);
// Update invoice items
db_query("DELETE from {storminvoice_items} WHERE invoice_vid = ". $node->vid);
_storminvoice_insert_items($node);
_storminvoice_aftersave($node);
}
}
function _storminvoice_beforesave(&$node) {
// Allow use of comma when inputting numerical values - str_replace with period decimal
$node->amount = str_replace(',', '.', $node->amount);
$node->tax1 = str_replace(',', '.', $node->tax1);
$node->tax2 = str_replace(',', '.', $node->tax2);
$node->total = str_replace(',', '.', $node->total);
$node->totalcustomercurr = str_replace(',', '.', $node->totalcustomercurr);
if (is_array($node->requestdate)) $node->requestdate = _storm_date_to_gmtimestamp($node->requestdate);
if (is_array($node->duedate)) $node->duedate = _storm_date_to_gmtimestamp($node->duedate);
if (is_array($node->paymentdate)) $node->paymentdate = _storm_date_to_gmtimestamp($node->paymentdate);
$s = "SELECT n.title FROM {node} AS n
INNER JOIN {stormorganization} AS o ON n.vid=o.vid
WHERE type='stormorganization' AND n.nid=%d";
$r = db_query($s, $node->organization_nid);
$o = db_fetch_object($r);
$node->organization_title = $o->title;
if (isset($node->project_nid)) {
$s = "SELECT n.title FROM {node} AS n
INNER JOIN {stormproject} AS p ON n.vid=p.vid
WHERE type='stormproject' AND n.nid=%d";
$r = db_query($s, $node->project_nid);
$p = db_fetch_object($r);
$node->project_title = (isset($p->title)) ? $p->title : NULL;
}
// Parse invoice items
// from linear to array/object combination
$j = 0;
$variable = 'items_'. $j . '_description';
while (isset($node->$variable)) {
if (empty($node->items[$j])) {
$node->items[$j] = new stdClass();
}
$node->items[$j]->description = $node->$variable;
$variable = 'items_'. $j .'_amount';
$node->items[$j]->amount = str_replace(',', '.', $node->$variable);
$variable = 'items_'. $j .'_tax1app';
$node->items[$j]->tax1app = $node->$variable;
$variable = 'items_'. $j .'_tax1percent';
$node->items[$j]->tax1percent = $node->$variable;
$variable = 'items_'. $j .'_tax2app';
$node->items[$j]->tax2app = $node->$variable;
$variable = 'items_'. $j .'_tax2percent';
$node->items[$j]->tax2percent = $node->$variable;
$variable = 'items_'. $j .'_weight';
$node->items[$j]->weight = (isset($node->$variable)) ? $node->$variable : $j;
$variable = 'items_'. $j .'_src_nid';
$node->items[$j]->src_nid = $node->$variable;
$variable = 'items_'. $j .'_src_vid';
$node->items[$j]->src_vid = $node->$variable;
// Update taxes
storm_taxation($node->items[$j]);
$j++;
$variable = 'items_'. $j .'_description';
}
}
function _storminvoice_insert_items($node) {
if (!empty($node->items)) {
foreach ($node->items as $j => $item) {
$node->items[$j]->src_nid = (isset($node->items[$j]->src_nid)) ? $node->items[$j]->src_nid : $node->src_nid;
$node->items[$j]->src_vid = (isset($node->items[$j]->src_vid)) ? $node->items[$j]->src_vid : $node->src_vid;
db_query("INSERT INTO {storminvoice_items}
(invoice_nid, invoice_vid, amount, description,
tax1app, tax1percent, tax1,
tax2app, tax2percent, tax2,
total, weight,
src_nid, src_vid
) VALUES
(%d, %d, %f, '%s',
%d, %f, %f,
%d, %f, %f,
%f, %d,
%d, %d
)",
$node->nid, $node->vid, $node->items[$j]->amount, $node->items[$j]->description,
$node->items[$j]->tax1app, $node->items[$j]->tax1percent, $node->items[$j]->tax1,
$node->items[$j]->tax2app, $node->items[$j]->tax2percent, $node->items[$j]->tax2,
$node->items[$j]->total, $node->items[$j]->weight,
$node->items[$j]->src_nid, $node->items[$j]->src_vid
);
}
db_query("DELETE FROM {storminvoice_items} WHERE invoice_vid=%d AND total=0 AND description='' ", $node->vid);
}
}
function _storminvoice_aftersave($node) {
// Updates totals
$s = "SELECT SUM(amount) tamount, SUM(tax1) ttax1, SUM(tax2) ttax2, SUM(total) ttotal
FROM {storminvoice_items} WHERE invoice_vid=%d";
$r = db_query($s, $node->vid);
$t = db_fetch_object($r);
$node->amount = $t->tamount;
$node->tax1 = $t->ttax1;
$node->tax2 = $t->ttax2;
$node->total = $t->ttotal;
if (!$node->totalcustomercurr) {
$node->totalcustomercurr = $node->total;
}
db_query("UPDATE {storminvoice} SET
amount=%f, tax1=%f, tax2=%f, total=%f, totalcustomercurr=%f WHERE vid = %d",
$node->amount, $node->tax1, $node->tax2, $node->total, $node->totalcustomercurr, $node->vid);
}
function storminvoice_nodeapi(&$node, $op, $teaser, $page) {
if ($node->type != 'storminvoice') {
return;
}
switch ($op) {
case 'delete revision':
// Notice that we're matching a single revision based on the node's vid.
db_query('DELETE FROM {storminvoice} WHERE vid = %d', $node->vid);
db_query('DELETE FROM {storminvoice_items} WHERE invoice_vid = %d', $node->vid);
break;
}
}
function storminvoice_delete($node) {
db_query('DELETE FROM {storminvoice} WHERE nid = %d', $node->nid);
db_query('DELETE FROM {storminvoice_items} WHERE invoice_nid = %d', $node->nid);
}
function storminvoice_load($node) {
$additions = db_fetch_object(db_query('SELECT * FROM {storminvoice} WHERE vid = %d', $node->vid));
// Load invoice items
$result = db_query('SELECT * FROM {storminvoice_items} WHERE invoice_vid = %d ORDER BY weight ASC', $node->vid);
$additions->items = array();
while ($item = db_fetch_object($result)) {
$additions->items[] = $item;
}
return $additions;
}
function storminvoice_view($node, $teaser = FALSE, $page = FALSE) {
$breadcrumb = array();
$breadcrumb[] = l(t('Home'), '<front>');
$breadcrumb[] = l(t('Invoices'), 'invoices');
drupal_set_breadcrumb($breadcrumb);
return theme('storminvoice_view', $node, $teaser = FALSE, $page = FALSE);
}
// ADMIN SETTINGS
function storminvoice_admin_settings() {
$form = array();
$form['storminvoice_payment_days'] = array(
'#type' => 'textfield',
'#title' => t('Number of days for invoice payment'),
'#default_value' => variable_get('storminvoice_payment_days', 30),
'#description' => t('Default number of days for invoice payment.'),
'#size' => 5,
);
$form['storminvoice_hours_per_day'] = array(
'#type' => 'textfield',
'#title' => t('Number of hours in a day'),
'#default_value' => variable_get('storminvoice_hours_per_day', 8),
'#description' => t('Number of hours in a day, used for calculating the daily rate.'),
'#size' => 3,
);
$form['storminvoice_payment_modes'] = array(
'#type' => 'textarea',
'#title' => t('Modes for invoice payment'),
'#default_value' => variable_get('storminvoice_payment_modes', ''),
'#description' => t('Modes for invoice payment.'),
);
$form['storminvoice_cover_note_subject'] = array(
'#type' => 'textfield',
'#title' => t('Cover note subject'),
'#default_value' => variable_get('storminvoice_cover_note_subject', t('Invoice from !site', array('!site' => variable_get('site_name', 'SuiteDesk')))),
'#description' => t('Default subject for cover note.'),
'#size' => 50,
);
$form['storminvoice_cover_note'] = array(
'#type' => 'textarea',
'#title' => t('Cover note for invoice'),
'#default_value' => variable_get('storminvoice_cover_note', 'Hope this comes easy to you.'),
'#description' => t('Default cover note for pdf invoice sent by email.'),
);
$form['storminvoice_payment_terms'] = array(
'#type' => 'textfield',
'#title' => t('Payment terms'),
'#default_value' => variable_get('storminvoice_payment_terms', t('Due on receipt')),
'#description' => t('Payment terms.'),
'#size' => 50,
);
$form['storminvoice_tcpdf_location'] = array(
'#type' => 'textfield',
'#title' => t('Location of tcpdf library'),
'#default_value' => variable_get('storminvoice_tcpdf_location', 'libraries/tcpdf'),
'#description' => t('The directory that contains the <a href="http://sourceforge.net/projects/tcpdf/files/">TCPDF library</a> (libraries/tcpdf is recommended).'),
'#size' => 50,
);
return system_settings_form($form);
}
// VIEWS HOOK
function storminvoice_views_api() {
return array(
'api' => 2,
'path' => drupal_get_path('module', 'storminvoice'),
);
}
// Helper function to return items for a particular invoice. Called from within the theme functions
function storminvoice_getitems($invoice_vid) {
$s = "SELECT sit.* FROM {storminvoice_items} AS sit WHERE sit.invoice_vid = %d ORDER BY sit.weight ASC";
$r = db_query($s, $invoice_vid);
$items = array();
while ($i = db_fetch_object($r)) {
$items[] = $i;
}
return $items;
}
/**
* @function
* Creates next incremental invoice number
*
* @return varchar text value of the calculated invoice number
*/
function storminvoice_get_invoice_number($requestdate) {
$s = "SELECT MAX(CAST(SUBSTRING_INDEX(sin.number, '/', 1) AS SIGNED)) FROM {node} n INNER JOIN {storminvoice} sin ON n.nid=sin.nid
WHERE n.type='storminvoice' AND YEAR(FROM_UNIXTIME(sin.requestdate))=YEAR(FROM_UNIXTIME(%d))";
$date = getdate($requestdate);
$new_invoice_number = (db_result(db_query($s, $requestdate)) + 1) .'/'. $date['year'];
return $new_invoice_number;
}
/**
* Try to obtain an hourly rate or fixed price for the new invoice item for a timetracking node.
* To do this we will use the first technique, among the following, to work:
* (1) try to obtain a rate from a ticket
* (2) try to obtain a rate from a task
* (3) try to obtain a rate from a project
* (4) try to obtain a rate from an organization
* (5) return an error if none of this worked.
*
* @return
* Variables in an array.
*/
function storminvoice_get_rate($node) {
$rate_array = array('pricemode_used' => '', 'rate_to_use' => 0);
$hours_per_day = variable_get('storminvoice_hours_per_day', 8);
$found = FALSE;
$node_list = array();
switch ($node->type) {
case 'stormtimetracking':
$node_list = array('ticket' => $node->ticket_nid, 'task' => $node->task_nid, 'project' => $node->project_nid, 'organization' => $node->organization_nid);
break;
case 'stormproject':
$node_list = array('project' => $node->nid, 'organization' => $node->organization_nid);
break;
case 'stormtask':
$node_list = array('task' => $node->nid, 'project' => $node->project_nid, 'organization' => $node->organization_nid);
break;
case 'stormticket':
$node_list = array('ticket' => $node->nid, 'task' => $node->task_nid, 'project' => $node->project_nid, 'organization' => $node->organization_nid);
break;
}
foreach ($node_list as $type => $nid) {
if ($nid) {
$parent_item = node_load($nid);
switch ($parent_item->pricemode) {
case 'hourly':
if ($parent_item->durationunit == 'hour') {
$found = TRUE;
$rate_array['rate_to_use'] = $parent_item->price;
}
elseif ($parent_item->durationunit == 'day') {
$found = TRUE;
$rate_array['rate_to_use'] = $parent_item->price * $hours_per_day;
}
break;
case 'daily':
if ($parent_item->durationunit == 'hour') {
$found = TRUE;
$rate_array['rate_to_use'] = $parent_item->price / $hours_per_day;
}
elseif ($parent_item->durationunit == 'day') {
$found = TRUE;
$rate_array['rate_to_use'] = $parent_item->price;
}
break;
case 'fixed':
$found = TRUE;
$rate_array['rate_to_use'] = 0;
break;
case 'fixed_price':
$found = TRUE;
$rate_array['rate_to_use'] = $parent_item->price;
break;
default:
continue;
}
}
if ($found == TRUE) {
$rate_array['pricemode_used'] = $parent_item->pricemode;
break;
}
}
if ($found == FALSE) {
drupal_set_message(t('Error whilst finding a rate from ticket, task, project and organization. Consider setting the pricemode and price for your client organizations to avoid this error.'), 'error');
}
return $rate_array;
}
function storminvoice_get_item_desc($rate_array, $node) {
switch ($rate_array['pricemode_used']) {
case 'hourly':
if ($node->type == 'stormtimetracking') {
$description = _format_small_date($node->trackingdate) . ' - ' . t('@dur @units work at @rate per hour on @desc', array('@dur' => $node->billing_duration, '@unit' => t($node->durationunit), '@rate' => $node->price, '@desc' => $node->title));
}
else {
$description = _format_small_date() . ' - ' . t('@dur @units work at @rate per hour on @desc', array('@dur' => $node->duration, '@unit' => t($node->durationunit), '@rate' => $node->price, '@desc' => $node->title));
}
break;
case 'daily':
if ($node->type == 'stormtimetracking') {
$description = _format_small_date($node->trackingdate) . ' - ' . t('@dur @units work at @rate per day on @desc', array('@dur' => $node->billing_duration, '@unit' => t($node->durationunit), '@rate' => $node->price, '@desc' => $node->title));
}
else {
$description = _format_small_date() . ' - ' . t('@dur @units work at @rate per day on @desc', array('@dur' => $node->duration, '@unit' => t($node->durationunit), '@rate' => $node->price, '@desc' => $node->title));
}
break;
case 'fixed':
if ($node->type == 'stormtimetracking') {
$description = _format_small_date($node->trackingdate) . ' - ' . t('@dur @units unbilled work on @desc', array('@dur' => $node->billing_duration, '@unit' => t($node->durationunit), '@desc' => $node->title));
}
else {
$description = _format_small_date() . ' - ' . t('@dur @units unbilled work on @desc', array('@dur' => $node->duration, '@unit' => t($node->durationunit), '@desc' => $node->title));
}
break;
case 'fixed_price':
switch ($node->type) {
case 'stormtimetracking':
$description = _format_small_date($node->trackingdate) . ' - ' . t('Work billed for @desc', array('@desc' => $node->title));
break;
case 'stormproject':
$description = t('Project billed: @desc', array('@desc' => $node->title));
break;
case 'stormtask':
$description = t('Task billed: @desc', array('@desc' => $node->title));
break;
case 'stormticket':
$description = t('Ticket billed: @desc', array('@desc' => $node->title));
break;
}
}
return $description;
}
function storminvoice_get_item_amount($rate_array, $node) {
switch ($rate_array['pricemode_used']) {
case 'hourly':
if ($node->type == 'stormtimetracking') {
$amount = $node->billing_duration * $rate_array['rate_to_use'];
}
else {
$amount = $node->duration * $rate_array['rate_to_use'];
}
break;
case 'daily':
if ($node->type == 'stormtimetracking') {
$amount = $node->billing_duration * $rate_array['rate_to_use'];
}
else {
$amount = $node->duration * $rate_array['rate_to_use'];
}
break;
case 'fixed':
$amount = $rate_array['rate_to_use'];
break;
case 'fixed_price':
$amount = $rate_array['rate_to_use'];
break;
}
return $amount;
}
/**
* Implementation of hook_token_list().
*/
function storminvoice_token_list($type='all') {
$tokens = array();
if (($type == 'node') || ($type == 'all')) {
$tokens['node']['storminvoice-number'] = t('SuiteDesk Invoice: Number.');
$tokens['node']['storminvoice-reference'] = t('SuiteDesk Invoice: Reference.');
$tokens['node']['storminvoice-organization-nid'] = t('SuiteDesk Invoice: Organization Node ID.');
$tokens['node']['storminvoice-organization-title'] = t('SuiteDesk Invoice: Organization Title.');
$tokens['node']['storminvoice-project-nid'] = t('SuiteDesk Invoice: Project Node ID.');
$tokens['node']['storminvoice-project-title'] = t('SuiteDesk Invoice: Project Title.');
$tokens['node']['storminvoice-requestdate'] = t('SuiteDesk Invoice: Request Date.');
$tokens['node']['storminvoice-duedate'] = t('SuiteDesk Invoice: Due Date.');
$tokens['node']['storminvoice-paymentdate'] = t('SuiteDesk Invoice: Payment Date.');
$tokens['node']['storminvoice-amount'] = t('SuiteDesk Invoice: Amount.');
$tokens['node']['storminvoice-total'] = t('SuiteDesk Invoice: Total.');
$tokens['node']['storminvoice-totalcustomer'] = t('SuiteDesk Invoice: Total in customer currency.');
$tokens['node']['storminvoice-taxexempt'] = t('SuiteDesk Invoice: Tax Exempt.');
if (variable_get('storm_tax1_app', 0)) {
$tokens['node']['storminvoice-tax1'] = t('SuiteDesk Invoice: '. variable_get('storm_tax1_name', 'VAT') .'.');
}
if (variable_get('storm_tax2_app', 0)) {
$tokens['node']['storminvoice-tax2'] = t('SuiteDesk Invoice: '. variable_get('storm_tax2_name', 'Tax 2') .'.');
}
}
return $tokens;
}
/**
* Implementation of hook_token_values().
*/
function storminvoice_token_values($type, $object = NULL) {
$values = array();
$node = $object;
if ((($type == 'node') || ($type == 'all')) && ($node->type === 'storminvoice')) {
$values['storminvoice-number'] = $node->number;
$values['storminvoice-reference'] = $node->reference;
$values['storminvoice-organization-nid'] = $node->organization_nid;
$values['storminvoice-organization-title'] = $node->organization_title;
$values['storminvoice-project-nid'] = $node->project_nid;
$values['storminvoice-project-title'] = $node->project_title;
$values['storminvoice-requestdate'] = format_date($node->requestdate, 'medium', '', variable_get('date_default_timezone', NULL));
$values['storminvoice-payment'] = format_date($node->paymentdate, 'medium', '', variable_get('date_default_timezone', NULL));
$values['storminvoice-duedate'] = format_date($node->duedate, 'medium', '', variable_get('date_default_timezone', NULL));
$values['storminvoice-amount'] = $node->amount;
$values['storminvoice-total'] = $node->total;
$values['storminvoice-totalcustomer'] = $node->totalcustomer;
$values['storminvoice-taxexempt'] = $node->taxexempt;
if (variable_get('storm_tax1_app', 0)) {
$values['storminvoice-tax1'] = $node->tax1;
}
if (variable_get('storm_tax2_app', 0)) {
$values['storminvoice-tax2'] = $node->tax2;
}
return $values;
}
}
function storminvoice_storm_dashboard_links($type) {
$links = array();
if ($type == 'page' || $type == 'block') {
$links[] = array(
'theme' => 'storm_dashboard_link',
'title' => t('Invoices'),
'icon' => 'storminvoice-item',
'path' => 'invoices',
'params' => array(),
'access_arguments' => 'Storm invoice: access',
'node_type' => 'storminvoice',
'add_type' => 'storminvoice',
'map' => array(),
'weight' => 120,
);
}
return $links;
}