Note: The same mechanism is also available for Sales Orders and Invoices.
You have added custom fields to quote items or/and quote entity types. You want Total Amount field and other totals to be calculated considering new custom fields.
You need to create a custom Hook for the Quote entity type.
Create a new file:
custom/Espo/Custom/Hooks/Quote/CalculateItems.php
<?php
namespace Espo\Custom\Hooks\Quote;
use Espo\ORM\Entity;
class CalculateItems
{
public static int $order = 10;
public function __construct(
// Define needed dependencies.
) {}
public function beforeSave(Entity $entity, array $options): void
{
if (!$entity->has('itemList')) {
$entity->loadItemListField();
}
$itemList = $entity->get('itemList');
$amount = 0.0;
foreach ($itemList as $item) {
$amount += $item->quantity * $item->unitPrice * $item->factor;
}
$entity->set('amount', $amount);
}
}
Note: For other entity types, e.g. SalesOrder, do the same but use SalesOrder namespace and file location instead of Quote.
In Quote's clientDefs you need to specify custom calculation handler:
File: custom/Espo/Custom/Resources/metadata/clientDefs/Quote.json
{
"calculationHandler": "custom:quote-calculation-handler"
}
Note: For Sales Orders use SalesOrder.json
file names. For Invoices use Invoice.json
file names.
Create a new file:
client/custom/src/quote-calculation-handler.js
define(['sales:quote-calculation-handler'], (Dep) => {
return class extends Dep {
// Define custom calculations here.
// Use client/modules/sales/quote-calculation-handler.js as an example.
/**
* Calculate.
*
* @param {import('model').default} model An order.
*/
calculate(model) {
super.calculate(model);
}
/**
* Calculate item.
*
* @param {import('model').default} model An item.
* @param {string} [field] A field that was changed.
*/
calculateItem(model, field) {
super.calculateItem(model, field);
}
/**
* Select product.
*
* @param {import('model').default} model
* @param {import('model').default} product
*/
selectProduct(model, product) {
super.selectProduct(model, product);
}
/**
* Get select product attributes.
*
* @param {import('model').default} model
* @return {string[]}
*/
getSelectProductAttributeList(model) {
super.getSelectProductAttributeList(model);
}
}
});
It's also possible to make certain product fields copied to a quote item on product selection. The logic is determined in selectProduct method.