Урок 1.13. Программирование баз данных в Delphi (Создаем модуль печати данных о приходе и расходе).
На этом уроке мы реализуем печать данных прихода, расхода и состояния склада. Печать будем реализовывать через MS Excel, то есть это будет не совсем печать, а экспорт данных в MS Excel, а там пользователь может сам выбрать, печатать или сохранить отчет. В начале приготовим шаблоны MS Excel, в которые будем выводить отчеты.
Шаблон ведомость по приходу продуктов питания.
В шаблоне использован стиль ссылок R1C1. Включается Параметры->Формулы-> Стиль ссылок R1C1. Файл сохраняем с типом файла Шаблон Excel 97-2003.
Результат работы программы:
Шаблон ведомость по расходу продуктов питания.
Шаблон остатки на складе.
Скачать шаблоны можно вместе с исходниками в конце статьи.
Далее в формы прихода и расхода внесем несколько изменений, необходимых для фильтрации документов прихода и расхода по дате.
Добавим на формы прихода и расхода по два компонента TDateTimePicker из вкладки Win32 и компонент TCheckBox из вкладки Standart. Кнопку Button для вызова печати. Размещаем, как показано на рисунке:
Аналогично для формы расхода.
В инспекторе объектов для обеих DateTimePicker ов обнуляем свойство Time.
А для события OnChange прописываем следующий код:
procedure TForm_prihod.DateTimePicker1Change(Sender: TObject);
begin
//Проверка установленных дат
if datetimepicker1.Date>datetimepicker2.Date then
begin
ShowMessage('Внимание. Начальная дата прихода больше конечной');
checkbox1.Checked:=false;
DateTimePicker1.Date:=DateUtils.StartOfTheMonth(now);
DateTimePicker2.Date:=date;
end;
end;
procedure TForm_prihod.DateTimePicker2Change(Sender: TObject);
begin
//Проверка установленных дат
if datetimepicker1.Date>datetimepicker2.Date then
begin
ShowMessage('Внимание. Начальная дата прихода больше конечной');
checkbox1.Checked:=false;
DateTimePicker1.Date:=DateUtils.StartOfTheMonth(now);
DateTimePicker2.Date:=date;
end;
end;
А для события OnClick компонента CheckBox1 пишем:
procedure TForm_prihod. Click(Sender: TObject);
//включение фильтра
begin
dm.table_prihod.Filtered:=checkbox1.Checked;
if checkbox1.Checked=true then
dm.table_prihod.Filter:='(date_prihoda>='+datetostr(datetimepicker1.Date)+')
and ('+ 'date_prihoda<='+datetostr(datetimepicker2.Date)+')';
end;
Аналогичные операции проделываем с формой расхода.
Далее переходим в DataModule (Unit_dm) и размещаем там компонент TADOQuery из вкладки dbGo (ADO). В свойстве Name задаем имя ADOQuery_print, в свойство Connection->Form_general.ADOConnection1. Затем из вкладки Data Access размещаем компонент TDataSource в свойстве Name задаем имя print, а в свойство DataSet->ADOQuery_print.
Сейчас создадим отдельный модуль для печати. Выбираем File->New->Unit – Delphi. Назовем его print.
Подключение интерфейса Excel происходит через модуль comobj.
Ниже привожу полный текст модуля с комментариями:
unit print;
interface
Uses Windows, Dialogs, SysUtils, Variants, DB, Excel_TLB, comobj, unit_dm;
{Внимание!!! Перед подключением модуля Excel_TLB, необходимо
импортировать библиотеку Excel. Для этого выберите Component->Import
Component->Import a Type Library-> находим MS Excel и следуем
инструкциям}
function CreateApplication(FileName:string):boolean;
procedure print_prihod(date_start,date_end:TDateTime;check:boolean);
procedure print_rashod(date_start,date_end:TDateTime;check:boolean);
procedure print_ostatki;
var
exl: OleVariant; WorkBook, Sheet: Variant;
implementation
function CreateApplication(FileName:string):boolean;
//создаем приложение excel
begin
try
//Создаем объект интерфейса для доступа к серверу COM
exl := CreateOleObject('Excel.Application');
// Отключаем реакцию Excel на события,
//чтобы ускорить вывод информации
exl.Application.EnableEvents := false;
//Создаем книгу и обращаемся к первому листу
Workbook := exl.Application.WorkBooks.Add(GetCurrentDir()+FileName);
Sheet := WorkBook.WorkSheets[1];
result:=true;
Except
showmessage('Внимание! Произошла ошибка при создании MS Excel приложения');
result:=false; exl.DisplayAlerts := False; // отключаем предупреждения
exl.Workbooks.Close; // закроем все книги
exl.Application.quit;
//освобождаем интерфейсы
Sheet := Unassigned;
WorkBook := Unassigned;
exl := Unassigned;
end;
end;
//печать прихода
procedure print_prihod(date_start,date_end:TDateTime;check:boolean);
var
ArrayData,ArrayData1,ArrayData2: Variant;
x,y,kdx,ndx,ndy,kdy,n,m,i:integer;
//ndx, ndy -начало диапазона по оси х (вправо) и по оси у (вниз)
//kdx, kdy -конец диапазона по оси х и по оси у
// ArrayData - двухмерный массив для продуктов
// ArrayData1 - двухмерный массив для единиц измерения
// ArrayData2 - массив для дат begin if CreateApplication('\Шаблоны\Ведомость прихода продуктов.xlt')=false then exit;
try
//делаем запрос на выбор продуктов и единиц измерения прихода
//и заполняем вариантный массив для продуктов и для единиц измерения
dm.ADOQuery_print.Active:=false;
dm.ADOQuery_print.SQL.Clear;
dm.ADOQuery_print.SQL.Add('SELECT products.product_name,
ed_izmer.ed_name FROM prihod LEFT JOIN ((storage LEFT JOIN products ON
storage.id_product = products.id) LEFT JOIN ed_izmer'+
' ON storage.id_ed_izmer = ed_izmer.id) ON prihod.id = storage.id_prihod GROUP BY products.product_name, ed_izmer.ed_name;');
dm.ADOQuery_print.Active:=True;
y:=dm.print.DataSet.RecordCount;//количество записей по продуктам в приходе
ArrayData := VarArrayCreate([1, y*2,1,1], varVariant); //двухмерный массив для продуктов
ArrayData1 := VarArrayCreate([1, y*2,1,1], varVariant); //двухмерный массив для единиц измерения
dm.ADOQuery_print.First;
for i:=1 to y*2 do //умножаем на два так как в шаблоне для наименования продукта
//используется высота ячейки в две клетки, приходится первую заполнять, а вторую пропускать
begin
if (i mod 2)<>0 then begin //шаг - каждый второй
ArrayData[i,1] :=dm.ADOQuery_print.FieldByName('product_name').AsString; //заполняем продукт
ArrayData[i+1,1] :=''; // оставляем пустой
ArrayData1[i,1] :=dm.ADOQuery_print.FieldByName('ed_name').AsString; //заполняем единицу измерения
ArrayData1[i+1,1] :='';// оставляем пустой
if dm.ADOQuery_print.eof<>true then dm.ADOQuery_print.next;
end;
end; //выполняем запрос на выбор даты прихода и заполняем вариантный массив2
dm.ADOQuery_print.Active:=false;
dm.ADOQuery_print.SQL.Clear;
//проверяем включен ли фильтр и формируем соответствующий запрос по дате или без
if check=false then dm.ADOQuery_print.SQL.Add('SELECT
prihod.date_prihoda FROM prihod GROUP BY prihod.date_prihoda ORDER BY
prihod.date_prihoda;')
else
begin
dm.ADOQuery_print.Parameters.AddParameter.Name:='date1';
dm.ADOQuery_print.Parameters.ParamByName('date1').DataType:=ftDateTime;
dm.ADOQuery_print.Parameters.AddParameter.Name:='date2';
dm.ADOQuery_print.Parameters.ParamByName('date2').DataType:=ftDateTime;
dm.ADOQuery_print.SQL.Add('SELECT prihod.date_prihoda
FROM prihod GROUP BY prihod.date_prihoda HAVING
(((prihod.date_prihoda)>=:date1 and
(prihod.date_prihoda)<=:date2)) ORDER BY prihod.date_prihoda; ');
dm.ADOQuery_print.Parameters.ParamByName('date1').Value:=date_start;
dm.ADOQuery_print.Parameters.ParamByName('date2').Value:=date_end;
end;
dm.ADOQuery_print.Active:=True;
x:=dm.print.DataSet.RecordCount;//количество записей дат
ArrayData2 := VarArrayCreate([1, x,1,1], varVariant);//массив для дат
dm.ADOQuery_print.First;
for i:=1 to x do
begin
//заполняем массив датами
ArrayData2[i,1] :=dm.ADOQuery_print.FieldByName('date_prihoda').AsString;
if dm.ADOQuery_print.eof<>true then dm.ADOQuery_print.next;
end;
// рисуем поле данных
ndy:=24; ndx:=18;
kdx:=18+x*4-1; kdy:=25;
//выделение диапазона ячеек
sheet.Range[sheet.cells[24,18],sheet.cells[25,21]].Select;
//объеденение ячеек
sheet.Range[sheet.cells[24,18],sheet.cells[25,21]].Merge;
//рисуем поле с цифрами
ndy:=23; ndx:=18;
kdx:=18+x*4-1; kdy:=23;
sheet.Range[sheet.cells[23,18],sheet.cells[23,21]].Select;
sheet.Range[sheet.cells[23,18],sheet.cells[23,21]].Merge;
Exl.Selection.HorizontalAlignment:=xlCenter;
sheet.cells[23,18].value:=3;
//рисуем поле с датами
ndy:=17; ndx:=18;
kdx:=18+x*4-1; kdy:=22;
sheet.Range[sheet.cells[17,18],sheet.cells[22,21]].Select;
sheet.Range[sheet.cells[17,18],sheet.cells[22,21]].Merge;
exl.Selection.Orientation := 90;
Exl.Selection.HorizontalAlignment:=xlCenter;
Exl.Selection.VerticalAlignment:=xlCenter;
//общее выделение и размножение вправо
if x>1 then begin
ndy:=17; ndx:=18;
kdx:=18+x*4-1; kdy:=25;
sheet.Range[sheet.cells[17,18],sheet.cells[25,21]].Select;
//автозаполнение выделенного диапазона
exl.selection.autofill(sheet.Range[sheet.cells[ndy,ndx], sheet.cells[kdy,kdx]], xlFillDefault);
end;
//рисуем шапку над датой
ndy:=15; ndx:=18;
kdx:=18+x*4-1; kdy:=16;
sheet.Range[sheet.cells[15,18],sheet.cells[16,kdx]].Select;
sheet.Range[sheet.cells[15,18],sheet.cells[16,kdx]].Merge;
ndy:=24; ndx:=2;
kdx:=18+x*4-1; kdy:=24+y*2-1;
//общее выделение и размножение вниз
if y>1 then begin
sheet.Range[sheet.cells[ndy,ndx],sheet.cells[ndy+1,kdx+5]].Select;
exl.selection.autofill(sheet.Range[sheet.cells[ndy,ndx], sheet.cells[kdy,kdx+5]],xlfillcopy); end;
//заполняем продуктами и еденицами измерения
sheet.Range[sheet.cells[24,2],sheet.cells[kdy,14]].value:= ArrayData;
sheet.Range[sheet.cells[24,15],sheet.cells[kdy,17]].value:= ArrayData1;
//в зависимости от фильтра по дате заполняем шапку датами начала периода и конца периода
if check=false then sheet.cells[8,27].value:=ArrayData2[1,1]+' - '+ArrayData2[x,1]
else sheet.cells[8,27].value:=DateToStr(date_start)+' - '+DateToStr(date_end);
// заполняем таблицу датами
dm.ADOQuery_print.First;
for i:=1 to x do
begin
sheet.cells[17,14+i*4].value:= dm.ADOQuery_print.fieldbyname('date_prihoda').AsString;
if dm.ADOQuery_print.eof<>true then dm.ADOQuery_print.next;
end;
//запрос на выбор продукта, единицы измерения, даты прихода и суммы
//формируем запрос и заполняем таблицу данными
dm.ADOQuery_print.Active:=false;
dm.ADOQuery_print.SQL.Clear;
dm.ADOQuery_print.SQL.Add('SELECT products.product_name,
ed_izmer.ed_name, prihod.date_prihoda, Sum(storage.quantity) AS
[Sum-quantity]'+
' FROM prihod LEFT JOIN ((storage LEFT JOIN products ON
storage.id_product = products.id) LEFT JOIN ed_izmer ON
storage.id_ed_izmer = ed_izmer.id) ON prihod.id = storage.id_prihod'+
' GROUP BY products.product_name, ed_izmer.ed_name, prihod.date_prihoda ORDER BY prihod.date_prihoda; ');
dm.ADOQuery_print.Active:=True;
dm.ADOQuery_print.First;
for m:=1 to y*2 do
for n:=1 to x do
if (m mod 2)<>0 then
begin
//выбираем продукт и единицу измерения и ищем совпадение по дате прихода
if
(dm.ADOQuery_print.Locate('product_name;ed_name;date_prihoda', VarArrayOf([ArrayData[m,1], ArrayData1[m,1],ArrayData2[n,1]]),[loCaseInsensitive,
loPartialKey])) then begin
sheet.cells[23+m,14+n*4].value:= dm.ADOQuery_print.fieldbyname('Sum-quantity').Value;
end;
end;
//показываем excel
exl.visible:=true;
//освобождаем память и интерфейс excel
ArrayData := Unassigned;
ArrayData1 := Unassigned;
ArrayData2 := Unassigned;
Sheet := Unassigned;
WorkBook := Unassigned;
exl := Unassigned;
Except
//в случае ошибки освобождаем ресурсы
showmessage('Внимание! Произошла ошибка при создании отчета');
exl.DisplayAlerts := False; // отключаем предупреждения
exl.Workbooks.Close; // закроем все книги
exl.Application.quit;
ArrayData := Unassigned;
ArrayData1 := Unassigned;
ArrayData2 := Unassigned;
Sheet := Unassigned;
WorkBook := Unassigned;
exl := Unassigned;
end;
end;
//печать расхода
procedure print_rashod(date_start,date_end:TDateTime;check:boolean);
var
ArrayData,ArrayData1,ArrayData2: Variant;
x,y,kdx,ndx,ndy,kdy,n,m,i:integer;
//ndx, ndy -начало диапазона по оси х (вправо) и по оси у (вниз)
//kdx, kdy -конец диапазона по оси х и по оси у
// ArrayData - двухмерный массив для продуктов
// ArrayData1 - двухмерный массив для единиц измерения
// ArrayData2 - массив для дат begin if CreateApplication('\Шаблоны\Ведомость расхода продуктов.xlt')=false then exit;
try
//делаем запрос на выбор продуктов и единиц измерения прихода
//и заполняем вариантный массив для продуктов и для единиц измерения
dm.ADOQuery_print.Active:=false;
dm.ADOQuery_print.SQL.Clear;
dm.ADOQuery_print.SQL.Add('SELECT products.product_name,
ed_izmer.ed_name FROM rashod_doc LEFT JOIN ((rashod LEFT JOIN products
ON rashod.id_product = products.id) LEFT JOIN ed_izmer'+
' ON rashod.id_ed_izmer = ed_izmer.id) ON rashod_doc.id =
rashod.id_rashod_doc GROUP BY products.product_name,
ed_izmer.ed_name;');
dm.ADOQuery_print.Active:=True;
y:=dm.print.DataSet.RecordCount;//количество записей по продуктам в приходе
ArrayData := VarArrayCreate([1, y*2,1,1], varVariant); //двухмерный массив для продуктов
ArrayData1 := VarArrayCreate([1, y*2,1,1], varVariant); //двухмерный массив для единиц измерения
dm.ADOQuery_print.First;
for i:=1 to y*2 do //умножаем на два так как в шаблоне для наименования продукта
//используется высота ячейки в две клетки, приходится первую заполнять, а вторую пропускать
begin
if (i mod 2)<>0 then begin //шаг - каждый второй
ArrayData[i,1] := dm.ADOQuery_print.FieldByName('product_name').AsString; //заполняем продукт
ArrayData[i+1,1] :=''; // оставляем пустой
ArrayData1[i,1] := dm.ADOQuery_print.FieldByName('ed_name').AsString; //заполняем единицу измерения
ArrayData1[i+1,1] :='';// оставляем пустой
if dm.ADOQuery_print.eof<>true then dm.ADOQuery_print.next;
end;
end;
//выполняем запрос на выбор даты прихода и заполняем вариантный массив2
dm.ADOQuery_print.Active:=false;
dm.ADOQuery_print.SQL.Clear;
//проверяем включен ли фильтр и формируем соответствующий запрос по дате или без
if check=false then dm.ADOQuery_print.SQL.Add('SELECT
rashod_doc.date_rashoda FROM rashod_doc GROUP BY rashod_doc.date_rashoda
ORDER BY rashod_doc.date_rashoda;')
else
begin
dm.ADOQuery_print.Parameters.AddParameter.Name:='date1';
dm.ADOQuery_print.Parameters.ParamByName('date1').DataType:=ftDateTime;
dm.ADOQuery_print.Parameters.AddParameter.Name:='date2';
dm.ADOQuery_print.Parameters.ParamByName('date2').DataType:=ftDateTime;
dm.ADOQuery_print.SQL.Add('SELECT
rashod_doc.date_rashoda FROM rashod_doc GROUP BY rashod_doc.date_rashoda
HAVING (((rashod_doc.date_rashoda)>=:date1 and
(rashod_doc.date_rashoda)<=:date2)) ORDER BY rashod_doc.date_rashoda;
');
dm.ADOQuery_print.Parameters.ParamByName('date1').Value:=date_start;
dm.ADOQuery_print.Parameters.ParamByName('date2').Value:=date_end;
end;
dm.ADOQuery_print.Active:=True;
x:=dm.print.DataSet.RecordCount;//количество записей дат
ArrayData2 := VarArrayCreate([1, x,1,1], varVariant);//массив для дат
dm.ADOQuery_print.First;
for i:=1 to x do
begin
//заполняем массив датами
ArrayData2[i,1] :=dm.ADOQuery_print.FieldByName('date_rashoda').AsString;
if dm.ADOQuery_print.eof<>true then dm.ADOQuery_print.next;
end;
// рисуем поле данных
ndy:=24; ndx:=18;
kdx:=18+x*4-1; kdy:=25;
//выделение диапазона ячеек
sheet.Range[sheet.cells[24,18],sheet.cells[25,21]].Select;
//объеденение ячеек
sheet.Range[sheet.cells[24,18],sheet.cells[25,21]].Merge;
//рисуем поле с цифрами
ndy:=23; ndx:=18;
kdx:=18+x*4-1; kdy:=23;
sheet.Range[sheet.cells[23,18],sheet.cells[23,21]].Select;
sheet.Range[sheet.cells[23,18],sheet.cells[23,21]].Merge;
Exl.Selection.HorizontalAlignment:=xlCenter;
sheet.cells[23,18].value:=3;
//рисуем поле с датами
ndy:=17; ndx:=18;
kdx:=18+x*4-1; kdy:=22;
sheet.Range[sheet.cells[17,18],sheet.cells[22,21]].Select;
sheet.Range[sheet.cells[17,18],sheet.cells[22,21]].Merge;
exl.Selection.Orientation := 90;
Exl.Selection.HorizontalAlignment:=xlCenter;
Exl.Selection.VerticalAlignment:=xlCenter;
//общее выделение и размножение вправо
if x>1 then begin
ndy:=17; ndx:=18;
kdx:=18+x*4-1; kdy:=25;
sheet.Range[sheet.cells[17,18],sheet.cells[25,21]].Select;
//автозаполнение выделенного диапазона
exl.selection.autofill(sheet.Range[sheet.cells[ndy,ndx], sheet.cells[kdy,kdx]], xlFillDefault);
end;
//рисуем шапку над датой
ndy:=15; ndx:=18;
kdx:=18+x*4-1; kdy:=16;
sheet.Range[sheet.cells[15,18],sheet.cells[16,kdx]].Select;
sheet.Range[sheet.cells[15,18],sheet.cells[16,kdx]].Merge;
ndy:=24; ndx:=2;
kdx:=18+x*4-1; kdy:=24+y*2-1;
//общее выделение и размножение вниз
if y>1 then begin
sheet.Range[sheet.cells[ndy,ndx], sheet.cells[ndy+1,kdx+5]].Select;
exl.selection.autofill(sheet.Range[sheet.cells[ndy,ndx], sheet.cells[kdy,kdx+5]], xlfillcopy); end;
//заполняем продуктами и еденицами измерения
sheet.Range[sheet.cells[24,2],sheet.cells[kdy,14]].value:= ArrayData;
sheet.Range[sheet.cells[24,15],sheet.cells[kdy,17]].value:= ArrayData1;
//в зависимости от фильтра по дате заполняем шапку датами начала периода и конца периода
if check=false then sheet.cells[8,27].value:= ArrayData2[1,1]+' - '+ ArrayData2[x,1]
else sheet.cells[8,27].value:= DateToStr(date_start) + ' - ' + DateToStr(date_end);
// заполняем таблицу датами
dm.ADOQuery_print.First;
for i:=1 to x do
begin
sheet.cells[17,14+i*4].value:= dm.ADOQuery_print.fieldbyname('date_rashoda').AsString;
if dm.ADOQuery_print.eof<>true then dm.ADOQuery_print.next;
end;
//запрос на выбор продукта, единицы измерения, даты расхода и суммы
//формируем запрос и заполняем таблицу данными
dm.ADOQuery_print.Active:=false;
dm.ADOQuery_print.SQL.Clear;
dm.ADOQuery_print.SQL.Add('SELECT products.product_name,
ed_izmer.ed_name, rashod_doc.date_rashoda, Sum(rashod.quantity) AS
[Sum-quantity]'+
' FROM rashod_doc LEFT JOIN ((rashod LEFT JOIN products ON
rashod.id_product = products.id) LEFT JOIN ed_izmer ON
rashod.id_ed_izmer = ed_izmer.id) ON rashod_doc.id =
rashod.id_rashod_doc'+
' GROUP BY products.product_name, ed_izmer.ed_name, rashod_doc.date_rashoda ORDER BY rashod_doc.date_rashoda; ');
dm.ADOQuery_print.Active:=True;
dm.ADOQuery_print.First;
for m:=1 to y*2 do
for n:=1 to x do
if (m mod 2)<>0 then
begin
//выбираем продукт и единицу измерения и ищем совпадение по дате расхода
if
(dm.ADOQuery_print.Locate('product_name;ed_name;date_rashoda', VarArrayOf([ArrayData[m,1], ArrayData1[m,1], ArrayData2[n,1]]), [loCaseInsensitive,
loPartialKey])) then begin
sheet.cells[23+m,14+n*4].value:=dm.ADOQuery_print.fieldbyname('Sum-quantity').Value;
end;
end;
//показываем excel
exl.visible:=true;
//освобождаем память и интерфейс excel
ArrayData := Unassigned;
ArrayData1 := Unassigned;
ArrayData2 := Unassigned;
Sheet := Unassigned;
WorkBook := Unassigned;
exl := Unassigned;
Except
//в случае ошибки освобождаем ресурсы
showmessage('Внимание! Произошла ошибка при создании отчета');
exl.DisplayAlerts := False; // отключаем предупреждения
exl.Workbooks.Close; // закроем все книги
exl.Application.quit;
ArrayData := Unassigned;
ArrayData1 := Unassigned;
ArrayData2 := Unassigned;
Sheet := Unassigned;
WorkBook := Unassigned;
exl := Unassigned;
end;
end;
//Печать остатков на складе
procedure print_ostatki;
var
ArrayData: Variant;
i:integer;
begin
//если остатков нет выходим
if dm.ostatki.DataSet.RecordCount=0 then begin
showmessage('На складе нет остатков');
exit;
end;
//создаем интерфейс Excel
if CreateApplication('\Шаблоны\Остатки на складе.xlt')=false then exit;
try
begin
//объявляем вариантный массив
ArrayData := VarArrayCreate([1, dm.ostatki.DataSet.RecordCount,1,6], varVariant);
dm.ADOQuery_ostatki.First;
for i:= 1 to dm.ostatki.DataSet.RecordCount do
begin
//Заполняем вариантный массив данными из запроса остатки
ArrayData[i,1] :=i;
ArrayData[i,2] :=dm.ADOQuery_ostatki.FieldByName('product_name').Value;
ArrayData[i,3] :=dm.ADOQuery_ostatki.FieldByName('ed_name').Value;
ArrayData[i,4] :=dm.ADOQuery_ostatki.FieldByName('ostatok').Value;
ArrayData[i,5] :=dm.ADOQuery_ostatki.FieldByName('summa').Value;
ArrayData[i,6] :=dm.ADOQuery_ostatki.FieldByName('sred_price').Value;
dm.ADOQuery_ostatki.Next;
end;
end;
//заполняем ячейки таблицы Excel из массива
sheet.Range['a4','f'+IntToStr(dm.ostatki.DataSet.RecordCount+3)].value:= ArrayData;
//показываем excel
exl.visible:=true;
//освобождаем память и интерфейс excel
ArrayData := Unassigned;
Sheet := Unassigned;
WorkBook := Unassigned;
exl := Unassigned;
Except
//в случае ошибки освобождаем ресурсы
showmessage('Внимание! Произошла ошибка при создании отчета');
exl.DisplayAlerts := False; // отключаем предупреждения
exl.Workbooks.Close; // закроем все книги
exl.Application.quit;
ArrayData := Unassigned;
Sheet := Unassigned;
WorkBook := Unassigned;
exl := Unassigned; end;
end;
end.
Как работать с Excel из Delphi, можно прочитать здесь, или найти в Интернете.
На форме приход дважды нажимаем на кнопку печат ь и пишем следующий код для события OnClick:
procedure TForm_prihod.Button4Click(Sender: TObject);
var date_start:TDate;
date_end:TDate;
check:boolean;
begin
//вызов процедуры печати
if CheckBox1.Checked=True then check:=true else check:=false ;
date_start:=(datetimepicker1.Date);
date_end:=(datetimepicker2.Date);
print_prihod(date_start,date_end,check);
end;
Не забываем прописать модуль print в uses для формы прихода. uses general, unit_dm, prihod_prod, print;
Аналогично делаем и для формы расхода. Для печати остатков на складе, размещаем на форме кнопку называем ее Печать и по событию OnClick вызываем процедуру печати print_ostatki;
Печать справочников вы можете организовать самостоятельно по аналогии с печатью остатков на складе.
Примеры запросов к базе, созданные в конструкторе MS Access, для выбора информации для отчета по приходу я оставил в базе для образца их можно удалить. Запросы называются:
ADOQuery_print1 - выбор продуктов и единиц измерения; ADOQuery_print2 – запрос на выбор дат прихода ADOQuery_print3 – запрос на выбор продукта, единицы измерения, даты прихода и суммы.
Как работать с конструктором MS Access, можно посмотреть в моей статье в блоге.
Такая проблема действительно есть и не только у вас. В интернете я честно сказать не нашел ни одного адекватного решения, если найдете то выкладывайте, думаю все будут благодарны. Эта ошибка исчезает если переключить режим DateTimePicker свойство DateMode в dmUpDown, но тогда им пользоваться не совсем удобно, но это лучше чем ничего.))))
Программа уходит в цикл только у меня при проверке установленных дат если при открытии формы в DateTimePicker2 сразу месяц пролистать назад? Думал может сам накосячил чего, скачал "исходник", ан нет опять в цикл...может в DateUtils проблема?
Под Delphi XE2 при вызове печати возникают ошибки типа:
Проблема связана с константами модуля Excel_TLB (этоxlMediumиxlCenter, возможно есть и другие, но пока прблемы только с этими), почему то Delphi XE2 их не очень хочет понимать. Пока для исправления могу предложить следующее:
В модуле Print перед разделом Var добавьте раздел констант
а в строках exl.Selection.Borders[------------].Weight := xlMedium; на exl.Selection.Borders[------------].Weight := Integer(xlMedium);
Можно также заменить xlMedium на xlThin, но тогда надо будет корректировать толщину линий в шаблоне иначе будет не красиво. Они отвечают за расположение текста в ячейке и за толщину линий.
вывести в эксель именно ту запись, которая мне нужна(например 26 числа приход товара был от иванова, петрова и сидорова), как вывести одного из них? (вот например двойным кликом по необходимой строке) можно так сделать
а такая ситуация, в один день большой приход товара от разных поставщиков как мне вывести одного только указав при этом название поставщика, вид упаковки, продукт ед.измерения количество, цена и сумма как тогда
Ну так сказать сразу сказать достаточно сложно. Посмотрите вот на эту строчку: Workbook := exl.Application.WorkBooks.Add(GetCurrentDir()+FileName); здесь GetCurrentDir() - получение пути к директории с ваши приложением, FileName - имя файла шаблона с указанием пути от директории с приложением до файла. Вот пример вызова - CreateApplication('\Шаблоны\Ведомость прихода продуктов.xlt'). Проверьте, правильно ли указан у вас путь и имя файла. Можно попробовать путь указать сразу в этой строке: Workbook := exl.Application.WorkBooks.Add(GetCurrentDir()+FileName); например так: Workbook := exl.Application.WorkBooks.Add('c:\\sklad\Шаблоны\Ведомость прихода продуктов.xlt'); Только укажите ваш полный путь и без ошибок.)))) А вообще желательно найти строку которая приводит к ошибке.)))
и почему -то ошибка в приходе и расходе, когда запись новую добавишь, сохраняешь и возникает ошибка невозможно найти строку для обновления.некоторые значения изменены со времени последнего ее чтения из-за чего это может быть?
Здравствуйте, спасибо что помогаете, у меня возникает ошибка что произошла ошибка при создании эксель объекта, можно подробнее расписать функцию create application что куда зачем заранее большое спасибо
Там вроде и так все строчки с комментариями.((( Смотрите в блоге, там есть статья по работе с Excel. http://basicsprog.ucoz.ru/blog/2011-09-30-8 . Попробуйте создать что нибудь подобное.)))
First chance exception at $759BC6E3. Exception class EAccessViolation with message 'Access violation at address 00551ADB in module 'storage.exe'. Read of address 00000000'. Process storage.exe (5272)
Ошибка доступа к памяти. Она может быть из-за чего угодно. Нужно искать строку кода, которая ее вызывает, а уже дальше думать как ее решать. Можно ее попытаться отловить при пошаговом выполнении программы или путем за комментирования фрагментов кода.)))))
Скорее всего ошибка связана с тем, что dm.print.DataSet.RecordCount; не имеет значения т.е. равно null. Это происходит в нескольких случаях: 1. Неправильно выполняется предыдущий запрос; 2. Нет данных о приходе т.е. DBGrid с приходом пустой. Соответственно для исправления ошибки нужно сделать проверку вида: if dm.print.DataSet.RecordCount<>0 then begin //что то выполняем end else begin //выводим сообщение об ошибке и освобождаем интерфейсы end; 3. Обращаетесь к закрытому или пустому DataSet . 4. И т.д.
К сожалению в рамках статей уроков разбирается лишь учебный вариант и всех возможных проверок не предусмотрено.)))