Giter Club home page Giter Club logo

gdmn-bot's Introduction

Чат-бот "Моя Зарплата" для Viber и Telegram. Подключается к программному комплексу автоматизации предприятия на платформе Гедымин и позволяет пользователю:

  • Просмотреть краткий и подробный расчетный листок за месяц или произвольный период
  • Сравнить данные по зарплате за два периода
  • Пересчитать суммы из расчетного листка в валюте
  • Узнать курс валюты, установленный Национальным Банком РБ, на любую дату
  • Узнать ближайшие дни рождения сотрудников предприятия
  • Просмотреть табель рабочего времени
  • Просмотреть календарный график рабочего времени
  • Разместить объявление на доске объявлений для своего подразделения или предприятия

Более подробно см. по этой ссылке.

Copyright (c) 2020-22 by Golden Software of Belarus, Ltd

gdmn-bot's People

Contributors

dependabot[bot] avatar gsbelarus avatar sunnycreature avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

andarist

gdmn-bot's Issues

Проверить с валютой

Российскуй рубль не загрузился
image

В описании расчетного листка не верно указывается год даты курса
image

демо-данные

Взять наиболее навороченные данные по сотруднику из действующего предприятия и на основе них создать демо-данные. Чтобы когда будем показывать кому-нибудь не надо это было делать на реальных данных.

Доска объявлений

  • Размещать объявления могут только пользователи с соответствующими правами.
  • Если у пользователя нет прав, то при нажатии на кнопку "Доска объявлений" он получает соответствующее сообщение и программа возвращается к главному меню.
  • Если у пользователя есть права, он получает приглашение ввести и отправить в чат текст объявления.
  • После отправки объявления в чат появляется меню с двумя кнопками: Опубликовать и Отмена. При нажатии на кнопку Отмена программа возвращается в главное меню. При нажатии на кнопку Опубликовать объявление будет опубликовано и появится у всех пользователей в чате.
  • Объявления хранятся на сервере в отдельном JSON файле.
  • Алгоритм отсылки аналогичный алгоритму отсылки расчетных листков. Мы храним дату каждого объявления и для каждого пользователя дату последнего доставленного ему объявления. При запуске рассылки мы ищем всех пользователей в состоянии главного меню и рассылаем им объявления. Далее механизм ставится на таймер и повторяется через заданный период. Для того, чтобы объявления дошли до тех адресатов, которые на момент рассылки были не в главном меню.

Автоматическая расылка расчетных листков

  1. Получаем с сервера расчетный листок (листки) по сотруднику.
  2. Записываем их на диск.
  3. Ищем AccountLink для данного сотрудника. Если нет, то на этом заканчиваем.
  4. Проверяем состояние машины из найденного AccountLink.
  5. Если машина не в состоянии MainMenu, то на этом заканчиваем.
  6. Исходя из даты payslipSentOn в AccountLink решаем появился ли более новый расчетный листок, который надо отослать пользователю.
  7. Отсылаем последний краткий расчетный листок в чат сотрудника.
  8. После отсылки листка машина должна остаться в состоянии MainMenu и меню должно быть выведено на экран. Т.е. как после выполненения команды из меню Расчетный листок.
  9. Обновляем поля payslipSentOn and lastUpdated в AccountLink.

По столовой

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

В первом случае надо понятно сообщить пользователю, что меню столовой можно просмотреть, только если на предприятии установлена программа "Гедымин: Общепит".

Во втором надо сообщить что меню на сегодня еще не было загружено. Попробуйте позже.

В третьем показываем меню столовой и возвращаемся в главное меню программы.

Ошибка с выбором даты в вайбере

Если выбрать Листок за период и в появившемся календаре сразу нажать на кнопку с номером года,
получим ошибку -- "Я запутался".

Функциональность первой очереди

Расчетные листки

  1. Посмотреть текущий (последний) расчетный листок.
  2. Посмотреть краткий расчетный листок и подробный, где раскрыты все начисления и удержания.
  3. Посмотреть расчетный листок за любой месяц любого года.
  4. Посмотреть суммарный расчетный листок за произвольный непрерывный период. Период задается в целых месяцах. Например, суммарный расчетный листок за 2019 год, т.е. с 1 по 12 месяц 2019-го года.
  5. Сравнить два расчетных листка за разные месяцы, в том числе и два суммарных расчетных листка за разные периоды одинаковой продолжительности. Например, 1-3 месяцы 2019 года сравнить с 1-3 месяцами 2020 года.
  6. Пересчитать расчетный листок в выбранной валюте. Курсы валют хранить в файле, как и другие данные. При обращении к курсу, если его нет в файле, получать его с сайта нац банка и записывать в файл. Суммы начисления/удержания за период пересчитываются по курсу на первый день периода. С пересчетом в валюту можно сделать в том числе и сравнение расчетных листков.
  7. После того, как на предприятии произведут окончательное итоговое начисление расчетные листки должны загружатся на сервер и появляться в чате у всех сотрудников автоматически. Информацию о том, какой последний расчетный листок был автоматически передан в чат сотрудника стоит держать в объекте, где мы связываем чат с сотрудником. При поступлении из внешней системы очередных данных по начислениям стоит пробегаться по всем сотрудникам и проверять их дату и если она больше, чем дата последнего автоматически переданного листка, то формировать листок и передавать в чат клиента.

Выбор месяца

+---+ +---+ +---+ +---+
|янв| |фев| |мар| |апр|
+---+ +---+ +---+ +---+
+---+ +---+ +---+ +---+
|янв| |фев| |мар| |апр|
+---+ +---+ +---+ +---+
+---+ +---+ +---+ +---+
|янв| |фев| |мар| |апр|
+---+ +---+ +---+ +---+
+---+ +---------+ +---+
| < | |   2020  | | > |
+---+ +---------+ +---+

Для выбора периода можно вывести два таких меню. Для выбора первой даты и последней.

Сравнительный расчетный листок за два периода

Начислено:           726  750  +22
==================================
Зарплата (чистыми):  617  630  +10
  К выдаче:          502  300  -200
  Удержания:         115
==================================
Налоги:              109
  Подоходный:         94
  Пенсионный:          7
  Профсоюзный:         7
==================================
Информация:
  Оклад:             450  500  +50

Дни рождения

  1. Выгружать на сервер список сотрудников предприятия с указанием подразделения и даты рождения.
  2. Ежедневно в установленное время (например, в 9:30) формировать список сотрудников, у которых сегодня день рождения. Если их меньше пяти, то выводить всех. Если больше, то выводить первые пять и вывести меню с кнопкой "Показать всех".
  3. Список сотрудников автоматически рассылается во все чаты, у которых в настройках установлена соответствующая опция.

Настройки

  1. В настройках акаунта пока будет только возможность включить/выключить автоматическое ежедневное оповещение о днях рождений. В будущем добавятся другие настройки.
  2. Настройки стоит хранить в том же объекте, где мы храним привязку чата к сотруднику предприятия.

Отсылка сообщений всем пользователям организации

Предполагаемый начальный функционал:

  1. Ответственный сотрудник предприятия (например, сотрудник отдела кадров) заходит на веб страницу.
  2. Вводит свой логин и пароль, аутентифицируется.
  3. На экране отображается список сообщений. Каждое сообщение имеет свойства:
    3.1. Текст
    3.2. Состояние
    3.3. Дата и время, когда отослано
    3.4. Дата и время, назначенные для отсылки сообщения
  4. Состояния: черновик, отослано, запланировано.
  5. Сотрудник может набрать сообщение и отослать его или назначить дату и время, когда сообщение будет автоматически отослано в чаты всем сотрудникам предприятия.

НЕ НАДО делать функционал по созданию, удалению учетных записей ответственных сотрудников. На первом этапе мы их просто пропишем в JSON файле.

Опять даты строкой из гедымина передаются неправильно

Это из последнего списка по БМКК:

    "188129360_534865013": {
      "firstName": "Виктор",
      "lastName": "XXXX",
      "patrName": "Константинович",
      "birthday": "1962.9.29",

Должен быть месяц 09.

И надо решить, если мы передаем даты как YYYY.MM.DD, то проверить чтобы везде-везде они передавались именно в таком формате.

Обработка ошибок неверных данных на стороне сервера

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

call oXMLHTTP.Send(JSON)
  on error goto 0

  If Err.Number <> 0 Then
    MsgBox "Ошибка передачи файла " & Err.Message
    bot_SendFile = False
  Else
    bot_SendFile = True
  End If

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

call oXMLHTTP.Send(JSON)
  on error goto 0

  If Err.Number <> 0 Then
    MsgBox "Ошибка передачи файла " & Err.Message
    bot_SendFile = False
  ElseIf oXMLHTTP.status <> 200 Then
    MsgBox "Ошибка обработки наддын на сервере"
    bot_SendFile = False
  Else
    bot_SendFile = True
  End If

"К выдаче"

От 2 разных пользователей получил пожелание предусмотреть строчку "К выдаче" в расчетном листке.

Доработки

  • Выводить или оклад или чтс (движения кроме внутренних перемещений)
  • Проверить перевод (чтс - hour rate, валюта)
  • Выгружать порядковый номер начислений\удержаний
  • Строка начислений\удержаний в 2 строки
  • Функция формирования кода
  • Дописать и добавить инструкции на сайт

Формат хранения данных по сотруднику

  1. Справочник начислений/удержаний/льгот/справочных параметров хранится на сервере в папке payslip.<id_customer> в файле accdedref.json. Так как структура начислений/удержаний на предприятии может меняться, то этот файл формируется каждый раз, когда выгружаются данные из сторонней системы. Структура файла и пример данных:
{
  [id: string]: {
    name: LName;
    parentId?: string;
    type: 'ACCRUAL' | 'DEDUCTION' | 'REFERENCE' | 'EXEMPTION';
  }
}

// пример данных
{
  'ruid_from_gedemin': {
    name: {
      ru: {
        name: 'Повременно (${days} дн, ${hours} ч)'
      },
      by: {
        name: 'Пачасова (${days} дз, ${hours} г)'
      },
      type: 'ACCRUAL'
   }
}
  1. Некоторые начисления/удержания подразумевают передачу дополнительных параметров. Например, в примере выше, передаются часы и дни. Их можно использовать для формирования строки в расчетный листок, используя в названии параметры. Как реализовать на js описано тут.
  2. По одному сотруднику все данные хранятся в файлах с разбивкой по годам. Имя файла: payslip.<id_customer>.<id_employee>.<YYYY>.json.
  3. Все файлы сотрудников одного предприятия за год хранятся в одном подкаталоге с именем YYYY внутри каталога payslip.<id_customer>.
  4. Каждое начисление/удержание/справочная информация имеет символьный уникальный код. Информация о начислении/удержании берется из отдельного файла-справочника, см. п. 1.
  5. Начисление/удержание/справочная информация могут иметь дополнительные параметры, которые передаются ввиде объекта.
  6. Строковое представление, которое человек видит в расчетном листке формируется на основании шаблона, дополнительных параметров и выбранной локализации. Например, если название повременного начисления задано шаблоном 'Повременно (${days} дн, ${hours} ч)', то информация о начислении за январь 2020 года будет представлена как:
{
  typeId: "ид типа, как задано в справочнике типов",
  dateBegin: "01.01.2020",
  dateEnd: "31.01.2020",
  s: 366.16, 
  data: {
    hours: 135,
    days: 17
  }
}  
  1. Если у пользователя выбран в настройках язык интерфейса русский, то на основе вышеуказанной информации будет сформирована в расчетном листке строка:
Повременно (17 дн, 135 ч)       366.16
  1. Если у пользователя выбран в настройках язык интерфейса белорусский, то на основе вышеуказанной информации будет сформирована в расчетном листке строка:
Пачасова (17 дз, 135 г)       366.16
  1. Каждое начисление/удержание/справка относится или к заданному периоду, или на конкретную дату (но не одновременно и на то, и на то).
  2. Гранулярность задания периода/даты по умолчанию принимается в один день. Пока мы не поддерживаем другие значения. Период включает все время от начала первой указанной даты, до окончания второй указанной даты. Например, если задан период от 1-го до 31-го января 2020 года, и используется гранулярность в один день, то в него попадут все даты, удовлетворяющие условию d >= new Date(2020, 01, 01) && d <new Date(2020, 02, 01).

Структура файла

{
  version: "1.0";
  employeeId: string;
  year: number;
  data: {
    type: string;
    dateBegin?: Date;
    dateEnd?: Date;
    date?: Date;
    granularity?: 'DAY';
    s: number;
    data?: any;
  }[]
}

Курсы валют

  • По нажатию на кнопку меню показывается меню выбора валюты. В этом же меню внизу присутствует кнопка В главное меню.
  • После выбора валюты показывается календарь выбора месяца и года.
  • После выбора месяца программа скачивает с сайта нацбанка курсы за каждый день выбранного месяца (если их еще нет в базе данных) и дописывает в базу данных.
  • Формируется сообщение с курсами за каждый день выбранного месяца. Одна строка -- один день. После отсылки в чат пользователя программа переходит в главное меню.

Подготовка строк для записи в JSON

Сделать функцию, которая получает на вход строку и превращает ее в строку, готовую для записи в JSON:

  1. Все двойные кавычки в строке заменяет на \"
  2. Все последовательности #13#10 заменяет на \n
  3. Все символы #13 заменяет на \n
  4. Все символы #10 заменяет на \n
  5. Все символы #9 заменяет на \t
  6. Берет строку в двойные кавычки

ошибка в процессе загрузки данных

[INFO] 26.06.2020 10:35:10: Data has been loaded from \gdmn-bot\data\payslip\sluckmk\519346493_667032157.json. Keys: 1...
[INFO] 26.06.2020 10:35:10: Data has been loaded from \gdmn-bot\data\payslip\sluckmk\519346493_667032157.json. Keys: 1...
[ERROR] 26.06.2020 10:35:10: Error: Missing departments, positions, payforms or salary arrays in user data. cust: sluckmk, empl: 519346493_667032157
[ERROR] 26.06.2020 10:35:10: Missing departments, positions, payforms or salary arrays in user data. cust: sluckmk, empl: 519346493_667032157

надо проверить, похоже после такой ошибки бот перестает отвечать на запросы.

Табель

JSon файл подразделений:

{ "version": "2.0",
  "data": {
    "123_456": { 
      "name": {
        "ru": {
          "name": "Консервный"
        }
      },
    "789_456": { 
      "name": {
        "ru": {
          "name": "Холодильник"
        }
      }
    }
  }
}
export interface IDepartments {
 [id: string]: IDepartment  
};
export interface IDepartment {
   name: LName;
};

JSon файл табеля рабочего времени:

{ "version": "2.0",
  "data": {
    "123456_78910": { 
      "2020.01.01": [{
        "dept": "123_456",
        "type": 0,
        "h": 8  
      }],
      "2020.01.02": [{
        "dept": "123_456",
        "t": 0,
        "h": 4  
      },
      {
        "dept": "789_456",
        "type": 1,
        "h": 4  
      }]
    }
  }
}
export interface ITimeSheets {
 [date: string]: ITimeSheet 
};
export interface ITimeSheet {
  data: {
    dept: string;
    t: string;
    h: number;
  }[];
};

слишком много цифр

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

image

Дни рождения

  • По нажатию на кнопку Дни рождения показываем дни рождения сотрудников за сегодня и завтра.
  • За каждый день показываем не более 12-ти человек. Если их больше, то в конце списка выводим надпись "и другие..."
  • По каждому человеку показываем 1) ФИО (полностью), 2) подразделение.
  • После вывода списка возвращаемся в Главное меню

Первый контакт с вайбер ботом

Когда в viber добавляешь бот, то он пишет что надо отослать любое сообщение для начала работы. При этом на сервере в логе кидает такую ошибку:
image

Памылка

  <rejected> TypeError: Cannot read property 'id' of undefined
      at ViberBot.<anonymous> (G:\Bot\gdmn-bot\src\bot.ts:632:45)
      at ViberBot.emit (events.js:315:20)
      at ViberBot.EventEmitter.emit (domain.js:485:12)
      at ViberBot._handleEventReceived (G:\Bot\gdmn-bot\node_modules\viber-bot\lib\viber-bot.js:253:9)
      at Duplex.<anonymous> (G:\Bot\gdmn-bot\node_modules\viber-bot\lib\viber-bot.js:224:9)
      at Duplex.emit (events.js:315:20)
      at Duplex.EventEmitter.emit (domain.js:485:12)
      at addChunk (_stream_readable.js:302:12)
      at readableAddChunk (_stream_readable.js:278:9)
      at Duplex.Readable.push (_stream_readable.js:217:10)
}
[INFO] 24.06.2020 14:56:40, chatId: 8mvDLK0WCb3djVfOYX/IPw==: SUBSCRIBED 8mvDLK0WCb3djVfOYX/IPw==
[INFO] 24.06.2020 14:57:53: Data has been loaded from G:\Bot\gdmn-bot\data\customers.json. Keys: 9...
{
  err: TypeError: Cannot read property 'id' of undefined
      at ViberBot.<anonymous> (G:\Bot\gdmn-bot\src\bot.ts:632:45)
      at ViberBot.emit (events.js:315:20)
      at ViberBot.EventEmitter.emit (domain.js:485:12)
      at ViberBot._handleEventReceived (G:\Bot\gdmn-bot\node_modules\viber-bot\lib\viber-bot.js:253:9)
      at Duplex.<anonymous> (G:\Bot\gdmn-bot\node_modules\viber-bot\lib\viber-bot.js:224:9)
      at Duplex.emit (events.js:315:20)
      at Duplex.EventEmitter.emit (domain.js:485:12)
      at addChunk (_stream_readable.js:302:12)
      at readableAddChunk (_stream_readable.js:278:9)
      at Duplex.Readable.push (_stream_readable.js:217:10)
} Promise {
  <rejected> TypeError: Cannot read property 'id' of undefined
      at ViberBot.<anonymous> (G:\Bot\gdmn-bot\src\bot.ts:632:45)
      at ViberBot.emit (events.js:315:20)
      at ViberBot.EventEmitter.emit (domain.js:485:12)
      at ViberBot._handleEventReceived (G:\Bot\gdmn-bot\node_modules\viber-bot\lib\viber-bot.js:253:9)
      at Duplex.<anonymous> (G:\Bot\gdmn-bot\node_modules\viber-bot\lib\viber-bot.js:224:9)
      at Duplex.emit (events.js:315:20)
      at Duplex.EventEmitter.emit (domain.js:485:12)
      at addChunk (_stream_readable.js:302:12)
      at readableAddChunk (_stream_readable.js:278:9)
      at Duplex.Readable.push (_stream_readable.js:217:10)

Отслеживать удаленные чаты

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

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

В будущем, когда мы подключим веб-хуки, мы сможем ловить события отключения (удаления) чатов, но даже в таком случае нам понадобится удаление по отключению, так как чат может быть удален, когда наш сервер выключен.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.