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

BTHAB-386: Add ‘product’ filter field to the ‘create contributions’ in bulk screen from quotations listing #1059

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
20 changes: 20 additions & 0 deletions CRM/Civicase/Form/CaseSalesOrderContributionCreate.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use Civi\Api4\CaseSalesOrder;
use Civi\Api4\CaseSalesOrderLine;
use Civi\Api4\Contribution;
use Civi\Api4\OptionValue;
use CRM_Certificate_ExtensionUtil as E;
Expand Down Expand Up @@ -46,6 +47,24 @@ public function buildQuickForm() {
'min' => 1,
], FALSE);

$caseSalesOrderLines = CaseSalesOrderLine::get(FALSE)
->addWhere('sales_order_id', '=', $this->id)
->execute();
$productIds = [];
foreach ($caseSalesOrderLines as $line) {
if (!empty($line['product_id'])) {
array_push($productIds, $line['product_id']);
}
}

$this->addEntityRef('products', ts('All Products'), [
'entity' => 'Product',
'placeholder' => 'All Products',
'class' => 'form-control',
'select' => ['minimumInputLength' => 0],
'api' => ['params' => ['id' => ['IN' => $productIds]]],
]);

if ($this->hasRemainingBalance()) {
$this->addElement('radio', 'to_be_invoiced', '', ts('Remaining Balance'),
self::INVOICE_REMAIN,
Expand Down Expand Up @@ -228,6 +247,7 @@ public function createContribution(array $values) {
'to_be_invoiced' => $values['to_be_invoiced'],
'percent_value' => $values['to_be_invoiced'] ==
self::INVOICE_PERCENT ? floatval($values['percent_value']) : 0,
'products' => $values['products'],
];

$url = CRM_Utils_System::url('civicrm/contribute/add', $query);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ public function run(CRM_Core_Form &$form, $formName) {
$status = CRM_Utils_Request::retrieve('sales_order_status_id', 'Integer');
$toBeInvoiced = CRM_Utils_Request::retrieve('to_be_invoiced', 'String');
$percentValue = CRM_Utils_Request::retrieve('percent_value', 'Float');
$products = CRM_Utils_Request::retrieve('products', 'String') ?? "";
$products = explode(",", $products) ?? [];

if (!$this->shouldRun($form, $formName, $salesOrderId)) {
return;
}
$lineItemGenerator = new salesOrderlineItemGenerator($salesOrderId, $toBeInvoiced, $percentValue);
$lineItemGenerator = new salesOrderlineItemGenerator($salesOrderId, $toBeInvoiced, $percentValue, $products);
$lineItems = $lineItemGenerator->generateLineItems();
$priceField = $this->getDefaultPriceSetFields();
\Civi::cache('short')->set('sales_order_line_items', $lineItems);
Expand Down
13 changes: 12 additions & 1 deletion CRM/Civicase/Service/CaseSalesOrderLineItemsGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@ class CRM_Civicase_Service_CaseSalesOrderLineItemsGenerator {
/**
* Constructs CaseSalesOrderLineItemsGenerator service.
*/
public function __construct(private int $salesOrderId, private string $type, private ?string $percentValue) {
public function __construct(
private int $salesOrderId,
private string $type,
private ?string $percentValue,
private ?array $products,
) {
$this->setSalesOrder();
$this->products = array_filter($this->products);
}

/**
Expand Down Expand Up @@ -73,6 +79,11 @@ private function getLineItemForSalesOrder() {
$item['quantity'] = ($this->type === self::INVOICE_PERCENT) ?
($this->percentValue / 100) * $item['quantity'] :
$item['quantity'];

if (!empty($this->products) && $this->type === self::INVOICE_PERCENT && !in_array($item['product_id'], $this->products)) {
continue;
}

$item['total'] = $item['quantity'] * floatval($item['unit_price']);
$item['tax'] = empty($item['tax_rate']) ? 0 : $this->percent($item['tax_rate'], $item['total']);

Expand Down
9 changes: 8 additions & 1 deletion Civi/Api4/Action/CaseSalesOrder/ContributionCreateAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ class ContributionCreateAction extends AbstractAction {
*/
protected $percentValue;

/**
* The products contribution should apply to.
*
* @var array
*/
protected $products;

/**
* Contribution Date.
*
Expand Down Expand Up @@ -106,7 +113,7 @@ private function createContribution() {
* Array of price fields.
*/
private function createContributionWithLineItems(int $salesOrderId, array $priceField): array {
$salesOrderContribution = new salesOrderlineItemGenerator($salesOrderId, $this->toBeInvoiced, $this->percentValue ?? 0);
$salesOrderContribution = new salesOrderlineItemGenerator($salesOrderId, $this->toBeInvoiced, $this->percentValue ?? 0, $this->products ?? []);
$lineItems = $salesOrderContribution->generateLineItems();

$taxAmount = $lineTotal = 0;
Expand Down
3 changes: 2 additions & 1 deletion ang/afsearchQuotations.aff.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

<div class="af-container row civicase__case-filter-form-elements" ng-show="expanded">
<af-field name="status_id" defn="{input_attrs: {multiple: true}}" class="col-md-2" />
<af-field name="DATE_quotation_date" defn="{search_range: true, time: false, input_attrs: {}, label: 'Date'}" class="col-md-4 civicase__ui-range" />
<af-field name="quotation_date" defn="{input_type: 'Select', search_range: true, time: true, options: [{id: '{}', label: 'Choose Date Range'}, {id: 'this.day', label: 'Today'}, {id: 'this.week', label: 'This week'}, {id: 'this.month', label: 'This calendar month'}, {id: 'this.quarter', label: 'This quarter'}, {id: 'this.fiscal_year', label: 'This fiscal year'}, {id: 'this.year', label: 'This calendar year'}, {id: 'previous.day', label: 'Yesterday'}, {id: 'previous.week', label: 'Previous week'}, {id: 'previous.month', label: 'Previous calendar month'}, {id: 'previous.quarter', label: 'Previous quarter'}, {id: 'previous.fiscal_year', label: 'Previous fiscal year'}, {id: 'previous.year', label: 'Previous calendar year'}, {id: 'ending.week', label: 'Last 7 days including today'}, {id: 'ending_30.day', label: 'Last 30 days including today'}, {id: 'ending_60.day', label: 'Last 60 days including today'}, {id: 'ending_90.day', label: 'Last 90 days including today'}, {id: 'ending.year', label: 'Last 12 months including today'}, {id: 'ending_2.year', label: 'Last 2 years including today'}, {id: 'ending_3.year', label: 'Last 3 years including today'}, {id: 'starting.day', label: 'Tomorrow'}, {id: 'next.week', label: 'Next week'}, {id: 'next.month', label: 'Next calendar month'}, {id: 'next.quarter', label: 'Next quarter'}, {id: 'next.fiscal_year', label: 'Next fiscal year'}, {id: 'next.year', label: 'Next calendar year'}, {id: 'starting.week', label: 'Next 7 days including today'}, {id: 'starting.month', label: 'Next 30 days including today'}, {id: 'starting_2.month', label: 'Next 60 days including today'}, {id: 'starting.quarter', label: 'Next 90 days including today'}, {id: 'starting.year', label: 'Next 12 months including today'}, {id: 'current.week', label: 'Current week to-date'}, {id: 'current.month', label: 'Current calendar month to-date'}, {id: 'current.quarter', label: 'Current quarter to-date'}, {id: 'current.year', label: 'Current calendar year to-date'}, {id: 'earlier.day', label: 'To end of yesterday'}, {id: 'earlier.week', label: 'To end of previous week'}, {id: 'earlier.month', label: 'To end of previous calendar month'}, {id: 'earlier.quarter', label: 'To end of previous quarter'}, {id: 'earlier.year', label: 'To end of previous calendar year'}, {id: 'greater.day', label: 'From start of current day'}, {id: 'greater.week', label: 'From start of current week'}, {id: 'greater.month', label: 'From start of current calendar month'}, {id: 'greater.quarter', label: 'From start of current quarter'}, {id: 'greater.year', label: 'From start of current calendar year'}, {id: 'less.week', label: 'To end of current week'}, {id: 'less.month', label: 'To end of current calendar month'}, {id: 'less.quarter', label: 'To end of current quarter'}, {id: 'less.year', label: 'To end of current calendar year'}, {id: 'previous_2.day', label: 'Previous 2 days'}, {id: 'previous_2.week', label: 'Previous 2 weeks'}, {id: 'previous_2.month', label: 'Previous 2 calendar months'}, {id: 'previous_2.quarter', label: 'Previous 2 quarters'}, {id: 'previous_2.year', label: 'Previous 2 calendar years'}, {id: 'previous_before.day', label: 'Day prior to yesterday'}, {id: 'previous_before.week', label: 'Week prior to previous week'}, {id: 'previous_before.month', label: 'Month prior to previous calendar month'}, {id: 'previous_before.quarter', label: 'Quarter prior to previous quarter'}, {id: 'previous_before.year', label: 'Year prior to previous calendar year'}, {id: 'greater_previous.week', label: 'From end of previous week'}, {id: 'greater_previous.month', label: 'From end of previous calendar month'}, {id: 'greater_previous.quarter', label: 'From end of previous quarter'}, {id: 'greater_previous.year', label: 'From end of previous calendar year'}, {id: 'previous_2.fiscal_year', label: 'Previous 2 fiscal years'}, {id: 'previous_before.fiscal_year', label: 'Fiscal year prior to previous fiscal year'}], input_attrs: {}, label: 'Date'}" class="col-md-4 civicase__ui-range" />
<af-field name="CaseSalesOrder_Case_case_id_01.start_date" class="col-md-4 civicase__ui-range" defn="{input_type: 'Select', search_range: true, options: [{id: '{}', label: 'Choose Date Range'}, {id: 'this.day', label: 'Today'}, {id: 'this.week', label: 'This week'}, {id: 'this.month', label: 'This calendar month'}, {id: 'this.quarter', label: 'This quarter'}, {id: 'this.fiscal_year', label: 'This fiscal year'}, {id: 'this.year', label: 'This calendar year'}, {id: 'previous.day', label: 'Yesterday'}, {id: 'previous.week', label: 'Previous week'}, {id: 'previous.month', label: 'Previous calendar month'}, {id: 'previous.quarter', label: 'Previous quarter'}, {id: 'previous.fiscal_year', label: 'Previous fiscal year'}, {id: 'previous.year', label: 'Previous calendar year'}, {id: 'ending.week', label: 'Last 7 days including today'}, {id: 'ending_30.day', label: 'Last 30 days including today'}, {id: 'ending_60.day', label: 'Last 60 days including today'}, {id: 'ending_90.day', label: 'Last 90 days including today'}, {id: 'ending.year', label: 'Last 12 months including today'}, {id: 'ending_2.year', label: 'Last 2 years including today'}, {id: 'ending_3.year', label: 'Last 3 years including today'}, {id: 'starting.day', label: 'Tomorrow'}, {id: 'next.week', label: 'Next week'}, {id: 'next.month', label: 'Next calendar month'}, {id: 'next.quarter', label: 'Next quarter'}, {id: 'next.fiscal_year', label: 'Next fiscal year'}, {id: 'next.year', label: 'Next calendar year'}, {id: 'starting.week', label: 'Next 7 days including today'}, {id: 'starting.month', label: 'Next 30 days including today'}, {id: 'starting_2.month', label: 'Next 60 days including today'}, {id: 'starting.quarter', label: 'Next 90 days including today'}, {id: 'starting.year', label: 'Next 12 months including today'}, {id: 'current.week', label: 'Current week to-date'}, {id: 'current.month', label: 'Current calendar month to-date'}, {id: 'current.quarter', label: 'Current quarter to-date'}, {id: 'current.year', label: 'Current calendar year to-date'}, {id: 'earlier.day', label: 'To end of yesterday'}, {id: 'earlier.week', label: 'To end of previous week'}, {id: 'earlier.month', label: 'To end of previous calendar month'}, {id: 'earlier.quarter', label: 'To end of previous quarter'}, {id: 'earlier.year', label: 'To end of previous calendar year'}, {id: 'greater.day', label: 'From start of current day'}, {id: 'greater.week', label: 'From start of current week'}, {id: 'greater.month', label: 'From start of current calendar month'}, {id: 'greater.quarter', label: 'From start of current quarter'}, {id: 'greater.year', label: 'From start of current calendar year'}, {id: 'less.week', label: 'To end of current week'}, {id: 'less.month', label: 'To end of current calendar month'}, {id: 'less.quarter', label: 'To end of current quarter'}, {id: 'less.year', label: 'To end of current calendar year'}, {id: 'previous_2.day', label: 'Previous 2 days'}, {id: 'previous_2.week', label: 'Previous 2 weeks'}, {id: 'previous_2.month', label: 'Previous 2 calendar months'}, {id: 'previous_2.quarter', label: 'Previous 2 quarters'}, {id: 'previous_2.year', label: 'Previous 2 calendar years'}, {id: 'previous_before.day', label: 'Day prior to yesterday'}, {id: 'previous_before.week', label: 'Week prior to previous week'}, {id: 'previous_before.month', label: 'Month prior to previous calendar month'}, {id: 'previous_before.quarter', label: 'Quarter prior to previous quarter'}, {id: 'previous_before.year', label: 'Year prior to previous calendar year'}, {id: 'greater_previous.week', label: 'From end of previous week'}, {id: 'greater_previous.month', label: 'From end of previous calendar month'}, {id: 'greater_previous.quarter', label: 'From end of previous quarter'}, {id: 'greater_previous.year', label: 'From end of previous calendar year'}, {id: 'previous_2.fiscal_year', label: 'Previous 2 fiscal years'}, {id: 'previous_before.fiscal_year', label: 'Fiscal year prior to previous fiscal year'}], input_attrs: {}, label: 'Application Date'}" />
<div class="col-md-1">
<button type="button" class="btn btn-link civicase__features-filters-clear" style="margin-top: 2em;">Clear All</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,41 @@
</div>
</div>

<div class="form-group" ng-show="$ctrl.data.toBeInvoiced === 'percent'" ng-if="relevantProducts && relevantProducts.length > 0">
<label class="col-sm-2 control-label">
Products
</label>
<div class="col-sm-5 civicase__ui-range">
<input class="form-control"
ng-model="$ctrl.data.products"
name="products"
placeholder="All Products"
crm-entityref="{
entity: 'Product',
select: { allowClear: true, 'minimumInputLength': 0, create: false, multiple: true },
api: {params: {id: {'IN': relevantProducts}}}
}"
/>
</div>
</div>
<div class="form-group" ng-show="$ctrl.data.toBeInvoiced === 'percent'" ng-if="!relevantProducts || relevantProducts.length == 0 ">
<label class="col-sm-2 control-label">
Products
</label>
<div class="col-sm-5 civicase__ui-range">
<input class="form-control"
ng-model="$ctrl.data.products"
name="empty-products"
placeholder="All Products"
crm-entityref="{
entity: 'Product',
select: { allowClear: true, 'minimumInputLength': 0, create: false, multiple: true },
api: {params: {id: {'IN': []}}}
}"
/>
</div>
</div>

<div class="form-group">
<label class="col-sm-2 control-label required-mark">
Financial Type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/
function quotationContributionBulkController ($q, $scope, crmApi4, searchTaskBaseTrait, CaseUtils, SalesOrderStatus) {
$scope.ts = CRM.ts('civicase');
$scope.relevantProducts = false;

const ctrl = angular.extend(this, $scope.model, searchTaskBaseTrait);
ctrl.stage = 'form';
Expand All @@ -31,6 +32,7 @@
percentValue: null,
statusId: null,
financialTypeId: null,
products: [],
date: $.datepicker.formatDate('yy-mm-dd', new Date())
};
ctrl.salesOrderStatus = SalesOrderStatus.getAll();
Expand All @@ -53,6 +55,11 @@
const chunkedIds = _.chunk(ctrl.ids, BATCH_SIZE);
for (const salesOrderIds of chunkedIds) {
try {
if (ctrl.data.products.length > 0) {
ctrl.data.products = ctrl.data.products.split(',');
} else {
ctrl.data.products = [];
}
const result = await crmApi4('CaseSalesOrder', 'contributionCreateAction', { ...ctrl.data, salesOrderIds });
contributionCreated += result.created_contributions_count ?? 0;
} catch (error) {
Expand All @@ -71,5 +78,25 @@
CRM.alert(message, ts('Success'), 'success');
});
};

$q(async function () {
CRM.$.blockUI();
const productIds = new Set();

for (const salesOrderId of ctrl.ids) {
const result = await CaseUtils.getSalesOrderAndLineItems(salesOrderId);
result.items.forEach((lineItem) => {
if (lineItem.product_id) {
productIds.add(lineItem.product_id);
}
});
}

$scope.$apply(() => {
$scope.relevantProducts = false;
$scope.relevantProducts = [...productIds];
});
CRM.$.unblockUI();
});
}
})(angular, CRM.$, CRM._);
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
$scope.ts = CRM.ts('civicase');
const ctrl = angular.extend(this, $scope.model, searchTaskBaseTrait);
ctrl.stage = 'form';
ctrl.products = [];
$scope.submitInProgress = false;

this.applyDiscount = () => {
Expand All @@ -47,5 +48,24 @@
CRM.alert(`Discount applied to ${Object.values(updatedSalesOrder).length} Quotation(s) successfully`, ts('Success'), 'success');
});
};

$q(async function () {
CRM.$.blockUI();
const productIds = new Set();

for (const salesOrderId of ctrl.ids) {
const result = await CaseUtils.getSalesOrderAndLineItems(salesOrderId);
result.items.forEach((lineItem) => {
if (lineItem.product_id) {
productIds.add(lineItem.product_id);
}
});
}

ctrl.products = [...productIds];
CRM.$('.crm-product-ref').val([...productIds]);
CRM.$('.crm-product-ref').change();
CRM.$.unblockUI();
});
}
})(angular, CRM._);
Loading
Loading