PHP

Создание товарной накладной в Excel на PHPExcel

Пример как сформировать товарную накладную в с помощью класса PHPExcel. Скачать класс можно с официального сайта или архив с сайта (1.8.0).

В результате получится файл в формате xlsx (Excel 2007). Его можно посмотреть тут или скрин:

Подключаем PHPExcel

require_once __DIR__ . '/PHPExcel/Classes/PHPExcel.php';
require_once __DIR__ . '/PHPExcel/Classes/PHPExcel/Writer/Excel2007.php';

Создаем экземпляр класса PHPExcel.

$xls = new PHPExcel();

Писать будем в первый лист.

$xls->setActiveSheetIndex(0);
$sheet = $xls->getActiveSheet();

Задаем ширину столбцов

Ширина задается в количестве символов.

$sheet->getColumnDimension('A')->setWidth(12);
$sheet->getColumnDimension('B')->setWidth(17);
$sheet->getColumnDimension('C')->setWidth(60);
$sheet->getColumnDimension('D')->setWidth(10);
$sheet->getColumnDimension('E')->setWidth(6);
$sheet->getColumnDimension('F')->setWidth(10);
$sheet->getColumnDimension('G')->setWidth(10);

Заголовок накладной

Для удобства заводим переменную $line, в ней будем считать номер строки.

$line = 1;
$sheet->setCellValue("A{$line}", 'Товарная накладная № 1 от ' . date('d.m.Y H:i'));

Объединяем ячейки по горизонтали.

$sheet->mergeCells("A{$line}:G{$line}");

Делаем выравнивание по центру вертикали и горизонтали.

$sheet->getStyle("A{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
$sheet->getStyle("A{$line}")->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);

Делаем текст жирным и увеличиваем шрифт.

$sheet->getStyle("A{$line}")->getFont()->setBold(true);
$sheet->getStyle("A{$line}")->getFont()->setSize(18);

Пропускаем строку после заголовка.

$line++;
$sheet->setCellValue("A{$line}", '');
$sheet->mergeCells("A{$line}:G{$line}");

Информация о поставщике

$line++;
$sheet->setCellValue("A{$line}", 'Поставщик:');
$sheet->setCellValue("B{$line}", htmlspecialchars_decode('ООО Рога'));
$sheet->getStyle("B{$line}")->getFont()->setBold(true);
$sheet->mergeCells("B{$line}:G{$line}");

$line++;
$sheet->setCellValue("B{$line}", 'Адрес: г. Москва, ул. Тверская, д.24, тел: 8 (923) 123-45-67'); 
$sheet->mergeCells("B{$line}:G{$line}");

Информация о покупателе

$line++;
$sheet->setCellValue("A{$line}", 'Покупатель:');
$sheet->setCellValue("B{$line}", 'Иванов Иван Иванович');
$sheet->getStyle("B{$line}")->getFont()->setBold(true);
$sheet->mergeCells("B{$line}:G{$line}");

$line++;
$sheet->setCellValue("B{$line}", 'Тел 9 (999) 999-99-99');
$sheet->mergeCells("B{$line}:G{$line}");

Пропускаем строку.

$line++;
$sheet->setCellValue("A{$line}", '');
$sheet->mergeCells("A{$line}:G{$line}");

Таблица с товарами

Запоминаем строку с которой начинается таблица чтобы потом сделать рамку.

$line++;
$start_table = $line;

Шапка таблицы

$sheet->setCellValue("A{$line}", 'п/п');
$sheet->setCellValue("B{$line}", 'Артикул');
$sheet->setCellValue("C{$line}", 'Название');
$sheet->setCellValue("D{$line}", 'Кол-во');
$sheet->setCellValue("E{$line}", 'Ед.');
$sheet->setCellValue("F{$line}", 'Цена');
$sheet->setCellValue("G{$line}", 'Сумма');

Стили для текста в шапки таблицы.

$sheet->getStyle("A{$line}:G{$line}")->getFont()->setBold(true); 
$sheet->getStyle("A{$line}:G{$line}")->getAlignment()->setWrapText(true);
$sheet->getStyle("A{$line}:G{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
$sheet->getStyle("A{$line}:G{$line}")->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);

В данном примере товары представлены в виде массива.

$prods = array(
    array(
        'sku'   => '8545775',
        'name'  => 'Боксерские перчатки GREEN HILL Super Star (без марки AIBA)',
        'price' => '6060',
        'count' => '2'
    ),
    array(
        'sku'   => '865645',
        'name'  => 'Боксерский мешок 120X35, 46 кг',
        'price' => '9900',
        'count' => '1'
    ),
    array(
        'sku'   => '865643',
        'name'  => 'Кронштейн для боксерского мешка',
        'price' => '4800',
        'count' => '3'
    ),
);

Далее в цикле выводим товары.

foreach ($prods as $i => $prod) {
    $line++;
    $sheet->setCellValue("A{$line}", ++$i);
    $sheet->setCellValue("B{$line}", $prod['sku']);
    $sheet->setCellValue("C{$line}", $prod['name']);
    $sheet->setCellValue("D{$line}", $prod['count']);
    $sheet->setCellValue("E{$line}", 'шт.');
    $sheet->setCellValue("F{$line}", number_format($prod['price'], 2, ',', ' '));
    $sheet->setCellValue("G{$line}", number_format($prod['price'] * $prod['count'], 2, ',', ' '));

    // Выравнивание текста в ячейках.
    $sheet->getStyle("A{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
    $sheet->getStyle("B{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
    $sheet->getStyle("C{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
    $sheet->getStyle("D{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
    $sheet->getStyle("E{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
    $sheet->getStyle("F{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
    $sheet->getStyle("G{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);

    // Подсчет "Итого".
    @$total += $prod['price'] * $prod['count'];
}

Добавляем рамку к таблице.

$sheet->getStyle("A{$start_table}:G{$line}")->applyFromArray(
    array(
        'borders' => array(
            'allborders' => array(
                'style' => PHPExcel_Style_Border::BORDER_THIN
            )
        )
    )
);

Итого

$line++;
$sheet->setCellValue("A{$line}", 'Итого:');
$sheet->mergeCells("A{$line}:F{$line}");

$sheet->setCellValue("G{$line}", number_format($total, 2, ',', ' '));
$sheet->getStyle("A{$line}:G{$line}")->getFont()->setBold(true);
$sheet->getStyle("A{$line}:G{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);

НДС (18% от итого)

$line++;
$sheet->setCellValue("A{$line}", 'В том числе НДС:');
$sheet->mergeCells("A{$line}:F{$line}");

$sheet->setCellValue("G{$line}", number_format(($total / 100) * 18, 2, ',', ' '));
$sheet->getStyle("A{$line}:G{$line}")->getFont()->setBold(true);
$sheet->getStyle("A{$line}:G{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);

Всего наименований

$line++;
$sheet->setCellValue(
    "A{$line}",
    'Всего наименований ' . count($prods) . ', на сумму ' . number_format($total, 2, ',', ' ') . ' руб.'
);
$sheet->mergeCells("A{$line}:G{$line}");

Сумма прописью

Здесь используется функция num2str() для получение суммы прописью, взято с https://habrahabr.ru/post/53210/

Еще нужно у суммы прописью сделать первую букву заглавной. Т.к. скрипт в UTF-8 функция ucfirst не работает, поэтому используется аналог - mb_ucfirst().

$line++;
$sheet->setCellValue("A{$line}", mb_ucfirst(num2str($total)));
$sheet->getStyle("A{$line}")->getFont()->setBold(true);
$sheet->mergeCells("A{$line}:G{$line}");

Файл готов

Отдаем его браузеру на скачивание

header("Expires: Mon, 1 Apr 1974 05:00:00 GMT");
header("Last-Modified: " . gmdate("D,d M YH:i:s") . " GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Content-type: application/vnd.ms-excel" );
header("Content-Disposition: attachment; filename=order.xlsx");

$objWriter = new PHPExcel_Writer_Excel2007($xls);
$objWriter->save('php://output'); 

Или сохраняем на сервере

$objWriter = new PHPExcel_Writer_Excel2007($xls);
$objWriter->save(__DIR__ . '/order.xlsx');

// Подключаем PHPExcel.
require_once __DIR__ . '/PHPExcel/Classes/PHPExcel.php';
require_once __DIR__ . '/PHPExcel/Classes/PHPExcel/Writer/Excel2007.php';

// Создаем экземпляр класса PHPExcel.
$xls = new PHPExcel();

// Писать будем в первый лист.
$xls->setActiveSheetIndex(0);
$sheet = $xls->getActiveSheet();

// Задаем ширину столбцов (ширина задается в количестве символов).
$sheet->getColumnDimension('A')->setWidth(12);
$sheet->getColumnDimension('B')->setWidth(17);
$sheet->getColumnDimension('C')->setWidth(60);
$sheet->getColumnDimension('D')->setWidth(10);
$sheet->getColumnDimension('E')->setWidth(6);
$sheet->getColumnDimension('F')->setWidth(10);
$sheet->getColumnDimension('G')->setWidth(10);

// Для удобства заводим переменную $line, в ней будем считать номер строки.
$line = 1;

// Заголовок накладной.
$sheet->setCellValue("A{$line}", 'Товарная накладная № 1 от ' . date('d.m.Y H:i'));

// Объединяем ячейки по горизонтали.
$sheet->mergeCells("A{$line}:G{$line}");

// Делаем выравнивание по центру вертикали и горизонтали.
$sheet->getStyle("A{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
$sheet->getStyle("A{$line}")->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);

// Делаем текст жирным и увеличиваем шрифт.
$sheet->getStyle("A{$line}")->getFont()->setBold(true);
$sheet->getStyle("A{$line}")->getFont()->setSize(18);

// Пропускаем строку после заголовка.
$line++;
$sheet->setCellValue("A{$line}", '');
$sheet->mergeCells("A{$line}:G{$line}");

// Информация о поставщике.
$line++;
$sheet->setCellValue("A{$line}", 'Поставщик:');
$sheet->setCellValue("B{$line}", htmlspecialchars_decode('ООО Рога'));
$sheet->getStyle("B{$line}")->getFont()->setBold(true);
$sheet->mergeCells("B{$line}:G{$line}");

$line++;
$sheet->setCellValue("B{$line}", 'Адрес: г. Москва, ул. Тверская, д.24, тел: 8 (923) 123-45-67'); 
$sheet->mergeCells("B{$line}:G{$line}");

// Информация о покупателе.
$line++;
$sheet->setCellValue("A{$line}", 'Покупатель:');
$sheet->setCellValue("B{$line}", 'Иванов Иван Иванович');
$sheet->getStyle("B{$line}")->getFont()->setBold(true);
$sheet->mergeCells("B{$line}:G{$line}");

$line++;
$sheet->setCellValue("B{$line}", 'Тел 9 (999) 999-99-99');
$sheet->mergeCells("B{$line}:G{$line}");

// Пропускаем строку.
$line++;
$sheet->setCellValue("A{$line}", '');
$sheet->mergeCells("A{$line}:G{$line}");

// Далее идет таблица с товарами.
// Запоминаем строку с которой начинается таблица чтобы потом сделать рамку.
$line++;
$start_table = $line;

// Шапка таблицы.
$sheet->setCellValue("A{$line}", 'п/п');
$sheet->setCellValue("B{$line}", 'Артикул');
$sheet->setCellValue("C{$line}", 'Название');
$sheet->setCellValue("D{$line}", 'Кол-во');
$sheet->setCellValue("E{$line}", 'Ед.');
$sheet->setCellValue("F{$line}", 'Цена');
$sheet->setCellValue("G{$line}", 'Сумма');    

// Стили для текста в шапки таблицы.
$sheet->getStyle("A{$line}:G{$line}")->getFont()->setBold(true); 
$sheet->getStyle("A{$line}:G{$line}")->getAlignment()->setWrapText(true);
$sheet->getStyle("A{$line}:G{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
$sheet->getStyle("A{$line}:G{$line}")->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);

// В данном примере товары представлены в виде массива.
$prods = array(
    array(
        'sku'   => '8545775',
        'name'  => 'Боксерские перчатки GREEN HILL Super Star (без марки AIBA)',
        'price' => '6060',
        'count' => '2'
    ),
    array(
        'sku'   => '865645',
        'name'  => 'Боксерский мешок 120X35, 46 кг',
        'price' => '9900',
        'count' => '1'
    ),
    array(
        'sku'   => '865643',
        'name'  => 'Кронштейн для боксерского мешка',
        'price' => '4800',
        'count' => '3'
    ),
);

// Далее в цикле выводим товары.
foreach ($prods as $i => $prod) {
    $line++;
    $sheet->setCellValue("A{$line}", ++$i);
    $sheet->setCellValue("B{$line}", $prod['sku']);
    $sheet->setCellValue("C{$line}", $prod['name']);
    $sheet->setCellValue("D{$line}", $prod['count']);
    $sheet->setCellValue("E{$line}", 'шт.');
    $sheet->setCellValue("F{$line}", number_format($prod['price'], 2, ',', ' '));
    $sheet->setCellValue("G{$line}", number_format($prod['price'] * $prod['count'], 2, ',', ' '));

    // Выравнивание текста в ячейках.
    $sheet->getStyle("A{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
    $sheet->getStyle("B{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
    $sheet->getStyle("C{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
    $sheet->getStyle("D{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
    $sheet->getStyle("E{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
    $sheet->getStyle("F{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
    $sheet->getStyle("G{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);

    // Подсчет "Итого".
    @$total += $prod['price'] * $prod['count'];
}

// Добавляем рамку к таблице.
$sheet->getStyle("A{$start_table}:G{$line}")->applyFromArray(
    array(
        'borders' => array(
            'allborders' => array(
                'style' => PHPExcel_Style_Border::BORDER_THIN
            )
        )
    )
);

// Итого.
$line++;
$sheet->setCellValue("A{$line}", 'Итого:');
$sheet->mergeCells("A{$line}:F{$line}");

$sheet->setCellValue("G{$line}", number_format($total, 2, ',', ' '));
$sheet->getStyle("A{$line}:G{$line}")->getFont()->setBold(true);
$sheet->getStyle("A{$line}:G{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);

// НДС (18% от итого).
$line++;
$sheet->setCellValue("A{$line}", 'В том числе НДС:');
$sheet->mergeCells("A{$line}:F{$line}");

$sheet->setCellValue("G{$line}", number_format(($total / 100) * 18, 2, ',', ' '));
$sheet->getStyle("A{$line}:G{$line}")->getFont()->setBold(true);
$sheet->getStyle("A{$line}:G{$line}")->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);

// Всего наименований.
$line++;
$sheet->setCellValue(
    "A{$line}", 
    'Всего наименований ' . count($prods) . ', на сумму ' . number_format($total, 2, ',', ' ') . ' руб.'
);
$sheet->mergeCells("A{$line}:G{$line}");

// Сумма прописью.
// Здесь используется функция для получение суммы прописью, взято с https://habrahabr.ru/post/53210/
function num2str($num) {
    $nul = 'ноль';
    $ten = array(
        array(
            '', 'один', 'два', 'три', 'четыре', 'пять',
            'шесть', 'семь', 'восемь', 'девять'
        ),
        array(
            '', 'одна', 'две', 'три', 'четыре', 'пять',
            'шесть', 'семь', 'восемь', 'девять'
        ),
    );
    $a20 = array(
        'десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать', 'пятнадцать', 
        'шестнадцать', 'семнадцать', 'восемнадцать', 'девятнадцать'
    );
    $tens = array(
        2 => 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят', 'семьдесят', 'восемьдесят', 'девяносто'
    );
    $hundred = array(
        '', 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот', 'семьсот', 'восемьсот', 'девятьсот'
    );
    $unit = array(
        array('копейка', 'копейки', 'копеек', 1),
        array('рубль', 'рубля', 'рублей', 0),
        array('тысяча', 'тысячи', 'тысяч', 1),
        array('миллион', 'миллиона', 'миллионов', 0),
        array('миллиард', 'милиарда', 'миллиардов', 0),
    );

    list($rub,$kop) = explode('.',sprintf("%015.2f", floatval($num)));
    $out = array();
    if (intval($rub) > 0) {
        foreach(str_split($rub, 3) as $uk => $v) {
            if (!intval($v)) continue;
            $uk = sizeof($unit) - $uk - 1; 
            $gender = $unit[$uk][3];
            list($i1,$i2,$i3) = array_map('intval', str_split($v, 1));
            $out[] = $hundred[$i1];
            if ($i2 > 1) $out[] = $tens[$i2] . ' ' . $ten[$gender][$i3];
            else $out[]= $i2 > 0 ? $a20[$i3] : $ten[$gender][$i3];
            if ($uk > 1) $out[] = morph($v, $unit[$uk][0], $unit[$uk][1], $unit[$uk][2]);
        }
    } else {
        $out[] = $nul;
    }

    $out[] = morph(intval($rub), $unit[1][0], $unit[1][1], $unit[1][2]);
    $out[] = $kop . ' ' . morph($kop,$unit[0][0], $unit[0][1], $unit[0][2]);
    return trim(preg_replace('/ {2,}/', ' ', join(' ',$out)));
}

function morph($n, $f1, $f2, $f5) {
    $n = abs(intval($n)) % 100;
    if ($n > 10 && $n < 20) return $f5;
    $n = $n % 10;
    if ($n > 1 && $n < 5) return $f2;
    if ($n == 1) return $f1;
    return $f5;
}

// Еще нужно у суммы прописью сделать первую букву заглавной. Т.к. скрипт в UTF-8 функция ucfirst не работает, поэтому используется аналог - mb_ucfirst.
if (!function_exists('mb_ucfirst')) {
    function mb_ucfirst($str, $enc = 'utf-8') {
        return
            mb_strtoupper(mb_substr($str, 0, 1, $enc), $enc) .
            mb_substr($str, 1, mb_strlen($str, $enc), $enc);
    }
}

$line++;
$sheet->setCellValue("A{$line}", mb_ucfirst(num2str($total)));
$sheet->getStyle("A{$line}")->getFont()->setBold(true);
$sheet->mergeCells("A{$line}:G{$line}");

// Файл готов, отдаем его браузеру на скачивание.
header("Expires: Mon, 1 Apr 1974 05:00:00 GMT");
header("Last-Modified: " . gmdate("D,d M YH:i:s") . " GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Content-type: application/vnd.ms-excel" );
header("Content-Disposition: attachment; filename=order.xlsx");

$objWriter = new PHPExcel_Writer_Excel2007($xls);
$objWriter->save('php://output');

// Или сохраняем на сервере.
$objWriter = new PHPExcel_Writer_Excel2007($xls);
$objWriter->save(__DIR__ . '/order.xlsx');
27 января 2017
В последнее время письма отправляемые с хостингов через функции mail() и mb_send_mail() часто попадают или совсем не...
cURL PHP – это библиотека предназначенная для получения и передачи данных через такие протоколы, как HTTP, FTP, HTTPS....
В статье приведены основные примеры работы с расширением PHP PDO. Такие как подключение к БД, получение, изменение и...
Библиотека GD дает возможность работать с изображениями в PHP. Далее представлены примеры как изменить размер, вырезать...