Developer Notes
Technical documentation for developers working with the Qoliber_StorePickup module.
Architecture Overview
The module follows Magento 2 best practices with a clean, extensible architecture:
Qoliber_StorePickup/
├── Api/ # Service contracts and interfaces
├── Block/ # Frontend blocks
├── Controller/ # Admin and frontend controllers
├── Model/ # Business logic and repositories
├── Observer/ # Event observers
├── Plugin/ # Interceptors
├── Service/ # Service layer
├── Ui/ # UI components
└── view/ # Frontend resources
Key Interfaces
Location Management
namespace Qoliber\StorePickup\Api;
interface LocationRepositoryInterface
{
public function save(\Qoliber\StorePickup\Api\Data\LocationInterface $location);
public function get(int $locationId);
public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria);
public function delete(\Qoliber\StorePickup\Api\Data\LocationInterface $location);
public function deleteById(int $locationId);
}
Time Slot Management
namespace Qoliber\StorePickup\Api;
interface TimeSlotManagementInterface
{
public function getAvailableSlots(int $locationId, string $date): array;
public function reserveSlot(int $locationId, string $date, string $time): bool;
public function releaseSlot(int $locationId, string $date, string $time): bool;
public function getSlotCapacity(int $locationId, string $date, string $time): int;
}
Database Schema
Main Tables
qoliber_store_pickup_location
- location_id (int, PK)
- name (varchar)
- code (varchar, unique)
- status (smallint)
- phone (varchar)
- email (varchar)
- street (text)
- city (varchar)
- region (varchar)
- postcode (varchar)
- country_id (varchar)
- latitude (decimal)
- longitude (decimal)
- opening_hours (json)
- lead_time (int)
- max_orders_per_slot (int)
- created_at (timestamp)
- updated_at (timestamp)
qoliber_store_pickup_order
- entity_id (int, PK)
- order_id (int, FK)
- location_id (int, FK)
- pickup_date (date)
- pickup_time (time)
- status (varchar)
- marked_ready_by (varchar)
- marked_ready_at (timestamp)
- picked_up_by (varchar)
- picked_up_at (timestamp)
Event Observers
The module dispatches and observes several events:
Custom Events
// Dispatched when order is marked ready
qoliber_storepickup_order_ready
// Data: ['order' => $order, 'pickup_order' => $pickupOrder]
// Dispatched when order is picked up
qoliber_storepickup_order_picked_up
// Data: ['order' => $order, 'pickup_order' => $pickupOrder]
// Dispatched when time slot is reserved
qoliber_storepickup_slot_reserved
// Data: ['location_id' => $locationId, 'date' => $date, 'time' => $time]
Observed Events
sales_quote_address_collect_totals_before
- Calculate pickup shippingcheckout_submit_all_after
- Save pickup details to ordersales_order_place_after
- Reserve time slot capacity
Plugin Integration Points
Checkout Integration
namespace Qoliber\StorePickup\Plugin\Checkout;
class ShippingInformationManagementPlugin
{
public function beforeSaveAddressInformation(
\Magento\Checkout\Model\ShippingInformationManagementInterface $subject,
$cartId,
\Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
) {
// Process pickup location selection
// Save pickup date/time to quote
}
}
JavaScript Components
Checkout Component
// view/frontend/web/js/view/checkout/shipping/store-selector.js
define([
'uiComponent',
'ko',
'mage/url',
'Magento_Checkout/js/model/quote'
], function (Component, ko, url, quote) {
return Component.extend({
defaults: {
template: 'Qoliber_StorePickup/checkout/shipping/store-selector'
},
selectLocation: function(location) {
// Handle location selection
},
loadTimeSlots: function() {
// Load available time slots via AJAX
}
});
});
API Endpoints
REST API (planned)
GET /V1/storepickup/locations
GET /V1/storepickup/locations/:id
POST /V1/storepickup/locations
PUT /V1/storepickup/locations/:id
DELETE /V1/storepickup/locations/:id
GET /V1/storepickup/timeslots/:locationId/:date
POST /V1/storepickup/orders/:orderId/ready
POST /V1/storepickup/orders/:orderId/pickup
GraphQL (Planned)
type Query {
storePickupLocations(
filter: StorePickupLocationFilterInput
pageSize: Int
currentPage: Int
): StorePickupLocations
storePickupTimeSlots(
locationId: Int!
date: String!
): [TimeSlot]
}
type Mutation {
setPickupLocation(
cartId: String!
locationId: Int!
pickupDate: String
pickupTime: String
): Cart
}
Extension Points
Adding Custom Fields to Locations
// via extension attributes in etc/extension_attributes.xml
<extension_attributes for="Qoliber\StorePickup\Api\Data\LocationInterface">
<attribute code="custom_field" type="string"/>
</extension_attributes>
Customizing Time Slot Logic
// Implement custom time slot provider
class CustomTimeSlotProvider implements \Qoliber\StorePickup\Api\TimeSlotProviderInterface
{
public function getSlots($locationId, $date)
{
// Custom logic for generating time slots
}
}
// Register in di.xml
<preference for="Qoliber\StorePickup\Api\TimeSlotProviderInterface"
type="Vendor\Module\Model\CustomTimeSlotProvider"/>
Email Template Variables
Available variables in email templates:
{{var order}} # Order object
{{var pickup_location}} # Location object
{{var pickup_date}} # Formatted pickup date
{{var pickup_time}} # Formatted pickup time
{{var pickup_location.name}} # Store name
{{var pickup_location.phone}} # Store phone
{{var pickup_location.street}} # Store address
Performance Considerations
Indexing
- Location data is flat for fast retrieval
- Time slot availability uses dedicated index table
- Quote/Order relation indexed for quick lookups
Caching
- Location list cached with tag
qoliber_storepickup_location
- Time slots cached per day with TTL
- Clear cache after location updates
Query Optimization
// Optimized location loading with specific fields
$this->locationCollection
->addFieldToSelect(['location_id', 'name', 'status'])
->addFieldToFilter('status', 1)
->setOrder('sort_order', 'ASC');
Testing
Unit Tests
Integration Tests
vendor/bin/phpunit -c dev/tests/integration/phpunit.xml vendor/qoliber/store-pickup/Test/Integration
API Tests
# Test location endpoint
curl -X GET "https://magento.test/rest/V1/storepickup/locations" \
-H "Authorization: Bearer {token}"
# Test time slots
curl -X GET "https://magento.test/rest/V1/storepickup/timeslots/1/2024-12-25" \
-H "Authorization: Bearer {token}"
Common Customizations
Custom Pickup Fee Calculation
class CustomPickupPriceCalculator
{
public function calculate($request)
{
// Custom logic based on location, time, items
$location = $this->getSelectedLocation();
$fee = $location->getBaseFee();
if ($this->isRushHour($request->getPickupTime())) {
$fee *= 1.5;
}
return $fee;
}
}
Integration with ERP
class ErpSyncObserver implements \Magento\Framework\Event\ObserverInterface
{
public function execute(\Magento\Framework\Event\Observer $observer)
{
$pickupOrder = $observer->getPickupOrder();
// Send pickup details to ERP
$this->erpApi->createPickupOrder([
'order_id' => $pickupOrder->getOrderId(),
'location_code' => $pickupOrder->getLocation()->getCode(),
'pickup_date' => $pickupOrder->getPickupDate()
]);
}
}
Troubleshooting
Debug Mode
Enable debug logging in etc/di.xml
:
<type name="Qoliber\StorePickup\Model\Logger">
<arguments>
<argument name="debugEnabled" xsi:type="boolean">true</argument>
</arguments>
</type>
Common Issues
Time slots not loading:
// Check JavaScript console for errors
// Verify AJAX endpoint: /storepickup/timeslot/available
// Check location has opening_hours JSON data
Location not saving:
// Check unique constraint on 'code' field
// Verify admin permissions for resource 'Qoliber_StorePickup::location_save'
// Check latitude/longitude format if map integration enabled
Support
For technical support and custom development: - Email: [email protected] - GitLab: Access available for partners - Documentation: https://docs.qoliber.com