Have you ever wanted to modify an order or an invoice directly in Prestashop? Tired of bypassing PS limitations via phpMyAdmin? You can create an Admin Controller in PS 1.7 to edit your invoices directly in your browser!
You can also view this tuto to completely customize an Admin Controller from scratch, with your Custom SQL Table
Objective
With this tutorial, you will be able to create an Admin Controller to create and edit any Prestashop class. We will take Order Invoice as an example. This way, we can add, remove and edit any field of any invoice in Prestashop.
This extension will be available in your Backoffice through a URL that looks like this:
https://my.presta.com/admin/index.php?controller=AdminCustomInvoices
We have to:
- Create the module and a simple Admin Controller
- Edit the Admin Controller to present all invoices in a list
- Edit it again to add/edit invoices
Create the Admin Controller (via a Module)
Admin Controllers only exist in a module. So we need to create a module first! Letβs create a dummy module in modules/fc_invoice/fc_invoice.php
:
<?php
if (!defined('_PS_VERSION_')) {exit;}
class Fc_Invoice extends Module {
public function __construct() {
$this->name = 'fc_invoice';
$this->tab = 'administration';
$this->version = '1.0.0';
$this->author = 'Florian Courgey';
$this->bootstrap = true;
parent::__construct();
$this->displayName = $this->l('FC Invoice Module');
$this->description = $this->l('Improve your store with FC modules');
$this->ps_versions_compliancy = ['min' => '1.7', 'max' => _PS_VERSION_];
}
public function install(){
return parent::install() && $this->_installTab();
}
protected function _installTab(){
$tab = new Tab();
$tab->class_name = 'AdminFcInvoice';
$tab->module = $this->name;
$tab->id_parent = (int)Tab::getIdFromClassName('DEFAULT');
$tab->icon = 'settings_applications';
$languages = Language::getLanguages();
foreach ($languages as $lang) {
$tab->name[$lang['id_lang']] = $this->l('FC Invoice');
}
$tab->save();
}
}
Then, activate it through the PrestShop Backoffice by looking for fc
:
Finally, we can create our actual AdminCustomInvoicesController
in modules/fc_module/controllers/admin/AdminFcInvoiceController.php
:
class AdminFcInvoiceController extends ModuleAdminController {
public function __construct(){
$this->bootstrap = true; // use Bootstrap CSS
$this->table = 'order_invoice'; // SQL table name, will be prefixed with _DB_PREFIX_
$this->className = 'OrderInvoice'; // PHP class name
$this->allow_export = true; // allow export in CSV, XLS..
$this->_orderBy = 'id_order_invoice';
}
}
Letβs check that everything is working well by heading to https://my.presta.com/admin/index.php?controller=AdminFcInvoice
:
NB: if you get a page not found error like below:
Prestashop uses Tab
to whitelist Controllers, you may need to execute directly in the database:
INSERT INTO `ps_tab` (id_parent, position, module, class_name, active)
VALUES (3, 6, 'fc_module', 'AdminFcInvoice', 1);
Add the List feature
For example, an Admin Controller to handle your invoices (list, view, create, edit and delete)
class AdminCustomInvoicesController extends ModuleAdminController {
public function __construct(){
// [...]
$this->fields_list = [
'id_order_invoice' => ['title' => $this->trans('ID', [], 'Admin.Global'),'class' => 'fixed-width-xs'],
'number' => ['title' => $this->trans('Number', [], 'Admin.Global'),'class' => 'fixed-width-xs'],
'date_add' => ['title' => $this->trans('Date', [], 'Admin.Global'), 'type'=>'datetime'],
'total_products_wt' => ['title' => $this->trans('Total products', [], 'Admin.Global'),
'align' => 'text-right',
'type' => 'price',
],
'total_shipping_tax_incl' => ['title' => $this->trans('Total shipping', [], 'Admin.Global'),
'align' => 'text-right',
'type' => 'price',
],
'total_paid_tax_incl' => ['title' => $this->trans('Total paid', [], 'Admin.Global'),
'align' => 'text-right',
'type' => 'price',
],
];
}
}
Which gives us the following list (sortable and filterable!!):
Add the edit & add features
Add $this->fields_form
and $this->addRowAction
:
class AdminCustomInvoicesController extends ModuleAdminController {
public function __construct(){
// [...]
$this->addRowAction('edit');
$this->addRowAction('details');
$this->fields_form = [
'legend' => ['title' => $this->l('Custom Invoice'),'icon' => 'icon-list-ul'],
'input' => [
['name' => 'date_add','type' => 'datetime','label' => 'Date add',],
['name'=>'number','type'=>'text','required' => true,'label' => 'Number',],
['name'=>'note','type'=>'textarea','label' => 'Note',],
['name'=>'delivery_number','type'=>'text','label'=>'Delivery number'],
['name'=>'delivery_date','type'=>'datetime','label'=>'Delivery date'],
['name'=>'total_discount_tax_excl','type'=>'text','label'=>'Total amount of discounts (no tax)'],
['name'=>'total_discount_tax_incl','type'=>'text','label'=>'Total amount of discounts (with tax)'],
['name'=>'total_shipping_tax_excl','type'=>'text','label'=>'Total cost of shipping (no tax)'],
['name'=>'total_shipping_tax_incl','type'=>'text','label'=>'Total cost of shipping (with tax)'],
['name'=>'total_products','type'=>'text','label'=>'Total cost of products (no tax)'],
['name'=>'total_products_wt','type'=>'text','label'=>'Total cost of products (with tax)'],
['name'=>'total_wrapping_tax_excl','type'=>'text','label'=>'Total cost of wrapping (no tax)'],
['name'=>'total_wrapping_tax_incl','type'=>'text','label'=>'Total cost of wrapping (with tax)'],
['name'=>'total_paid_tax_excl','type'=>'text','label'=>'Total paid (no tax)'],
['name'=>'total_paid_tax_incl','type'=>'text','label'=>'Total paid (with tax)'],
['name'=>'shop_address','type'=>'textarea','label'=>'Shop address'],
],
'submit' => ['title' => $this->trans('Save', [], 'Admin.Actions'),],
];
}
}
This new code adds buttons in the last column:
And hitting Edit results in a nice form, ready to be edited!
Going further
4. a) Add customer info in the list
class AdminCustomInvoicesController extends ModuleAdminController {
public function __construct(){
// [...]
$this->_select = 'concat(upper(c.lastname), " ", c.firstname) as customer';
$this->_join = '
JOIN '._DB_PREFIX_.'orders o ON (o.id_order = a.id_order)
JOIN '._DB_PREFIX_.'customer c ON (c.id_customer = o.id_customer)
';
$this->fields_list = [
// [...]
'customer' => ['title' => $this->trans('Customer', [], 'Admin.Global')],
// [...]
];
}
}
Displays the customer name in a column:
4. b) Add order info above the form
class AdminCustomInvoicesController extends ModuleAdminController {
// [...]
public function renderForm(){
$invoice = $this->object; // get invoice
$order = $invoice->getOrder(); // get order
$customer = $order->getCustomer(); // get customer
$currency = new Currency($order->id_currency); // get currency
// add some info in an HTML string
$info = '<div class="panel">';
$info .= '<div class="panel-heading"><i class="icon-list-ul"></i> Order informations</div>';
$info .= "Order reference: {$order->reference}<br/>";
$info .= "Customer: {$customer->firstname} {$customer->lastname}<br/>";
$info .= "Total_paid : ".Tools::displayPrice($order->total_paid, $currency)."<br/>";
$info .= "Get total paid : ".Tools::displayPrice($order->getTotalPaid(), $currency)."<br/>";
$info .= "Payment: {$order->payment}<br/>";
$info .= "Order state : {$order->getCurrentOrderState()->name[$this->context->language->id]}";
$info .= '</div>';
// push the HTML to $this->content
$this->content .= $info;
return parent::renderForm();
}
}
And we now have a simple panel that serves as a header to render some data about the order and the customer:
4. c) Delete an invoice
USE WITH CARE, you CANNOT undo a deletion!!!
class AdminCustomInvoicesController extends ModuleAdminController {
public function __construct(){
// [...]
$this->addRowAction('delete');
$this->bulk_actions = [
'delete' => [
'text' => 'Delete',
'icon' => 'icon-power-off'
],
];
}
}
This new code will add buttons to delete via one-shot and bulk:
Full version of the Code
Hosted on https://gist.github.com/floriancourgey/cb63fd5abd93e1109fcf624a68307403
Mobile and responsive version: