Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BTHA-188: Fix quotes to order feature issues #1038

Merged
merged 5 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<?php

use Civi\Api4\PriceField;
use Civi\Api4\PriceFieldValue;
use Civi\Api4\PriceSet;
use CRM_Civicase_ExtensionUtil as E;
use CRM_Civicase_Service_CaseSalesOrderLineItemsGenerator as salesOrderlineItemGenerator;

Expand Down Expand Up @@ -27,6 +30,14 @@ public function run(CRM_Core_Form &$form, $formName) {
}
$lineItemGenerator = new salesOrderlineItemGenerator($salesOrderId, $toBeInvoiced, $percentValue);
$lineItems = $lineItemGenerator->generateLineItems();
$priceField = $this->getDefaultPriceSetFields();
\Civi::cache('short')->set('sales_order_line_items', $lineItems);

$submittedValues = [];
foreach ($lineItems as $index => &$lineItem) {
$submittedValues[] = $priceField[$index]['id'];
}
$form->assign('lineItemSubmitted', json_encode($submittedValues));

CRM_Core_Resources::singleton()
->addScriptFile(E::LONG_NAME, 'js/sales-order-contribution.js')
Expand Down Expand Up @@ -61,4 +72,25 @@ public function shouldRun(CRM_Core_Form $form, string $formName, ?int $salesOrde
&& !empty($salesOrderId);
}

/**
* Returns default contribution price set fields.
*
* @return array
* Array of price fields
*/
private function getDefaultPriceSetFields(): array {
$priceSet = PriceSet::get(FALSE)
->addWhere('name', '=', 'default_contribution_amount')
->addWhere('is_quick_config', '=', 1)
->execute()
->first();

return PriceField::get(FALSE)
->addWhere('price_set_id', '=', $priceSet['id'])
->addChain('price_field_value', PriceFieldValue::get(FALSE)
->addWhere('price_field_id', '=', '$id')
)->execute()
->getArrayCopy();
}

}
125 changes: 125 additions & 0 deletions CRM/Civicase/Hook/alterContent/AddSalesOrderLineToContribution.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

/**
* Adds sales order line items to the contribution.
*/
class CRM_Civicase_Hook_alterContent_AddSalesOrderLineToContribution {

/**
* Stores the sales order line items retrieved from the cache.
*
* @var array
*/
private $salesOrderLineItems;

/**
* Constructs the AddSalesOrderLindeToContribution class.
*
* @param string $content
* The content to be altered.
* @param array $context
* The context for the hook.
* @param string $tplName
* The name of the template.
*/
public function __construct(private &$content, private $context, private $tplName) {
$this->salesOrderLineItems = \Civi::cache('short')->get('sales_order_line_items');
}

/**
* Add sales order line items to the contribution.
*/
public function run() {
if (!$this->shouldRun()) {
return;
}

$this->addLineItems();
}

/**
* Adds sales order line items to the contribution.
*
* This method retrieves the sales order line items from the cache, and then
* updates the corresponding input fields in the contribution
* page's HTML content.
*/
public function addLineItems() {
$dom = new DomDocument();
$dom->loadHTML($this->content);

$table = $dom->getElementById('info');

// Delete the first row (index 0)
if ($table) {
$firstRow = $table->getElementsByTagName('tr')->item(0);
if ($firstRow) {
$table->removeChild($firstRow);
}
}

$rows = $table->getElementsByTagName('tr');
// Set the values in the DOM.
foreach ($this->salesOrderLineItems as $index => $item) {
if ($index < $rows->length) {
// Get the current row.
$row = $rows->item($index);

// Remove 'hiddenElement' class if it exists.
$row->setAttribute('class', str_replace('hiddenElement', '', $row->getAttribute('class')));

// Set the values from the line item array.
$inputs = $row->getElementsByTagName('input');
$selects = $row->getElementsByTagName('select');

foreach ($inputs as $input) {
$name = $input->getAttribute('name');

if (strpos($name, 'qty') !== FALSE) {
$input->setAttribute('value', $item['qty']);
}
elseif (strpos($name, 'tax_amount') !== FALSE) {
$input->setAttribute('value', $item['tax_amount']);
}
elseif (strpos($name, 'line_total') !== FALSE) {
$input->setAttribute('value', $item['line_total']);
}
elseif (strpos($name, 'unit_price') !== FALSE) {
$input->setAttribute('value', $item['unit_price']);
}
elseif (strpos($name, 'label') !== FALSE) {
$input->setAttribute('value', $item['label']);
}
}

foreach ($selects as $select) {
$name = $select->getAttribute('name');

if (strpos($name, 'financial_type_id') !== FALSE) {
foreach ($select->getElementsByTagName('option') as $option) {
if ($option->getAttribute('value') == $item['financial_type_id']) {
$option->setAttribute('selected', 'selected');
break;
}
}
}
}
}
}

\Civi::cache('short')->delete('sales_order_line_items');

$this->content = $dom->saveHTML();
}

/**
* Determines whether the hook should run.
*
* @return bool
* TRUE if the hook should run, FALSE otherwise.
*/
public function shouldRun() {
return $this->tplName === "CRM/Contribute/Page/Tab.tpl" && $this->context == "page" && !empty($this->salesOrderLineItems);
}

}
2 changes: 1 addition & 1 deletion CRM/Civicase/Service/CaseSalesOrderLineItemsGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ private function lineItemToContributionLineItem(array $item) {
'entity_table' => 'civicrm_contribution',
'financial_type_id' => $item['financial_type_id'],
'line_total' => round($item['total'], 2),
'unit_price' => round($item['unit_price'], 2),
'unit_price' => $item['unit_price'],
];
}

Expand Down
5 changes: 4 additions & 1 deletion CRM/Civicase/Service/CaseSalesOrderOpportunityCalculator.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ private function calculateOpportunityFinancialAmount($contributions) {
private function getContributions($caseId) {
$contributions = Contribution::get(FALSE)
->addSelect('*', 'Opportunity_Details.Quotation')
->addWhere('Opportunity_Details.Case_Opportunity', '=', $caseId);
->addWhere('Opportunity_Details.Case_Opportunity', '=', $caseId)
->addJoin('CaseSalesOrder AS case_sales_order', 'INNER',
['Opportunity_Details.Quotation', '=', 'case_sales_order.id']
);

if ($this->deletingContributionId !== NULL) {
$contributions->addWhere('id', '!=', $this->deletingContributionId);
Expand Down
13 changes: 13 additions & 0 deletions civicase.php
Original file line number Diff line number Diff line change
Expand Up @@ -632,3 +632,16 @@ function civicase_civicrm_searchTasks(string $objectName, array &$tasks) {
}
}
}

/**
* Implements hook_civicrm_alterContent().
*/
function civicase_civicrm_alterContent(&$content, $context, $tplName, &$object) {
$hooks = [
new CRM_Civicase_Hook_alterContent_AddSalesOrderLineToContribution($content, $context, $tplName),
];

foreach ($hooks as $hook) {
$hook->run();
}
}
12 changes: 12 additions & 0 deletions js/contribution-entityref-field.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@
if (field.value) {
$(`[name^=${field.name}_]`).val(field.value).trigger('change');
}

$(`[name^=${field.name}_]`).on('change', field, function (event) {
const f = event.data;

$(`[name^=${f.name}_]`)
.attr('placeholder', f.placeholder)
.attr('disabled', false)
.crmEntityRef({
entity: f.entity,
create: false
});
});
});
});
});
Expand Down
52 changes: 14 additions & 38 deletions js/sales-order-contribution.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
});
};

$(document).one('crmLoad', function () {
$(document).one('crmLoad', async function () {
const params = CRM.vars['uk.co.compucorp.civicase'];
const salesOrderId = params.sales_order;
const salesOrderStatusId = params.sales_order_status_id;
Expand All @@ -16,7 +16,6 @@
const lineItems = JSON.parse(params.line_items);
const caseCustomField = params.case_custom_field;
const quotationCustomField = params.quotation_custom_field;
let count = 0;

const apiRequest = {};
apiRequest.caseSalesOrders = ['CaseSalesOrder', 'get', {
Expand All @@ -29,56 +28,33 @@
}];

if (Array.isArray(lineItems)) {
CRM.$.blockUI();
CRM.$('form#Contribution').css('visibility', 'hidden');
await new Promise(resolve => setTimeout(resolve, 1000));
CRM.api4(apiRequest).then(function (batch) {
const caseSalesOrder = batch.caseSalesOrders[0];

lineItems.forEach(lineItem => {
addLineItem(lineItem.qty, lineItem.unit_price, lineItem.label, lineItem.financial_type_id, lineItem.tax_amount);
});

$('#total_amount').val(0);
$('#lineitem-add-block').show().removeClass('hiddenElement');
$('#contribution_status_id').val(batch.optionValues[0].value);
$('#source').val(`Quotation ${caseSalesOrder.id}`).trigger('change');
$('#contact_id').select2('val', caseSalesOrder.client_id).trigger('change');
$('input[id="total_amount"]', 'form.CRM_Contribute_Form_Contribution').trigger('change');
$(`<input type="hidden" value="${salesOrderId}" name="sales_order" />`).insertBefore('#source');
$(`<input type="hidden" value="${toBeInvoiced}" name="to_be_invoiced" />`).insertBefore('#source');
$(`<input type="hidden" value="${percentValue}" name="percent_value" />`).insertBefore('#source');
$(`<input type="hidden" value="${salesOrderStatusId}" name="sales_order_status_id" />`).insertBefore('#source');
$('#totalAmount, #totalAmountORaddLineitem, #totalAmountORPriceSet, #price_set_id, #choose-manual').hide();
$(' #totalAmountORaddLineitem, #totalAmountORPriceSet, #price_set_id, #choose-manual').hide();

if ($('#customData')) {
CRM.$(`[name^=${caseCustomField}_]`).val(caseSalesOrder.case_id).trigger('change');
CRM.$(`[name^=${quotationCustomField}_]`).val(caseSalesOrder.id).trigger('change');
}
waitForElement($, '#customData', function ($, elem) {
$(`[name^=${caseCustomField}_]`).val(caseSalesOrder.case_id).trigger('change');
$(`[name^=${quotationCustomField}_]`).val(caseSalesOrder.id).trigger('change');
CRM.$(`[name^=${caseCustomField}_]`).val(caseSalesOrder.case_id).trigger('change');
CRM.$(`[name^=${quotationCustomField}_]`).val(caseSalesOrder.id).trigger('change');
});
}).finally(() => {
CRM.$.unblockUI();
CRM.$('form#Contribution').css('visibility', 'visible');
});
}

/**
* @param {number} quantity Item quantity
* @param {number} unitPrice Item unit price
* @param {string} description Item description
* @param {number} financialTypeId Item financial type
* @param {number|object} taxAmount Item tax amount
*/
function addLineItem (quantity, unitPrice, description, financialTypeId, taxAmount) {
const row = $($(`tr#add-item-row-${count}`));
row.show().removeClass('hiddenElement');
quantity = +parseFloat(quantity).toFixed(10); // limit to 10 decimal places

$('input[id^="item_label"]', row).val(ts(description));
$('select[id^="item_financial_type_id"]', row).select2('val', financialTypeId);
$('input[id^="item_qty"]', row).val(quantity);

const total = quantity * parseFloat(unitPrice);

$('input[id^="item_unit_price"]', row).val(unitPrice);
$('input[id^="item_line_total"]', row).val(CRM.formatMoney(total, true));

$('input[id^="item_tax_amount"]', row).val(taxAmount);

count++;
}
});
})(CRM.$, CRM._);
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,12 @@
});
</script>
{/literal}

{literal}

<style>
#bootstrap-theme #status {
appearance: auto;
}
</style>
{/literal}
Loading