generated from MokoConsulting/Template-Joomla
feat: build out core helpers — 2 files added
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
namespace Moko\Plugin\System\MokoSuiteRealty\Helper;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Database\DatabaseInterface;
|
||||
|
||||
/**
|
||||
* Offer management — submit, counter, accept/reject, contingency tracking.
|
||||
*/
|
||||
class OfferHelper
|
||||
{
|
||||
/**
|
||||
* Submit an offer on a listing.
|
||||
*/
|
||||
public static function submit(int $listingId, int $buyerContactId, float $offerAmount, array $conditions = []): object
|
||||
{
|
||||
if ($offerAmount <= 0) {
|
||||
throw new \InvalidArgumentException('Offer amount must be positive.');
|
||||
}
|
||||
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$offer = (object) [
|
||||
'listing_id' => $listingId,
|
||||
'buyer_contact_id' => $buyerContactId,
|
||||
'offer_amount' => round($offerAmount, 2),
|
||||
'conditions' => !empty($conditions) ? json_encode($conditions) : null,
|
||||
'status' => 'submitted',
|
||||
'submitted_at' => Factory::getDate()->toSql(),
|
||||
];
|
||||
|
||||
$db->insertObject('#__mokosuiterealty_offers', $offer, 'id');
|
||||
|
||||
return (object) ['success' => true, 'offer_id' => (int) $offer->id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all offers for a listing.
|
||||
*/
|
||||
public static function getForListing(int $listingId): array
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->select('o.*, cd.name AS buyer_name, cd.email_to')
|
||||
->from($db->quoteName('#__mokosuiterealty_offers', 'o'))
|
||||
->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = o.buyer_contact_id')
|
||||
->where('o.listing_id = ' . (int) $listingId)
|
||||
->order('o.offer_amount DESC'));
|
||||
|
||||
return $db->loadObjectList() ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept an offer — marks listing as pending and rejects other offers.
|
||||
*/
|
||||
public static function accept(int $offerId): object
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->select('listing_id')
|
||||
->from('#__mokosuiterealty_offers')
|
||||
->where('id = ' . (int) $offerId));
|
||||
$listingId = (int) $db->loadResult();
|
||||
|
||||
if (!$listingId) {
|
||||
return (object) ['success' => false, 'error' => 'Offer not found'];
|
||||
}
|
||||
|
||||
$db->transactionStart();
|
||||
|
||||
try {
|
||||
// Accept this offer
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->update('#__mokosuiterealty_offers')
|
||||
->set($db->quoteName('status') . ' = ' . $db->quote('accepted'))
|
||||
->set('accepted_at = ' . $db->quote(Factory::getDate()->toSql()))
|
||||
->where('id = ' . (int) $offerId));
|
||||
$db->execute();
|
||||
|
||||
// Reject all other offers on this listing
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->update('#__mokosuiterealty_offers')
|
||||
->set($db->quoteName('status') . ' = ' . $db->quote('rejected'))
|
||||
->where('listing_id = ' . $listingId)
|
||||
->where('id != ' . (int) $offerId)
|
||||
->where($db->quoteName('status') . ' = ' . $db->quote('submitted')));
|
||||
$db->execute();
|
||||
|
||||
// Update listing to pending
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->update('#__mokosuiterealty_listings')
|
||||
->set($db->quoteName('status') . ' = ' . $db->quote('pending'))
|
||||
->where('id = ' . $listingId));
|
||||
$db->execute();
|
||||
|
||||
$db->transactionCommit();
|
||||
|
||||
return (object) ['success' => true, 'listing_id' => $listingId];
|
||||
} catch (\Throwable $e) {
|
||||
$db->transactionRollback();
|
||||
return (object) ['success' => false, 'error' => 'Accept failed'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
namespace Moko\Plugin\System\MokoSuiteRealty\Helper;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Database\DatabaseInterface;
|
||||
|
||||
/**
|
||||
* Showing management — schedule, feedback, calendar, open house sign-in.
|
||||
*/
|
||||
class ShowingHelper
|
||||
{
|
||||
/**
|
||||
* Schedule a showing.
|
||||
*/
|
||||
public static function schedule(int $listingId, int $buyerContactId, int $agentId, string $datetime): object
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$showing = (object) [
|
||||
'listing_id' => $listingId,
|
||||
'buyer_contact_id' => $buyerContactId,
|
||||
'agent_id' => $agentId,
|
||||
'scheduled_at' => $datetime,
|
||||
'status' => 'requested',
|
||||
'created_at' => Factory::getDate()->toSql(),
|
||||
];
|
||||
|
||||
$db->insertObject('#__mokosuiterealty_showings', $showing, 'id');
|
||||
|
||||
return (object) ['success' => true, 'showing_id' => (int) $showing->id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get showings for a listing.
|
||||
*/
|
||||
public static function getForListing(int $listingId): array
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->select('s.*, cd_buyer.name AS buyer_name, cd_agent.name AS agent_name')
|
||||
->from($db->quoteName('#__mokosuiterealty_showings', 's'))
|
||||
->join('LEFT', $db->quoteName('#__contact_details', 'cd_buyer') . ' ON cd_buyer.id = s.buyer_contact_id')
|
||||
->join('LEFT', $db->quoteName('#__contact_details', 'cd_agent') . ' ON cd_agent.id = s.agent_id')
|
||||
->where('s.listing_id = ' . (int) $listingId)
|
||||
->order('s.scheduled_at DESC'));
|
||||
|
||||
return $db->loadObjectList() ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agent calendar — all showings for an agent in a date range.
|
||||
*/
|
||||
public static function getAgentCalendar(int $agentId, string $from, string $to): array
|
||||
{
|
||||
if (!\DateTime::createFromFormat('Y-m-d', $from) || !\DateTime::createFromFormat('Y-m-d', $to)) {
|
||||
throw new \InvalidArgumentException('Date parameters must be Y-m-d format.');
|
||||
}
|
||||
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->select('s.id, s.scheduled_at, s.status')
|
||||
->select('l.address, l.city, l.price')
|
||||
->select('cd.name AS buyer_name, cd.telephone')
|
||||
->from($db->quoteName('#__mokosuiterealty_showings', 's'))
|
||||
->join('INNER', $db->quoteName('#__mokosuiterealty_listings', 'l') . ' ON l.id = s.listing_id')
|
||||
->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = s.buyer_contact_id')
|
||||
->where('s.agent_id = ' . (int) $agentId)
|
||||
->where('DATE(s.scheduled_at) BETWEEN ' . $db->quote($from) . ' AND ' . $db->quote($to))
|
||||
->where($db->quoteName('s.status') . ' != ' . $db->quote('cancelled'))
|
||||
->order('s.scheduled_at ASC'));
|
||||
|
||||
return $db->loadObjectList() ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Record showing feedback from buyer.
|
||||
*/
|
||||
public static function recordFeedback(int $showingId, int $interestLevel, ?string $comments = null): bool
|
||||
{
|
||||
if ($interestLevel < 1 || $interestLevel > 5) {
|
||||
throw new \InvalidArgumentException('Interest level must be 1-5.');
|
||||
}
|
||||
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
$filter = \Joomla\Filter\InputFilter::getInstance();
|
||||
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->update('#__mokosuiterealty_showings')
|
||||
->set($db->quoteName('status') . ' = ' . $db->quote('completed'))
|
||||
->set('interest_level = ' . (int) $interestLevel)
|
||||
->set('feedback = ' . $db->quote($comments ? $filter->clean($comments, 'STRING') : ''))
|
||||
->set('completed_at = ' . $db->quote(Factory::getDate()->toSql()))
|
||||
->where('id = ' . (int) $showingId));
|
||||
$db->execute();
|
||||
|
||||
return $db->getAffectedRows() > 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user