Скрипт «пробного приложения» который реализует функции реестра счетов.
Приложение состоит из следующих страниц:
1. Реестр (список) счетов
2. Карта счета (добавление счета, правка уже существующего счета)
Функции приложения:
1. Создание нового счета (с заполнением полей и позиций)
2. Правка существующего счета (с возможностью редактировать все введенные данные, менять состав позиций)
Пришло мне тестовое задание на сайте фриланса, с первого взгляда простейшее приложение, которое можно реализовать за час — два, да можно но и ошибок можно много сделать, попробую рассказать и описать весь процесс.
Из задания сказано:
Создать пробное приложение — реестр счетов.
Поля счета: номер, дата, сумма счета – связана с позициями счета.
Каждый счет включает в себя произвольное количество позиций. Поля позиции: наименование, количество (может быть дробным), цена, стоимость. В этом тестовом задании все поля можно заполнять вручную.Приложение состоит из следующих страниц:
1. Реестр (список) счетов
2. Карта счета (добавление счета, правка уже существующего счета)Функции приложения:
1. Создание нового счета (с заполнением полей и позиций)
2. Правка существующего счета (с возможностью редактировать все введенные данные, менять состав позиций)
Видим здесь как минимум две таблицы для базы данных, таблица «СЧЕТ» и таблица «ПОЗИЦИИ».
База данных приложения «реестр счетов»
В таблице «СЧЕТ», я ее назвал [gd_schets], поле «summa» это сумма стоимостей позиций счета, т.е. счет может состоять из нескольких позиций. Кто то скажет «зачем отдельное поле для этого, всегда же можно выбрать позиции счета и подсчитать сумму?» — да, можно, но это лишний запрос к базе данных и этот запрос выполняется в тот момент когда надо БЫСТРО, а при добавлении счета в систему это время не так критично и не так много места занимает.
В таблице «ПОЗИЦИИ», я ее назвал [gd_position], поле stoimost скорее всего лишнее и смысла ее хранить нет, т.к. при отображении счета «стоимость» вычисляется из умножения «количества» на «цену» и легче это сделать с помощью языка программирования PHP.
Поле [id_schet] отвечает за связь позиции с счетом, необходимо чтобы мы могли знать к какому «счету» относится «позиция».
С базой данных разобрались, теперь можно заняться проектированием интерфейса и продумыванием логики приложения.
Для визуального интерфейса приложения я использовал bootstrap фреймворк, он удобен для написания админок и содержит в себе все самое необходимое для красивого отображения информации.
Страница «Реестр (список) счетов»
Здесь, по моему мнению, не хватает поля «название» счета, чтобы в списке таких счетов можно было бы найти по смыслу счет, но не я заказчик.
Страница списка элементов, представляет собой таблицу с заглавием и строками — счетами, у каждого счета есть две кнопки «удалить» и «редактировать».
Также должна быть кнопка «Добавить» счет.
Страница «Карта счета» добавление/редактирование все в одном
Интерфейс представляет собой форму в которой реализована функция добавления позиций в счет. Вот тут есть много вопросов и несколько реализаций добавления позиций в счет.
Первый способ самый простой для программиста.
- Создаем счет — отдельная форма.
- Добавляем в счет одну позицию — отдельная форма.
- Для редактирования счета необходима еще форма с возможностью удаления и редактирования позиций — это еще один велосипед.
Получается если в счете 100 позиций, тогда необходимо будет 100 раз открывать форму добавления позиции в счет и 100 раз нажимать кнопку «добавить», нет ни какой интерактивности и юзабелити.
Второй способ удобный для клиента (его и реализуем)
- Создаем счет — отдельная форма, она же и является формой для добавления позиций.
При создании счета, открывается форма на которой есть кнопка [+] с помощью которой добавляем позиции в счет. Не надо перегружать форму и производить лишних действий. При редактировании счета вызывается та же форма добавления.
Третий способ, не знаю для кого удобный
- Создаем «позиции» — отдельная форма, после создания нескольких они будут висеть в системе без привязки к счету
- Выбираем позиции для создания счета — с помощью галочек выбираем позиции и создаем счет.
- Форма редактирования счета должна быть.
Способ имеет место быть, и в некоторых случаях клиенты привыкли так работать.
Итак мы остановились на втором способе, т.к. удобнее и так сказал клиент.
На форме первая строка представляет собой «форму» для позиции, заполняете «наименование», «количество» и «цену», стоимость автоматически считается. И нажимаете [+] «позиция» добавляется в счет, с возможностью редактирования «позиции» и удаления с помощью кнопки [-].
И в принципе все на данном этапе с интерфейсами закончили.
Программный код «реестра счетов»
Код для отображения списка счетов.
$sql = 'SELECT * FROM '.$table_schet.' ORDER BY data_c DESC'; $rez = $DB->QUR_SEL($sql); if(!$rez['err']){ $out['kol']=$rez['kol']; $out['items']=array(); foreach($rez['rez'] as $key => $val){ $val['data'] = date('H:i d.m.Y',$val['data_c']); $out['items'][]=$val; } }
выбираем из таблицы счетов данные и заносим в массив $out[‘items’], далее этот массив передаем в smarty для формирования html кода:
{if $mod.action=="view"} <div class="page-header"> <h1>Просмотр всех счетов</h1> </div> <table class="table table-condensed" style="margin-bottom:0px;background-color: #E4E4E4;"> <tr> <td width="80%">{$nav}</td> <td width="20%"><a href="/{$mod.url}/add/" class="btn btn-info btn-xs" title="Добавить"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Добавить</a></td> </tr> </table> <table class="table table-condensed"> <tr> <th width="5%">№</th> <th width="15%">Дата</th> <!--th width="40%">Название</th--> <th width="30%">Сумма</th> <th width="10%">Действия</th> </tr> {if $schets.kol!=0}{foreach from=$schets.items item=it} <tr> <td>{$it.id}</td> <td>{$it.data}</td> <!--td>{$it.name}</td--> <td>{$it.summa}</td> <td> <a href="/{$mod.url}/dele/id/{$it.id}" class="btn btn-danger btn-xs" title="удалить" onclick="return confirm('Я полностью понимаю, что я делаю и действительно хочу удалить?') ? true : false;"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></a> <a href="/{$mod.url}/edit/id/{$it.id}" class="btn btn-success btn-xs" title="редактировать"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span></a> </td> </tr> {/foreach} {else}<tr><td colspan=5>Нет записей!</td></tr>{/if} </table> <table class="table table-condensed" style="margin-bottom:0px;background-color: #E4E4E4;"> <tr> <td width="80%">{$nav}</td> <td width="20%"><a href="/{$mod.url}/add/" class="btn btn-info btn-xs" title="Добавить"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Добавить</a></td> </tr> </table> {/if}
Код для отображения «редактирования/добавления» счетов
if($_REQUEST['action']=='edit'){ $id = $out['id'] = (int)$_REQUEST['id']; $sql = 'SELECT * FROM '.$table_position.' WHERE id_schet='.$id.' ORDER BY id'; $rez = $DB->QUR_SEL($sql); if(!$rez['err']){ $out['kol']=$rez['kol']; $out['items']=array(); foreach($rez['rez'] as $key => $val){ $out['items'][]=$val; } } //$out['items'] = array_merge($out['items'], $_SESSION['schets']['new']); $main=false; }
Здесь в переменной $_REQUEST определяется имеется ли запрос на редактирование, если да, тогда определяем id редактируемого элемента и выполняем запрос к БД.
Закомментированная строка позволяет не потерять созданные позиции при ошибочных действиях!
Для формирования HTML кода опять передаем переменную $out в $smarty
<form class="form-horizontal" method="POST"> <table class="table" id="schets"> <thead><tr> <th width="50%">Наименование</th> <th width="10%">Кол-во</th> <th width="10%">Цена</th> <th width="10%">Стоимость</th> <th width="20%"></th> </tr></thead> <tr id="tr_add"> <td><input type="TEXT" name="name[]" id="new_name" value="" class="form-control"></td> <td><input type="TEXT" name="kolvo[]" id="new_kolvo" value="" class="form-control kol"></td> <td><input type="TEXT" name="price[]" id="new_price" value="" class="form-control price"></td> <td class="stoimost" id=""></td> <td><a href="#" class="btn btn-success add_pos" title="добавить"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></a></td> </tr> {if count($schets.items)}{foreach from=$schets.items item=it} <tr id="tr_{$it.id}"> <td><input type="TEXT" name="name[{$it.id}]" value="{$it.name}" class="form-control"></td> <td><input type="TEXT" name="kolvo[{$it.id}]" value="{$it.kol}" class="form-control kol"></td> <td><input type="TEXT" name="price[{$it.id}]" value="{$it.price}" class="form-control price"></td> <td class="stoimost" id="">{$it.stoimost}</td> <td><a href="#" class="btn btn-danger del_pos" title="удалить" id="{$it.id}"><span class="glyphicon glyphicon-minus" aria-hidden="true"></span></a><input type="HIDDEN" name="id[{$it.id}]" value="{$it.id}"></td> </tr> {/foreach}{/if} </table> <hr> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <a href="/{$mod.url}/view/" class="btn btn-default">Назад</a> {if $mod.action=="add"} <button type="submit" class="btn btn-warning" name="add">Добавить</button> <input type="HIDDEN" id="id_schet" name="id" value="0">{/if} {if $mod.action=="edit"} <button type="submit" class="btn btn-success" name="edit">Редактировать</button> <input type="HIDDEN" id="id_schet" name="id" value="{$schets.id}"> {/if} </div> </div> </form>
Здесь представлен код который подходит и для добавления и для редактирования счета и позиций. Но это только HTML он формирует интерфейс для пользователя, интерактивность и «удобность» необходимо реализовать на стороне клиента с помощью jQuery (или JavaScript). Я использую jQuery.
Код для подсчета стоимости позиции, берется количество, цена и умножаются
$(document).on("change", ".price, .kol", function() { var kol=parseFloat($(this).closest('tr').find('.kol').val()); var price=parseFloat($(this).closest('tr').find('.price').val()); if(kol!="NaN"&&price!="NaN") var s = kol*price; else var s = 0; var stoim=$(this).closest('tr').find('.stoimost'); $(stoim).html(s); });
Код для добавления позиции в счет, здесь используется ajax технология, которая позволяет не обновляя страницу изменять ее содержимое
$(".add_pos").click(function(){ var data = 'mod=schets&ajdes=add_pos&id_schet='+$("#id_schet").val(); data += '&name='+$("#new_name").val()+'&kol='+$("#new_kolvo").val()+'&price='+$("#new_price").val(); $.ajax({url: "index.php",dataType : "json",cache:false,method:'POST',data:data}).done(function( data ) { if(data.err==0){//OK var tr = '<tr id="tr_'+data.id+'"><td><input type="TEXT" name="name[]" value="'+data.name+'" class="form-control"></td><td><input type="TEXT" name="kolvo[]" value="'+data.kol+'" class="form-control kol"></td><td><input type="TEXT" name="price[]" value="'+data.price+'" class="form-control price"></td><td class="stoimost" id="st_'+data.id+'">'+data.stoimost+'</td><td><a href="#" class="btn btn-danger del_pos" title="удалить" id="'+data.id+'"><span class="glyphicon glyphicon-minus" aria-hidden="true"></span></a></td></tr>'; $("#schets").append(tr); $("#new_name").val("").focus(); $("#new_kolvo").val("1"); $("#new_price").val("1"); }else{//error } }); return false; });
В переменной data формируется POST данные для скрипта index.php в формате json передаются данные, как на сервер, так и обратно. И если нет ошибок, тогда в таблицу «счета» добавляем строку с новой позицией.
Код на стороне сервера который принимает данные по ajax.
if($_POST['ajdes']=='add_pos'){ //ДОБАВЛЕНИЕ $out['id'] = (-1)*rand(0,10000); $out['id_schet'] = (int)$_POST['id_schet']; $out['name'] = htmlspecialchars($_POST['name']); $out['kol'] = (float)$_POST['kol']; $out['price'] = (float)$_POST['price']; $out['stoimost'] = (float)($out['kol'] * $out['price']); $_SESSION['schets']['new'][] = $out; $out['err']=0; }
Здесь используем $_SESSION для хранения временных данных, до момента создания счета, в момент создания мы все временные данные записываем в таблицу базы данных.
После формирования счета позициями, сохраняем наши данные.
if(isset($_POST['add'])){ $_REQUEST['action']='view'; $sql = 'INSERT INTO '.$table_schet.' VALUES (0,'.mktime().',0)'; $rez = $DB->QUR($sql); if(!$rez['err']){ $id_schet = $rez['id']; $summa = 0; foreach($_POST['name'] as $key => $val)if($val!=''){ $name = htmlspecialchars($_POST['name'][$key]); $kolvo = (float)$_POST['kolvo'][$key]; if($kolvo=='') $kolvo=0; $price = (float)$_POST['price'][$key]; if($price=='') $price=0; $stoim = $kolvo * $price; $summa += $stoim; $sql = 'INSERT INTO '.$table_position.' VALUES (0,'.$id_schet.',"'.addslashes($name).'",'.$kolvo.','.$price.','.$stoim.')'; $rez = $DB->QUR($sql); } $sql = 'UPDATE '.$table_schet.' SET summa='.$summa.' WHERE id='.$id_schet.''; $rez = $DB->QUR($sql); unset($_SESSION['schets']['new']); } }
Здесь уже обходим переменную $_POST т.е. данные из форму добавления/редактирования счета и добавляем позиции в таблицу базы данных, но перед этим создав сам счет и получив его номер
$id_schet = $rez['id'];
Вот в принципе и все. Осталось только добавить кучу проверок на ввод данных и оптимизировать логику работы.
P.S. для людей которые ищут все готовое я могу за отдельную плату сделать все за них.