Skip to content

Commit

Permalink
Merge pull request #983 from compucorp/BTHAB-202-max-premium
Browse files Browse the repository at this point in the history
BTHAB-202: Ensure the quote product price doesnt exceed the max price
  • Loading branch information
olayiwola-compucorp authored Oct 17, 2023
2 parents 1816a85 + be43b78 commit a8dece8
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 3 deletions.
28 changes: 27 additions & 1 deletion Civi/Api4/Action/CaseSalesOrder/SalesOrderSaveAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Civi\Api4\Generic\AbstractSaveAction;
use Civi\Api4\Generic\Result;
use Civi\Api4\Generic\Traits\DAOActionTrait;
use Civi\Api4\Product;
use CRM_Civicase_BAO_CaseSalesOrder as CaseSalesOrderBAO;
use CRM_Core_Transaction;

Expand Down Expand Up @@ -46,6 +47,7 @@ protected function writeRecord($items) {
$output = [];
foreach ($items as $salesOrder) {
$lineItems = $salesOrder['items'];
$this->validateLinItemProductPrice($lineItems);
$total = CaseSalesOrderBAO::computeTotal($lineItems);
$salesOrder['total_before_tax'] = $total['totalBeforeTax'];
$salesOrder['total_after_tax'] = $total['totalAfterTax'];
Expand Down Expand Up @@ -122,7 +124,7 @@ private function updateOpportunityDetails($salesOrderId): void {
->execute()
->first();

if (empty($caseSalesOrder)) {
if (empty($caseSalesOrder) || empty($caseSalesOrder['case_id'])) {
return;
}

Expand All @@ -143,4 +145,28 @@ protected function fillMandatoryFields(&$params) {
$params['invoicing_status_id'] = $caseSaleOrderContributionService->calculatePaymentStatus();
}

/**
* Ensures the product price doesnt exceed the max price.
*
* @param array $lineItems
* Sales Order line items.
*/
protected function validateLinItemProductPrice(array &$lineItems) {
array_walk($lineItems, function (&$lineItem) {
if (!empty($lineItem['product_id'])) {
$product = Product::get()
->addSelect('cost')
->addWhere('id', '=', $lineItem['product_id'])
->execute()
->first();

if ($product && !empty($product['cost']) && $product['cost'] < $lineItem['subtotal_amount']) {
$lineItem['unit_price'] = $product['cost'];
$lineItem['quantity'] = 1;
$lineItem['subtotal_amount'] = CaseSalesOrderBAO::getSubTotal($lineItem);
}
}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@

$scope.isUpdate = false;
$scope.formValid = true;
$scope.membeshipTypesProductDiscountPercentage = 0;
$scope.roundTo = roundTo;
$scope.formatMoney = formatMoney;
$scope.submitInProgress = false;
Expand All @@ -43,6 +42,7 @@
$scope.currencyCodes = CurrencyCodes.getAll();
$scope.handleClientChange = handleClientChange;
$scope.handleProductChange = handleProductChange;
$scope.membeshipTypesProductDiscountPercentage = 0;
$scope.handleCurrencyChange = handleCurrencyChange;
$scope.salesOrderStatus = SalesOrderStatus.getAll();
$scope.defaultCaseId = $location.search().caseId || null;
Expand Down Expand Up @@ -316,6 +316,28 @@

item.subtotal_amount = item.unit_price * item.quantity * ((100 - item.discounted_percentage) / 100) || 0;
$scope.$emit('totalChange');
validateProductPrice(index);
}

/**
* Ensures the product price doesnt exceed the max price
*
* @param {number} index index of the sales order line item
*/
function validateProductPrice (index) {
const productId = $scope.salesOrder.items[index].product_id;
const shouldCompareCost = productId && productsCache.has(productId) && parseFloat(productsCache.get(productId).cost) > 0;
if (!shouldCompareCost) {
return;
}

const cost = productsCache.get(productId).cost;
if ($scope.salesOrder.items[index].subtotal_amount > cost) {
$scope.salesOrder.items[index].quantity = 1;
$scope.salesOrder.items[index].unit_price = parseFloat(cost);
CRM.alert('The quotation line item(s) have been set to the maximum premium price', ts('info'), 'info');
calculateSubtotal(index);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use Civi\Api4\CaseSalesOrder;
use Civi\Api4\CaseSalesOrderLine;
use CRM_Civicase_Test_Fabricator_Contact as ContactFabricator;
use CRM_Civicase_Test_Fabricator_Product as ProductFabricator;

/**
* CaseSalesOrder.SalesOrderSaveAction API Test Case.
Expand All @@ -17,7 +18,7 @@ class Civi_Api4_CaseSalesOrder_SalesOrderSaveActionTest extends BaseHeadlessTest
/**
* Setup data before tests run.
*/
public function setUp() {
public function setUp(): void {
$contact = ContactFabricator::fabricate();
$this->registerCurrentLoggedInContactInSession($contact['id']);
}
Expand Down Expand Up @@ -162,4 +163,30 @@ public function testDetachedLineItemsAreRemovedFromSalesOrderOnUpdate() {
$this->assertEquals($salesOrder['items'][0]['id'], $salesOrderLine[0]['id']);
}

/**
* Test line item subtotal doesnt exceed product maximum cost.
*/
public function testSubTotalDoesntExceedProductCost() {
$productPrice = 200;
$salesOrder = $this->getCaseSalesOrderData();
$salesOrder['items'][] = $this->getCaseSalesOrderLineData(
['unit_price' => 200, 'quantity' => 2]
);

$salesOrder['items'][0]['product_id'] = ProductFabricator::fabricate(['cost' => 200])['id'];

$salesOrderId = CaseSalesOrder::save()
->addRecord($salesOrder)
->execute()
->jsonSerialize()[0]['id'];

$salesOrderLine = CaseSalesOrderLine::get()
->addWhere('sales_order_id', '=', $salesOrderId)
->execute()
->jsonSerialize();

$this->assertNotEmpty($salesOrderLine);
$this->assertEquals($productPrice, $salesOrderLine[0]['subtotal_amount']);
}

}

0 comments on commit a8dece8

Please sign in to comment.