Skip to content

Commit 7fe9db7

Browse files
committed
Updated: Quantity-Based Pricing for Room Services
1 parent a27e55c commit 7fe9db7

12 files changed

Lines changed: 373 additions & 88 deletions

File tree

AGENTS.md

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# AGENTS.md
2+
3+
## Project Overview
4+
5+
QloApps is an open-source hotel reservation and property management platform. It enables hotels to manage rooms, bookings, guests, and payments through a web-based system.
6+
7+
## Purpose of this File
8+
9+
This document provides guidance for AI coding agents contributing to the QloApps project. It defines conventions, safety rules, and workflows to ensure consistent and secure code generation.
10+
11+
## Technology Stack
12+
13+
- **Language:** PHP 8.1–8.4 (backend), Smarty 3.x (templates), JavaScript/jQuery (frontend)
14+
- **Database:** MySQL 5.7, 8.0+; MariaDB 10.5, 10.6, 10.11, 11.0, 11.2, 11.4
15+
- **Architecture:** MVC with hook-based module system
16+
- **License:** OSL-3.0 (core), AFL-3.0 (modules)
17+
- **Required PHP Extensions:** PDO_MySQL, cURL, OpenSSL, SOAP, GD, SimpleXML, DOM, Zip, Phar
18+
19+
## Environment Setup
20+
21+
Install dependencies:
22+
```bash
23+
composer install
24+
```
25+
26+
Clear caches:
27+
```bash
28+
rm -rf cache/smarty/compile/* cache/smarty/cache/*
29+
rm -f cache/class_index.php
30+
```
31+
32+
Clear class cache after adding or modifying overrides.
33+
34+
## Project Structure
35+
36+
```
37+
.
38+
├── classes/ # Core models extending ObjectModel
39+
├── controllers/ # Front and admin controllers
40+
│ ├── admin/
41+
│ └── front/
42+
├── modules/ # Feature modules with isolated functionality
43+
├── override/ # Core class and controller overrides
44+
├── themes/ # Smarty templates (.tpl files)
45+
├── config/ # Configuration files (settings.inc.php contains secrets)
46+
├── cache/ # Generated cache files
47+
├── tests/ # PHPUnit test suite
48+
```
49+
50+
**QloApps Agent Skills:** Install reusable development skills using:
51+
```bash
52+
npx skills add Qloapps/agent-skills
53+
```
54+
Available skills: module-development, payment-module-development, stats-module-development. Check installed skills before implementing new functionality.
55+
56+
## Architecture Overview
57+
58+
**MVC Pattern**
59+
- Models: classes/ extending ObjectModel
60+
- Controllers: FrontController or AdminController
61+
- Views: Smarty templates (.tpl)
62+
63+
**Modules**
64+
- Located in modules/<modulename>/
65+
- Provide isolated functionality
66+
- Integrate using hooks
67+
68+
**Overrides**
69+
- Located in override/
70+
- Extend core classes using the Core suffix
71+
- Clear class cache after creating overrides
72+
73+
**Context**
74+
- Access runtime objects using Context::getContext()
75+
76+
## Core vs Module Development Rules
77+
78+
**When working on a module:**
79+
1. Never modify core files directly
80+
2. Use **hooks** to integrate module functionality into core features
81+
3. If no suitable hook exists, create a **custom hook** — but only if the hook placement is generic and useful for other modules too
82+
4. Use **overrides** only as a last resort — overrides can conflict with other module override files and require manual resolution
83+
84+
**When working on a core feature:**
85+
- Make changes directly in core files
86+
- Do not use hooks or overrides for core-to-core changes
87+
88+
## Coding Conventions
89+
90+
**Classes:** PascalCase — `HotelBookingData`
91+
**Methods:** camelCase — `getBookingDetails()`
92+
**Variables:** camelCase — `$hotelId`
93+
**Constants:** UPPER_SNAKE_CASE — `BOOKING_STATUS_CONFIRMED`
94+
**Database Tables:** _DB_PREFIX_ + lowercase_snake — `qlo_hotel_booking`
95+
**Config Keys:** MODULENAME_SETTING — `HOTELRESERVATION_ENABLED`
96+
**Files:** One class per file, filename matches class name
97+
**Templates:** lowercase-hyphens.tpl — `booking-form.tpl`
98+
99+
Add PHPDoc blocks to all classes and methods.
100+
101+
## Translation
102+
103+
Never hardcode user-facing English strings — always wrap them in the appropriate translation method.
104+
105+
| Context | Method |
106+
|---------|--------|
107+
| Module main file | `$this->l('string')` |
108+
| Module admin controller | `$this->l('string')` |
109+
| Module front controller | `$this->module->l('string', 'controllerName')` |
110+
| Module classes | `$objModule->l('string', 'ClassName')` |
111+
| Core admin controller | `$this->l('string')` |
112+
| Core front controller | `Tools::displayError('string')` |
113+
| Smarty template (core) | `{l s='string'}` |
114+
| Smarty template (module) | `{l s='string' mod='modulename'}` |
115+
116+
## Multi-language
117+
118+
- Always include `id_lang` in queries that return translatable content
119+
- Use `Context::getContext()->language->id` for the current language
120+
121+
## Module Development Guidelines
122+
123+
**Module Location:** modules/<modulename>/
124+
125+
**Required Files:**
126+
- <modulename>.php — Main class extending Module
127+
- config.xml — Module metadata
128+
129+
**Optional Directories:**
130+
- classes/ — Module-specific models
131+
- controllers/ — Module controllers
132+
- views/templates/ — Smarty templates
133+
- upgrade/ — Version migration scripts
134+
135+
**Hook Integration:**
136+
- Register hooks in install() method
137+
- Unregister hooks in uninstall() method
138+
- Keep hook handlers lightweight
139+
140+
**Configuration:**
141+
- Store settings using Configuration::updateValue()
142+
- Retrieve settings using Configuration::get()
143+
- Prefix config keys with module name
144+
145+
## Database Rules
146+
147+
**Table Prefix:** Always use _DB_PREFIX_ constant instead of hardcoding the table prefix
148+
149+
**Escaping:**
150+
- Strings: pSQL($value)
151+
- Integers: (int)$value
152+
- Table/column names: bqSQL($name)
153+
154+
**Preferred Access:** Use ObjectModel for CRUD operations instead of raw SQL queries
155+
156+
**Prohibited:** Never concatenate raw user input into SQL queries.
157+
158+
## Security Guidelines
159+
160+
**Input Handling**
161+
- Use Tools::getValue() for request parameters
162+
- Cast numeric values to (int)
163+
- Escape strings using pSQL()
164+
- Validate input using Validate class methods
165+
166+
**Output Escaping**
167+
- Use Tools::safeOutput() in PHP
168+
- Use Smarty escape modifiers in templates
169+
170+
**Sensitive Data**
171+
- Never expose config/settings.inc.php
172+
- Never commit API keys, passwords, or tokens
173+
174+
**Authorization**
175+
- Verify permissions before performing admin operations
176+
177+
## Testing
178+
179+
Testing infrastructure is being configured. Check tests/ directory for available tests before running.
180+
181+
## AI Agent Workflow
182+
183+
1. Check installed agent skills before implementing new functionality
184+
2. Search codebase for similar implementations before creating new code
185+
3. Extend existing classes rather than duplicating functionality
186+
4. Follow patterns established in surrounding code
187+
5. Reuse existing utilities: Tools, Validate, Db, Configuration classes
188+
6. Prioritize consistency with existing codebase over new approaches
189+
190+
After making changes:
191+
- Clear caches if modifying templates or overrides
192+
- Add PHPDoc to new methods
193+
194+
## Safety Rules
195+
196+
**Agents must not:**
197+
- Delete files unless explicitly instructed
198+
- Run git commands automatically
199+
- Modify composer.json without approval
200+
- Modify config/settings.inc.php
201+
- Execute DROP, TRUNCATE, or destructive SQL
202+
- Modify core files directly when working on a module (use hooks or override system)
203+
204+
**Agents must:**
205+
- Use pSQL() for strings and (int) for IDs in SQL
206+
- Use Tools::getValue() for request parameters
207+
- Escape all output
208+
- Check installed agent skills before implementing new functionality
209+
- Follow project naming conventions
210+
- Validate inputs before processing
211+
212+
**Agents should ask before:**
213+
- Deleting any files
214+
- Running git commands
215+
- Changing dependencies
216+
- Modifying database schema
217+
- Altering payment or booking logic
218+
219+
---
220+
221+
**Resources:**
222+
- Documentation: https://docs.qloapps.com
223+
- Forum: https://forums.qloapps.com
224+
- GitHub: https://github.com/Qloapps/QloApps
225+
- Security Issues: support@qloapps.com

admin/themes/default/template/helpers/tree/tree.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123
124124
function startTree(idElem) {
125125
if (typeof $.fn.tree === 'undefined') {
126-
setTimeout(startTree, 100);
126+
setTimeout(function() { startTree(idElem); }, 100);
127127
return;
128128
}
129129

classes/Cookie.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,14 @@ public function update($nullValues = false)
332332
$this->logout();
333333
}
334334

335+
if (isset($this->_content['id_customer']) && isset($this->_content['passwd']) && (int)$this->_content['id_customer']) {
336+
if (Validate::isLoadedObject($objCustomer = new Customer((int)$this->_content['id_customer']))) {
337+
if ($objCustomer->passwd != $this->_content['passwd']) {
338+
$this->logout();
339+
}
340+
}
341+
}
342+
335343
if (!isset($this->_content['date_add'])) {
336344
$this->_content['date_add'] = date('Y-m-d H:i:s');
337345
}

classes/controller/AdminController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2397,7 +2397,7 @@ protected function filterTabModuleList($tab_modules_list)
23972397
$must_have_module_list = file_get_contents(_PS_ROOT_DIR_.Module::CACHE_FILE_MUST_HAVE_MODULES_LIST);
23982398
if (!empty($must_have_module_list) && $must_have_module_list_xml = @simplexml_load_string($must_have_module_list)) {
23992399
$must_have_module_list_array = array();
2400-
if (is_object($country_module_list_xml->module)) {
2400+
if (is_object($must_have_module_list_xml->module)) {
24012401
foreach ($must_have_module_list_xml->module as $l => $mo) {
24022402
$all_module_list[] = (string)$mo->name;
24032403
}

controllers/admin/AdminOrdersController.php

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,54 @@ public function initPageHeaderToolbar()
368368
'icon' => 'process-icon-new'
369369
);
370370
}
371+
if ($this->display == 'view') {
372+
/** @var Order $order */
373+
if (Validate::isLoadedObject($order = $this->loadObject())) {
374+
// hotel link in header
375+
if ($idHotel = HotelBookingDetail::getIdHotelByIdOrder($order->id)) {
376+
$this->page_header_toolbar_btn['hotel'] = array(
377+
'href' => $this->context->link->getAdminLink('AdminAddHotel').'&id='.$idHotel.'&updatehtl_branch_info',
378+
'desc' => $this->l('View Hotel'),
379+
'class' => 'icon-building',
380+
'target' => true,
381+
);
382+
}
383+
384+
if (Configuration::get('PS_INVOICE') && $order->hasInvoice() && !$this->lite_display) {
385+
$this->page_header_toolbar_btn['file'] = array(
386+
'short' => $this->l('Invoice'),
387+
'href' => $this->context->link->getAdminLink('AdminPdf').'&submitAction=generateInvoicePDF&id_order='.$order->id,
388+
'desc' => $this->l('View invoice'),
389+
'class' => 'icon-file-text',
390+
'target' => true,
391+
);
392+
}
393+
394+
$this->page_header_toolbar_btn['print'] = array(
395+
'short' => $this->l('Print'),
396+
'href' => 'javascript:window.print()',
397+
'desc' => $this->l('Print order'),
398+
'class' => 'icon-print',
399+
);
400+
401+
if ($this->tabAccess['edit'] === 1) {
402+
if (((int) $order->isReturnable())
403+
&& !$order->hasCompletelyRefunded(Order::ORDER_COMPLETE_CANCELLATION_OR_REFUND_REQUEST_FLAG, 0, 0)
404+
) {
405+
$orderTotalPaid = $order->getTotalPaid();
406+
$orderDiscounts = $order->getCartRules();
407+
$hasOrderDiscountOrPayment = ((float)$orderTotalPaid > 0 || $orderDiscounts) ? true : false;
408+
$this->page_header_toolbar_btn['cancel'] = array(
409+
'short' => ($hasOrderDiscountOrPayment) ? $this->l('Refund') : $this->l('Cancel'),
410+
'id' => 'desc-order-standard_refund',
411+
'desc' => ($hasOrderDiscountOrPayment) ? $this->l('Initiate refund') : $this->l('Cancel bookings'),
412+
'class' => 'icon-exchange',
413+
'target' => true,
414+
);
415+
}
416+
}
417+
}
418+
}
371419

372420
parent::initPageHeaderToolbar();
373421
}
@@ -476,55 +524,6 @@ public function renderForm()
476524

477525
public function initToolbar()
478526
{
479-
if ($this->display == 'view') {
480-
/** @var Order $order */
481-
if (Validate::isLoadedObject($order = $this->loadObject())) {
482-
// hotel link in header
483-
if ($idHotel = HotelBookingDetail::getIdHotelByIdOrder($order->id)) {
484-
$this->toolbar_btn['hotel'] = array(
485-
'href' => $this->context->link->getAdminLink('AdminAddHotel').'&id='.$idHotel.'&updatehtl_branch_info',
486-
'desc' => $this->l('View Hotel'),
487-
'class' => 'icon-building',
488-
'target' => true,
489-
);
490-
}
491-
492-
if (Configuration::get('PS_INVOICE') && $order->hasInvoice() && !$this->lite_display) {
493-
$this->toolbar_btn['file'] = array(
494-
'short' => $this->l('Invoice'),
495-
'href' => $this->context->link->getAdminLink('AdminPdf').'&submitAction=generateInvoicePDF&id_order='.$order->id,
496-
'desc' => $this->l('View invoice'),
497-
'class' => 'icon-file-text',
498-
'target' => true,
499-
);
500-
}
501-
502-
$this->toolbar_btn['print'] = array(
503-
'short' => $this->l('Print'),
504-
'href' => 'javascript:window.print()',
505-
'desc' => $this->l('Print order'),
506-
'class' => 'icon-print',
507-
);
508-
509-
if ($this->tabAccess['edit'] === 1) {
510-
if (((int) $order->isReturnable())
511-
&& !$order->hasCompletelyRefunded(Order::ORDER_COMPLETE_CANCELLATION_OR_REFUND_REQUEST_FLAG, 0, 0)
512-
) {
513-
$orderTotalPaid = $order->getTotalPaid();
514-
$orderDiscounts = $order->getCartRules();
515-
$hasOrderDiscountOrPayment = ((float)$orderTotalPaid > 0 || $orderDiscounts) ? true : false;
516-
$this->toolbar_btn['cancel'] = array(
517-
'short' => ($hasOrderDiscountOrPayment) ? $this->l('Refund') : $this->l('Cancel'),
518-
'id' => 'desc-order-standard_refund',
519-
'desc' => ($hasOrderDiscountOrPayment) ? $this->l('Initiate refund') : $this->l('Cancel bookings'),
520-
'class' => 'icon-exchange',
521-
'target' => true,
522-
);
523-
}
524-
}
525-
}
526-
}
527-
528527
$res = parent::initToolbar();
529528
if (Context::getContext()->shop->getContext() != Shop::CONTEXT_SHOP && isset($this->toolbar_btn['new']) && Shop::isFeatureActive()) {
530529
unset($this->toolbar_btn['new']);
@@ -3710,6 +3709,7 @@ public function renderView()
37103709
'ROOM_STATUS_CHECKED_OUT' => HotelBookingDetail::STATUS_CHECKED_OUT,
37113710
'ALLOTMENT_MANUAL' => HotelBookingDetail::ALLOTMENT_MANUAL,
37123711
'order_convenience_fee_services' => $orderConvenienceFeeServices,
3712+
'page_header_toolbar_btn' => $this->page_header_toolbar_btn,
37133713
);
37143714

37153715
return parent::renderView();

controllers/admin/AdminThemesController.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -714,8 +714,11 @@ public function processDelete()
714714
}
715715

716716
$ids_themes = Tools::unSerialize(Configuration::get('PS_ADDONS_THEMES_IDS'));
717-
if (array_key_exists($obj->directory, $ids_themes)) {
718-
unset($ids_themes[$obj->directory]);
717+
718+
if(is_array($ids_themes)) {
719+
if (array_key_exists($obj->directory, $ids_themes)) {
720+
unset($ids_themes[$obj->directory]);
721+
}
719722
}
720723

721724
$obj->removeMetas();

0 commit comments

Comments
 (0)