
Файлы модуля можно скачать в конце статьи.
Вид на странице оформления заказа, который будет в итоге:

Реализована проверка на заполнение обязательных полей с выводом соответствующего сообщения. Используется маска ввода телефонного номера.
Данные, указанные покупателем в нашем способе доставки, будут выводиться в меню «Продажи» - Заказы, а так же в печатных формах.
Для того, чтобы начать писать свои модули, нужно понимание концепции MVC, т.к. данная CMS построена с учетом данной архитектуры. Знание MVC, позволяет быстро освоить OpenСart тем, кто имеет такой опыт работы, в отличии от того же WordPress, который очень специфичен. MVC используют самые популярные PHP-фреймворки, например Yii2. Другими словами, если вы не знакомы с MVC и ООП - в начале советую начать с них.
Правда архитектура MVC в OpenСart не идеальна, например основной код содержится в контроллерах, а модели используются в основном только для работы с базой данных, но разобраться будет не сложно.
Документации для разработчиков по CMS OpenСart мало, поэтому возможны неточности в объяснении порядка и процесса подключения файлов т.к. в основном все приходится смотреть и познавать из исходного кода.
Наш модуль (дополнение) будет включаться в админ-панели, так же как и остальные способы доставки:
ДОПОЛНЕНИЯ - Дополнения - Список (Доставка)
поэтому начнем с админки.
Чтобы не писать код всех файлов тут на странице, скачайте АРХИВ и открывайте самостоятельно файл о котором буду писать.
Первым создаем файл контроллера - admin\controller\extension\shipping\ksl.php
В папке admin\controller\extension содержатся контроллеры всех дополнений, которые есть в админ-панели. Так как наше дополнение относится к способам доставки, файл создаем в папке shipping (с английского - перевозка/доставка). Все контроллеры в данной папке описывают свой способ доставки. Так же они все наследуются от стандартного класса контроллера Controller для доступа к его методам. Наш модуль будет называться "Ksl", поэтому, в нашем случае, название класса контроллера будет:class ControllerExtensionShippingKsl
Название класса контроллера должно быть согласно формата Controller(НазваниеПапки)(НазваниеФайла). Первые буквы папки и файла должны быть заглавными, так же в названии следует опускать расширение файла.
При выборе из списка дополнений "Доставка", вызывается контроллер ControllerExtensionExtensionShipping из файла
admin\controller\extension\extension\shipping.php
который с помощью функции glob() формирует список всех файлов содержащихся в папке shipping, куда, автоматически, попадет и наш файл контроллера.
Содержимое нашего файла контроллера ksl.php аналогично другим в данной папке. То есть, при создании проще всего скопировать другой и подставить его название (в нашем случае "ksl") вместо названия другого способа доставки во всех строках, где нужно.
Названия файлов для дополнений (модулей) принято делать одинаковыми, давая уникальные названия, которые не совпадут с другими файлами. Кроме того так удобнее находить файлы отдельного модуля, что может понадобиться для редактирования и удаления.
В OpenСart принято основной функционал описывать именно в контроллерах. В данном контроллере всего 2 метода: index() и validate(). В первом, строкой
$this->load->language('extension/shipping/ksl');загружаем языковой файл. Следующей строкой сразу устанавливаем заголовок страницы:
$this->document->setTitle($this->language->get('text_heading_title'));Далее загружаем модель:
$this->load->model('setting/setting')Потом проверяется существование POST-запроса:
if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) {...}данный код сработает в случае внесения изменений в метод доставки - включение/выключение, порядок сортировки и др. Так же тут вызывается второй метод контроллера - validate(), в котором проверяется имеет ли данный пользователь права на изменение нашего способа доставки. Если нет - выводится сообщение языкового файла "У Вас нет прав для управления данным модулем!"
Далее в нашем контроллере формируется ассоциативный массив данных:
$data['header'] = $this->load->controller('common/header'); $data['column_left'] = $this->load->controller('common/column_left'); $data['footer'] = $this->load->controller('common/footer');данные которого будут использованы в файле представлении (виде).
Например так получаем и сохраняем фразы на текущем языке:
$data['heading_title'] = $this->language->get('heading_title'); ...
Так же формируется массив для "хлебных крошек" админки, используемый для удобной навигации
$data['breadcrumbs']
В этот же массив добавляются и значения из базы данных (БД), для этого подключаются дополнительные модели, например
$this->load->model('localisation/geo_zone');
Для вывода стандартных частей страницы - шапки, подвала и др. подключаются нужные контроллеры, которые формируют и возвращают данные части страницы. Потом их так же сохраняем в массив $data:
$data['header'] = $this->load->controller('common/header'); $data['column_left'] = $this->load->controller('common/column_left'); $data['footer'] = $this->load->controller('common/footer');
В конце метода index() в строке
$this->response->setOutput($this->load->view('extension/shipping/ksl', $data));осуществляется вызов метода setOutput() класса Response, который готовит к выводу файл представление extension/shipping/ksl.php передавая массив данных $data.
Языковой файл admin\language\ru-ru\extension\shipping\ksl.php
Языковой файл используется для хранения текстовых данных в виде ассоциативных массивов, которые потом загружаются в контроллер и передаются в файл представления для вывода на странице. В принципе, прописать нужные сообщения и фразы можно было бы сразу в контроллере, но языковые файлы выполняют еще одну важную функцию - с помощью их создается перевод на нужный (текущий) язык. По-умолчанию это русский и английский. Наш модуль будет так же выводиться на двух языках.С содержимым файла разобраться не сложно. Например:
$_['ksl_phone'] = 'Номер телефона';
Ключами (ksl_phone) выступают фразы или буквы написанные латиницей, одинаковые для всех языковых файлов. Значения же - полная фраза на соответствующем языке.
Так же создадим аналог данного файла, содержащий английские фразы
admin\language\en-gb\extension\shipping\ksl.php
Данный массив в английской версии примет вид:
$_['ksl_phone'] = 'Phone number';Как я уже писал, в контроллере значение языкового файла (соответствующего текущему языку приложения) можно получить так:
$data['text_enabled'] = $this->language->get('text_enabled');
Файл представление для админ-панели admin\view\template\extension\shipping\ksl.tpl
Файлы-представления (виды) в OpenСart имеют расширение .tpl Файлы с данным расширением используются (обычно) для написания в них HTML-кода, который впоследствии будет "вытащен" PHP и выведен пользователю на конкретных страницах. То есть это можно использовать для разделения работы верстальщика и php кодера, чтобы тот же верстальщик не поменял, пусть случайно, php код. Обычно в таких файлах на месте, где должен быть вывод какой-то php переменной, стоит шаблон такого плана:<b>{username}</b>который потом в скрипте заменяется на нужное значение
$user_panel = str_replace("{username}", $username, $user_panel);Особого смысла использования расширения .tpl в файлах видов OpenСart я не увидел.
OpenСart использует CSS/HTML-фреймворк Bootstrap для создания адаптивного дизайна страниц, в нашем модуле будем делать так же. Впрочем, как я уже писал, достаточно было бы скопировать вид другого способа доставки, который уже имеется и поменять название того дополнения на "ksl".
Наш файл-представление (вид) первой строкой
<?php echo $header; ?><?php echo $column_left; ?>выводит шапку сайта и содержимое левой колонки, которое было получено в массиве $data из контроллера. В представлении, обращаться к элементам данного массива можно напрямую, так как элементы массива предварительно распаковываются при передаче в вид - ключи становятся названиями переменных, а значения ассоциативных массивов значениями этих переменных.
Далее у нас обычная HTML разметка страницы с выводом нужных данных из все того же массива $data в т.ч. с использованием циклов, проверок и прочего.
Так же имеется форма:
<form action="<?php echo $action; ?>" method="post" enctype="multipart/form-data" id="form-ksl" class="form-horizontal"> ... </form>Ее вид в админ-панели:

она передает введенные данные POST запросом в наш контроллер в котором, как раз и была проверка на поступивший POST запрос. Само значение атрибута action у нас так же передается в вид из контроллера строкой
$data['action'] = $this->url->link('extension/shipping/ksl', 'token=' . $this->session->data['token'], true);Ну и в конце файла вида выводится содержимое подвала строкой
<?php echo $footer; ?>Данное содержимое мы так же получили из контроллера, в котором вызывали контроллер ответственный за вывод подвала страницы.
На этом закончена часть по созданию и выводу в Админ-панели нашего способа доставки в разделе
ДОПОЛНЕНИЯ - Дополнения - Список (Доставка).
Правда еще мы сделаем, чтобы метод доставки отображался на странице самого заказа, но позже, а пока займемся frontendом, то есть частью с которой будет работать посетитель сайта.
Стоит еще заметить, что при разработке своих модулей может потребоваться создание и отдельного файла модели (в контексте OpenСart это класс ответственный за работу с базой данных). В нашем случае в этом не было необходимости, т.к. мы использовали стандартные модели, подключив их в контроллере, которые работают с любым способом доставки в т.ч. и нашим.
Файлы, ответственные за генерирование страниц которые видит обычный посетитель сайта, находятся в директории catalog. В дальнейшей работе нам не потребуется создавать отдельный контроллер, как для админ-панели, т.к. обрабатывать данные всех способов доставки будет один общий контроллер
catalog\controller\checkout\shipping_method.php
уже существующий в фреймворке. Но будет создана отдельная модель для модуля ksl, т.к. данный контроллер подключает модели всех способов доставки в цикле.
Порядок выполнения скриптов для вывода страницы оформления Заказа.
Напомню, что страница оформления Заказа, это страница, на которую посетитель сайта попадает из корзины, нажав ссылку "Оформить заказ". Как я уже писал, при создании модуля приходится разбирать существующий код данного фреймворка чтобы понять порядок подключения файлов и какие данные они друг другу передают. Поэтому пройдемся по теории. К сожалению с документацией для разработчиков у OpenСart пока не очень, поэтому почти все нижеизложенное (как и вышеизложенное) я описал исходя из самостоятельного разбора работы данной CMS.Стандартная страница заказа состоит из нескольких вкладок, каждой из которых соответствует свой контроллер, модель и шаблон.
Общий контроллер страницы заказа находится в файле catalog\controller\checkout\checkout.php
он выводит общий файл представление catalog\view\theme\default\template\checkout\checkout.tpl
в котором так же находится много JS скриптов, которые отслеживают нажатия кнопок на каждой из закладок и AJAX запросами сохраняют данные в сессии так же открывая следующую закладку. После заполнения всех закладок вызывается контроллер из файла catalog\controller\checkout\confirm.php который передает данные в модель catalog\model\checkout\order.php для сохранения в БД. Подробнее ниже.
Рассмотрим закладку выбора способа доставки.
Контроллер способов доставки (общий): catalog\controller\checkout\shipping_method.php
В нем в строке
$results = $this->model_extension_extension->getExtensions('shipping');в массив $results попадают все методы доставки. Далее, в цикле
foreach ($results as $result) {…}загружается модель каждого из методов. Далее из БД выбираются все поля и сохраняются в $quote, нужные из которых сортируются и в строке
$this->session->data['shipping_methods'] = $method_data;сохраняются в сессию.
Затем контроллер формирует общий массив $data, получает дополнительные данные из языковых файлов, например
$data['text_comments'] = $this->language->get('text_comments');так же данные из сессии сохраняет в $data['shipping_methods']
и передает массив данных $data в вид.
Модель отдельного способа доставки: catalog\model\extension\shipping\ksl.php
Данные перечисленные в массиве $quote_data['ksl'] = array(…);
можно получить в представлении, в массиве $quote. Например echo $quote['ksl_comment'];
Вид способов доставки (общий): catalog\view\theme\default\template\checkout\shipping_method.tpl
При загрузке файла вида, данные из массива ($data) распаковываются, ключи становятся названиями переменных и к ним можно обратиться напрямую:
вместо
$this->data['shipping_methods']так
$shipping_methods
В виде, в цикле
<?php foreach ($shipping_methods as $shipping_method) {...} ?>выводятся, поочереди, все методы доставки, в которых дополнительным циклом выводятся нужные данные и поля.
Перед подключением отдельных шаблонов (оплата, доставка и тд.) подключается общий шаблон станицы оформления заказа
catalog\view\theme\default\template\checkout\checkout.tpl
который также содержит JS скрипты, отсылающие разные AJAX запросы по нажатию на кнопки подтверждений.
Если, например, была нажата кнопка «подтвердить» на закладке «Способ доставки», то сработает событие
$(document).delegate('#button-shipping-method', 'click', function() {...т.к. кнопка имела id='button-shipping-method'
которое отправит данные AJAX-запросом (без перезагрузки) из формы в контроллер
catalog\controller\checkout\shipping_method.php метод save()
В данном методе, значения сохраняются в сессию, например комментарии:
$this->session->data['comment'] = strip_tags($this->request->post['comment']);
При нажатии предпоследней кнопки подтверждения на странице заказа на закладке «Способ оплаты», вызывается контроллер
catalog\controller\checkout\confirm.php
который загружает модель catalog\model\checkout\order.php в строке
$this->load->model('checkout/order');затем строкой
$this->session->data['order_id'] = $this->model_checkout_order->addOrder($order_data);вызывает метод addOrder($data), который записывает все данные заказа и сохраняет в сессию id заказа.
По нажатию последней кнопки «Подтверждение заказа», к сохраненным данным заказа в базе данных, добавляется единица (true) в ячейку order_status_id. Без этого заказ считается неподтвержденным и не отображается в админке в списке заказов.
Оформленный заказ в БД хранится в таблице oc_order.
Теперь практика.
Создаем (или в вашем случае - открываем) файл модели catalog\model\extension\shipping\ksl.php
Данный файл так же является близнецом файлов отвечающих за другие способы доставки и хранимых в той же папке "shipping".Название класса модели задается по тем же правилам, что и название класса контроллера. В нашем случае это будет
class ModelExtensionShippingKsl{}
который наследуется от общего класса Model и содержит всего один метод getQuote().
Первым делом загружается языковой файл, далее идет выборка из БД согласно переданной методу геозоне, что в нашем модуле и не только в нем не используется, но ломать общий шаблон модели доставки не будем.
Далее создаются и заполняются массивы данных $quote_data и $method_data на основе языкового файла и данных введенных вручную. Так, можно указать стоимость доставки для своего метода в массиве в строке
'cost' => 0.00,
но в нашем способе доставки Новой Почтой стоимость может меняться.
Далее все данные собираются в один массив, который и возвращается контроллеру вызвавшему нашу модель:
return $method_data;
Языковой файл catalog\language\ru-ru\extension\shipping\ksl.php
Данный файл заполняем по тем же правилам, что и аналогичный файл для админ-панели (см.выше), указывая в нем сообщения и названия полей формы, которую должен будет заполнить покупатель. Не забываем создать и файл с английским переводом catalog\language\en-gb\extension\shipping\ksl.phpПредставление для вывода своего способа доставки
Согласно порядка вывода страницы оформления заказа, который я расписал выше, выводом способов доставки занимается один общий файл:catalog\view\theme\default\template\checkout\shipping_method.tpl
поэтому создавать отдельный вид мы не будем. Но в данном файле не предусмотрен вывод формы которую необходимо заполнить покупателю, содержащую такую нужную информацию для доставки "Новой Почтой" как
- населенный пункт;
- номер отделения службы доставки;
- ФИО получателя;
- номер телефона получателя
НазваниеДополнения.ocmod.xml
Служит модификатор для внесения изменений в файлы фреймворка - создает виртуальную копию файла с которым производились изменения. Данные файлы, с внесенными изменениями, вы можете найти в папке
system\storage\modification
Подробно останавливаться не буду, т.к это займет очень много места в моей и так немаленькой статье.
Рассмотрим лишь некоторые моменты.
В элементе file указываем путь к файлу, в который необходимо внести изменения, первый такой файл у нас тот самый общий, для всех способов доставки вид
<file path="catalog/view/theme/*/template/checkout/shipping_method.tpl">Звездочка означает "любой набор символов", в данном случае любая тема, которая находится в папке theme.
В файле темы находим строку
- <?php echo $quote['text']; ?>вместо нее я хочу разместить свой код, для чего указываю
<add position="replace">...есть так же варианты не замены, а вставки до или после найденного, как использовано ниже.
В вставляемом коде я проверяю обрабатывается ли в данный момент в цикле наш способ доставки, и если да - вызываю JS скрипт запуска маски ввода телефонного номера для нашей формы и далее вывод всех нужных мне полей формы.
Как писалось в порядке выполнения скриптов, на каждой закладке страницы оформления Заказа есть кнопка "Продолжить", по нажатию которой срабатывает действие одного из JS скриптов общего файла вида catalog\view\theme\default\template\checkout\checkout.tpl который отправляет AJAX запрос с введенными данными. Данные закладки "Способ доставки" отсылаются в контроллер controller\checkout\shipping_method.php в метод save(), что видно в строке
$(document).delegate('#button-shipping-method', 'click', function() { $.ajax({ url: 'index.php?route=checkout/shipping_method/save', type: 'post', data: $('#collapse-shipping-method input[type=\'radio\']:checked, #collapse-shipping-method textarea'), ..тут мы так же видим, в свойстве data, каких двух полей данные будут переданы. А именно - какой пункт доставки выбран и поле ввода комментария. Нам же нужно добавить значения из своих полей, поэтому добавляем в файл-модификатор ksl.ocmod.xml новые данные. Как и при вставке самой формы - указываем какой файл будем менять, находим нужную строку и меняем ее таким образом:
<add position="replace"><![CDATA[ data: $('#collapse-shipping-method input[type=\'radio\']:checked, #collapse-shipping-method textarea, #ksl-shipping-method #ksl_city, #ksl-shipping-method #ksl_number, #ksl-shipping-method #ksl_name, #ksl-shipping-method #ksl_phone, #ksl-shipping-method textarea'), ]]>Следующим блоком файла модификатора является изменение файла catalog/controller/checkout/shipping_method.php, который полученные из скрипта данные в сессию. Я решил добавить свои данные после сохранения значения стандартного поля комментариев, строки
$this->session->data['comment'] = strip_tags($this->request->post['comment']);Первым делом проверяем выбран ли наш способ доставки:
if($this->request->post['shipping_method'] == 'ksl.ksl')и если выбран - проверяем заполненность обязательных полей:
if(empty($this->request->post['ksl_city'])...в случае, если нет - заполняем массив $json['error'], который в данном методе используется для сбора информации об ошибках, а далее в JS скрипте для вывода сообщения пользователю об ошибке:
$json['error']['warning'] = $this->language->get('ksl_required');Если все нормально - сохраняем данные в сессию, предварительно удалив, возможные теги из текста:
if (!$json) { $this->session->data['ksl_city'] = strip_tags($this->request->post['ksl_city']); ...
Далее, как я писал выше в "Порядке выполнения скриптов", при нажатии кнопки подтверждения на закладке выбора способа оплаты, JS скрипт вызывает контроллер catalog/controller/checkout/confirm.php, который уже считывает данные из сессии и сохраняет в БД. Поэтому аналогичным образом меняем этот файл, добавив сохранение данных своих полей после сохранения поля с комментарием.
Полей у нас не мало и таких нет в таблице oc_order, в которую сохраняются все поля по каждому отдельному заказу, будь то оплата, доставка и тд. Но данная таблица содержит поле shipping_custom_field, которое по-умолчанию пусто и в которое, исходя из его названия, и нужно сохранять произвольные поля касающиеся доставки. Т.к. полей формы несколько, сохраняем их в массив $kslShipping, который и записываем в shipping_custom_field:
$order_data['shipping_custom_field'] = $kslShipping;
Почему $order_data['shipping_custom_field']? Проанализировав дальнейший код контроллера ControllerCheckoutConfirm, видим, что все данные стекаются в ассоциативный массив $order_data, у которого ключи соответствуют названиям полей таблицы oc_order. Так же, ниже мы видим строки
$this->load->model('checkout/order');<span class="redactor-invisible-space"></span> $this->session->data['order_id'] = $this->model_checkout_order->addOrder($order_data);в которых загружается модель ответственная за работу с таблицей заказа, а методу, ответственному за сохранение данных в БД, как раз, передается массив $order_data для сохранения.
Подключаем стили и скрипты.
Для придания форме доставки нужного вида я создал небольшой файл стилей, который можно найти в catalog/view/theme/default/stylesheet/ksl_shipping.cssВ подключении так же использован модификатор, хотя ситуации бывают разные, можно было вообще не создавать отдельный файл стилей, но так больше порядка. Вы можете попытаться вставить нужные стили в уже существующий файл стилей темы, но проблема в том, что стандартный файл стилей темы по-умолчанию подключается раньше, чем файл-модификатор создаст его измененную копию, поэтому такой способ не сработает.
Поэтому подключим свой файл стилей (в модификаторе ksl.ocmod.xml) модифицируя контроллер отвечающий за вывод шапки сайта
catalog/controller/common/header.php
Находим там строку
$data['styles'] = $this->document->getStyles();в которой формируется массив стилей. Видим, что массив возвращает метод getStyles() объекта Document Object.
Опять же, проанализировав код класса данного объекта, находим там метод addStyle() формирующий массив всех стилей. Это нам и надо, поэтому, перед этой строкой контроллера, прописываем путь к своему файлу стилей:
$this->document->addStyle('catalog/view/theme/default/stylesheet/ksl_shipping.css');
На счет JS скриптов - все точно так же и даже методы Document Object названы аналогично:
- getScripts() - получить массив скриптов;
- addScript() - добавить скрипт в массив.
$this->document->addScript('catalog/view/javascript/ksl-shipping/ksl-shipping.js');данным скриптом я сделал скрытие полей формы при выборе другого способа доставки чем наш, а так же автоматическое переключение на "Новую почту, при клике на любом из полей формы нашего способа доставки.
$this->document->addScript('catalog/view/javascript/ksl-shipping/jquery.maskedinput.min.js');данной строкой добавляем скрипт работающий с маской ввода телефонного номера для нашего поля. Скрипт скачан с этого сайта, там же и его описание. Саму маску можно поменять в модификаторе, там, где мы добавляли форму, в строке
<input type="text" id="ksl_phone" class="form-control" name="ksl_phone" placeholder="+38 (___) __-__-___">
На этот создание frontend-части, выводящей выбор способа доставки "Новая Почта" посетителям магазина завершено.
Администратор интернет-магазина должен иметь доступ к тем данным которые покупатель указал на странице оформления заказа. Поэтому добавим вывод информации указанной в нашем модуле добавляющем способ доставки "Новая Почта" на страницу заказа, а так же в печатные формы, которые могут быть использованы.
Вид страницы заказа в админ-панели:

Добавляем вывод данных модуля в Админ-панель, на страницу ПРОДАЖИ - Заказы - заказ.
Нам нет нужды создавать для этой цели свои файлы, контроллеры, модели, виды... Это можно осуществить вставив свой код в необходимые файлы, поэтому опять используем файл-модификатор ksl.ocmod.xml.
За сбор необходимых данных из базы данных по каждому отдельному заказу, отвечает контроллер ControllerSaleOrder, находящийся, как можно догадаться, в
admin/controller/sale/order.php
Он содержит такие, нужные нам методы:
- info() - вывод страницы заказа;
- invoice() - печать счета;
- shipping() - печать списка доставки
$order_info = $this->model_sale_order->getOrder($order_id);загружают, с помощью метода getOrder() модели ModelSaleOrder, данные из таблиц связанных с заказом, в т.ч. и таблицу oc_order, куда мы сохраняли массив данных из формы нашего способа доставки, которую заполняет покупатель.
По-умолчанию данное поле таблицы (shipping_custom_field) контроллером не сохраняется и не используется. Поэтому нужно сохранить самостоятельно:
if(!empty($order_info['shipping_custom_field'])){ if(__FUNCTION__ =='info' || __FUNCTION__ =='invoice' || __FUNCTION__ =='shipping'){ $data['ksl_shipping_fields'] = $order_info['shipping_custom_field']; ...Сначала проверяем есть ли данные для вывода в поле shipping_custom_field таблицы. Далее я сделал проверку на название текущего метода, т.к строка
$order_info = $this->model_sale_order->getOrder($order_id);после которой нужно вставить сохранение данных, присутствует так же и в других, ненужных нам методах. Чтобы не дублировать в модификаторе блок вставки 3 раза для трех нужных методов, я сделал эту проверку.
Потом мы просто сохраняем в массив $data все нужные данные для вывода в админке на странице Заказа.
Далее модифицируем файл admin/view/template/sale/order_info.tpl который отвечает за вывод данной страницы в Админ-панели.
Тут все просто - вставляем в нужное нам место, я решил разместить вывод информации про доставку "Новой Почтой" перед блоком "Детали заказа". В модификаторе это отражено как третий по-порядку блок (счет с нуля), начинающийся строкой
<div class="panel panel-default">Потом создал обычную таблицу, с учетом Bootstrap, для вывода и подставил вывод значений переменных.
Аналогичным образом модифицировал файлы отвечающие за вывод печатных форм заказа. Так как они почти идентичны, прописал их оба вместе:
<file path="admin/view/template/sale/order_invoice.tpl|admin/view/template/sale/order_shipping.tpl">
Все, модуль готов! Еще раз даю ссылку на его скачивание. Он сразу готов к использованию, но конечно можно еще много всего улучшить и добавить какой-то функционал. В данной статье я постарался подробно описать весь процесс передачи данных от пользователя до сохранения их в БД, поэтому сможете подправить модуль под свои нужды.
ответ на комментарий Игорь от 24.06.2018