invoice.module in Invoice 7
Same filename and directory in other branches
Invoice module
This module was developed by Platina Designs, http://www.platinadesigns.nl
@author Pieter Vogelaar <ps.vogelaar@platinadesigns.nl>
File
invoice.moduleView source
<?php
/**
* @file
* Invoice module
*
* This module was developed by Platina Designs, http://www.platinadesigns.nl
*
* @author Pieter Vogelaar <ps.vogelaar@platinadesigns.nl>
*/
// Set locale so money has the right format for the preferred culture
setlocale(LC_MONETARY, variable_get('invoice_locale', 'en_US.utf8'));
// Require classes
require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'invoice') . '/invoice_classes.inc';
// Require AJAX functions
require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'invoice') . '/invoice_ajax.inc';
// Require form functions
require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'invoice') . '/invoice_form.inc';
// Require helper functions
require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'invoice') . '/invoice_helpers.inc';
// Require API functions
require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'invoice') . '/invoice_api.inc';
/**
* Implements hook_node_info()
*/
function invoice_node_info() {
return array(
'invoice' => array(
'name' => t('Invoice'),
'base' => 'invoice',
'description' => t("If you want to add an invoice, use this content type."),
'has_title' => TRUE,
'title_label' => t('Title'),
'has_body' => TRUE,
'body_label' => t('Body'),
),
);
}
/**
* Implements hook_menu()
*/
function invoice_menu() {
$items = array();
$items['invoices'] = array(
'title' => 'Invoices',
'page callback' => 'invoice_invoices',
'access arguments' => array(
'access invoices',
),
);
$items['invoice/set/template'] = array(
'title' => 'Edit invoice',
'page callback' => 'invoice_set_template',
'access arguments' => array(
'administer invoices',
),
'type' => MENU_CALLBACK,
);
$items['invoice/print/%'] = array(
'title' => 'Invoice in HTML print format',
'page callback' => 'invoice_view_print',
'page arguments' => array(
2,
),
'access arguments' => array(
'access invoices',
),
'type' => MENU_CALLBACK,
);
$items['invoice/pdf/%'] = array(
'title' => 'Invoice in PDF format',
'page callback' => 'invoice_view_pdf',
'page arguments' => array(
2,
),
'access arguments' => array(
'access invoices',
),
'type' => MENU_CALLBACK,
);
$items['invoice/set/pay_status/%/%'] = array(
'title' => 'Set invoice pay status',
'page callback' => 'invoice_set_pay_status',
'page arguments' => array(
3,
4,
),
'access arguments' => array(
'administer invoices',
),
'type' => MENU_CALLBACK,
);
$items['invoice/search/customer'] = array(
'title' => 'Search customer',
'page callback' => 'invoice_search_customer',
'page arguments' => array(
3,
),
'access callback' => 'invoice_user_access_handler',
'access arguments' => array(
array(
'administer invoices',
'administer own invoices',
),
),
'type' => MENU_CALLBACK,
);
$items['invoice/get/customer_info'] = array(
'title' => 'Get customer info',
'page callback' => 'invoice_get_customer_info',
'access callback' => 'invoice_user_access_handler',
'access arguments' => array(
array(
'administer invoices',
'administer own invoices',
),
),
'type' => MENU_CALLBACK,
);
$items['invoice/save/item'] = array(
'title' => 'Save item',
'page callback' => 'invoice_save_item',
'access callback' => 'invoice_user_access_handler',
'access arguments' => array(
array(
'administer invoices',
'administer own invoices',
),
),
'type' => MENU_CALLBACK,
);
$items['invoice/edit/item'] = array(
'title' => 'Edit item',
'page callback' => 'invoice_edit_item',
'access callback' => 'invoice_user_access_handler',
'access arguments' => array(
array(
'administer invoices',
'administer own invoices',
),
),
'type' => MENU_CALLBACK,
);
$items['invoice/delete/item'] = array(
'title' => 'Delete item',
'page callback' => 'invoice_delete_item',
'access callback' => 'invoice_user_access_handler',
'access arguments' => array(
array(
'administer invoices',
'administer own invoices',
),
),
'type' => MENU_CALLBACK,
);
$items['admin/config/system/invoice'] = array(
'title' => 'Invoice',
'page callback' => 'invoice_settings',
'access arguments' => array(
'administer invoices',
),
'description' => 'Create and manage invoices.',
'type' => MENU_NORMAL_ITEM,
);
$items['invoice/installed_locales'] = array(
'title' => 'Installed locales on your system',
'page callback' => 'invoice_installed_locales',
'access arguments' => array(
'administer invoices',
),
'type' => MENU_CALLBACK,
);
$items['invoice/api/token'] = array(
'page callback' => '_invoice_api_session_token',
'access callback' => 'invoice_api_access_handler',
'type' => MENU_CALLBACK,
);
$items['invoice/api/invoice.json/findAll'] = array(
'title' => 'GET invoices',
'page callback' => 'invoice_api_invoice',
'page arguments' => array(
2,
3,
),
'access callback' => 'invoice_api_access_handler',
'type' => MENU_CALLBACK,
);
$items['invoice/api/invoice.pdf/%'] = array(
'title' => 'GET an invoice in PDF format',
'page callback' => 'invoice_api_invoice',
'page arguments' => array(
2,
3,
),
'access callback' => 'invoice_api_access_handler',
'type' => MENU_CALLBACK,
);
$items['invoice/api/invoice.html/%'] = array(
'title' => 'GET an invoice in HTML format',
'page callback' => 'invoice_api_invoice',
'page arguments' => array(
2,
3,
),
'access callback' => 'invoice_api_access_handler',
'type' => MENU_CALLBACK,
);
$items['invoice/api/invoice.json/%'] = array(
'title' => 'GET / PUT / DELETE an invoice',
'page callback' => 'invoice_api_invoice',
'page arguments' => array(
2,
3,
),
'access callback' => 'invoice_api_access_handler',
'type' => MENU_CALLBACK,
);
$items['invoice/api/invoice.json'] = array(
'title' => 'POST an invoice',
'page callback' => 'invoice_api_invoice',
'page arguments' => array(
2,
),
'access callback' => 'invoice_api_access_handler',
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* User access handler to support multiple permissions for a menu item
*
* @param array $permissions
* @return boolean
*/
function invoice_user_access_handler(array $permissions = array()) {
$allow = FALSE;
if (count($permissions) > 0) {
foreach ($permissions as $permission) {
if (user_access($permission)) {
$allow = TRUE;
break;
}
}
}
else {
$allow = TRUE;
}
return $allow;
}
/**
* API access handler
*/
function invoice_api_access_handler() {
return TRUE;
// Access is handled later on in the API
}
/**
* Implements hook_perm()
*/
function invoice_permission() {
return array(
'access invoices' => array(
'title' => t('Access invoices'),
'description' => t('Provides access to invoices.'),
),
'administer invoices' => array(
'title' => t('Administer invoices'),
'description' => t('Provides the privilege to administer invoices.'),
),
'administer own invoices' => array(
'title' => t('Administer own invoices'),
'description' => t('Provices the privilege to administer own created invoices.'),
),
);
}
/**
* Implements hook_init()
*/
function invoice_init() {
_invoice_api_dispatch();
_invoice_add_css_js();
}
/**
* Implements hook_node_access()
*
* @param object $node
* @param string $op
* @param object $account
* @return boolean
*/
function invoice_node_access($node, $op, $account) {
$type = is_string($node) ? $node : $node->type;
if ($type == 'invoice') {
if ($op == 'view') {
if (user_access('access invoices', $account)) {
return NODE_ACCESS_ALLOW;
}
}
if ($op == 'create') {
if (user_access('administer invoices', $account) || user_access('administer own invoices', $account)) {
return NODE_ACCESS_ALLOW;
}
}
if ($op == 'update') {
if (user_access('administer invoices', $account) || user_access('administer own invoices', $account) && $account->uid == $node->uid) {
return NODE_ACCESS_ALLOW;
}
}
if ($op == 'delete') {
if (user_access('administer invoices', $account) || user_access('administer own invoices', $account) && $account->uid == $node->uid) {
return NODE_ACCESS_ALLOW;
}
}
return NODE_ACCESS_DENY;
}
}
/**
* Implements hook_theme()
*
* @param array $existing
* @param string $type
* @param string $theme
* @param string $path
* @return array
*/
function invoice_theme($existing, $type, $theme, $path) {
return array(
'invoice_body' => array(
'variables' => array(
'node' => NULL,
'type' => NULL,
),
),
'invoice_markup' => array(
'variables' => array(
's_fieldname' => NULL,
'value' => NULL,
's_title' => NULL,
),
),
);
}
/**
* Implements hook_load()
*
* @param object|array $node One node or an array with multiple nodes
* @return mixed
*/
function invoice_load($nodes) {
$multiple = is_array($nodes) ? true : false;
if (!is_array($nodes)) {
$nodes = array(
$nodes,
);
}
foreach ($nodes as $node) {
// Get all information for this invoice
$invoice = db_query("SELECT *,ii.iid as invoice_number,ii.leading_zeros AS leading_zeros,\n ii.prefix AS prefix, ii.description AS invoice_description, ii.pay_limit AS pay_limit,\n ic.description AS customer_description, t.name as template\n FROM {invoice_invoices} ii\n LEFT JOIN {invoice_customers} ic ON ic.invoice_id = ii.iid\n LEFT JOIN {invoice_templates} t ON ii.tid = t.tid\n WHERE nid = :nid\n GROUP BY ii.iid", array(
':nid' => $node->nid,
))
->fetchObject();
$totals = _invoice_get_invoice_totals($invoice->invoice_number);
$invoice->extotal = $totals['extotal'];
$invoice->inctotal = $totals['inctotal'];
$invoice->vattotal = $totals['vattotal'];
// Determine template to use
$template = !empty($invoice->template) ? $invoice->template : variable_get('invoice_default_template', 'default');
// Set locale so money has the right format for the preferred culture
$locale = _invoice_get_variable($template, 'locale');
if ($locale) {
setlocale(LC_MONETARY, $locale);
}
// Calculate vat totals per different VAT percentage
$vattotals = array();
$result = db_query("SELECT vat, (quantity*unitcost)*((vat / 100) +1) * (1 - (1 / ((vat / 100) +1))) as vattotal\n FROM {invoice_items} WHERE invoice_id = :invoice_id", array(
':invoice_id' => $invoice->invoice_number,
))
->fetchAll();
// SUM all vat totals per different VAT percentage
foreach ($result as $row) {
if (!isset($vattotals[$row->vat])) {
$vattotals[$row->vat] = array(
'vattotal' => $row->vattotal,
);
}
else {
$vattotals[$row->vat]['vattotal'] += $row->vattotal;
}
}
// Round every total per different VAT percentage
// and add a formatted version
foreach ($vattotals as $key => $total) {
$vattotals[$key]['vattotal'] = _invoice_round($total['vattotal'], 2);
$vattotals[$key]['formatted_vattotal'] = _invoice_format_money($total['vattotal'], 2);
}
// Set totals
$extotal = _invoice_round($invoice->extotal, 2);
$inctotal = _invoice_round($invoice->inctotal, 2);
$vattotal = _invoice_round($invoice->vattotal, 2);
// Add general invoice information to the node object
$node->invoice = array(
'invoice_number' => $invoice->invoice_number,
'formatted_invoice_number' => _invoice_get_formatted_invoice_number($invoice->invoice_number, NULL, $node->created),
'invoice_number_zerofill' => $invoice->leading_zeros,
'invoice_number_prefix' => $invoice->prefix,
'description' => $invoice->invoice_description,
'vat' => $vattotals,
'pay_status' => $invoice->pay_status,
'pay_limit' => $invoice->pay_limit,
'template' => $template,
'vattotal' => $vattotal,
'extotal' => $extotal,
'inctotal' => $inctotal,
'formatted_vattotal' => _invoice_format_money($vattotal, 2),
'formatted_extotal' => _invoice_format_money($extotal, 2),
'formatted_inctotal' => _invoice_format_money($inctotal, 2),
'formatted_created' => format_date($node->created, 'custom', _invoice_get_variable($template, 'date_format')),
);
// Add customer information to the node object
$node->customer = array(
'cid' => $invoice->cid,
'customer_number' => $invoice->customer_number,
'name' => null,
'company_name' => $invoice->company_name,
'firstname' => $invoice->firstname,
'lastname' => $invoice->lastname,
'fullname' => $invoice->lastname . (!empty($invoice->firstname) ? ', ' . $invoice->firstname : ''),
'street' => $invoice->street,
'building_number' => $invoice->building_number,
'zipcode' => $invoice->zipcode,
'city' => $invoice->city,
'state' => $invoice->state,
'country' => $invoice->country,
'coc_number' => $invoice->coc_number,
'vat_number' => $invoice->vat_number,
'description' => $invoice->customer_description,
);
// Add invoices items to the node object
$node->invoice_items = array();
$result = db_query("SELECT * FROM {invoice_items}\n WHERE invoice_id = :invoice_id ORDER BY weight ASC, created ASC, iid ASC", array(
':invoice_id' => $invoice->invoice_number,
))
->fetchAll();
foreach ($result as $row) {
// Add invoice item row to the array
$node->invoice_items[] = array(
'iid' => $row->iid,
'weight' => $row->weight,
'description' => $row->description,
'quantity' => $row->quantity,
'unitcost' => $row->unitcost,
'vat' => $row->vat,
'formatted_exunitcost' => _invoice_round_and_format_money($row->unitcost, 3),
'formatted_incunitcost' => _invoice_round_and_format_money($row->unitcost * _invoice_vat_percent_to_decimal($row->vat), 2),
'formatted_extotal' => _invoice_round_and_format_money($row->quantity * $row->unitcost, 2),
'formatted_inctotal' => _invoice_round_and_format_money($row->quantity * $row->unitcost * _invoice_vat_percent_to_decimal($row->vat), 2),
);
}
}
return $multiple ? $nodes : $nodes[0];
}
/**
* Implements hook_node_presave()
*
* @param object $node
*/
function invoice_node_presave($node) {
if ($node->type == 'invoice') {
// If true we are creating a new invoice
if (intval($node->invoice_number) == 0) {
// Get new invoice number
if (intval($node->user_defined_invoice_number) > 0) {
$node->invoice_number = $node->user_defined_invoice_number;
}
else {
$node->invoice_number = _invoice_get_new_invoice_number();
}
}
// Save the title, this must happen when creating AND editing a node because otherwise
// the pathauto module will give an error
if (intval($node->invoice_number) > 0) {
$node->title = t('Invoice') . ' #' . _invoice_get_formatted_invoice_number($node->invoice_number, $node);
}
if (!isset($node->customer_number) || $node->customer_number < 1) {
$template = _invoice_get_chosen_template();
$tid = (int) db_query("SELECT tid FROM {invoice_templates} WHERE name = :name", array(
':name' => $template,
))
->fetchField();
// Get customer number
if (!empty($node->company_name)) {
$customer_number = db_query("SELECT customer_number FROM {invoice_customers} ic\n JOIN {invoice_invoices} ii ON ic.invoice_id = ii.iid AND ii.tid = :tid\n WHERE company_name = :company_name AND country = :country LIMIT 1", array(
':tid' => $tid,
':company_name' => $node->company_name,
':country' => $node->country,
))
->fetchField();
}
elseif (!empty($node->lastname)) {
$customer_number = db_query("SELECT customer_number FROM {invoice_customers} ic\n JOIN {invoice_invoices} ii ON ic.invoice_id = ii.iid AND ii.tid = :tid\n WHERE lastname = :lastname AND zipcode = :zipcode AND building_number = :building_number\n LIMIT 1", array(
':tid' => $tid,
':lastname' => $node->lastname,
':zipcode' => $node->zipcode,
':building_number' => $node->building_number,
))
->fetchField();
}
// If customer number is still empty, get a new one
if (empty($customer_number)) {
$customer_number = 1 + db_query("SELECT MAX(customer_number) FROM {invoice_customers} ic\n JOIN {invoice_invoices} ii ON ic.invoice_id = ii.iid AND ii.tid = :tid", array(
':tid' => $tid,
))
->fetchField();
}
// Add customer number to the node object
$node->customer_number = $customer_number;
}
// Set language undefined
$node->language = 'und';
}
}
/**
* Invoice settings
*/
function invoice_settings() {
// A little test to see if the function _invoice_round() rounds well on every server
if (_invoice_round(38.675, 2) != 38.68) {
drupal_set_message(t('TEST: The answer of _invoice_round(38.675, 2) must be 38.68, but is @answer!' . ' This problem is maybe caused by your PHP version.', array(
'@answer' => _invoice_round(38.675, 2),
)), 'error');
}
$content = '';
$content = drupal_get_form('invoice_settings_form');
return $content;
}
/**
* Submit function for the settings form
*/
function invoice_settings_form_submit(&$form_state, $form) {
$fv =& $form['values'];
// Invoice specific settings
variable_set('invoice_locale', $fv['locale']);
variable_set('invoice_date_format', $fv['date_format']);
variable_set('invoice_vat', $fv['vat']);
variable_set('invoice_pay_limit', $fv['pay_limit']);
variable_set('invoice_invoice_number_zerofill', $fv['invoice_number_zerofill']);
variable_set('invoice_invoice_number_prefix', $fv['invoice_number_prefix']);
variable_set('invoice_default_template', $fv['default_template']);
// Display columns specific settings
variable_set('invoice_display_column_vat', $fv['display_column_vat']);
variable_set('invoice_display_column_exunitcost', $fv['display_column_exunitcost']);
variable_set('invoice_display_column_incunitcost', $fv['display_column_incunitcost']);
variable_set('invoice_display_column_extotal', $fv['display_column_extotal']);
variable_set('invoice_display_column_inctotal', $fv['display_column_inctotal']);
// Supplier specific settings
variable_set('invoice_supplier_company_name', $fv['supplier_company_name']);
variable_set('invoice_supplier_street', $fv['supplier_street']);
variable_set('invoice_supplier_building_number', $fv['supplier_building_number']);
variable_set('invoice_supplier_zipcode', $fv['supplier_zipcode']);
variable_set('invoice_supplier_city', $fv['supplier_city']);
variable_set('invoice_supplier_state', $fv['supplier_state']);
variable_set('invoice_supplier_country', $fv['supplier_country']);
variable_set('invoice_supplier_phone', $fv['supplier_phone']);
variable_set('invoice_supplier_fax', $fv['supplier_fax']);
variable_set('invoice_supplier_email', $fv['supplier_email']);
variable_set('invoice_supplier_web', $fv['supplier_web']);
variable_set('invoice_supplier_coc_number', $fv['supplier_coc_number']);
variable_set('invoice_supplier_vat_number', $fv['supplier_vat_number']);
// API specific settings
variable_set('invoice_api_allowed_ips', $fv['api_allowed_ips']);
variable_set('invoice_api_root_username', $fv['api_root_username']);
$templates = _invoice_get_templates();
foreach ($templates as $template) {
$count = db_query("SELECT COUNT(*) FROM {invoice_templates} WHERE name = :name", array(
':name' => $template,
))
->fetchField();
if ($count == 0) {
db_insert('invoice_templates')
->fields(array(
'name' => $template,
))
->execute();
}
// Numeric fields in this query will become a zero if nothing is filled in, instead of the
// overloading system. So numeric fields that are empty will take over the general setting
// and update that in the database for this template.
db_update('invoice_templates')
->fields(array(
'locale' => $fv[$template . '_locale'],
'date_format' => $fv[$template . '_date_format'],
'vat' => $fv[$template . '_vat'] != '' ? $fv[$template . '_vat'] : $fv['vat'],
'pay_limit' => $fv[$template . '_pay_limit'] != '' ? $fv[$template . '_pay_limit'] : $fv['pay_limit'],
'supplier_company_name' => $fv[$template . '_supplier_company_name'],
'supplier_street' => $fv[$template . '_supplier_street'],
'supplier_building_number' => $fv[$template . '_supplier_building_number'],
'supplier_zipcode' => $fv[$template . '_supplier_zipcode'],
'supplier_city' => $fv[$template . '_supplier_city'],
'supplier_state' => $fv[$template . '_supplier_state'],
'supplier_country' => $fv[$template . '_supplier_country'],
'supplier_phone' => $fv[$template . '_supplier_phone'],
'supplier_fax' => $fv[$template . '_supplier_fax'],
'supplier_email' => $fv[$template . '_supplier_email'],
'supplier_web' => $fv[$template . '_supplier_web'],
'supplier_coc_number' => $fv[$template . '_supplier_coc_number'],
'supplier_vat_number' => $fv[$template . '_supplier_vat_number'],
'api_username' => $fv[$template . '_api_username'],
// set default value if we deal with a new template
'display_column_vat' => $count > 0 ? $fv[$template . '_display_column_vat'] : 0,
// set default value if we deal with a new template
'display_column_exunitcost' => $count > 0 ? $fv[$template . '_display_column_exunitcost'] : 1,
// set default value if we deal with a new template
'display_column_incunitcost' => $count > 0 ? $fv[$template . '_display_column_incunitcost'] : 1,
// set default value if we deal with a new template
'display_column_extotal' => $count > 0 ? $fv[$template . '_display_column_extotal'] : 1,
// set default value if we deal with a new template
'display_column_inctotal' => $count > 0 ? $fv[$template . '_display_column_inctotal'] : 1,
))
->condition('name', $template)
->execute();
}
drupal_set_message(t('Succesfully saved'));
}
/**
* Overview of all invoices
*/
function invoice_invoices() {
$content = '';
//Our header defenition
$header = array(
array(
'data' => t('Invoice #'),
'field' => 'ii.iid',
),
array(
'data' => t('Customer'),
'field' => 'c.customer',
),
array(
'data' => t('Total (ex)'),
'field' => 'extotal',
),
array(
'data' => t('Total (inc)'),
'field' => 'inctotal',
),
array(
'data' => t('Created'),
'field' => 'invoices.created',
),
array(
'data' => _invoice_get_icon('bullet_black', NULL, array(
'title' => t('sort by @s', array(
'@s' => t('Pay status'),
)),
)),
'field' => 'ii.pay_status',
),
t('Actions'),
);
if (!isset($_GET['order']) || empty($_GET['order'])) {
$_GET['order'] = t("Invoice #");
$_GET['sort'] = "desc";
}
$query = db_select('invoice_invoices', 'ii')
->extend('PagerDefault')
->limit(15);
$query
->fields('ii', array(
'iid',
'nid',
'pay_limit',
'pay_status',
));
$query
->fields('c', array(
'company_name',
'lastname',
'firstname',
));
$query
->fields('n', array(
'created',
));
$query
->addExpression('it.name', 'template');
$query
->leftJoin('node', 'n', 'ii.nid = n.nid');
$query
->leftJoin('invoice_customers', 'c', 'ii.iid = c.invoice_id');
$query
->leftJoin('invoice_templates', 'it', 'ii.tid = it.tid');
$query
->groupBy('ii.iid')
->orderBy('n.nid', 'DESC');
$count_query = db_select('invoice_invoices', 'ii');
$count_query
->addExpression('COUNT(*)');
$query
->setCountQuery($count_query);
$result = $query
->execute();
$rows = array();
foreach ($result as $row) {
// Set locale so money has the right format for the preferred culture
$locale = _invoice_get_variable($row->template, 'locale');
if ($locale) {
setlocale(LC_MONETARY, $locale);
}
// Get locale settings
$a_locales = localeconv();
// Set formatted create date
$created = format_date($row->created, 'custom', variable_get('invoice_date_format', ''));
// If no default invoice date format is set, use the small drupal date format
if (empty($created)) {
$created = format_date($row->created, 'small');
}
// Get invoice totals
$a_totals = _invoice_get_invoice_totals($row->iid);
// Set pay status
if ($row->pay_status == 'paid') {
$pay_status = _invoice_get_icon('bullet_green', NULL, array(
'title' => t('Paid'),
));
}
else {
$pay_status = _invoice_get_icon('bullet_yellow', NULL, array(
'title' => t('Unpaid'),
));
}
// Check if invoice has pay limit, if yes see if the date exceeded it
if ($row->pay_status == 'unpaid' && $row->pay_limit > 0) {
if (mktime(0, 0, 0, date('m'), date('d'), date('Y')) > $row->created + 86400 * $row->pay_limit) {
$pay_status = _invoice_get_icon('bullet_red', NULL, array(
'title' => t('Overtime'),
));
}
}
// Set user actions
$actions = '';
if (_invoice_user_has_admin_access_to_invoice($row->iid)) {
$deleteIcon = '';
if ($row->pay_status != 'paid') {
$deleteIcon = _invoice_get_icon('delete', 'node/' . $row->nid . '/delete', array(
'title' => t('Delete'),
));
}
$editIcon = _invoice_get_icon('edit', 'node/' . $row->nid . '/edit', array(
'title' => t('Edit'),
));
$actions = $editIcon . $deleteIcon;
}
// Set admin actions
if (user_access('administer invoices')) {
$setPaidIcon = '';
if ($row->pay_status != 'paid') {
$setPaidIcon = _invoice_get_icon('accept', 'invoice/set/pay_status/' . $row->iid . '/paid/' . _invoice_getvars_array_to_string($_GET), array(
'title' => t('Set paid'),
));
}
$setUnpaidIcon = '';
if ($row->pay_status == 'paid') {
$setUnpaidIcon = _invoice_get_icon('coins_delete', 'invoice/set/pay_status/' . $row->iid . '/unpaid/' . _invoice_getvars_array_to_string($_GET), array(
'title' => t('Set unpaid'),
));
}
$actions .= $setPaidIcon . $setUnpaidIcon;
}
// Set customer
if (!empty($row->company_name)) {
$customer = _invoice_get_icon('building') . ' ' . check_plain($row->company_name);
}
else {
$customer = _invoice_get_icon('user') . ' ' . check_plain($row->lastname) . (!empty($row->firstname) ? ', ' . check_plain($row->firstname) : '');
}
// Add row
$rows[] = array(
'invoices.iid' => l(_invoice_get_formatted_invoice_number($row->iid, NULL, $row->created), 'node/' . $row->nid),
'customer' => $customer,
'extotal' => _invoice_round_and_format_money($a_totals['extotal'], 2),
'inctotal' => _invoice_round_and_format_money($a_totals['inctotal'], 2),
'invoices.created' => $created,
'ii.status' => $pay_status,
'actions' => $actions,
);
}
$variables = array(
'header' => $header,
'rows' => $rows,
);
$content .= theme('table', $variables);
$content .= theme('pager');
return $content;
}
/**
* Implements hook_validate()
*/
function invoice_validate($node, $form, &$form_state) {
if ($node->op != t('Delete')) {
// Count invoice items
$count = db_query("SELECT COUNT(*) FROM {invoice_items} WHERE uid = :uid AND invoice_id = :invoice_id", array(
'uid' => $GLOBALS['user']->uid,
'invoice_id' => $node->invoice_number,
))
->fetchField();
// Display an error if there are no invoice items
if ($count == 0) {
form_set_error('description', t('You have to fill in at least one invoice item!'));
}
if (empty($node->company_name) && empty($node->lastname)) {
form_set_error('company_name', t('Company name and lastname may not both be empty!'));
}
}
if ($node->op == t('Save')) {
$possible_new_invoice_number = _invoice_get_new_invoice_number(true);
// If user defined invoice number is higher than the new possible invoice number,
// use the defined invoice number as the new invoice number
if (isset($node->user_defined_invoice_number) && intval($node->user_defined_invoice_number) > 0) {
if ($node->user_defined_invoice_number <= $possible_new_invoice_number) {
form_set_error('user_defined_invoice_number', t('The user defined invoice number is not greater than the latest invoice number "@invoice_number"!', array(
'@invoice_number' => $possible_new_invoice_number,
)));
}
}
}
}
/**
* Implements hook_insert()
*/
function invoice_insert($node) {
// Set user ID
$uid = $GLOBALS['user']->uid;
// Set template ID
$tid = db_query("SELECT tid FROM {invoice_templates} WHERE name = :name", array(
':name' => $node->template,
))
->fetchField();
// Set pay status
$pay_status = isset($node->pay_status) && 'paid' == $node->pay_status ? 'paid' : 'unpaid';
// Create invoice
db_insert('invoice_invoices')
->fields(array(
'iid' => $node->invoice_number,
'nid' => $node->nid,
'leading_zeros' => empty($node->invoice_invoice_number_zerofill) ? variable_get('invoice_invoice_number_zerofill', 0) : $node->invoice_invoice_number_zerofill,
'prefix' => empty($node->invoice_invoice_number_prefix) ? variable_get('invoice_invoice_number_prefix', NULL) : $node->invoice_invoice_number_prefix,
'description' => $node->invoice_description,
'tid' => $tid,
'pay_limit' => $node->pay_limit,
'pay_status' => $pay_status,
'uid' => $uid,
))
->execute();
db_insert('invoice_customers')
->fields(array(
'customer_number' => $node->customer_number,
'company_name' => $node->company_name,
'firstname' => $node->firstname,
'lastname' => $node->lastname,
'street' => $node->street,
'building_number' => $node->building_number,
'zipcode' => $node->zipcode,
'city' => $node->city,
'state' => $node->state,
'country' => $node->country,
'coc_number' => $node->coc_number,
'vat_number' => $node->vat_number,
'description' => $node->customer_description,
'invoice_id' => $node->invoice_number,
))
->execute();
if (isset($GLOBALS['invoice_api']) && true === $GLOBALS['invoice_api']) {
if (count($node->invoice_items) > 0) {
foreach ($node->invoice_items as $item) {
// Round the price to 3 decimals
$unitcost = round($item['unitcost'], 3);
// Insert invoice item into the invoice items table
$lastInsertId = db_insert('invoice_items')
->fields(array(
'description' => $item['description'],
'vat' => (double) $item['vat'],
'quantity' => (double) $item['quantity'],
'unitcost' => (double) $unitcost,
'weight' => (int) $item['weight'],
'invoice_id' => $node->invoice_number,
'uid' => $uid,
'created' => time(),
))
->execute();
}
}
}
else {
db_update('invoice_items')
->fields(array(
'invoice_id' => $node->invoice_number,
))
->condition('uid', $uid)
->condition('invoice_id', 0)
->execute();
}
db_update('node')
->fields(array(
'promote' => 0,
))
->condition('type', 'invoice')
->condition('nid', $node->nid)
->execute();
unset($_SESSION['invoice_template']);
}
/**
* Implements hook_update()
*/
function invoice_update($node) {
$user_id = $node->uid;
// Only whith the permission "administer invoices" you are allowed to change invoices
// created by other users.
if (isset($GLOBALS['invoice_api']) && true === $GLOBALS['invoice_api'] || user_access('administer invoices')) {
$accessGranted = TRUE;
}
else {
// Make sure that this invoice belongs to this user
$count = db_query("SELECT COUNT(*) FROM {invoice_invoices} WHERE iid = :iid AND uid = :uid", array(
':iid' => $node->invoice_number,
':uid' => $user_id,
))
->fetchField();
$accessGranted = $count > 0 ? TRUE : FALSE;
}
if ($accessGranted) {
// Get template ID
$tid = db_query("SELECT tid FROM {invoice_templates} WHERE name = :name", array(
':name' => $node->template,
))
->fetchField();
// Set pay status
$pay_status = isset($node->pay_status) && 'paid' == $node->pay_status ? 'paid' : 'unpaid';
// Update invoice
db_update('invoice_invoices')
->fields(array(
'leading_zeros' => $node->invoice_invoice_number_zerofill,
'prefix' => $node->invoice_invoice_number_prefix,
'description' => $node->invoice_description,
'tid' => $tid,
'pay_limit' => $node->pay_limit,
'pay_status' => $pay_status,
'uid' => $user_id,
))
->condition('iid', $node->invoice_number)
->execute();
// Update customers
db_update('invoice_customers')
->fields(array(
'customer_number' => $node->customer_number,
'company_name' => $node->company_name,
'firstname' => $node->firstname,
'lastname' => $node->lastname,
'street' => $node->street,
'building_number' => $node->building_number,
'zipcode' => $node->zipcode,
'city' => $node->city,
'state' => $node->state,
'country' => $node->country,
'coc_number' => $node->coc_number,
'vat_number' => $node->vat_number,
'description' => $node->description,
))
->condition('invoice_id', $node->invoice_number)
->execute();
// Invoice items
if (isset($GLOBALS['invoice_api']) && true === $GLOBALS['invoice_api']) {
if (count($node->invoice_items) > 0) {
// Delete existing invoice items
db_delete('invoice_items')
->condition('invoice_id', $node->invoice_number)
->execute();
// Insert new existing invoice items
foreach ($node->invoice_items as $item) {
// Round the price to 3 decimals
$unitcost = round($item['unitcost'], 3);
// Insert invoice item into the invoice items table
$lastInsertId = db_insert('invoice_items')
->fields(array(
'description' => $item['description'],
'vat' => (double) $item['vat'],
'quantity' => (double) $item['quantity'],
'unitcost' => (double) $unitcost,
'weight' => (int) $item['weight'],
'invoice_id' => $node->invoice_number,
'uid' => $user_id,
'created' => time(),
))
->execute();
}
}
}
else {
// It's not needed to update invoice item data because they are directly updated by every AJAX call.
// However it's possible for a user whith the "administer invoices" permission to change the author
db_update('invoice_items')
->fields(array(
'uid' => $user_id,
))
->condition('invoice_id', $node->invoice_number)
->execute();
}
}
else {
drupal_set_message(t('You are not the owner of this invoice!'), 'error');
}
db_update('node')
->fields(array(
'promote' => 0,
))
->condition('type', 'invoice')
->condition('nid', $node->nid)
->execute();
unset($_SESSION['invoice_template']);
}
/**
* Implemenation of hook_delete()
*/
function invoice_delete(&$node) {
$invoice_number = db_query("SELECT iid FROM {invoice_invoices} WHERE nid = :nid", array(
':nid' => $node->nid,
))
->fetchField();
db_delete('invoice_invoices')
->condition('iid', $invoice_number)
->execute();
db_delete('invoice_customers')
->condition('invoice_id', $invoice_number)
->execute();
db_delete('invoice_items')
->condition('invoice_id', $invoice_number)
->execute();
unset($_SESSION['invoice_template']);
}
/**
* Implements hook_view()
*
* @param object $node
* @param string $view_mode
*/
function invoice_view($node, $view_mode) {
$variables = array(
'node' => $node,
'type' => 'view',
);
$body = theme('invoice_body', $variables);
$node->content['body'] = array(
'#markup' => '<div class="view">' . $body . '</div>',
'#weight' => 0,
);
$links = '<div class="invoice-links">';
$links .= _invoice_get_icon('pdf', 'invoice/pdf/' . $node->invoice['invoice_number'], array(
'width' => '30',
'height' => '30',
), 'jpg');
$links .= _invoice_get_icon('print', 'invoice/print/' . $node->invoice['invoice_number'], array(
'width' => '30',
'height' => '30',
), 'jpg');
$links .= '</div>';
$node->content['invoice_links'] = array(
'#markup' => $links,
'#weight' => 1,
);
return $node;
}
/**
* Excludes node type "invoice" from search results
*
* @param object $query
*/
function invoice_query_alter(&$query) {
$is_search = FALSE;
foreach ($query
->getTables() as $table) {
if ($table['table'] == 'search_index') {
$is_search = TRUE;
}
}
if ($is_search) {
$query
->condition('n.type', 'invoice', '<>');
}
}
/**
* List of installed locales
*/
function invoice_installed_locales() {
$locales = _invoice_get_installed_system_locales();
if (count($locales) > 0 && !empty($locales[0])) {
$content = '<ul>';
foreach ($locales as $locale) {
$content .= '<li>' . $locale . '</li>';
}
$content .= '</ul>';
}
else {
$content = 'No locales found. But maybe the "locale -a" command is not supported on your server.';
}
return $content;
}
/**
* Set the status of an invoice to paid
*/
function invoice_set_pay_status($invoice_number, $status) {
if ($status != 'paid' && $status != 'unpaid') {
drupal_set_message(t('Invalid invoice pay status'), 'error');
}
else {
db_update('invoice_invoices')
->fields(array(
'pay_status' => $status,
))
->condition('iid', $invoice_number)
->execute();
if ($status == 'unpaid') {
drupal_set_message(t('Succesfully changed pay status of invoice @invoice_number to "unpaid"', array(
'@invoice_number' => $invoice_number,
)));
}
else {
drupal_set_message(t('Succesfully changed pay status of invoice @invoice_number to "paid"', array(
'@invoice_number' => $invoice_number,
)));
}
}
$exp = explode('?', $_GET['q']);
$query_string = '?q=&' . $exp[1];
$a_query_vars = _invoice_getvars_string_to_array($query_string);
drupal_goto('invoices', $a_query_vars);
}
/**
* Display the invoice in HTML print format
*/
function invoice_view_print($invoice_number) {
echo _invoice_get_html($invoice_number);
}
/**
* Display the invoice in PDF format
*/
function invoice_view_pdf($invoice_number) {
$nid = db_query("SELECT nid FROM {invoice_invoices} WHERE iid = :iid", array(
':iid' => $invoice_number,
))
->fetchField();
// include the dompdf library
define('DOMPDF_ENABLE_PHP', TRUE);
// Allow PHP code
define('DOMPDF_ENABLE_REMOTE', TRUE);
// Allow images defined by URL
$error = _invoice_dompdf_include_lib();
if ($error) {
drupal_goto('node/' . $nid);
}
else {
$node = node_load($nid);
$html = _invoice_get_html($invoice_number, $node, 'pdf');
$dompdf = new DOMPDF();
$dompdf
->load_html($html);
// @todo: Make configurable
$dompdf
->set_paper('A4');
$dompdf
->render();
$dompdf
->stream(t('invoice') . '-' . $node->invoice['formatted_invoice_number'] . ".pdf", array(
'Attachment' => 1,
));
exit;
}
}
/**
* Theme function for displaying the invoice
*/
function theme_invoice_body($variables) {
$node = $variables['node'];
$type = $variables['type'];
$content = '<div class="invoice-wrapper">';
require_once dirname(__FILE__) . '/templates/' . $node->invoice['template'] . '.inc';
drupal_add_css(drupal_get_path('module', 'invoice') . '/templates/' . $node->invoice['template'] . '.css');
$content_function = '_invoice_' . $node->invoice['template'] . '_get_template_output';
$content .= $content_function($node, $type);
$content .= '</div>';
return $content;
}
/**
* Add extra markup info to a markup form field
*
* @param string $s_field_name
* @param mixed $value
* @param string $s_title
*/
function theme_invoice_markup($s_field_name, $value, $s_title) {
$markup_before = '<div id="edit-' . $s_field_name . '-wrapper" class="form-item">
<label for="edit-' . $s_field_name . '">' . $s_title . ':</label><div>';
$markup_after = '</div></div>';
return $markup_before . $value . $markup_after;
}
Functions
Name | Description |
---|---|
invoice_api_access_handler | API access handler |
invoice_delete | Implemenation of hook_delete() |
invoice_init | Implements hook_init() |
invoice_insert | Implements hook_insert() |
invoice_installed_locales | List of installed locales |
invoice_invoices | Overview of all invoices |
invoice_load | Implements hook_load() |
invoice_menu | Implements hook_menu() |
invoice_node_access | Implements hook_node_access() |
invoice_node_info | Implements hook_node_info() |
invoice_node_presave | Implements hook_node_presave() |
invoice_permission | Implements hook_perm() |
invoice_query_alter | Excludes node type "invoice" from search results |
invoice_settings | Invoice settings |
invoice_settings_form_submit | Submit function for the settings form |
invoice_set_pay_status | Set the status of an invoice to paid |
invoice_theme | Implements hook_theme() |
invoice_update | Implements hook_update() |
invoice_user_access_handler | User access handler to support multiple permissions for a menu item |
invoice_validate | Implements hook_validate() |
invoice_view | Implements hook_view() |
invoice_view_pdf | Display the invoice in PDF format |
invoice_view_print | Display the invoice in HTML print format |
theme_invoice_body | Theme function for displaying the invoice |
theme_invoice_markup | Add extra markup info to a markup form field |