Проблема с датой публикации из-за часовых поясов и использования SystemDateGet()

Сводка

Мы столкнулись с проблемой, когда функция systemDateGet() возвращает AOS, где требуется локальная дата при определении даты для публикации.

Подробнее

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

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

  • Мы запускаем один AOS с настройкой часового пояса для головного офиса. Нет возможности ввести дополнительные AOS.

  • Функция SystemDateGet() возвращает дату AOS, поскольку пользователь не меняет дату своего сеанса Axapta.

  • Ряд системных полей в базе данных, связанных с датами публикации, основаны на DATE, а не на UTCDATETIME.

  • Мы используем AX2009 SP1 RU7.

    • Kernel version 5.0.1500.4570
    • Версия приложения 5.0.1500.4570
    • Версия локализации: Бразилия, Китай, Япония, Таиланд, Индия
  • Я знаю, что функция SystemDateGet() была разработана для возврата даты AOS, если пользователь не изменит дату своего сеанса, и в этом случае эта дата возвращается.

  • У каждого пользователя есть соответствующая настройка часового пояса в профиле пользователя.

Проблема

Одним из примеров проблемы является попытка пользователя опубликовать журнал с финансовыми операциями, в котором проверяется период книги, чтобы убедиться, что он открыт. Например, пользователь находится в Англии и пытается опубликовать журнал в 15:00 30 ноября по местному времени, стандартный код Axapta использует функцию systemDateGet() для определения даты, используемой при проверке (определение того, открыт). В нашем случае AOS базируется в Брисбене, Австралия, и функция systemDateGet() возвращает 1 декабря (1:00 по местному времени 1 декабря).

Другой пример проблемы — когда счет-фактура разносится в США в пятницу, а день недели, в котором находится AOS, — суббота. Нам нужна система для записи местной даты.

Вопрос

Помимо перезаписи всего системного кода, включающего systemDateGet(), более 2000 сущностей, есть ли другие варианты, которые можно использовать для решения проблемы получения правильной локальной даты?

Ограничения решения.

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

  • В настоящее время нет возможности приобрести дополнительные AOS.


person Barry Bayliss    schedule 08.12.2011    source источник


Ответы (2)


Создайте функцию в классе Global:

static date systemDateGetLocal()
{
    return DateTimeUtil::date(DateTimeUtil::applyTimeZoneOffset(DateTimeUtil::utcNow(), DateTimeUtil::getUserPreferredTimeZone()));
}

Затем в Info.watchDog() выполните:

systemDateSet(systemDateGetLocal());

Это может быть только частичное решение, watchDog выполняется только на стороне клиента.

person Jan B. Kjeldsen    schedule 08.12.2011
comment
Ян, спасибо за ответ. К сожалению, метод watchDog не может корректно обработать ситуацию. Ниже приведено решение, которое я придумал. - person Barry Bayliss; 22.02.2012

Вот быстрое обновление. Дайте мне знать, если есть какие-либо проблемы или ситуации, которые необходимо решить.

ИСПОЛЬЗОВАНИЕ МЕСТНОГО ВРЕМЕНИ

Введение.
Следующее обновление находится в стадии разработки, так как решение еще не полностью протестировано во всех ситуациях.

Проблема.
Если часовой пояс пользователя отличается от часового пояса AOS, функции timeNow() и systemDateGet() возвращают сведения об AOS, когда требуется местная дата и время.

Уточнения:
Когда в Axapta изменяется локальная дата и время, функция systemDateGet() возвращает локальную дату, однако функция timeNow() по-прежнему возвращает время AOS.

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

Скобки были изменены на { и }, чтобы это можно было опубликовать.

КЛАСС: ГЛОБАЛЬНЫЙ

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

static server void setSessionDateTime(
    inventSiteId    inventSiteId = '', 
    utcDateTime     reference = dateTimeUtil::utcNow())
{
    str                             sql;
    sqlStatementExecutePermission   perm;
    connection                      conn        = new UserConnection();
    timeZone                        timeZone;
    int                             ret;
    ;

    if (inventSiteId)
    {
        timeZone = inventSite::find(inventSiteId).Timezone;
    }
    else
    {
        timeZone = dateTimeUtil::getCompanyTimeZone();
    }

    //This is to get around the kernel validation of changing timezones
    //when the user has more than one session open.

    sql     = strfmt("Update userInfo set preferredTimeZone = %1 where userInfo.id = '%2'", enum2int(timeZone), curUserId());
    perm    = new SQLStatementExecutePermission(sql);
    perm.assert();

    ret = conn.createStatement().executeUpdate(sql);

    dateTimeUtil::setUserPreferredTimeZone(timeZone);
    dateTimeUtil::setSystemDateTime(reference);

    CodeAccessPermission::revertAssert();
}

static int localTime()
{
    utcDateTime tmp;
    ;
    setSessionDateTime();

    tmp = dateTimeUtil::applyTimeZoneOffset( dateTimeUtil::utcNow(), dateTimeUtil::getCompanyTimeZone());
    return dateTimeUtil::time(tmp);
}

Следующий метод был реализован как перекрестная проверка, чтобы убедиться, что systemDateGet() возвращает ожидаемое значение.

static date localDate()
{
    utcDateTime tmp;
    ;
    setSessionDateTime();

    tmp = dateTimeUtil::applyTimeZoneOffset( dateTimeUtil::utcNow(), dateTimeUtil::getCompanyTimeZone());
    return dateTimeUtil::date(tmp);
}

КЛАСС: ПРИМЕНЕНИЕ

Измените метод setDefaultCompany. Добавьте строку setSessionDateTime(); сразу после супервызова. Это сделано для того, чтобы время менялось, когда пользователь меняет компанию (еще одно требование, которое мне было дано).

КЛАСС:ИНФО

Чтобы система использовала правильную дату/время с начала сеанса.

void startupPost()
{
    ;
    setSessionDateTime();
}

Измените метод canViewAlertInbox(), добавив строку setSessionDateTime(); в качестве первой строки. Это необходимо, если у пользователя открыто несколько форм для разных компаний.

Изменения, зависящие от локализации:

В зависимости от вашего пакета обновления и локализации вам потребуется изменить функцию объектов, чтобы использовать функцию localTime(), заменив timeNow(). ВАЖНОЕ ПРИМЕЧАНИЕ. Не изменяйте класс BatchRun для использования новой функции localTime, так как это остановит его правильную работу.

В нашей системе было около 260 экземпляров, которые можно было изменить. Если вы не используете все модули и локализации, фактическое количество строк, которые вам нужно изменить, будет меньше.

Последнее примечание:

В коде есть несколько вызовов today(). Я еще не просмотрел каждую строку, чтобы убедиться, что она закодирована правильно, т.е. с использованием today() вместо systemDateGet().

Известные проблемы:

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

person Barry Bayliss    schedule 22.02.2012