Перейти к содержимому

Пробное приложение — реестр счетов

schet_1Скрипт «пробного приложения» который реализует функции реестра счетов.

Приложение состоит из следующих страниц:
1. Реестр (список) счетов
2. Карта счета (добавление счета, правка уже существующего счета)

Функции приложения:
1. Создание нового счета (с заполнением полей и позиций)
2. Правка существующего счета (с возможностью редактировать все введенные данные, менять состав позиций)

Пришло мне тестовое задание на сайте фриланса, с первого взгляда простейшее приложение, которое можно реализовать за час — два, да можно но и ошибок можно много сделать, попробую рассказать и описать весь процесс.

Из задания сказано:

Создать пробное приложение — реестр счетов.
Поля счета: номер, дата, сумма счета – связана с позициями счета.
Каждый счет включает в себя произвольное количество позиций. Поля позиции: наименование, количество (может быть дробным), цена, стоимость. В этом тестовом задании все поля можно заполнять вручную.

Приложение состоит из следующих страниц:
1. Реестр (список) счетов
2. Карта счета (добавление счета, правка уже существующего счета)

Функции приложения:
1. Создание нового счета (с заполнением полей и позиций)
2. Правка существующего счета (с возможностью редактировать все введенные данные, менять состав позиций)

Видим здесь как минимум две таблицы для базы данных, таблица «СЧЕТ» и таблица «ПОЗИЦИИ».

База данных приложения «реестр счетов»

База данных СЧЕТА

В таблице «СЧЕТ», я ее назвал [gd_schets], поле «summa» это сумма стоимостей позиций счета, т.е. счет может состоять из нескольких позиций. Кто то скажет «зачем отдельное поле для этого, всегда же можно выбрать позиции счета и подсчитать сумму?» — да, можно, но это лишний запрос к базе данных и этот запрос выполняется в тот момент когда надо БЫСТРО, а при добавлении счета в систему это время не так критично и не так много места занимает.

В таблице «ПОЗИЦИИ», я ее назвал [gd_position], поле stoimost скорее всего лишнее и смысла ее хранить нет, т.к. при отображении счета «стоимость» вычисляется из умножения «количества» на «цену» и легче это сделать с помощью языка программирования PHP.

Поле [id_schet] отвечает за связь позиции с счетом, необходимо чтобы мы могли знать к  какому «счету» относится «позиция».

С базой данных разобрались, теперь можно заняться проектированием интерфейса и продумыванием логики приложения.

Для визуального интерфейса приложения я использовал bootstrap фреймворк, он удобен для написания админок и содержит в себе все самое необходимое для красивого отображения информации.

Страница «Реестр (список) счетов»

Страница реестр счетов

Здесь, по моему мнению, не хватает поля «название» счета, чтобы в списке таких счетов можно было бы найти по смыслу счет, но не я заказчик.

Страница списка элементов, представляет собой таблицу с заглавием и строками — счетами, у каждого счета есть две кнопки «удалить» и «редактировать».

Также должна быть кнопка «Добавить» счет.

Страница «Карта счета» добавление/редактирование все в одном

Реестр счетов добавление счета

Интерфейс представляет собой форму в которой реализована функция добавления позиций в счет. Вот тут есть много вопросов и несколько реализаций добавления позиций в счет.

Первый способ самый простой для программиста.

  1. Создаем счет — отдельная форма.
  2. Добавляем в счет одну позицию — отдельная форма.
  3. Для редактирования счета необходима еще форма с возможностью удаления и редактирования позиций — это еще один велосипед.

Получается если в счете 100 позиций, тогда необходимо будет 100 раз открывать форму добавления позиции в счет и 100 раз нажимать кнопку «добавить», нет ни какой интерактивности и юзабелити.

Второй способ удобный для клиента (его и реализуем)

  1. Создаем счет — отдельная форма, она же и является формой для добавления позиций.

При создании счета, открывается форма на которой есть кнопка [+] с помощью которой добавляем позиции в счет. Не надо перегружать форму и производить лишних действий. При редактировании счета вызывается та же форма добавления.

Третий способ, не знаю для кого удобный

  1. Создаем «позиции» — отдельная форма, после создания нескольких они будут висеть в системе без привязки к счету
  2. Выбираем позиции для создания счета — с помощью галочек выбираем позиции и создаем счет.
  3. Форма редактирования счета должна быть.

Способ имеет место быть, и в некоторых случаях клиенты привыкли так работать.

Итак мы остановились на втором способе, т.к. удобнее и так сказал клиент.

На форме первая строка представляет собой «форму» для позиции, заполняете «наименование», «количество» и «цену», стоимость автоматически считается. И нажимаете [+] «позиция» добавляется в счет, с возможностью редактирования «позиции» и удаления с помощью кнопки [-].

И в принципе все на данном этапе с интерфейсами закончили.

Программный код «реестра счетов»

Код для отображения списка счетов.

$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. для людей которые ищут все готовое я могу за отдельную плату сделать все за них.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.