feat: PartsHelper #17
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
namespace Moko\Plugin\System\MokoSuiteField\Helper;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Database\DatabaseInterface;
|
||||
|
||||
/**
|
||||
* Parts management — common parts lookup, usage tracking, reorder alerts, cost tracking.
|
||||
*/
|
||||
class PartsHelper
|
||||
{
|
||||
/**
|
||||
* Get frequently used parts by trade for quick-add on work orders.
|
||||
*/
|
||||
public static function getCommonParts(string $trade = '', int $limit = 20): array
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->select('p.id, p.title AS name, p.sku, p.price, p.cost_price, p.stock_quantity')
|
||||
->select('COUNT(wi.id) AS usage_count')
|
||||
->from($db->quoteName('#__mokosuite_crm_products', 'p'))
|
||||
->join('LEFT', $db->quoteName('#__mokosuitefield_wo_items', 'wi') . ' ON wi.product_id = p.id')
|
||||
->where($db->quoteName('p.published') . ' = 1')
|
||||
->group('p.id')
|
||||
->order('usage_count DESC');
|
||||
|
||||
if ($trade) {
|
||||
$query->join('LEFT', $db->quoteName('#__mokosuitefield_work_orders', 'wo') . ' ON wo.id = wi.wo_id')
|
||||
->where($db->quoteName('wo.trade') . ' = ' . $db->quote($trade));
|
||||
}
|
||||
|
||||
$db->setQuery($query, 0, $limit);
|
||||
return $db->loadObjectList() ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Record part usage on a work order.
|
||||
*/
|
||||
public static function usePart(int $woId, int $productId, int $quantity, ?float $unitPrice = null): int
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
if ($unitPrice === null) {
|
||||
$db->setQuery($db->getQuery(true)->select('price')->from('#__mokosuite_crm_products')->where('id = ' . (int) $productId));
|
||||
$unitPrice = (float) $db->loadResult();
|
||||
}
|
||||
|
||||
$item = (object) [
|
||||
'wo_id' => $woId,
|
||||
'product_id' => $productId,
|
||||
'quantity' => $quantity,
|
||||
'unit_price' => $unitPrice,
|
||||
'line_total' => round($unitPrice * $quantity, 2),
|
||||
'created' => Factory::getDate()->toSql(),
|
||||
];
|
||||
|
||||
$db->insertObject('#__mokosuitefield_wo_items', $item, 'id');
|
||||
|
||||
// Deduct from stock
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->update('#__mokosuite_crm_products')
|
||||
->set('stock_quantity = stock_quantity - ' . (int) $quantity)
|
||||
->where('id = ' . (int) $productId));
|
||||
$db->execute();
|
||||
|
||||
return (int) $item->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parts that are low on stock and frequently used in field service.
|
||||
*/
|
||||
public static function getLowStockParts(int $limit = 20): array
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->select('p.id, p.title AS name, p.sku, p.stock_quantity, p.reorder_point, p.cost_price')
|
||||
->select('(SELECT COUNT(*) FROM #__mokosuitefield_wo_items wi WHERE wi.product_id = p.id) AS field_usage')
|
||||
->from($db->quoteName('#__mokosuite_crm_products', 'p'))
|
||||
->where($db->quoteName('p.stock_quantity') . ' <= ' . $db->quoteName('p.reorder_point'))
|
||||
->where($db->quoteName('p.reorder_point') . ' > 0')
|
||||
->where('p.id IN (SELECT DISTINCT product_id FROM #__mokosuitefield_wo_items)')
|
||||
->order('(p.reorder_point - p.stock_quantity) DESC'), 0, $limit);
|
||||
|
||||
return $db->loadObjectList() ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parts cost summary for a work order.
|
||||
*/
|
||||
public static function getWoPartsCost(int $woId): object
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->select('COUNT(*) AS item_count')
|
||||
->select('SUM(quantity) AS total_qty')
|
||||
->select('COALESCE(SUM(line_total), 0) AS total_cost')
|
||||
->from('#__mokosuitefield_wo_items')
|
||||
->where('wo_id = ' . (int) $woId));
|
||||
|
||||
return $db->loadObject() ?: (object) ['item_count' => 0, 'total_qty' => 0, 'total_cost' => 0];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user