(Создаем форму для просмотра остатков продуктов на складе)
На этом уроке мы дополним нашу базу еще двумя таблицами для учета информации о расходе. Создадим SQL запросы к базе для формирования остатков продуктов на складе и создадим форму для просмотра остатков продуктов по этим запросам.
Прежде чем приступить к созданию формы, необходимо добавить в нашу базу две таблицы первая для хранения информации о расходных накладных (назовем ее rashod_doc, она является аналогом таблицы prihod) и вторая для хранения информации о расходе продуктов по расходной накладной (назовем ее rashod, является аналогом таблицы storage). Откроем нашу базу storage.mdb в MS Access 2007 и создадим таблицу с именем rashod_doc. Кто не помнит, как это сделать смотрите урок 1.1.
Далее создаем следующие поля таблицы rashod_doc: id (счетчик) – нужно задать как ключевое, number_docum (текстовый, в свойствах поля- размер поля- 20) – номер расходной накладной date_rashoda (тип данных - Дата/время) – дата расхода, sum(тип данных -денежный, в
свойствах поля- формат поля- основной, число десятичных знаков - 4) – сумма расхода.
Затем создаем таблицу rashod со следующими полями: id (счетчик) – нужно задать как ключевое, id_product (тип данных –числовой, в свойствах поля- размер поля- Длинное целое) – код продукта, id_product_group (тип данных –числовой, в свойствах поля- размер поля- Длинное целое) – код группы продуктов, quantity(тип данных -денежный, в
свойствах поля- формат поля- основной, число десятичных знаков - 3) – количество, price(тип данных -денежный, в
свойствах поля- формат поля- основной, число десятичных знаков - 4) – цена, id_rashod_doc (тип данных –числовой, в свойствах поля- размер поля- Длинное целое) – код расхода. id_ed_izmer (тип данных –числовой, в свойствах поля- размер поля- Длинное целое) – код единицы измерения.
Сейчас немножко о том, как это все будет работать. Для того, чтобы посмотреть остатки продуктов на складе мы создадим три запроса к нашей базе. Первый запрос будет группировать продукты из таблицы storage по полям id_product и id_ed_izmer с одновременным суммированием по количеству (поле quantity) и сумме (поле price* quantity). Второй запрос будет делать тоже, что и первый, но по таблице rashod. А третий запрос будет находить разницу между первым и вторым запросом, и выводить эти данные в нашу форму.
И так приступим к созданию формы. Запускает наш проект, и создаем новую форму File->New->Form –Delphi В инспекторе объектов устанавливаем следующие свойства для новой формы
Caption-> Остатки на складе; FormStyle -> fsMDIChild; Name -> Form_ostatki;
Сохраните модуль с именем ostatki.
Пропишем Uses general, unit_dm; после раздела implementation в программном модуле unit ostatki.
А программном модуле unit general главной формы пропишем Uses ostatki;
Переходим в дата модуль (unit_dm) и размещаем там три компонента TADOQuery из вкладки dbGo(ADO) и один компонент TDataSource из вкладки Data Access.
Для ADOQuery1 в инспекторе объектов устанавливаем следующие свойства: Connection-> Form_general.ADOConnection1; Name-> ADOQuery_prihod;
SQL-> (TStrings) -> вставляем следующий запрос
SELECT storage.id_product, Sum([storage].[price]*[storage].[quantity]) AS sum_price, Sum(storage.quantity) AS [Sum-quantity], storage.id_ed_izmer FROM storage GROUP BY storage.id_product, storage.id_ed_izmer;
Свойство Active устанавливаем в True;
Для ADOQuery2 в инспекторе объектов устанавливаем следующие свойства:
Connection-> Form_general.ADOConnection1; Name-> ADOQuery_rashod; SQL-> (TStrings) -> вставляем следующий запрос
SELECT rashod.id_product, Sum([rashod].[price]*[rashod].[quantity]) AS sum_price, Sum(rashod.quantity) AS [Sum-quantity], rashod.id_ed_izmer FROM rashod GROUP BY rashod.id_product, rashod.id_ed_izmer;
Свойство Active устанавливаем в True;
Для ADOQuery3 в инспекторе объектов устанавливаем следующие свойства:
Connection-> Form_general.ADOConnection1; Name-> ADOQuery_ostatki; SQL-> (TStrings) -> вставляем следующий запрос
SELECT products.product_name, ADOQuery_prihod.id_product, [ADOQuery_prihod].[Sum-quantity]-IIf([ADOQuery_rashod].[Sum-quantity]>0,[ADOQuery_rashod].[Sum-quantity],0) AS ostatok, [ADOQuery_prihod].[sum_price]-IIf([ADOQuery_rashod].[sum_price]>0,[ADOQuery_rashod].[sum_price],0) AS summa, ADOQuery_prihod.id_ed_izmer, products.id_product_group, ed_izmer.ed_name FROM ((ADOQuery_prihod LEFT JOIN ADOQuery_rashod ON ADOQuery_prihod.id_product = ADOQuery_rashod.id_product) LEFT JOIN ed_izmer ON ADOQuery_prihod.id_ed_izmer = ed_izmer.id) LEFT JOIN products ON ADOQuery_prihod.id_product = products.id;
Свойство Active устанавливаем в True;
Для составления запросов я использовал конструктор запросов в MS Access 2007, затем затем переключался в режим SQL и копировал запрос из в наш проект в Delphi. Созданные запросы в режиме конструктора MS Access 2007 можно посмотреть в исходниках к этому уроку в файле storage.mdb. Они называются ADOQuery_ostatki, ADOQuery_prihod, ADOQuery_rashod. Для просмотра запроса в режиме конструктора в MS Access 2007 нажмите правую кнопку мыши на этом запросе и выберите конструктор.
Внимание в этом уроке была обнаружена ошибка (смотрите комментарии 3-6) запросы ADOQuery_prihod, ADOQuery_rashod должны быть созданы в самой базе. Для их создания откройте базу storage.mdb в MS Access выберите создание -> конструктор запросов, закройте добавление таблиц и щелкнув правой кнопкой мыши выберите режим SQL. Далее скопируйте туда текст соответствующего запроса и сохраните его под соответствующим именем. К сожалению другого варианта исправления ошибки пока предложить не могу. Добавленные в Unit_dm компоненты ADOQuery_prihod и ADOQuery_rashod нужно удалить.
Для компонента DataSource1 устанавливаем свойства:
DataSet->ADOQuery_ostatki; Name->ostatki.
Далее переходим к нашей форме ostatki и размещаем на ней из вкладки Data Controls компонент TDBGrid. Для него устанавливаем следующие свойства:
Сейчас мы выберем нужные нам поля из запроса ADOQuery_ostatki для отображения их в форме. Для этого перейдем в дата модуль (unit_dm) проекта и выберем компонент ADOQuery_ostatki. В структуре (Structure) -> ADOQuery_ostatki -> Fields -> щелкнем правой кнопкой мыши и выберем Add all fields.
Выбираем поле product_name и в инспекторе объектов устанавливаем свойство: DisplayLabel -> Продукт; DisplayWidth -> 30;
Выбираем поле ed_name и в инспекторе объектов устанавливаем свойство: DisplayLabel -> Ед.изм.; DisplayWidth -> 10;
Выбираем поле id_product и в инспекторе объектов устанавливаем свойство: Visible->False;
Выбираем поле ostatok и в инспекторе объектов устанавливаем свойство: DisplayFormat ->0.000 DisplayLabel -> Остаток; DisplayWidth -> 10;
Выбираем поле summa и в инспекторе объектов устанавливаем свойство: DisplayFormat ->0.00 DisplayLabel ->Сумма; DisplayWidth -> 10;
Выбираем поле id_ed_izmer и в инспекторе объектов устанавливаем свойство: Visible->False;
Выбираем поле id_product_group и в инспекторе объектов устанавливаем свойство: Visible->False;
Далее создаем вычисляемое поле sred_price для вычисления средней стоимости продуктов» .
Для этого в структуре (Structure) -> products -> Fields -> щелкнем правой кнопкой мыши и выберем New field….
и заполняем, как показано на рисунке
Name->sred_price; Type->Float; Calculated; жмем ОК.
Выбираем созданное поле (sred_price) и в инспекторе объектов устанавливаем свойство: DisplayFormat ->0.00 DisplayLabel ->Средняя цена; DisplayWidth -> 10;
переходим на вкладку Events и дважды жмем мышь на событии OnCalcFields;
В открывшемся обработчике событий пишем следующий код:
procedure Tdm.ADOQuery_ostatkiCalcFields(DataSet: TDataSet);
begin
//если остатки продуктов не равны 0 то вычисляем среднюю стоимость продукта
if ADOQuery_ostatki.FieldByName('ostatok').Value<>0 then
ADOQuery_ostatki.FieldByName('sred_price').Value:=
ADOQuery_ostatki.FieldByName('summa').Value/ ADOQuery_ostatki.FieldByName('ostatok').Value
else ADOQuery_ostatki.FieldByName('sred_price').Value:=0;
end;
Возвращаемся снова к нашей форме ostatki и в обработчике событий для события OnActivate пишем следующий код:
procedure TForm_ostatki.FormActivate(Sender: TObject); begin // переоткрываем запросы при активации формы //тем самым обновляем данные на форме dm.ADOQuery_ostatki.Active:=false; dm.ADOQuery_ostatki.Active:=true; end;
Для события OnClose:
procedure TForm_ostatki.FormClose(Sender: TObject; var Action: TCloseAction); begin //закрываем запросы и закрываем форму dm.ADOQuery_ostatki.Active:=false; Action:=cafree; end;
Сейчас сделаем запуск формы «Остатки на складе» из главной формы. Для этого выбираем в меню Project -> Options… Выбираем Forms и перемещаем Form_ostatki из Auto-create forms в Avalable forms.
Переходим на главную форму, щелкаем мышкой по компоненту MainMenu1, а затем по пункту Просмотр склада
И пишем следующий обработчик события:
procedure TForm_general.N9Click(Sender: TObject); begin f:=0; //проверяем, активна ли наша форма if ActiveMDIChild.Caption<>'Остатки на складе' then begin //если нет то ищем ее среди неактивных и если находим, то показываем ее for i:=0 to form_general.MDIChildCount-1 do if form_general.MDIChildren[i].Caption='Остатки на складе' then begin MDIChildren[i].Show;f:=1;end; end else f:=1; //если форма еще не создана, то создаем ее if f<>1 then Tform_ostatki.Create(Application); end;
Вот и все готово, сохраняем и запускаем проект.
Скачать исходники урока с исправлениями можно здесь.
Здравствуйте. Подскажите пожалуйста как убрать ошибку в приходе и расходе "Невозможно найти строку для обновления....." когда вносишь данные. Делала вроде как у Вас а вносить не дает новые данные. Что не так Я пробовала добавлять и каскадное обновление и делалла схему данных, потом ее удалила, но все равно никак не получается. Подскажите пожалуйста Заранее спасибо
В данном варианте ни как, здесь используется общий учет по наименованию продукта не зависимо от цены и поставщика. Для того чтобы учитывать реальную, а не среднюю стоимость товаров, а также вести учет по поставщикам, необходимо организовывать партионный учет. Т.е. в расходе учитывать из какого прихода (партии) произведен расход, тогда с помощью запросов можно будет получать информацию как о приходе так и о расходе у любого поставщика по номеру прихода (партии).)))
Я вот переделываю вашу программу под учет ГСМ, подскажите мне пожалуйста поподробней как мне сделать просмотр итогов на каждый автомобиль в отдельности, какие нужны именно запросы?
Что бы понять какие будут запросы, нужно вначале продумать как вы реализуете связь между приходом и расходом. Пример склада , который реализован на сайте очень примитивный. В базе хранятся документы по приходу и расходу, а связь между ними осуществляется только по Id продукта, поэтому зная Id продукта в расходе нельзя определить поставщика этого продукта потому, что информации в таблицах расхода не о приходе не о поставщике не содержится. )))
Уважаемый администратор, помогите пожалуйста разобраться с запросами sql. Есть таблица. В ней расположены три ячейка : оклад, премия и итог. Необходимо чтобы при работе программы в форме мы вносили в таблицу оклад и премию а в ячейке итог это суммировалось и автоматом заносилось в итог. Как это будет выглядеть на sql запросе? Я только понял что нужно выбрать данные, но не понял как занести в нужную ячейку таблицы и главное как правильно написать имена ячеек. Вот как я написал но это не работает! SELECT Зарплаты.Оклад, Зарплаты.Премия, Зарплаты.Оклад+ Зарплаты.Премия AS Зарплаты.Сумма FROM Зарплаты;
Поле "Итог" у вас получается вычисляемым и я не вижу смысла хранить его вообще в таблице т.к. в любое время эти данные можно получить через поля "Оклад" и "Премия" с помощью запроса. А запрос должен выглядеть как то так: SELECT Зарплаты.Оклад, Зарплаты.Премия, [Зарплаты.Оклад]+[Зарплаты.Премия] AS Итог FROM Зарплаты;
Можно наверное и так, но лучше сразу получить это в запросе. В ADOQuery в свойство SQL-> TStrings добавьте запрос, который я написал выше, но с учетом названий ваших полей. Активируйте запрос и через DataSorce подключите его к DBGrid. Должны автоматом отобразиться все поля, которые есть в запросе.)))
Кидаете на форму компоненты ADOQuery, Datasource, DBgrid. У ADOQuery свойство Connection -> ADOConnection. У DataSource свойство DataSet -> ADOQuery. У DBGrid свойство DataSource -> DataSource. В компоненте ADOConnection должно быть настроено подключение к базе. Далее активируйте запрос в ADOQuery свойство Active -> True и в DBGrid должны отобразиться поля таблицы.)))
Можно с самого начала, а то я запутался(. По аналогу Вашего проекта я создал главную форму. Там есть: -MainMenu -ADOConnection1 -ADOTAble1 -ADOTable2
Затем создал подчиненную форму. В ней есть: -DBGrid1 -ADOQuery1 -DataSource -3 кнопки. но это не суть
Для получения данных в DBGrid1 я в свойстве Connection объекта DataSource выбрал Form1.ADOConnection1. Далее я в св-ве DataSet я поставил Form1.ADOTable1. (Можно выбрать либо 1 либо 2). А в форме DBGrid1 в опции DataSource я выбрал DataSource1. В результате все занесенные в базу данные повились, но ничего не рассчитывается. Скажите мне, тупому студенту что я сделал не так. Очень надеюсь на Вашу помощь -
Теперь создавайте вычисляемое поля для вашей таблицы и событие OnCalcFields в котором будет происходить вычисление этого поля. ADOQuery здесь вам пока не нужен.
Для создания вычисляемого поля вструктуре (Structure) -> Зарплаты{ADOTable1} ->Fields -> щелкнем правой кнопкой мыши и выберем Newfield….
В поле Name: пишем Итог; в поле Type: выбираем Float или Integer (в зависимости от ваших данных); выбираем поле Calculated; и жмем ОК Для события OnCalcFields компонента ADOTable1 пишем:
Код
procedure TForm1.ADOTable1CalcFields(DataSet: TDataSet); begin ADOTable1.FieldByName('Итог').Value := ADOTable1.FieldByName('оклад').Value+ADOTable1.FieldByName('премия').Value; end;
Возможно название ваших компонентов будут отличаться от этих.)))))
А у меня выдаёт 2 ошибки: 1[DCC Error] prihod.pas(20): E2065 Unsatisfied forward or external declaration 'TForm_prihod.FormCreate'(Форма "prihod" ) При нажатии выводит на строку procedure FormCreate(Sender: TObject); и в чём тут ошибка не пойму, если строчку удалить вообще, то получается вместо 2-ух ошибок, 6... 2 Ошибка [DCC Fatal Error] prihod_prod.pas(63): E2063Could not compile used unit 'prihod.pas' При нажатии выводит на строку Uses general, prihod, unit_dm, select_prod; ( Форма "prihod_prod"), подскажите пожалуйста, что делать
Вторая ошибка возникает из-за первой. По поводу первой могу сказать следующее: программа встретила какую то процедуру или тип. Либо описание этой процедуры или типа расположено позже (ниже по тексту), чем его применение. Конкретно, что делать в данной ситуации, можно сказать, только увидев соответствующий блок кода.
Но пока могу посоветовать удалить полностью процедуру procedure FormCreate(Sender: TObject); ее описание и ссылку на событие OnCreate формы т.к. в ошибке показывается именно эта процедура, а затем создать данное событие заново.))))
К сожалению, здесь все намного сложнее. Остатки не хранятся в базе, а вычисляются из таблиц storage и rashod_prod с помощью запроса. Поэтому, что бы удалить запись из формы остатки нужно удалить соответствующие записи из таблиц storage и rashod_prod, а затем пересчитать данные в таблицах prihod и rashod.))))))))
Извените еще раз ....А это делается тоже через SQL запрос? и не нужны определенные записи, мне нужно чтоб форма ostatki очищалась вся? Извените еще раз за беспокойство)))))
Я вам уже писал, что остатки формируются с помощью запроса в котором считается разность между приходом и расходом. Поэтому, что бы очистить остатки вам надо с помощью SQL запроса на удаление, удалить все записи из прихода и расхода. Только я не очень понимаю зачем это вам надо?
Хотел добавит такую функцию чтоб при истекание определённого время в остаток товар отображался красным цветом в запросе добавил поле time_hraneniya (отображаться нормально), но не знаю как правильно прописать сравнение, точто я прописал (выделена красным) не правильно
if dbgrid1.DataSource.DataSet.RecordCount<>0 then begin If dm.ADOQuery_ostatki.FieldByName('ostatok').Value<dm.ADOQuery_ostatki.FieldByName('time_hraneniya') then with DBGrid1.Canvas do begin Brush.Color := clGreen; Font.Color := clWhite; FillRect(Rect); if Column.Alignment = taRightJustify then TextOut(Rect.Right - 2 - TextWidth(Column.Field.Text), Rect.Top+2, Column.Field.Text) else TextOut(Rect.Left + 2, Rect.Top + 2, Column.Field.Text); end;
Для того, чтобы добавить такую функцию, вам нужно в запросах прихода и расхода и остатки добавить поля "дата прихода", а затем в запросе остатки добавить поле время хранения. Тогда условие подсвечивания будет выглядеть как то, так: if (sysutils.Date-dm.ADOQuery_ostatki.FieldByName('date_prihoda').Value)<dm.ADOQuery_ostatki.FieldByName('time_hraneniya').Value then.... По другому боюсь у вас вряд ли получится. Дело в том что в этих урока разобран наиболее простой вариант вычисления остатков по средней цене прихода без учета времени хранения т.е. весь приход складывается в не зависимости от даты прихода и цены, точно также с расходом, а в вашем случае необходимо разделить приход по времени и учесть это же время в расходе(расход какой даты прихода был произведен) это необходимо для того, что бы учесть время хранения. ))))