Новое…

Java Android. Урок 12 (“Делаем игру”)

После того, как мы закончили делать достаточно простую игру “Быки и коровы”, научились базовым методам программирования под Андроид, можно приступить к работе над более сложными вещами. Будем делать 2D игру “леталка-стрелялка”. Такие игры ещё иногда называют плоскими, т.к. движение персонажей происходит только вверх-вниз и вправо-влево. Однако уже здесь потребуются знания несколько большие, чем просто программирование. Возможно, когда вы учились в школе, то спрашивали себя,- Зачем мне эта математика, зачем мне эта физика?- Вот как раз для программирования это всё и нужно, если вы кончено собираетесь делать игры и программы хоть как-то отдалённо моделирующих окружающий нас мир. Как я обычно говорю, программирование это всего лишь инструмент, вроде молотка, чтобы не заколачивать гвозди руками. Но чтобы сколачивать дом, вам нужно всё рассчитать, прежде чем брать в руки молоток и обладать какими-то базовыми знаниями в области строительства. В играх чуть более сложных, чем та, которую мы делали, это тоже становится неизбежно необходимостью. Но огорчаться по этому вопросу не нужно, всё что потребуется из этих наук, я буду это использовать, и тут вам останется либо проверять меня и вступать в полемику, либо поверить наслово. Сразу скажу – идея игры, которую мы будем делать, я позаимствовал с другой, прочно уже забытой игры, но это не меняет нашей задачи. Выглядеть она будет как-то так:

Видео самого экшена:

После её написания, полагаю, вы сможете уже делать любую другую игру. Делать мы её будем совместно, а так же вы сможете:

  • Создать для неё какой-нибудь объект
  • Дать исходник его мне
  • Я опубликую игру с ним на своём аккаунте https://apps.rustore.ru/developer/ZFQbCo1eEp3jOqp5fKusWsz0L%2B8vFUgs, а в самом приложении будет информация о вас, как о разработчике со ссылкой на ваш сайт и/или email.

Итак, приступим, что тут у нас есть; а есть следующее:

  • Игровое поле, которое чуть меньше экрана по высоте, и на несколько экранов в право и влево.
  • Есть масштабный экран сверху.
  • Есть один пользовательский объект, которым он может управлять и перемещать его в пределах экрана.
  • Само рабочее поле тоже подвижное, но только влево и вправо.
  • Объекты могут стрелять – пользовательский объект лазером, игровые юниты – каждое своим вооружением.
  • Игровые юниты могут быть различной конфигурации.
  • Элементы управления вынесены на рабочий экран.
  • В игровом поле есть рельеф местности.
  • Есть юниты испытывающие гравитацию.
  • Все юниты имеют “физику”, т.е. двигаются с ускорением и торможением.
  • Игра должна иметь звуковые эффекты.
  • Игра должна иметь динамические сцены, присущие играм такого типа.

Все это мы будем реализовывать не при помощи каких-то движков, типа Unity, а непосредственно сами. Как понимаете, тут в 10 уроков мы никак не уложимся.

Имя проекта Salvador, что переводится как “Спасающий” (с испанского). Почему так – просто имя проекта должно быть латиницей, а английский язык как-то слегка приелся)). В игре будут юниты, которых нужно будет спасать)).

Для формирования рабочей области экрана, нам придется отказаться от стандартных элементов. По этому метод создания MainActivity будет начинаться так:

Всё что этот код делает, так это скрывает такие системные элементы на экране, как часы, уровень мобильной связи, верхний бар Активити и т.п. В результате нам нужно получить идеально черный экран, полностью и целиком. Но вопросы должно вызывать следующее – я закомментировал setContentView(), который вызывается с параметром xml окна, а вместо этого создаю экземпляр какого-то класса GameEngine и передаю его в качестве параметра для своего setContentView(). Из этого действа можно заключить, что мой GameEngine – это какой-то класс, который способен принять в качестве параметра метод setContentView(), и который и будет рисовать наш экран, а вот стандартный нам не нужен. Как теперь понятно, что и сам activity_main.xml (который среда разработки создала автоматически) нам теперь тоже не нужен:

Теперь создадим этот самый класс GameEngine (ПКМ по названию пакета):

Вот так:

В результате среда разработки создаст нам этот класс:

Однако, такой класс метод setContentView() в качестве параметра не примет, т.к. параметром его должно быть что-то, что наследуется от класса View – помните в прошлом курсе мы делали activity_main.xml и корневым элементом был ConstraintLayout (наследный от View). Наш класс мы тоже должны наследовать от него, по этому сделаем так:

Тут как только мы попытались добавить это наследование, Андроид Студио стала ругаться, что мол если ты наследуешь, то обязательно реализуй конструктор того, от чего наследуешь, т.е. от суперкласса View.. Все они нам сейчас не нужны, нам нужен только первый и то, немного доработанный, выберем его и окейнем:

Получается теперь так:

Однако, если вы парой минут назад заметили, я вызывал конструктор GameEngine с двумя параметрами, вторым из которых был параметры экрана (в модуле MainActivity). Доработаем с учетом этого:

Этот параметр нужен нам будет чтобы иметь представление какого размера экран устройства, на котором запускает игру пользователь. Он как раз в себе эту информацию и хранит. Заметьте, я не трогаю суперкласс, его формат вызова строго определен во View.

Пока писал эту статью заметил, что одна версия Андроид некорректно отрабатывает один метод, по этому переделайте его на вот так:

Однако давайте теперь запустим наше приложение и посмотрим что будет на экране, а должно быть вот так:

Кстати, повернуть экран можно вот этими кнопками:

Поскольку мы наследовали наш класс GameEngine от класс View, то как следствие можем пользоваться и его методами, т.е. использовать наследование класса в полной мере, а именно – не просто будем вызывать метод родительского класса, а вообще перепишем его на свой собственный. Нужно нам это потому, что класс View в своём составе имеет метод onDraw(). Этот метод делает ни что иное, как отображает элементы этого класса View на экране – непосредственно их рисует при помощи графических методов. Происходит это тогда, когда View получает запрос на то, что теперь вдруг данные на экране стали недостоверными. Причин может быть много, например одна из них это то, что мы только что открыли приложение. Откуда там достоверным данным, их нет, мы же только что его открыли. Также, если ввиду функционала, на экране происходят какие-то изменения, например, как в прошлом уроке мы таймер на экран выводили и меняли раз в секунду его значение на экране. Когда мы записывали новое значение в TextView автоматически его в родительском классе (в том же самом View) вызывался метод недостоверности сведений и опять-таки запускался следом метод onDraw() для новой отрисовки элемента. Давайте сделаем наконец-таки и мы этот метод и отрисуем чёрный квадрат Малевича экран. Добавим в наш класс GameEngine этот метод и ещё одну переменную класса:

Ну вообще круть! Так что собственно происходит, давайте подытожим:

  • Создаётся наше Активити
  • При создании создаётся экземпляр нашего класса GameEngine, который наследуется от базового класса View
  • Этот экземпляр передаётся методу отрисовки Активити, а поскольку он наследованный от View, то вызывается сразу следом метод недостоверности содержимого (метод invalidate() ).
  • Этот метод invalidate() выполняет ряд функционала, частным случаем которого является вызов метода onDraw() класса View, но этот метод теперь в нашем классе GameEngine, по этому вызов происходит не родительского метода, а нашего. А в нашем методе уже есть код установить цвет отрисовки на чёрный (параметр Color.BLACK) и вывести его на канву canvas (полотно рисования, экранное полотно, холст) методом drawPaint().

Теперь добавим ещё ряд методов и посмотрим как рисовать на экране что-то вразумительное:

Тут видно для чего нам нужно было знать параметры экрана (чтобы знать где находится правый край) и посмотрели как нарисовать простой тонкий прямоугольник размером в ширину экрана и по высоте 10 пикселей (по координатам Y от 100 до 110). Заметьте, рисование происходит в два этапа – сначала устанавливаем стиль того как и каким цветом будем рисовать, а потом – что будем рисовать (геометрию фигуры).

Задание по уроку:

1. Самостоятельно разберитесь какие методы рисуют линию, точку, круг

2. Нарисуйте согласно плану нашей разработки эту верхнюю часть масштабного представления (саму красную рамку и белый позиционер текущего экрана).

3. Попробуйте нарисовать посередине экрана, как в нашем проекте, космический корабль игрока:

Если всё получилось как нужно – молодцы! Если нет, давайте разберёмся что не так.

Урок 11 Урок 13

El Vinto, 2023 (Copyright)

Java Android. Урок 11 (“Динамика”)

Продолжаем изучение Java Android. На прошлом курсе мы сделали приложение “Быки и коровы”. Сама игра интересная, но нет в ней какой-нибудь мало-мальской динамики. Давайте её немного доработаем и приступим к более живеньким проектам.

Сделаем так, чтобы на ход игроку отводилось определенное время, например 30 секунд. Если игрок не успевает сделать ход, то он проиграл. Но игрока нужно подтрунивать, а для этого вывести на экран что-то похожее на таймер, чтобы игрок его видел, спешил, старался быстрее думать, но в общем всё, как в жизни.

Сначала я размещу View, куда буду потом отправлять каждый отсчет таймера:

После этого нужно сделать так, чтобы после первого хода он считал, а с каждым следующим сбрасывался и считал снова.

Тут следует сделать теперь небольшое отступление. В Android программа выполняется нелинейно. Если говорить очень просто, то процесс выполнения следующий:

  • Возникает какое-нибудь событие, запускается его обработчик, возвращается из выполнения.
  • Возникает новое событие, выполняется, возвращается и т.д.

Часто бывает так, что во время одного события происходит другое, ещё не дав ему завершиться, например, вы играете на телефоне в шахматы, вдруг приходит входящий звонок, который сворачивает шахматы и запускает приложение телефона. Звонок завершается, возвращается всё опять к шахматам. Т.е. события выстраиваются в очередь выполнения в разных приоритетах. Однако, есть события, которые должны выполняться независимо от очереди, либо в собственной ветке очередей. Например, вы переходите в телефоне в браузер, переходите на какую-то страницу и сразу начинаете её листать, но она при этом продолжает дозагружать картинки. Часто наверно такое встречали? Т.е. программа не заставляет вас скучно ждать пока не загрузит всю страницу целиком, а даёт возможность с ней работать по мере поступления контента. Такая работа называется параллельной. Т.е. есть пользователь и его взаимодействие с устройством, и есть что-то внутри, что должно работать параллельно. Всё заточено на то, чтобы не заставлять пользователя чего-то ждать, по крайней мере, чтобы пользователь мог изменить ход или логику работы в любой момент времени. Например, страница долго не загружается и пользователю надоело ждать, он должен в любой момент прервать этот процесс – закрыть страницу и возможно поискать другую, которая будет грузиться быстрее. Если пользователю мешать управлять устройством, либо делать это с запаздыванием, с плохим откликом, работать будет некомфортно, будет создаваться ощущение медлительности устройства. По этому совместная работа устройства и пользователя вынесена для разработчиков в отдельную парадигму и называется поток пользовательского интерфейса UIThread (User Interface Thread). Это поток, которому отводится определенное комфортное для пользователя время, как правило измеряемое миллисекундами. За это время программа должна как-то успеть отреагировать на действия пользователя и ответить на экране, если это необходимо. Всё, что превосходит это комфортное время, называется термином “Длительное выполнение” и должно быть распараллелено, т.е. вынесено в отдельный поток выполнения. Говоря иначе, если время чего-то предполагается таким, что пользователь может заметить какую-то задержку реакции, это нужно запускать отдельным потоком, и уже по мере его завершения как-то дать понять пользователю, что оно завершено, а НЕ(!) блокировать работу устройства от начала выполнения и до конца, пока оно не завершиться. Возвращаясь к примеру о браузере и странице это выглядит так – пользователь ввел адрес страницы и нажал кнопку “Перейти”. В этот момент браузер быстро принял этот адрес из поля EditText, быстро создаёт параллельный поток, передает ему этот адрес и запускает его, возвращая пользователю возможность продолжать работу с устройством. Само создание нового потока занимает столь малое время, что пользователь даже не успевает заметить, но в это время уже параллельный поток делаёт длительную операцию по обращению к серверу с нужной страницей, загрузки её в браузер, распознавание кода, дозагрузка картинок и т.п., время от времени передавая в пользовательский поток малыми порциями элементы загрузки, например, показать очередную картинку. В нашей задаче должно быть всё тоже самое. Пользователь сохраняет контроль над устройством, но параллельно приложение должно отсчитывать таймер и периодически сообщать его новое значение в пользовательский поток, который и будет уже его отображать на экране.

Самое простое создание отдельного потока – это создание экземпляра класса Runnable, давайте его добавим в наш основной класс MainActivity сразу после generateComNumber():

Теперь вначале класса MainActivity поместим несколько переменных класса:

И ещё добавим один метод где-нибудь тоже после generateComNumber():

Теперь давайте разбираться что тут для чего. Класс GameTimer наследуется от Runnable и обязательно должен реализовывать метод run(). Этот метод и есть то самое, что будет внутри него выполняться параллельно. А выполняться там будет вечный цикл, который будет прерываться каждую итерацию на 1 секунду и снова, и снова повторяться. Вся его задача – это передать короткое сообщение пользовательскому потоку. Это сообщение не содержит в себе никакой информации, т.к. сам факт этого сообщения говорит о том, что снова параллельный поток подождал 1 секунду времени и повторяет итерацию. А ведь нам это и надо, чтобы кто-то пользовательский интерфейс взбадривал каждую секунду.

Переменная sender и соответствующая константа SENDER_REFRESH совсем не обязательны, в нашем случае весь метод mainMessage() мог использоваться для реализации функционала отсчета, однако я решил показать как нужно получать параметры из другого потома, если вдруг нужно передать ещё какую-нибудь информацию.

Теперь нужно создать экземпляр этого класса и передать этот экземпляр в конструктор специального системного объекта Thread. Чтобы не вносить сумятицу, поток этот будет создаваться в специальном методе, а завершаться в противоположном (см.далее):

Можно заметить странную конструкцию у объявления переменной mainHandler. Она хранит экземпляр специального класса, который опрашивает пул поступающих сообщений, в частности будет принимать сообщения от нашего класса GameTimer и вызывать mainMessage() с этим параметром; это происходит благодаря встраиванию класса с имплементацией (метод handleMessage() как раз и есть реализация).

Чтобы параллельный потом не остался “висеть” в памяти и фанатично отматывать километры кода даже после завершения игры (он же параллельный независимый поток), нужно при завершении приложения его тоже ПРАВИЛЬНО завершить. Для этого достаточно вызвать уже заранее подготовленный нами метод:

Если посмотрим внимательно, этот метод stopAll() наш, и делает он всего лишь только то, что в переменную isRunnig помещает false, “вечный” цикл прерывается и поток выходит из выполнения, сам экземпляр класса потока уничтожится системой автоматически при завершении Активити.

Метод onStop() вызывается когда происходит остановка работы Активити (в отличии onStart(), который автоматически вызывается при старте).

Вообще порядок вызовов методов “жизни” Активити называется lifecycle и описан тут:

https://developer.android.com/guide/components/activities/activity-lifecycle

, однако по представленной там картинке и так становится понятным как всё остальное:

Тут хорошо видно, что после onStop() может быть одно из трех, но по нашему алгоритму либо окончательно выйдет из приложения, либо возвратится и повторно создаст наш таймер.


Задание по уроку:

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

2. Самостоятельно найдите описание и назначение контейнера RelativeLayout. 

3. Попробуйте сделать так, чтобы когда оставшееся время становилось менее 10 секунд, таймер начинал показывать десятые доли секунды. Используйте RelativeLayout для отображения долей секунды в уменьшенном виде:

В динамике это выглядит так:

Если всё получилось как нужно – молодцы! Если нет, давайте разберёмся что не так.

Урок 10 Урок 12

El Vinto, 2023 (Copyright)

Как для сайта выделить терабайты дисковой памяти и ничего за это не платить

Вот вы решили сделать свой сайт, выбрали уже доменное имя, купили хостинг и закинули движок на сервер хостера. К вам стали подтягиваться посетители, вы стали постить тексты, фото, видо и вот вдруг обнаруживается, что места больше нет. Вы заходите в панель управления, а там предлагается объём до 100 ГБ и стоить это будет “космос”. Так что, кина не будет? (цит. из ф. “Джентельмены удачи”). Ну, мы же айтишники, мы понимаем, что в ИТ можно всё!..ну, почти всё))…

Для начала нам потребуется перевести хостинг на VPS. И вы не поверите, но абонентская плата будет меньше за те же ресурсы. По крайней мере у меня на Бегете (на нём я около 5 лет) это было именно так. Кстати, вот партнерская ссылка, по которой и мне будет профит и вам скидка:

https://beget.com/p1223557

Для тех, кто не имел с этим дело скажу. Обычный хостинг – это фактически папка на сервере, где ещё таких как вы могут быть сотни. Один мощный сервер распределяет свои ресурсы между множеством сайтов таких же как и вы клиентов. Поддержка, обслуживание и работы на этом сервере требуют квалифицированный персонал, по этому за это берутся отдельные деньги. VPS – это вам даётся сервер с уже предустановленной операционной системой, в частности Ubuntu, и установленным на неё ПО для работы сайта. Чаще всего это пакет LAMP (Linux, Apache, MySQL, PHP). Сам сервере виртуальный, но для нас это не имеет никакого значения, т.к. ничто не будет отличать его от реального. Но и здесь хостер выделит вам всё равно те же 10 ГБ (начальный уровень), а за всё что больше придётся доплатить…это если вы что-то не знаете, что знаю я)))…Открываем папку моего сайта https://elvinto.ru и смотрим размер свободного месте на диске:

Внимательно смотрим в правый нижний угол – 94 ГБ и з 1.8 ТБ свободно. Теперь взглянем, что в моём тарифном плане в панели управления:

Получается как в сказке “Карлик нос” – избушка изнутри больше, чем снаружи. Так-то оно так, да не совсем. Пора открывать карты)).

Есть у меня на этом VPS-сервере портал в другое измерение, а именно – на мой домашний комп. Весь процесс сводится к монтировании домашней папки в папку VPS-сервера. Домашний сервер у меня тоже на Ubuntu, дома потребуется “белый” IP, по этому и для этого примера тоже буду писать исходя из этого. Пользователь на VPS vpsuser, на домашнем компе – homeuser. В папке homeuser есть папка content, которая и является тем терабайтным хранилищем, которое мы подключаем.

Настроим сначала сервер VPS.

Для этого сначала нужно установить sshfs:

sudo apt-get install sshfs

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

ssh-keygen -b 4096

В результате получим в папке ~/.ssh пару файлов – публичный и приватный ключ. Теперь публичный ключ можно скопировать себе на домашний комп. Сделал я это просто – выложил на сайте по ссылке прямо в корне elvinto.ru/id_rsa.pub (сейчас там нет этого файла, чтобы не засорять корень), а с домашнего компа скачал из браузера. Бояться, что его кто-то украдёт не надо, т.к. красть его глупо, это публичный ключ и расшифровать закодированную им информацию может только обладатель приватного ключа, который остался в папке .ssh на VPS (вы же ничего не напутали?!!)) ).

Настройка домашнего сервера.

Создайте удобочитаемую папку на том диске, где у вас эти самые терабайты. У меня в примере это /home/homeuser/content. Теперь в папку /home/.ssh закиньте id_rsa.pub и переименуйте его в authorized_keys (если такой файл уже есть, то допишите в конец этого файла ваш id_rsa.pub). У файла authorized_keys обязательно должны быть права (с другими может отказать в авторизации по ключу и потребует ввести пользователь и пароль):

chmod 0600 authorized_keys

Снова продолжаем настраивать VPS.

Теперь нужно в файл /etc/rc.local (если его нет, см.далее) перед exit 0 добавить нужный текст (для сайта на WordPress – расширяется папка wp-content/uploads):

sshfs -o idmap=user,allow_other,reconnect,ServerAliveInterval=600,IdentityFile=/home/vpsuser/.ssh/id_rsa -p 22 homeuser@ВашБелыйIPадрес:/home/homeuser/content /ПутьКВашемуСайту/wp-content/uploads

Если файла rc.local нет, значит сервис rc-local просто не включен, такое встречается на Ubuntu >= 20.04. Всё делается просто. Создайте при помощи, например, nano или mcedit этот файл и дайте ему права на запуск. Файл должен начинаться со спец.строки и заканчиваться exit 0:

Теперь нужно включить сервис rc-local:

sudo systemctl enable rc-local

и стартануть его:

sudo systemctl start rc-local.service

Проверяем на VPS папку, куда монтировали, она должна ссылаться на ваш домашний сервер.

П.С.:

Вы должны понимать, что качество вашего сайта после этого будет обеспечиваться двумя основными факторами – стабильность и скорость домашнего инета и надежность домашнего хранилища. Но если речь идет о бесплатном обеспечении сайта терабайтами, то уж постарайтесь это осуществить…как-то))).

El Vinto, 2023

Программирование 1С. Урок 10

Типичная ситуация в работе программиста 1С – приходит вдруг с утра к вам в кабинет финансовый директор компании и заявляет. Товарищ программист, что-то какая-то бодяга у нас в управленческом учете. На кой ляд нам такое количество разных видов приходных документов? И на мелочевку, и на бытовую технику, и продукты питания. Наши экономисты уже забодались бегать от одного документа к другому. Хочу чтобы это всё учитывалось одним документом…и уходит…а вы после этого ещё минут пять сидите вот так:

…но это если вы только начинаете программировать на 1С, а вот если есть небольшой опыт, то сразу же поймете, что задача хотя и нетривиальная, но всё же не такая сложная. Давайте убедимся в этом наглядно. Добавить нужные табличные части в документ поступления товаров услуг – это не проблема, и мы это делать уже умеем. А вот что делать с данными? Как их после этого перебросить в этот документ, ну не вводить же их заново!

Для начала всё-таки модернизируем наш документ ПоступлениеТоваровУслуг, создав там табличные части по тому виду, которые они были в этих документах, но их придётся переименовать, т.к. нельзя в одном объекте делать табличные части или реквизиты с одинаковым названием:

На форму тоже добавим все эти табличные части.

Теперь саму форму нужно немного доработать – помните мы считали сумму по строке при помощи процедуры общего модуля? Она ведь сейчас будет считать только для табличной части “Товары”, т.к. мы это в ней явно указали, а для остальных табличных частей будет выдавать ошибку выполнения, т.к. программа будет пытаться обратиться к текущей строке таблицы Товары, в то время как текущая строка будет находится вообще в другой табличной части. Давайте доработаем код процедуры и сделаем его таким:

Видите, я могу обратиться к табличной части не только “через точку”, но и через именованный массив – программа будет брать имя табличной части из переменной ИмяТабличнойЧасти, которое мы будем передавать при вызове этой процедуры:

Кстати, а вы не заметили, что что-то тут лишнее? Две процедуры выполняют одно и то же. Давайте мы оставим только одну процедуру и дадим ей немного общее наименование, а вторая вообще больше не нужна:

Но на эти процедуры ссылались события в двух колонках – “Количество” и “Цена”, по этому там тоже надо теперь указать эту новую процедуру:

Тоже самое сделать и для колонки количества, указав ту же самую процедуру:

Добавим теперь на каждую табличную часть новую вкладку со своей табличной частью, а то сейчас у нас только товары и услуги. Делается это при помощи добавления новой страницы (это сама вкладка):

, указав элемент “Страница”:

, а затем и самой табличной части перетаскиванием справа налево:

Теперь дадим самой вкладке вразумительное имя по аналогии предыдущими:

Теперь добавьте реквизит документа “ИдентификаторСтарогоДокумента”, значение которого станет понятно чуть позже:

Теперь познакомимся с новый объектом метаданных – Обработка, и сразу же добавим новую с именем “ПереносДанных”:

Сделайте ещё одну подсистему и включите туда эту обработку:

Теперь добавим форму:

Теперь на форму нужно добавить единственную кнопку, по нажатию которой будет происходить перенос данных из существующих документов в наш новый переделанный с множеством табличных частей. Добавьте новый элемент “Кнопка” на форму:

К слову говоря, подобным способом добавляются элементы на все формы, в т.ч. справочников и документов, но мы делали это немного другим способом, т.к. у нас был элемент отображения реквизита и мы просто перетягивали его справа налево.

Кнопка появилась, но теперь нужно добавить команду – реакцию по нажатию на кнопку. Для этого нужно открыть вкладку “Команды” и добавить новую:

Теперь в свойствах кликнуть по созданию обработчика:

В этот раз обработчик будет включать в себя две процедуры – на клиенте и на сервере, т.к. нам понадобятся процедуры и функции, которые характерны только для серверного исполнения. В результате платформа автоматически подготовит их для программирования:

Теперь нужно возвратиться обратно в графическое представление формы и привязать кнопку к команде и дать человеческие наименования:

Переходим снова в код. Делаем так:

Давайте разбираться что тут. Сначала я сделал запрос для того чтобы перебрать все уже созданные документы ПоступлениеМелочевки.

Получаю уникальный идентификатор каждого документа. Этот идентификатор – объект платформы, который легко можно преобразовать в строку. Строка получается 36 символов. Каждый объект данных 1С имеет уникальную идентификацию в пределах одной базы. Она нужна мне чтобы привязать создаваемые автоматически документы с исходным “старым” документом. Такая идентификация мне нужна чтобы однозначно определить перегружался ли этот документ уже или нет, на случай, если я захочу запустить обработку повторно. Повторный запуск может потребоваться, если при первом что-то пошло не так или мы уже успели внести ещё данных по мелочевки “старым” способом.

НайтиДокументПоИдентификатору() – это нестандартная функция, как может показаться, которая будет искать документ по признаку идентификатора; я разместил её в этом же модуле:

Эта функция при помощи простого запроса с условием ищет тот документ ПоступленияТоваровУслуг, где реквизит ИдентификаторСтарогоДокумента будет равен идентификатору исходного документа (мелочёвки, продуктов питания и т.п.). Если результат поиска отрицательный, то функция возвратит значение равное “Неопределено”.

После этого я проверяю удалось ли найти документ. Если не удалось, то будет создан новый и сразу же определен реквизит с идентификатором и дата (без указания даты документ не удастся записать).

Следующие действия – это перенести построчно табличную часть с товарами. Тут должно быть понятно – берём строку исходного документа – создаём строку конечного документа.

В результате получаем что-то вроде этого:

Тут хорошо видно, что два первых документа без идентификатора – это те, что создавали интерактивно (вручную), а два с идентификатором – это те, что переносили обработкой (именно тогда мы его устанавливали). Сам документ теперь выглядит так:

Задание по уроку:

1. Добавьте на форму документа ПоступлениеТоваровУслуг страницу с табличной части “ПродуктыПитания”, сделайте необходимый расчет суммы по строке.

2. Добавьте в обработку перенос документа ПродуктыПитания

3. Ранее я говорил, что перед проведением документа всегда автоматически происходит его запись, однако при этом принудительно делаю перед этим запись. Как думаете, почему я так сделал?

Если всё получилось как нужно – молодцы! Если нет, давайте разберёмся что не так.

El Vinto, 2023

Программирование 1С. Урок 9

Продолжим изучение 1С. Делаем мы это на живом примере, разрабатывая нашу собственную конфигурацию домашнего бюджета. Если вы по честному делаете мои домашние задания, то у вас уже должен быть создан справочники единиц измерения и продуктов питания, и одно привязано к другому, а также документы учета продуктов питания. Кстати, вы наверно обратили внимание, что учет количества продуктов питания идет с точностью до 3 знака, а размер числового параметра 15 (ведь некоторые продукты весовые с точностью до грамма).

Если вы уже успели заметить, у нас есть документ реализации для бытовой техники, но нет аналогичных документов для мелочевки и конечно же не будет для продуктов питания. Как думаете, почему?..Правильно, эти товары мы не будем продавать, а финансово говоря – будем их списывать по мере потребления. По этому будут документы похожие, но не совсем.

Теперь давайте сделаем аналогичный товарам отчет и по продуктам питания, чтобы знать сколько у нас их на остатках:

Хотелось бы в этом отчете увидеть ещё и единицу измерения, которую мы указывали для каждого товара. Конечно картофель вряд ли кто будет измерять поштучно, а мясо кусками, но всё же очень важно здесь отразить чтобы потом ничего не напутать, особенно если мы в будущем будем учитывать молоко и станет вдруг не ясно, считаем мы бутылками или литрами. Так вот, сделать это можно как минимум двумя способами (может есть и ещё, но навскидку не вспомню)) ).

Способ 1.

Раскрыть номенклатуру прямо в полях и вытянуть направо:

Способ 2.

Объявить получение нового поля в запросе:

А после этого это поле появится в настройках:

Обратите внимание, что по прежнему ничего не мешает сделать и первым способом, т.к. это поле есть и там и там; это два разных указателя на одно и тоже поле:

Что называется, выбирайте тот, который больше импонирует. Сделаем первым способом и увидим:

Вот только выглядит это не совсем впечатляюще. Обычно принято такие важные параметры выделять отдельной колонкой, но 1С увидела, что единица находится в подчинении номенклатуры и встроила в одну колонку с продуктом питания, автоматически отделив запятой.

А если по второму способу:

Тут у нас уже отдельное поле и 1С автоматически не стала встраивать его к номенклатуре, т.к. оно независимо полученное из запроса. Однако, и при первом способе можно получить такой же результат, если принудительно указать, что поля должны быть в отдельных колонка, для чего их нужно сгруппировать специальным способом:

Перенести поля туда методом Drag&Drop:

и указать тип группы как “В отдельной колонке”:

Получаем аналогичный результат:

Нужно привыкать к тому, что в программировании многие задачи можно решить множеством способов, и часто изначально не всегда представляется возможным понять какой из них будет наиболее рациональным. Также и сама платформа 1С позволяет решать задачи несколькими способами, как это было только что представлено; выбор лежит на программисте, какой способ для конкретной задачи он находит более оптимальным.

Теперь, когда мы обладаем кучей информации о том что у нас есть дома, пардон, на складе, и продукты питания, и мелочевка, и бытовая техника, есть информация о том кто что когда покупал, но всё-таки как-то не ясно кто в доме больше всего транжирит деньги. Кончено, подозрение есть, что это маманька со своей косметикой и новыми платьями, но это надо всё-таки проверить чтобы не быть голословным))). Вот только как нам собрать теперь все эти данные, а собрать их не так сложно. Давайте сделаем новый отчет, в котором будет представлена сводная информация, запрос у него будет таким:

Тут как-то получилось не очень и всё в куче. Давайте вначале поймем, что точная дата нам не особо нужна для анализа, а вот месяц, в котором эти затраты были, очень важен, т.к. все мы ориентируемся на зарплаты, а кто-то на пенсии и дотации. Давайте отчет представим разделенным по периодам. Делается это так, сначала добавим новую группировку:

И укажем вот такое поле:

В этой группировке создам ещё для месяца и для домочадца, перетащим в нижнюю иерархию детальные записи и в результате получаем вот так:

Посмотрим что получилось, а получилось весьма даже не плохо, кроме дат – вместо года и месяца в иерархи полная дата:

Такое представление ещё хуже, чем было. Давайте сделаем красиво, по этому поставьте курсор в иерархии групп на группировку года, выберите настройку группировки, а не отчета, теперь кликнем во вкладке “Условное оформление” по зеленому плюсу и добавим новую строку:

Т.е. текст формата:

ДФ=yyyy

Тоже самое сделайте и для группировки месяца, но формат будет другой (латинские буквы М):

ДФ=MMMM

Как понимаете, “y” (буква игрек, уай) – это от слова year (год), а “M” – это month (месяц). После этого получается так:

Ну, и теперь осталось отчет, что называется, полирнуть. Ну, в смысле отполировать.

Укажите расчет ресурсов следующим способом:

И казалось бы от такой манипуляции что радикально должно нового появиться, а получается так:

Т.е. программа посчитала все промежуточные итоги в каждой группировке, а ведь это нам и нужно было. Оказывается в моём отчете больше всех порастратил брат в сентябре, накупив холодильников))).

Идем далее. Поступление разных товаров в дом – это хорошо, но это полноценно всё-таки не отражает затраты. Дело в том, что поступление может быть и разным, например, взяли телевизор в кредит, но мы не потратились при этом ни на копейку. С другой стороны, мы могли дать друзьям шуруповёрт на недельку, он вышел с нашего остатка, но при этом денег мы за него не получили. По этому по всем правилам любое приобретение состоит из двух частей – передача товара или услуг и оплата. Но для того чтобы что-то платить нужны деньги, а в финансовом смысле – источники средств. Для фирмы – это выручка, инвестиции и уставной капитал, а для нашей конфигурации – это заработная плата, стипендия, пенсия и кредиты. Пора учитывать этот раздел. Сделаем документ “ПоступлениеДенежныхСредств”, который получается крайне простой:

Однако, тут нет важной для учета информации, какой источник доходов, чтобы потом определить, это у бати такая зарплата маленькая или у бабуси крутая пенсия)). Можно конечно сюда тоже добавить справочник, но вариантов не так много, и новые вряд ли появятся. По этому такие объекты чаще делают не на основе справочника, а на основе фиксированного списка (чтобы пользователь не мог создать ничего лишнего, а мог использовать только то, что предусмотрено конфигурацией). Есть для этого специальный объект – перечисление, но заполняется он данными не в рабочей среде, а непосредственно в конфигураторе. Давайте создадим такой в соответствующем разделе:

И добавим в него несколько значений:

Вот этипх:

Значение “Прочее” нужно чтобы пользователь мог туда включать всё остальное, что не входит в эти категории источников дохода, но тем не менее происходит, например, выиграл в лотерею, нашёл на улице, подарили на День рождения и т.д.

И добавим теперь реквизит с этим типом в наш новый документ:

В результате получится так:

Задание по уроку:

1. Добавьте во все товарные справочники единицы измерения, добавьте в сводный отчет количества прихода и единицы измерения.

2. Создайте новое перечисление “ТипПоступленияТоваров” со значениями “Покупка”, “Аренда” и сделайте реквизит в документе ПоступленияТоваровУслуг.

3. Создайте регистр накопления для учета денежных средств и сделайте соответствующую обработку проведения для документа поступления денежных средств так, как вы понимаете это.

Если всё получилось как нужно – молодцы! Если нет, давайте разберёмся что не так.

El Vinto, 2023

Программирование 1С. Урок 8

Давайте продолжим делать нашу конфигу, пардон, конфигурацию. И теперь настало время посмотреть, а что у нас на экране происходит, а происходит вот что:

Т.е. иными словами – что-то не так с интерфейсом. Всё в кучу, и справочники, и документы, и отчеты. Так не пойдет. Интерфейс должен быть безупречен, по крайней мере мы должны стараться так делать. Если пользователь в таком виде откроет программу 1С, он будет мягко говоря опечален, ведь ему работать с этим каждый день. Давайте начнем приводить в удобный вид. Для этого на платформе 1С специальный объект, который называется “Подсистема”.

Давайте добавим две новых подсистемы вот так и дадим им имена “Справочники” и “Документы”:

Получится как-то так:

Обратите внимание, тут случайно можно одну подсистему поместить по иерархии внутрь другой, но должно быть так – обе корневые. Теперь откроем каждую и по очереди установим справочники в свою подсистему:

, а документы – в свою:

Перезапустим 1С и посмотрим что получается:

Согласитесь, теперь намного круче. Однако, платформа 1С позволяет пользовательский интерфейс ещё немного донастроить уже в режиме предприятия (в рабочей среде, а не в конфигураторе). Делается это тут:

Теперь пользуясь перетаскиванием мыши расставьте панели в таком порядке:

Нажмите “ОК” и посмотрите как получилось:

Совсем другое дело – так намного удобнее и лучше, но тоже не особо. Дело в том, что негласно принято так, что сначала в разделах должны следовать справочники, а потом документы, если они выделены одновременно в разных разделах. Т.е. нужно изменить порядок. Это уже настройки конфигурации, по этому кликните правой кнопкой мыши по самой конфигурации и выберите пункт касаемый командного интерфейса:

Пользуясь кнопками вверх-вниз расположите в нужном порядке:

Теперь выглядит ещё лучше:

Теперь вспомним как мы смотрели проведение по регистру товаров на складах. Правильно, открывали функции для технического специалиста и искали там наши проводки. Делать это не всегда удобно, особенно если мы работаем с конкретным документом. Хотелось бы как-то сразу никуда не переходя посмотреть какие проводки он создаёт. Сейчас мы сделаем некоторые манипуляции и посмотрим о чём я говорю.

Откройте форму документа ПоступлениеТоваровУслуг и перейдите на вкладку “Командный интерфейс”:

Откройте иерархию “Перейти” и посмотрите что там как раз наш регистр:

Помните, когда мы указывали, что наш документ ПоступлениеТоваровУслуг является регистратором регистра ТоварыНаСкладах? Вот, программа это помнит и понимает, что наверно вы захотите возможно включить этот регистр в командный интерфейс, но уже не всей конфигурации, а командный интерфейс именно этого вида документов. Включите видимость и перезайдите в 1С:

Открыв документ видим что-то новое:

И это не что иное, что мы собственно и хотели – записи регистра, созданные именно этим документом:

Задание по уроку:

1. Сделайте раздел командного интерфейса для отчетов и расположите там наш отчет “Товары на складах”

2. Добавьте командный интерфейс с регистром ТоварыНаСкладах для документа РеализацияТоваров.

3. Создайте два новый справочника ЕдиницыИзмерения и ПродуктыПитания. У продуктов питания добавьте реквизит ЕдиницаИзмерения:

Включите их командный интерфейс справочников. Создайте две единицы измерения “шт” и “кг”. Создайте несколько позиций продуктов питания, указав в какой она измеряется единице измерения:

Сделайте вид документа “ПоступленияПродуктовПитания” и внесите несколько документов в учет, что-то вроде такого:

Если всё получилось как нужно – молодцы! Если нет, давайте разберёмся что не так.

El Vinto, 2023

Начало программирования вообще с нуля. Часть 5. Клиент и сервер.

В современном мире (2023 год) все приложения и программы, с которыми мы так или иначе сталкиваемся живут в определенном виртуальном поле, называемом клиент-серверной организацией. Даже если мы запускаем обычный калькулятор на нашем планшете и предназначен он только для простых арифметических операций, он всё равно находится в клиент-серверном пространстве, просто для него ещё не придумано управляющего сервера и он выступает только оффлайн клиентом, т.е. клиентом без взаимодействия с какими-либо ещё клиентами. Если рассматриваем приложения вроде какой-нибудь социальной сети или онлайн-магазина, да что говорить, самый обычный браузер на компьютере – это полноценная часть клиент-серверного пространства, в котором им отведена почётная роль клиента. Сервера находятся где-то далеко, может быть на другом конце света, а может быть в соседнем здании и обрабатывают запросы клиентов. Так устроено это взаимодействие простыми словами. С точки зрения рядового пользователя, это просто приложение, которому нужен интернет и которое с ним как-то взаимодействует. Но мы хотим узнать с точки зрения программирования, и по этому будем рассматривать их независимо друг от друга как совершенно разные части (называемые узлами) этой взаимодействующей системы. Иногда запросов от клиентов происходит так много, что сервера не справляются и тогда было введено новое слово в обиход – “Облако”. Облачные системы – это взаимодействующие между собой сервера, которые в равной степени и взаимозаменяемо могут обрабатывать запросы от множества клиентов в рамках единой задачи. Например, облачные хранилища позволяет обрабатывать запросы от клиентов по хранению каких-либо данных. Желающих хранить данные не на своём компьютере оказалось много, по этому отдельные сервера стали не справляться и их стали объединять в единые сети обмена информации и при этом размещать в разных уголках Земного шара. Так появились облачные хранилища. По этому когда вы отправляете очередную свою фотку в “Облако”, то где она физически будет находиться в мире никто не знает, кроме самой облачной системы. Может в России, может в Африке, а может и так, что часть фотки в Бразилии, а часть во Владивостоке. Облачное хранилище само перераспределяет нагрузку, находит оптимальные алгоритмы хранения и передачи данных в зависимости от спроса, и распределяет всё что в него поступает так, как считает в процессе расчета наиболее рациональным.

Получается, что клиенты и сервер выглядят так:

Когда речь заходит об облаке, это выглядит так:

Так вот, к чему это я…С точки зрения программирования, программист должен понимать где его функционал должен работать на сервере, а где на клиенте. Например, нужно вывести сообщение пользователю, что товара нет в онлайн-магазине. Где должен выполняться этот код, на клиенте или сервере? Оказывается, что для того чтобы это действительно работало, программист должен написать для выполнения на сервере определенный код, который вычислит, что товара больше нет, передать при очередном запросе клиента, что количество некого товара равно нулю. Затем должен написать на клиенте чтобы он с определенной периодичностью опрашивал сервер на предмет изменения количества того или иного товара, принимать количество, и если оно равно нулю, то выдавать на клиенте это сообщение. Т.е. программисту нужно написать одну программу на сервере для ответной реакции по запросу клиента, и другую программу работающую на каждом клиенте, которая опрашивает сервер, выводит сообщения, принимает вводимые данные будь то клики по экрану или адрес получателя, или ваш телефон для связи. Аналогично работают и различные социальные сети. Ваш планшет с определенной периодичностью опрашивает сервер на предмет того, нет ли для вас нового сообщения, в этот момент может заодно передаёт ваше сообщение для кого-то, опрашивает не появилась ли на различных каналах ещё одна новость, не загрузил ли кто новый видос на сервер. К счастью, если вы только начинающий программист, и работаете по этой специальности недавно, то вам не придётся делать программу и для клиента и для сервера, обычно новичков ставят только на клиент и дают документацию о том как нужно обращаться к серверу (так называемые API обмена). Подытоживая сказанное, программист должен чётко и хорошо понимать, что выполняется на сервере, а что на клиенте, в связи с этим и доступные методы, классы и т.п. могут работать в зависимости от того где выполняются, а достаточно часто и могут быть вообще уникальны, только для выполнения на сервере или только для выполнения на клиенте. Например, на клиенте онлайн-магазина нельзя обратиться напрямую к базе данных сервера, а на сервере нельзя вывести сообщение для пользователя.

В клиент серверном варианте есть один нюанс. Одно дело когда у вас приложение онлайн магазина или социальной сети, другое, когда открываете страницу в браузере. Нет, не подумайте чего плохого))), и то и другое действительно клиент-серверное взаимодействие…с точки зрения организации системы. Но с точки зрения программирования, совершенно разное. Дело в том, что приложение уже написано и опубликовано, вы его скачали из магазина приложений в законченном виде, и лишь иногда делаете обновления, когда сочтёте это нужным. Если речь идет о клиент-серверном взаимодействии через браузер, то у вас нет на вашем домашнем компьютере заранее кода сайта чтобы его запустить для обмена с сервером, по этому при каждом обращении к сайту, он сначала выгружает с сервера в ваш браузер некий программный код (называемый обычно скрипт) и запускает его, и вот после этого и происходит взаимодействие клиента и сервера для обмена. Именно по этому раньше распространялось так много вирусов именно через сайты, так как играли злую шутку несовершенство операционных систем, самих браузеров и анализаторов кода, который выполнялся. При появлении мобильных приложений, перед публикацией магазин приложений почти всегда сначала проверяет его на наличие вирусов, вредоносного кода, да и сам разработчик должен быть зарегистрированным в этом магазине и предоставить свои персональные данные, чтобы было с кого спросить. С сайтами так делать в большинстве случаев не получается, т.к. нет механизма достоверной проверки кода этих сайтов (они расположены не централизированно в каком-то магазине, например, в магазине каких-то сайтов, а разбросаны в разных частях света на разных серверах и разных странах с разной юрисдикцией).

В клиент-серверной организации обмена часто всё сводится к тому чтобы передать некую информацию с одного клиента на другого. И это не только работа социальных сетей. Да, обычный онлайн-магазин – это тоже отчасти передача с одного клиента на другого – вы сделали заказ, количество товара на складе уменьшилось, и каждому клиенту рассылается новый остаток после вашего взаимодействия. Иными словами, сервер выполняет роль вычислительного звена и арбитра, можно сказать важного посредника, который организовывает всю работу взаимодействия.

Должен тут же сказать, чтобы не вводить вас в заблуждение, что клиент-серверная организация хотя и является доминирующей в мире информационных технологий, но не является единственной. Погружаться в другие на этом этапе знакомства с ИТ нет, но для эрудиции скажу, – есть ещё так называемые системы “Точка-точка” или “Пир-ту-пир”, или “peer-to-peer”, или они же “p2p”, или они же “Пиринговые сети”. Это систему у которых нет серверов вообще. Они взаимодействуют исключительно на прямом обращении одного клиента к другому, хотя было равно сказать и обращении одного сервера к другому. Типичным примером таких систем являются торрент-клиенты. В схеме это выглядит так:

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

Клиент-серверная организация накладывает определенные правила, особенно если речь идет о взаимодействии вроде как в вашем браузере, когда вы открываете очередную страницу какого-то сайта. Дело в том, что все устройства пользователей разные, разные производители, разные комплектующие, или вообще настолько разные как iOs, Android и MS Windows, а браузер, как я только что сказал, отправляет клиенту код, который должен на нём выполняться. Определить точно не всегда удаётся ввиду того, что часто клиент скрывает достоверную и подробную информацию о своей системе ввиду информационной безопасности, а выполнить код на нём всё равно нужно. По этой причине часть выполнения была возложена на операционные системы, способные воспринимать одни и те же скрипты так, чтобы клиентский компьютер их понял, этот процесс известен ещё со времён языка Бейсик и называется трансляцией. Т.е. сервер отправляет каждому клиенту чаще всего один и тот же скрипт работающий по одинаковому алгоритму, а клиент его уже сам дополнительно анализирует и преобразует в тот код, который поддерживает его устройство. Такой подход называется мультиплатформенным, т.е. один код для разных платформ, по этому к счастью, программисту чаще всего не надо задумываться о том, какое же устройство будет конечным на клиенте, и сосредоточиться только на том функционале, который ему необходимо реализовать; конечное устройство само должно позаботиться о том чтобы правильно воспринять тот или иной скрипт.

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

Здесь в терминологии есть путаница, сетью называют как физическую сеть (в виде проводов), логическую сеть, ту самую, которую мы называем интернетом, когда каждый компьютер имеет свой адрес, также называемый IP-адрес (айпи-адрес), и обращение идет по нему, и сетью ещё могут называть бесконечное количество виртуальных сетей, т.е. одних логических сетей внутри других логических сетей, например VPN-сети, пиринговые сети и т.п.

Протоколов придумано великое множество. Есть стандартные, а есть уникальные, придуманные программистами для определенных обменов. Например, есть протокол TCP, который обеспечивает выход компьютера в интернет, он стандартный и строго соответствует всегда одним и тем же правилам, по этому подключенный компьютер в интернет сразу знает как общаться с вашим провайдером или с мобильным оператором. Но есть протоколы, которые никак не стандартизованы, например различные онлайн-магазины, соц.сети. Для них обмен регламентирован только фантазией программистов организовывавших этот обмен. Однако и тут, если обменную систему разрабатывает не один программист, а целая команда, тоже потребуется вводить внутренние стандарты для этой самой команды, и ориентироваться на них. Это означает, что нельзя например взять клиентское приложение одной социальной сети, немного переделать его и оно заработает для другой социальной сети. Вероятность успеха такого подхода стремиться к нулю, и всё по тому, что протоколы обмена будут разными, придуманными своей командой разработки со своей спецификой и особенностями.

В соответствии с вышесказанным, программирование для клиента и для сервера имеет ряд отличий, а также для того и другого придуманы специальные языки программирования. Наиболее известные языки используемые для клиент-серверной организации:

Для клиента:

  • Java Android (только для устройств на базе Android)
  • Swift (только для систем на базе iOS)
  • Javascript (только для браузеров, но для любых устройств)
  • Kotlin (для Android)

Для сервера:

  • PHP
  • Java servlet
  • Perl
  • Asp

Такие языки как Си и Паскаль позволяют писать как серверное программное обеспечение, так и клиентское, но требует намного более высокой квалификации и опыта. С другой стороны, язык 1С, ввиду специфики самой платформы, позволяет писать код одновременно и для серверной части и для клиентской, при этом требует совершенно иной квалификации, чем программист Си или Паскаль, хотя сам код 1С обладает намного меньшей функциональностью.

Перечисленные языки для клиента так или иначе накладывают ограничения на то, на каком клиенте они будут выполнятся, например, на языке Java Android нельзя написать для устройств на iOS (нет таких компиляторов) и наоборот. Для Javascript нельзя скомпилировать ни для Android, ни для iOS, ни на Windows, но если на этих системах есть браузер, поддерживающий Javascript (а такие браузеры наверно сейчас все), то тогда можно этот код выполнять через этот браузер и на том и на этом устройстве, с той или иной операционной системой.

Подобных ограничений для серверов не бывает, т.к. сервер изначально настраивается на работу с тем или иным кодом (php, java servlet и т.д.) и программный код пишется под определенный транслятор или компилятор, который, как понимаете, изначально всегда известен. Т.е. программист на php сразу будет настраивать или выбирать свой сервер с установленным транслятором php, а например, программист на Java Servlet установит на сервере программу Tomcat. Сами эти языки способы делать практически одно и тоже в примерно равных требованиях к ресурсам, по этому, скорее, это дело вкуса и возможностей, на чём делать сервер.

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

  • Сам язык Си
  • PHP
  • Java
  • Javascript

Они позволяют осуществлять различные программные фантазии как на серверах, так и на клиентах, и других языков я сторонюсь только по одной причине – уж больно они не похожи на Си, а часто бывает так, что на решение той или иной проблемы нужно время. Искать проблему одновременно и в серверном коде и клиентском происходит намного быстрее, если сам синтаксис языков похож. Тут нет равных языку 1С, в котором не просто один синтаксис и для клиента и для сервера, так это и есть один и тот же язык)). Плохо, что 1С требует отдельной платформы, да и сервера 1С пока ещё не бесплатные)).

El Vinto, 2023

Программирование 1С. Урок 7

Теперь можно открыть ещё одну занавесь. В прошлом уроке я сказал, что модули условно видны. Всё дело в том, что современное программирование всё более тесно связано с так называемой клиент-серверной моделью. Об этом я рассказываю ТУТ. В этом смысле 1С не исключение, по этому программист должен понять, какая часть его кода будет работать у пользователя на его персональном компьютере и какая часть кода будет работать на сервере. По этому, если вы делаете общий модуль, процедуры и функции которого будут общими для каких-то задач из разных областей вашей конфигурации, вы должны заранее определить будет этот модуль виден на клиенте, на сервере или и там и там. Однако, вы должны понимать, что все модули, которые имеют в своём определении “на клиенте”, будут выгружаться в пользовательскую среду на каждый компьютер, и это должно быть действительно оправдано с точки зрения оптимизации системы. В тоже время определение модуля “на сервере” тоже должно быть оправдано, чтобы пользовательский клиентский компьютер не гнал лишний траффик, например, только для того чтобы взять часть строки, выполняя это на сервере. Например следующий код:

Функция ПолучитьЛевыйСимволИзСтроки(НекаяСтрока)

Возврат Лев(НекаяСтрока, 1);

КонецФункции

Этот код совершенно нерационально делать на сервере, если вы будете вызывать его только с клиента. Однако, если и на сервере вам придётся выполнять эту функцию, то у вас не будет иного варианта, как сделать её на сервере, т.к. серверный код не может выполнять код, если он определен только как клиентский. Есть вариант конечно же и ещё третий вариант, но рациональность его условна и действует только в определенных случаях – это разместить в двух разных модулях (в серверном и клиентском) этот одинаковый код.

А вот такой код никогда не получится выполнить на клиенте:

Функция ПолучитьЛевыйСимволИзСтроки(НекаяСтрока)

Запрос = Новый Запрос;

// Как-то обрабатываем сам запрос или его результат

КонецФункции

И проблема здесь лишь одна – объект “Запрос” невозможно создать на клиенте, т.к. на клиентском компьютере нет возможности напрямую работать с базой данных.

Но это всё хорошо и красиво звучит в теории, а что с практикой, вы резонно можете сказать,- Автор, дай пример, где это использовать в нашей конфигурации, которую мы уже 7-й урок делаем!- Да пожалуйста!- ответил я.- Помните как мы считали сумму по строке в формах документов поступления товаров и реализации товаров? А делали мы это так:

Кстати, вы наверно внимательный слушатель курса и по этому заметили, что в модуле формы есть перед каждой процедурой и функцией ключевые слова “&НаКлиенте”, “&НаСервере”, а например, в модуле проведения документов их нет нигде. Всё дело в том, что в форме можно выполнять как клиентский код, так и серверный, только нужно сказать точно, где будет располагаться ваша процедура или функция, а в модуле объекта все пользовательские функции и процедуры выполняются на сервере.

Тут нужно сразу сказать, что всё не так однозначно. В серверных модулях 1С можно выполнить такую 100% клиентскую процедуру, как вывести сообщение пользователю. Не знаю точно, но это наверно единственная клиентская процедура, которую сервер транспортирует на клиента, причём только на того, который её исполняет. Т.е. если сделать вот так:

То будет вот так (всё нормально работает):

Так вот, отвлеклись, а теперь возвращаемся далее к коду. Мы видим, что эта процедура встречается в трёх видах документов – поступление мелочёвки, поступление товаров и услуг и реализации товаров и услуг. Эта процедура одинаковая для всех этих видов документа, так спрашивается, зачем нам её создавать аж три раза, если можно сделать одну в каком-нибудь общем модуле. Давайте так и сделаем. Кликните там, где я показывал это уже ранее и создайте новый общий модуль и при помощи ПКМ вызовите его свойства. Дайте ему имя “ОбщиеПроцедурыИФункцииКлиент” и укажите область видимости “Клиент”:

Флажка “Сервер” наоборот быть не должно, т.к. в нашей этой процедуре есть чисто клиентский объект формы, который так и называется “Объект” и гнать его на сервер не нужно (на сервере мы не будем как-то использовать форму объекта, да и не получится это сделать). Если вы всё же попытаетесь передать серверу то, что должно существовать как объект только на клиенте, то получите что-то вроде этого:

Т.е. в ошибке значится, что невозможно воспроизвести объект клиентской формы на сервере ввиду отсутствия такой возможности; но оно и понятно – кому на сервере форму показывать, соседним лампочкам в дата-центре? ))

Скопируем нашу процедуру из документа и вставим её в этом модуле:

Теперь её нужно немного доработать. Во первых, продолжение к разговору об условной видимости. В таком виде процедура не будет видна только из этого же модуля. Откуда-нибудь из другого места, из формы, из другого модуля, хоть на клиенте, хоть где, эта процедура будет невидимой. Чтобы она стала видимой нужно сделать вот так:

Т.е. обозначить её как экспортную процедуру. Это волшебное слово сразу сделает её видимой из любого клиентского модуля. Подчёркиваю – именно клиентского, т.к. мы в самом модуле флажок поставили только у области расположения “Клиент”.

Так, дорабатываем дальше. Дело в том, что в общем модуле платформа совершенно не понимает такие объекты как Элементы и Объект. Это свойства формы, а процедура в модуле абстрагирована от какого-либо объекта кроме самой конфигурации. По этому ссылку на объект формы нужно передать как параметр и кое-что поменять:

Как видите, я фактически только добавил параметр (в круглых скобках) и в трёх местах сделал родительский объект. Т.е. сказал платформе,- “Элементы” и “Объект” – это не самостоятельные свойства непойми чего, а это свойства объекта ОбъектФормы, переданного в качестве параметра.

Сам модуль формы тоже изменим до вот такого вида (эта процедура в этом модуле формы больше нам не нужна, теперь есть общая):

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

Теперь запустите 1С. НО(!!!!) не как обычно, а вот так:

После этого запустится казало бы самая обычная среда, но только она будем отлавливать момент, пока функционал тем или иным образом не окажется в этой отмеченной строке. После этого платформа 1С поставит на паузу все свои действия и переведет указатель. Для того чтобы выполнение оказалось тут, что нужно сделать?- Правильно, зайти в документ поступления товаров и стереть с количества товара число и поставить его же снова (ну это как бы мы таким образом число поменяли на другое, для системы это всё равно событие изменение реквизита). Как только введете ENTER или кликните по другому полю, платформа 1С автоматически возвратит вас в конфигуратор (не закрывая рабочей среды, а только приостановив его) на эту самую строку, и стрелкой поверх нашего кружка укажет это:

Это будет означать, что выполнение программы действительно оказалось именно здесь. Там мы совершенно случайно познакомились ещё и с таким мощным инструментом 1С, как отладка. Теперь можно продолжить всё-таки доделать программе и завершить самой алгоритм, для этого нужно нажать функциональную клавишу F5, либо прямо прервать работу рабочей среды (ну мало ли мы что-то страшное написали и во чтобы то ни стало нужно всё остановить), тут надо будет пойти в меню, либо нажать Shift+F5:

Кстати, продолжить выполнять можно и по одному выражению, для этого нужно воспользоваться клавишами F10 и F11 (это тоже как и F10, только будет “входить” внутрь вызываемых процедур и функций,- тут проще сравнить и посмотреть разницу между ними воочию, чем долго объяснять)) ).

Теперь, когда мы знаем что такое отладка, давайте добавим в документ поступления товаров и услуг ещё два стандартные процедуры – ПередЗаписью и ПриЗаписи:

Поставьте в конце каждой процедуры точку останова, запустите рабочую среду в режиме отладки на нажмите кнопку “Провести и закрыть”. Нажимая F5, посмотрите внимательно по каким точкам и в какой последовательности будет выполняться функционал – сначала вызывается процедура ПередЗаписью, затем ПослеЗаписи и только лишь после этого ОбработкаПроведения:

В такой последовательности проводятся все документы 1С, это специфика платформы. Кстати у справочников почти также, только у них нет возможности проведения и соответственно процедуры проведения, но ПередЗаписью и ПриЗаписи – есть. Из названия самих процедур можно узнать, что одно происходит до того как изменения объекта оказались в базе данных 1С, второе как раз после этого, но до проведения, а третье вызывается, когда уже с документом произведено всё, что касается записи, и теперь нужно сделать всё, что касаемо проведения.

Теперь немного коснёмся пользовательского интерфейса, т.е. того с чем пользователь взаимодействует. Опять возвращаемся к нашей сумме по строке в документах. Всё с ней вроде неплохо, кроме одного – сумма сейчас доступна для редактирования; взял, случайно кликнул туда уже после того как она посчитана и получится вот так:

Ну вот как такое может быть?- 3 на 1500 равно двум…Это не дело, хорошо бы сделать так, чтобы поле это было заблокировано от ввода данных. Сначала для этого нужно в графическом представлении формы вызвать свойство этого реквизита, запрятано это вот туда:

А реквизита за блокировку аж два:

Отключение доступности блокирует реквизит полностью и от копирования и изменения, а свойство “ТолькоПросмотр” блокирует реквизит только от изменения. В нашем случае более правильным будет указать, что реквизит только для просмотра:

Сделаем так и перезапустим 1С, проверим, что теперь кликнуть и изменить реквизит суммы уже не получится:

Задание по уроку:

1. Доработайте остальные формы документов для вычисления суммы при помощи нашей общей процедуры. Убедитесь при помощи отладчика, что это действительно так.

2. Добавьте в справочник “БытоваяТехника” ещё один реквизит и назовите его “СоставноеНаименование”. Сделайте так, чтобы при записи справочника этот реквизит заполнялся автоматически составными словами из наименования и в скобках бренд. Вот так:

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

Если всё получилось как нужно – молодцы! Если нет, давайте разберёмся что не так.

El Vinto, 2023