Unverified Commit 9578151c authored by Monish Deb's avatar Monish Deb Committed by GitHub
Browse files

Merge pull request #20 from monishdeb/issue-6

Add ability to add line items to line item edit
parents 454855fe 3ac52ac7
<?php
require_once 'CRM/Core/Form.php';
/**
* Form controller class
*
* @see http://wiki.civicrm.org/confluence/display/CRMDOC43/QuickForm+Reference
*/
class CRM_Lineitemedit_Form_Add extends CRM_Core_Form {
protected $_contributionID;
protected $_isQuickConfig;
protected $_fieldNames;
public function preProcess() {
$this->_contributionID = CRM_Utils_Request::retrieve('contribution_id', 'Positive', $this);
$this->_fieldNames = CRM_Lineitemedit_Util::getLineitemFieldNames(TRUE);
}
/**
* Set default values.
*
* @return array
*/
public function setDefaultValues() {
return array(
'line_total' => 0.00,
'tax_amount' => 0.00,
);
}
public function buildQuickForm() {
foreach ($this->_fieldNames as $fieldName) {
if ($fieldName == 'line_total') {
$this->add('text', 'line_total', ts('Total amount'), array(
'size' => 6,
'maxlength' => 14,
'readonly' => TRUE)
);
continue;
}
elseif ($fieldName == 'price_field_value_id') {
$options = array('' => '- select any price-field -') + CRM_Lineitemedit_Util::getPriceFieldLists($this->_contributionID);
$this->add('select', $fieldName, ts('Price Field'), $options, TRUE);
continue;
}
$properties = array(
'entity' => 'LineItem',
'name' => $fieldName,
'context' => 'add',
'action' => 'create',
);
if ($fieldName == 'financial_type_id') {
CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes);
$properties['options'] = $financialTypes;
}
elseif ($fieldName == 'tax_amount') {
$properties['readonly'] = TRUE;
}
$this->addField($fieldName, $properties, TRUE);
}
$this->assign('fieldNames', $this->_fieldNames);
$this->assign('currency', CRM_Core_DAO::getFieldValue(
'CRM_Financial_DAO_Currency',
CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $this->_contributionID, 'currency'),
'symbol',
'name'
));
$this->assign('taxRates', json_encode(CRM_Core_PseudoConstant::getTaxRates()));
$this->addFormRule(array(__CLASS__, 'formRule'), $this);
$this->addButtons(array(
array(
'type' => 'submit',
'name' => ts('Add Item'),
'isDefault' => TRUE,
),
array(
'type' => 'cancel',
'name' => ts('Close'),
),
));
parent::buildQuickForm();
}
public static function formRule($fields, $files, $self) {
$errors = array();
$canChangeQuantity = CRM_Lineitemedit_Util::isPriceFieldSupportQtyChange($fields['price_field_value_id']);
if ($fields['qty'] == 0) {
$errors['qty'] = ts('Line quantity cannot be zero');
}
if ($canChangeQuantity) {
$unitPrice = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $fields['price_field_value_id'], 'amount');
if ($unitPrice != $fields['unit_price']) {
$errors['unit_price'] = ts('You cannot change unit price of this text price field.');
}
}
else {
if (!CRM_Utils_Rule::integer($fields['qty'])) {
$errors['qty'] = ts('Please enter a whole number quantity.');
}
}
return $errors;
}
public function postProcess() {
$params = $this->exportValues();
$this->submit($params);
}
public function submit($values) {
// check for any cancelled line item which was recorded for same price field,
// if found then use its ID update it rather then creating a new line item as
// civicrm doesn't allow multiple line item registered against same
// contribution and price field ID
$previousLineItem = civicrm_api3('LineItem', 'get', array(
'contribution_id' => $this->_contributionID,
'price_field_value_id' => $values['price_field_value_id'],
));
$entityId = NULL;
if (!empty($previousLineItem['values'])) {
$entityId = $previousLineItem['values'][$previousLineItem['id']]['entity_id'];
}
list($entityTable, $entityID) = CRM_Lineitemedit_Util::addEntity(
$values['price_field_value_id'],
$this->_contributionID,
$values['qty'],
$entityId
);
$newLineItemParams = array(
'entity_table' => $entityTable,
'entity_id' => $entityID,
'contribution_id' => $this->_contributionID,
'price_field_id' => CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $values['price_field_value_id'], 'price_field_id'),
'label' => $values['label'],
'qty' => $values['qty'],
'unit_price' => CRM_Utils_Rule::cleanMoney($values['unit_price']),
'line_total' => CRM_Utils_Rule::cleanMoney($values['line_total']),
'price_field_value_id' => $values['price_field_value_id'],
'financial_type_id' => $values['financial_type_id'],
);
if (!empty($previousLineItem['id'])) {
$newLineItemParams['id'] = $previousLineItem['id'];
if ($entityTable == 'civicrm_participant') {
$newLineItemParams['label'] = $previousLineItem['label'];
}
}
$newLineItem = civicrm_api3('LineItem', 'create', $newLineItemParams);
// calculate balance, tax and paidamount later used to adjust transaction
$updatedAmount = CRM_Price_BAO_LineItem::getLineTotal($this->_contributionID);
$taxAmount = CRM_Lineitemedit_Util::getTaxAmountTotalFromContributionID($this->_contributionID);
// Record adjusted amount by updating contribution info and create necessary financial trxns
$trxn = CRM_Lineitemedit_Util::recordAdjustedAmt(
$updatedAmount,
$this->_contributionID,
$taxAmount
);
$lineItem = civicrm_api3('LineItem', 'getsingle', array('id' => $newLineItem['id']));
// record financial item on addition of lineitem
if ($trxn) {
CRM_Lineitemedit_Util::insertFinancialItemOnAdd($lineItem, $trxn);
}
elseif ($newLineItemParams['line_total'] == 0) {
$trxn = CRM_Lineitemedit_Util::createFinancialTrxnEntry($this->_contributionID, $newLineItemParams['line_total']);
CRM_Lineitemedit_Util::insertFinancialItemOnAdd($lineItem, $trxn);
}
CRM_Core_Session::singleton()->pushUserContext(CRM_Utils_System::url(CRM_Utils_System::currentPath()));
if ($entityTable == 'civicrm_membership') {
$contactId = CRM_Core_DAO::getFieldValue('CRM_Contribute_BAO_Contribution',
$this->_contributionID,
'contact_id'
);
$this->ajaxResponse['updateTabs']['#tab_member'] = CRM_Contact_BAO_Contact::getCountComponent('membership', $contactId);
}
}
public function testSubmit($params) {
$this->_contributionID = $params['contribution_id'];
$this->_fieldNames = CRM_Lineitemedit_Util::getLineitemFieldNames(TRUE);
$this->submit($params);
}
}
<?php
/**
* Collection of upgrade steps.
*/
class CRM_Lineitemedit_Upgrader extends CRM_Lineitemedit_Upgrader_Base {
/**
* Example: Run an external SQL script when the module is installed.
*
*/
public function install() {
CRM_Lineitemedit_Util::generatePriceField();
}
/**
* Example: Run a couple simple queries.
*
* @return TRUE on success
* @throws Exception
*
*/
public function upgrade_2000() {
$this->ctx->log->info('Applying update 2000');
CRM_Lineitemedit_Util::generatePriceField();
return TRUE;
}
}
<?php
// AUTO-GENERATED FILE -- Civix may overwrite any changes made to this file
/**
* Base class which provides helpers to execute upgrade logic
*/
class CRM_Lineitemedit_Upgrader_Base {
/**
* @var varies, subclass of ttis
*/
static $instance;
/**
* @var CRM_Queue_TaskContext
*/
protected $ctx;
/**
* @var string, eg 'com.example.myextension'
*/
protected $extensionName;
/**
* @var string, full path to the extension's source tree
*/
protected $extensionDir;
/**
* @var array(revisionNumber) sorted numerically
*/
private $revisions;
/**
* Obtain a reference to the active upgrade handler.
*/
static public function instance() {
if (! self::$instance) {
// FIXME auto-generate
self::$instance = new CRM_Lineitemedit_Upgrader(
'biz.jmaconsulting.lineitemedit',
realpath(__DIR__ .'/../../../')
);
}
return self::$instance;
}
/**
* Adapter that lets you add normal (non-static) member functions to the queue.
*
* Note: Each upgrader instance should only be associated with one
* task-context; otherwise, this will be non-reentrant.
*
* @code
* CRM_Lineitemedit_Upgrader_Base::_queueAdapter($ctx, 'methodName', 'arg1', 'arg2');
* @endcode
*/
static public function _queueAdapter() {
$instance = self::instance();
$args = func_get_args();
$instance->ctx = array_shift($args);
$instance->queue = $instance->ctx->queue;
$method = array_shift($args);
return call_user_func_array(array($instance, $method), $args);
}
public function __construct($extensionName, $extensionDir) {
$this->extensionName = $extensionName;
$this->extensionDir = $extensionDir;
}
// ******** Task helpers ********
/**
* Run a CustomData file.
*
* @param string $relativePath the CustomData XML file path (relative to this extension's dir)
* @return bool
*/
public function executeCustomDataFile($relativePath) {
$xml_file = $this->extensionDir . '/' . $relativePath;
return $this->executeCustomDataFileByAbsPath($xml_file);
}
/**
* Run a CustomData file
*
* @param string $xml_file the CustomData XML file path (absolute path)
*
* @return bool
*/
protected static function executeCustomDataFileByAbsPath($xml_file) {
require_once 'CRM/Utils/Migrate/Import.php';
$import = new CRM_Utils_Migrate_Import();
$import->run($xml_file);
return TRUE;
}
/**
* Run a SQL file.
*
* @param string $relativePath the SQL file path (relative to this extension's dir)
*
* @return bool
*/
public function executeSqlFile($relativePath) {
CRM_Utils_File::sourceSQLFile(
CIVICRM_DSN,
$this->extensionDir . '/' . $relativePath
);
return TRUE;
}
/**
* Run one SQL query.
*
* This is just a wrapper for CRM_Core_DAO::executeSql, but it
* provides syntatic sugar for queueing several tasks that
* run different queries
*/
public function executeSql($query, $params = array()) {
// FIXME verify that we raise an exception on error
CRM_Core_DAO::executeSql($query, $params);
return TRUE;
}
/**
* Syntatic sugar for enqueuing a task which calls a function in this class.
*
* The task is weighted so that it is processed
* as part of the currently-pending revision.
*
* After passing the $funcName, you can also pass parameters that will go to
* the function. Note that all params must be serializable.
*/
public function addTask($title) {
$args = func_get_args();
$title = array_shift($args);
$task = new CRM_Queue_Task(
array(get_class($this), '_queueAdapter'),
$args,
$title
);
return $this->queue->createItem($task, array('weight' => -1));
}
// ******** Revision-tracking helpers ********
/**
* Determine if there are any pending revisions.
*
* @return bool
*/
public function hasPendingRevisions() {
$revisions = $this->getRevisions();
$currentRevision = $this->getCurrentRevision();
if (empty($revisions)) {
return FALSE;
}
if (empty($currentRevision)) {
return TRUE;
}
return ($currentRevision < max($revisions));
}
/**
* Add any pending revisions to the queue.
*/
public function enqueuePendingRevisions(CRM_Queue_Queue $queue) {
$this->queue = $queue;
$currentRevision = $this->getCurrentRevision();
foreach ($this->getRevisions() as $revision) {
if ($revision > $currentRevision) {
$title = ts('Upgrade %1 to revision %2', array(
1 => $this->extensionName,
2 => $revision,
));
// note: don't use addTask() because it sets weight=-1
$task = new CRM_Queue_Task(
array(get_class($this), '_queueAdapter'),
array('upgrade_' . $revision),
$title
);
$this->queue->createItem($task);
$task = new CRM_Queue_Task(
array(get_class($this), '_queueAdapter'),
array('setCurrentRevision', $revision),
$title
);
$this->queue->createItem($task);
}
}
}
/**
* Get a list of revisions.
*
* @return array(revisionNumbers) sorted numerically
*/
public function getRevisions() {
if (! is_array($this->revisions)) {
$this->revisions = array();
$clazz = new ReflectionClass(get_class($this));
$methods = $clazz->getMethods();
foreach ($methods as $method) {
if (preg_match('/^upgrade_(.*)/', $method->name, $matches)) {
$this->revisions[] = $matches[1];
}
}
sort($this->revisions, SORT_NUMERIC);
}
return $this->revisions;
}
public function getCurrentRevision() {
// return CRM_Core_BAO_Extension::getSchemaVersion($this->extensionName);
$key = $this->extensionName . ':version';
return CRM_Core_BAO_Setting::getItem('Extension', $key);
}
public function setCurrentRevision($revision) {
// We call this during hook_civicrm_install, but the underlying SQL
// UPDATE fails because the extension record hasn't been INSERTed yet.
// Instead, track revisions in our own namespace.
// CRM_Core_BAO_Extension::setSchemaVersion($this->extensionName, $revision);
$key = $this->extensionName . ':version';
CRM_Core_BAO_Setting::setItem($revision, 'Extension', $key);
return TRUE;
}
// ******** Hook delegates ********
public function onInstall() {
$files = glob($this->extensionDir . '/sql/*_install.sql');
if (is_array($files)) {
foreach ($files as $file) {
CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file);
}
}
$files = glob($this->extensionDir . '/xml/*_install.xml');
if (is_array($files)) {
foreach ($files as $file) {
$this->executeCustomDataFileByAbsPath($file);
}
}
if (is_callable(array($this, 'install'))) {
$this->install();
}
$revisions = $this->getRevisions();
if (!empty($revisions)) {
$this->setCurrentRevision(max($revisions));
}
}
public function onUninstall() {
if (is_callable(array($this, 'uninstall'))) {
$this->uninstall();
}
$files = glob($this->extensionDir . '/sql/*_uninstall.sql');
if (is_array($files)) {
foreach ($files as $file) {
CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file);
}
}
$this->setCurrentRevision(NULL);
}
public function onEnable() {
// stub for possible future use
if (is_callable(array($this, 'enable'))) {
$this->enable();
}
}
public function onDisable() {
// stub for possible future use
if (is_callable(array($this, 'disable'))) {
$this->disable();
}
}
public function onUpgrade($op, CRM_Queue_Queue $queue = NULL) {
switch ($op) {
case 'check':
return array($this->hasPendingRevisions());
case 'enqueue':
return $this->enqueuePendingRevisions($queue);
default:
}
}
}
......@@ -48,23 +48,23 @@ class CRM_Lineitemedit_Util {
$permissions[] = CRM_Core_Permission::DELETE;
}
$mask = CRM_Core_Action::mask($permissions);
foreach ($lineItems as $key => $lineItem) {
$actions = array(
'id' => $lineItem['id'],
);
$actionLinks = $links;
if ($lineItem['qty'] == 0) {
if ($lineItem['qty'] == 0 || $lineItem['line_total'] == 0) {
unset($actionLinks[CRM_Core_Action::DELETE]);
}
$lineItemTable['rows'][$key] = array(
'id' => $lineItem['id'],
'item' => $lineItem['label'],
'item' => CRM_Utils_Array::value('label', $lineItem),
'financial_type' => CRM_Core_PseudoConstant::getLabel('CRM_Contribute_BAO_Contribution', 'financial_type_id', $lineItem['financial_type_id']),
'qty' => $lineItem['qty'],
'unit_price' => $lineItem['unit_price'],
'total_price' => $lineItem['line_total'],
'tax_amount' => CRM_Utils_Array::value('tax_amount', $lineItem, 0.00),
'currency' => $order['currency'],
'actions' => CRM_Core_Action::formLink($actionLinks, $mask, $actions),
);
......@@ -439,10 +439,11 @@ ORDER BY ps.id, pf.weight ;
* @param int $contributionId
* @param money $taxAmount
* @param bool $createTrxn
* @param bool $return
*
* @return bool|\CRM_Core_BAO_FinancialTrxn
*/
public static function recordAdjustedAmt($updatedAmount, $contributionId, $taxAmount = NULL, $createTrxn = TRUE) {
public static function recordAdjustedAmt($updatedAmount, $contributionId, $taxAmount = NULL, $createTrxn = TRUE, $return = FALSE) {
$contribution = civicrm_api3('Contribution', 'getsingle', array(
'return' => array("total_amount"),
'id' => $contributionId,
......@@ -486,11 +487,11 @@ ORDER BY ps.id, pf.weight ;
// update contribution status and total amount without trigger financial code
// as this is handled in current BAO function used for change selection
$updatedContributionDAO->id = $contributionId;
$contriParams = ['id' => $contributionId];
if (!$skip) {
$updatedContributionDAO->contribution_status_id = array_search($contributionStatusVal, $contributionStatuses);
$contriParams['contribution_status_id'] = array_search($contributionStatusVal, $contributionStatuses);
if ($contributionStatusVal == 'Pending') {
$updatedContributionDAO->is_pay_later = TRUE;
$contriParams['is_pay_later'] = TRUE;
}
}
$updatedContribution = civicrm_api3(
......@@ -501,11 +502,12 @@ ORDER BY ps.id, pf.weight ;
'return' => array('fee_amount'),
)
);
$updatedContributionDAO->total_amount = $updatedAmount;
$updatedContributionDAO->net_amount = $updatedAmount - CRM_Utils_Array::value('fee_amount', $updatedContribution, 0);
$contriParams['total_amount'] = $updatedAmount;
$contriParams['net_amount'] = $updatedAmount - CRM_Utils_Array::value('fee_amount', $updatedContribution, 0);
if ($taxAmount) {
$updatedContributionDAO->tax_amount = $taxAmount;
$contriParams['tax_amount'] = $taxAmount;
}
$updatedContributionDAO->copyValues($contriParams);
$updatedContributionDAO->save();
if (!$createTrxn) {
......@@ -514,6 +516,10 @@ ORDER BY ps.id, pf.weight ;
$adjustedTrxn = self::createFinancialTrxnEntry($contributionId, $balanceAmt);
}
if ($return) {
return [$adjustedTrxn, $contriParams];
}
return $adjustedTrxn;