List of all members | Public Types | Properties | Public Slots | Public Member Functions | Public Attributes | Protected Slots | Protected Attributes
RecurrenceWidget Class Reference

The RecurrenceWidget gives the user a unified interface for telling the system how often certain events occur. More...

#include <recurrencewidget.h>

Inheritance diagram for RecurrenceWidget:
QWidget Ui::RecurrenceWidget Ui_RecurrenceWidget

Public Types

enum  RecurrenceChangePolicy { NoPolicy, IgnoreFuture, ChangeFuture }
 
enum  RecurrencePeriod {
  Never, Minutely, Hourly, Daily,
  Weekly, Monthly, Yearly, Custom
}
 

Properties

bool endDateVisible
 Return whether the end date field is visible. More...
 
bool endTimeVisible
 Return whether the end time field is visible. More...
 
bool maxVisible
 
RecurrencePeriod minPeriod
 
bool startDateVisible
 
bool startTimeVisible
 

Public Slots

virtual void clear ()
 Set the RecurrenceWidget to its default state. More...
 
virtual bool save (bool intxn, RecurrenceChangePolicy cp, QString *msg=0)
 
virtual void set (bool recurring, int frequency, QString period, QDateTime startDateTime, QDateTime endDateTime, int max, QString style="KeepNone")
 
virtual void set (bool recurring=false, int frequency=1, QString period=QString("W"), QDate startDate=QDate::currentDate(), QDate endDate=QDate(), int max=1, QString style="KeepNone")
 
virtual void setEndDate (QDate p)
 
virtual void setEndDateTime (QDateTime p)
 
virtual void setEndDateVisible (bool p)
 
virtual void setEndTime (QTime p)
 
virtual void setEndTimeVisible (bool p)
 
virtual void setFrequency (int p)
 
virtual void setMax (int p)
 
virtual void setMaxVisible (bool p)
 
virtual void setMinPeriod (RecurrencePeriod minPeriod)
 
virtual bool setParent (int pid, QString ptype)
 
virtual void setPeriod (QString p)
 
virtual void setPeriod (RecurrencePeriod p)
 
virtual void setRecurring (bool p)
 
virtual void setStartDate (QDate p)
 
virtual void setStartDateTime (QDateTime p)
 
virtual void setStartDateVisible (bool p)
 
virtual void setStartTime (QTime p)
 
virtual void setStartTimeVisible (bool p)
 
virtual void setStyle (QString p)
 

Public Member Functions

 RecurrenceWidget (QWidget *parent=0, const char *name=0)
 
 ~RecurrenceWidget ()
 
virtual Q_INVOKABLE QDate endDate () const
 Return the date the recurrence is set to end.
More...
 
virtual Q_INVOKABLE QDateTime endDateTime () const
 Return the date and time the recurrence is set to end. More...
 
virtual Q_INVOKABLE bool endDateVisible () const
 
virtual Q_INVOKABLE QTime endTime () const
 Return the time of day the recurrence is set to end. More...
 
virtual Q_INVOKABLE bool endTimeVisible () const
 
virtual Q_INVOKABLE int frequency () const
 Return the frequency (number of periods) the recurrence is set to run. More...
 
virtual Q_INVOKABLE RecurrenceChangePolicy getChangePolicy ()
 
virtual Q_INVOKABLE bool isRecurring () const
 
virtual Q_INVOKABLE int max () const
 
virtual bool maxVisible () const
 
virtual Q_INVOKABLE RecurrencePeriod minPeriod () const
 
virtual Q_INVOKABLE bool modified () const
 
virtual Q_INVOKABLE int parentId () const
 
virtual Q_INVOKABLE QString parentType () const
 
virtual Q_INVOKABLE RecurrencePeriod period () const
 
virtual Q_INVOKABLE QString periodCode () const
 
void retranslateUi (QWidget *RecurrenceWidget)
 
void retranslateUi (QWidget *RecurrenceWidget)
 
void setupUi (QWidget *RecurrenceWidget)
 
void setupUi (QWidget *RecurrenceWidget)
 
virtual Q_INVOKABLE QDate startDate () const
 
virtual Q_INVOKABLE QDateTime startDateTime () const
 
virtual bool startDateVisible () const
 
virtual Q_INVOKABLE QTime startTime () const
 
virtual Q_INVOKABLE bool startTimeVisible () const
 
virtual Q_INVOKABLE RecurrencePeriod stringToPeriod (QString p) const
 Convert a string representation of the period to a RecurrencePeriod. More...
 
virtual Q_INVOKABLE QString style () const
 

Public Attributes

QRadioButton * _all
 
DateCluster_dates
 
QTimeEdit * _endTime
 
XSpinBox_frequency
 
QLabel_frequencyLit
 
QSpacerItem * _hspacer
 
XSpinBox_max
 
XLabel_maxLit
 
QRadioButton * _none
 
QRadioButton * _one
 
XComboBox_period
 
QGroupBox_recurring
 
QTimeEdit * _startTime
 
QGridLayout * gridLayout
 
QGridLayout * gridLayout_2
 
QSpacerItem * verticalSpacer
 

Protected Slots

virtual void languageChange ()
 

Protected Attributes

QDateTime _eot
 
int _id
 
int _parentId
 
QString _parentType
 
QDateTime _prevEndDateTime
 
int _prevFrequency
 
int _prevMax
 
int _prevParentId
 
QString _prevParentType
 
RecurrencePeriod _prevPeriod
 
bool _prevRecurring
 
QDateTime _prevStartDateTime
 
QString _prevStyle
 

Detailed Description

The RecurrenceWidget gives the user a unified interface for telling the system how often certain events occur.

In general recurrences are stored in the recur table, but this is not required. Two basic values describe a recurrence:

A recurrence with the period set to W (= week) and frequency of 3 will repeat once every three weeks.

In addition, there are three options for how to handle recurrences if a recurrence is missed (this can happen by having the client closed when a scheduled action was supposed to occur, or by having a long-running action fail to finish before another scheduled action is supposed to occur). If a recurrence is missed, upon running the next one will be scheduled for the first time that it would have run that is after the current time (For example, if an event is scheduled to occur every 5 minutes and runs at 4:10 PM, but the application is closed at 4:11 PM and not reopened until 4:53 PM, the next one will be scheduled at 4:55 PM). If there are multiple recurrences scheduled in the series for times that have also past, the three ways to handle them are -Keep None: Delete all of them, and schedule events from using the given interval from the current time -Keep One: Delete all but the most recent, reschedule it for the current time, and schedule the rest from the current time -Keep All: Reschedule all passed events to the current time, and schedule the next one using the interval from the current time

To add a new kind of recurring event or item, you need to change data in the database, stored procedures, triggers, and application code. We'll use Invoice in the examples here, with 'I' as the internal code value, even though invoices already use this mechanism (xTuple ERP 3.5.1 and later).

Add a column to the parent table to track the recurrence parent/child relationship. The column name must follow this pattern: [tablename]_recurring_[tablename]_id , e.g. invchead_recurring_invchead_id :

ALTER TABLE invchead ADD COLUMN invchead_recurring_invchead_id INTEGER;
COMMENT ON COLUMN invchead.invchead_recurring_invchead_id IS 'The first invchead record in the series if this is a recurring Invoice. If the invchead_recurring_invchead_id is the same as the invchead_id, this record is the first in the series.';

Add a RecurrenceWidget to the .ui for the window that will maintain data of this type (e.g. invoice.ui).

Initialize the widget in the window's constructor:

_recur->setParent(-1, 'I');

Update the widget again when the window gets an id for the object. This usually happens in either the set(), save(), or sSave() method for new objects, depending on the class, and in populate() when editing existing objects:

// cNew case in invoice::set()
_recur->setParent(_invcheadid, 'I');
...
// invoice::populate()
_recur->setParent(q.value("invchead_recurring_invchead_id").toInt(), "I");

Ask the user how to handle existing recurrences before saving the data and before starting a transaction. getChangePolicy will return RecurrenceWidget::NoPolicy) if the user chooses to cancel. Then when preparing to insert or update the main record, make sure to set the recurrence parentage. Finally, after the insert/update, save the recurrence and check for errors:

bool %save()
{
// error checking
RecurrenceWidget::ChangePolicy cp = _recur->getChangePolicy();
return false;
XSqlQuery beginq("BEGIN;");
XSqlQuery qry;
...
// set up qry to insert or update
if (_recur->isRecurring())
qry.bindValue(":invchead_recurring_invchead_id", _recur->parentId());
...
// execute the insert/update
...
QString errmsg;
if (! _recur->save(true, cp, &errmsg))
{
rollbackq.exec();
systemError(this, errmsg, __FILE__, __LINE__);
return false;
}
...
// finish processing
XSqlQuery commitq("COMMIT;");
return true;
}

To maintain the recurrence relationships, there must be a function that copies an existing record based on its id and gives the copy a different timestamp or date. It must have a function signature like one of these:

  1. copy[tablename](INTEGER, TIMESTAMP WITH TIME ZONE)
  2. copy[tablename](INTEGER, TIMESTAMP WITHOUT TIME ZONE)
  3. copy[tablename](INTEGER, DATE)

The copy function can take additional arguments as well, but they will be ignored by the recurrence maintenance functions. The data type of each argument must be listed in the recurtype table's recurtype_copyargs column (see below) so the appropriate casting can be done for the date or timestamp and an appropriate number of NULL arguments can be passed. The copy function must copy the [tablename]_recurring_[tablename]_id column.

There can be a function to delete records of this type as well. If there is one, it must accept a single integer id of the record to delete.

Add a row to the recurtype table to describe how the recurrence stored procedures interact with the events/items of this type ('I' == Invoice).

INSERT INTO recurtype (recurtype_type, recurtype_table, recurtype_donecheck,
recurtype_schedcol, recurtype_limit,
recurtype_copyfunc, recurtype_copyargs, recurtype_delfunc
) VALUES ('I', 'invchead', 'invchead_posted',
'invchead_invcdate', NULL,
'copyinvoice', '{integer,date}', 'deleteinvoice');

If there isn't a delete function, set the recurtype_delfunc to NULL. Existing records will be deleted when necessary with an SQL DELETE statement.

The DELETE trigger on the table should clean up the recurrence information:

CREATE OR REPLACE FUNCTION _invcheadBeforeTrigger() RETURNS "trigger" AS $$
DECLARE
_recurid INTEGER;
_newparentid INTEGER;
BEGIN
IF (TG_OP = 'DELETE') THEN
-- after other stuff not having to do with recurrence
SELECT recur_id INTO _recurid
FROM recur
WHERE ((recur_parent_id=OLD.invchead_id)
AND (recur_parent_type='I'));
IF (_recurid IS NOT NULL) THEN
SELECT invchead_id INTO _newparentid
FROM invchead
WHERE ((invchead_recurring_invchead_id=OLD.invchead_id)
AND (invchead_id!=OLD.invchead_id))
ORDER BY invchead_invcdate
LIMIT 1;
IF (_newparentid IS NULL) THEN
DELETE FROM recur WHERE recur_id=_recurid;
ELSE
UPDATE recur SET recur_parent_id=_newparentid
WHERE recur_id=_recurid;
UPDATE invchead SET invchead_recurring_invchead_id=_newparentid
WHERE invchead_recurring_invchead_id=OLD.invchead_id
AND invchead_id!=OLD.invchead_id;
END IF;
END IF;
RETURN OLD;
END IF;
RETURN NEW;
END;
$$ LANGUAGE 'plpgsql';
Todo:
Simplify this so the developer of a new recurring item/event doesn't have to work so hard. There should be a way to (1) have the recurrence widget update the [tablename]_recurring_[tablename]_id column when saving the recurrence (this would save a couple of lines) and (2) there should be a way to simplify or eliminate the code in the DELETE triggers. Some of this stuff really belongs in an AFTER trigger instead of a before trigger.
See also
_invcheadBeforeTrigger
copyInvoice
createRecurringItems
deleteInvoice
deleteOpenRecurringItems
invoice
openRecurringItems
recur
recurtype
splitRecurrence

Member Enumeration Documentation

◆ RecurrenceChangePolicy

Enumerator
NoPolicy 
IgnoreFuture 
ChangeFuture 

◆ RecurrencePeriod

Enumerator
Never 
Minutely 
Hourly 
Daily 
Weekly 
Monthly 
Yearly 
Custom 

Property Documentation

◆ endDateVisible

bool RecurrenceWidget::endDateVisible
readwrite

Return whether the end date field is visible.

◆ endTimeVisible

bool RecurrenceWidget::endTimeVisible
readwrite

Return whether the end time field is visible.

◆ maxVisible

bool RecurrenceWidget::maxVisible
readwrite

◆ minPeriod

RecurrenceWidget::RecurrencePeriod RecurrenceWidget::minPeriod
readwrite

◆ startDateVisible

bool RecurrenceWidget::startDateVisible
readwrite

◆ startTimeVisible

bool RecurrenceWidget::startTimeVisible
readwrite

Constructor & Destructor Documentation

◆ RecurrenceWidget()

RecurrenceWidget::RecurrenceWidget ( QWidget parent = 0,
const char *  name = 0 
)

◆ ~RecurrenceWidget()

RecurrenceWidget::~RecurrenceWidget ( )

Member Function Documentation

◆ clear

void RecurrenceWidget::clear ( )
virtualslot

Set the RecurrenceWidget to its default state.

◆ endDate()

QDate RecurrenceWidget::endDate ( ) const
virtual

Return the date the recurrence is set to end.

◆ endDateTime()

QDateTime RecurrenceWidget::endDateTime ( ) const
virtual

Return the date and time the recurrence is set to end.

◆ endDateVisible()

virtual Q_INVOKABLE bool RecurrenceWidget::endDateVisible ( ) const
virtual

◆ endTime()

QTime RecurrenceWidget::endTime ( ) const
virtual

Return the time of day the recurrence is set to end.

◆ endTimeVisible()

virtual Q_INVOKABLE bool RecurrenceWidget::endTimeVisible ( ) const
virtual

◆ frequency()

int RecurrenceWidget::frequency ( ) const
virtual

Return the frequency (number of periods) the recurrence is set to run.

◆ getChangePolicy()

RecurrenceWidget::RecurrenceChangePolicy RecurrenceWidget::getChangePolicy ( )
virtual

◆ isRecurring()

bool RecurrenceWidget::isRecurring ( ) const
virtual

◆ languageChange

void RecurrenceWidget::languageChange ( )
protectedvirtualslot

◆ max()

int RecurrenceWidget::max ( ) const
virtual

◆ maxVisible()

virtual bool RecurrenceWidget::maxVisible ( ) const
virtual

◆ minPeriod()

virtual Q_INVOKABLE RecurrencePeriod RecurrenceWidget::minPeriod ( ) const
virtual

◆ modified()

bool RecurrenceWidget::modified ( ) const
virtual

◆ parentId()

int RecurrenceWidget::parentId ( ) const
virtual

◆ parentType()

QString RecurrenceWidget::parentType ( ) const
virtual

◆ period()

RecurrenceWidget::RecurrencePeriod RecurrenceWidget::period ( ) const
virtual

◆ periodCode()

QString RecurrenceWidget::periodCode ( ) const
virtual

◆ retranslateUi() [1/2]

void Ui_RecurrenceWidget::retranslateUi ( QWidget RecurrenceWidget)
inlineinherited

◆ retranslateUi() [2/2]

void Ui_RecurrenceWidget::retranslateUi ( QWidget RecurrenceWidget)
inlineinherited

◆ save

bool RecurrenceWidget::save ( bool  intxn,
RecurrenceChangePolicy  cp,
QString *  msg = 0 
)
virtualslot

◆ set [1/2]

void RecurrenceWidget::set ( bool  recurring,
int  frequency,
QString  period,
QDateTime  startDateTime,
QDateTime  endDateTime,
int  max,
QString  style = "KeepNone" 
)
virtualslot

◆ set [2/2]

void RecurrenceWidget::set ( bool  recurring = false,
int  frequency = 1,
QString  period = QString("W"),
QDate  startDate = QDate::currentDate(),
QDate  endDate = QDate(),
int  max = 1,
QString  style = "KeepNone" 
)
virtualslot

◆ setEndDate

void RecurrenceWidget::setEndDate ( QDate  p)
virtualslot

◆ setEndDateTime

void RecurrenceWidget::setEndDateTime ( QDateTime  p)
virtualslot

◆ setEndDateVisible

void RecurrenceWidget::setEndDateVisible ( bool  p)
virtualslot

◆ setEndTime

void RecurrenceWidget::setEndTime ( QTime  p)
virtualslot

◆ setEndTimeVisible

void RecurrenceWidget::setEndTimeVisible ( bool  p)
virtualslot

◆ setFrequency

void RecurrenceWidget::setFrequency ( int  p)
virtualslot

◆ setMax

void RecurrenceWidget::setMax ( int  p)
virtualslot

◆ setMaxVisible

void RecurrenceWidget::setMaxVisible ( bool  p)
virtualslot

◆ setMinPeriod

void RecurrenceWidget::setMinPeriod ( RecurrencePeriod  minPeriod)
virtualslot

◆ setParent

bool RecurrenceWidget::setParent ( int  pid,
QString  ptype 
)
virtualslot

◆ setPeriod [1/2]

void RecurrenceWidget::setPeriod ( QString  p)
virtualslot

◆ setPeriod [2/2]

void RecurrenceWidget::setPeriod ( RecurrencePeriod  p)
virtualslot

◆ setRecurring

void RecurrenceWidget::setRecurring ( bool  p)
virtualslot

◆ setStartDate

void RecurrenceWidget::setStartDate ( QDate  p)
virtualslot

◆ setStartDateTime

void RecurrenceWidget::setStartDateTime ( QDateTime  p)
virtualslot

◆ setStartDateVisible

void RecurrenceWidget::setStartDateVisible ( bool  p)
virtualslot

◆ setStartTime

void RecurrenceWidget::setStartTime ( QTime  p)
virtualslot

◆ setStartTimeVisible

void RecurrenceWidget::setStartTimeVisible ( bool  p)
virtualslot

◆ setStyle

void RecurrenceWidget::setStyle ( QString  p)
virtualslot

◆ setupUi() [1/2]

void Ui_RecurrenceWidget::setupUi ( QWidget RecurrenceWidget)
inlineinherited

◆ setupUi() [2/2]

void Ui_RecurrenceWidget::setupUi ( QWidget RecurrenceWidget)
inlineinherited

◆ startDate()

QDate RecurrenceWidget::startDate ( ) const
virtual

◆ startDateTime()

QDateTime RecurrenceWidget::startDateTime ( ) const
virtual

◆ startDateVisible()

virtual bool RecurrenceWidget::startDateVisible ( ) const
virtual

◆ startTime()

QTime RecurrenceWidget::startTime ( ) const
virtual

◆ startTimeVisible()

virtual Q_INVOKABLE bool RecurrenceWidget::startTimeVisible ( ) const
virtual

◆ stringToPeriod()

RecurrenceWidget::RecurrencePeriod RecurrenceWidget::stringToPeriod ( QString  p) const
virtual

Convert a string representation of the period to a RecurrencePeriod.

This is used to convert between the values stored in the recur table and the enumerated values used internally by the RecurrenceWidget. It also accepts human-readable values, such as "Minutes" and "Hours".

Parameters
pThe recur_period or human-readable period name to convert. This is case-sensitive. Currently accepted values are m or Minutes, H or Hours, D or Days, W or Weeks, M or Months, Y or Years, C or Custom. If current translation includes the period names, the translations are also accepted.
Returns
The RecurrencePeriod that matches the input or Never if no match was found.

◆ style()

QString RecurrenceWidget::style ( ) const
virtual

Member Data Documentation

◆ _all

QRadioButton * Ui_RecurrenceWidget::_all
inherited

◆ _dates

DateCluster * Ui_RecurrenceWidget::_dates
inherited

◆ _endTime

QTimeEdit * Ui_RecurrenceWidget::_endTime
inherited

◆ _eot

QDateTime RecurrenceWidget::_eot
protected

◆ _frequency

XSpinBox * Ui_RecurrenceWidget::_frequency
inherited

◆ _frequencyLit

QLabel * Ui_RecurrenceWidget::_frequencyLit
inherited

◆ _hspacer

QSpacerItem * Ui_RecurrenceWidget::_hspacer
inherited

◆ _id

int RecurrenceWidget::_id
protected

◆ _max

XSpinBox * Ui_RecurrenceWidget::_max
inherited

◆ _maxLit

XLabel * Ui_RecurrenceWidget::_maxLit
inherited

◆ _none

QRadioButton * Ui_RecurrenceWidget::_none
inherited

◆ _one

QRadioButton * Ui_RecurrenceWidget::_one
inherited

◆ _parentId

int RecurrenceWidget::_parentId
protected

◆ _parentType

QString RecurrenceWidget::_parentType
protected

◆ _period

XComboBox * Ui_RecurrenceWidget::_period
inherited

◆ _prevEndDateTime

QDateTime RecurrenceWidget::_prevEndDateTime
protected

◆ _prevFrequency

int RecurrenceWidget::_prevFrequency
protected

◆ _prevMax

int RecurrenceWidget::_prevMax
protected

◆ _prevParentId

int RecurrenceWidget::_prevParentId
protected

◆ _prevParentType

QString RecurrenceWidget::_prevParentType
protected

◆ _prevPeriod

RecurrencePeriod RecurrenceWidget::_prevPeriod
protected

◆ _prevRecurring

bool RecurrenceWidget::_prevRecurring
protected

◆ _prevStartDateTime

QDateTime RecurrenceWidget::_prevStartDateTime
protected

◆ _prevStyle

QString RecurrenceWidget::_prevStyle
protected

◆ _recurring

QGroupBox * Ui_RecurrenceWidget::_recurring
inherited

◆ _startTime

QTimeEdit * Ui_RecurrenceWidget::_startTime
inherited

◆ gridLayout

QGridLayout * Ui_RecurrenceWidget::gridLayout
inherited

◆ gridLayout_2

QGridLayout * Ui_RecurrenceWidget::gridLayout_2
inherited

◆ verticalSpacer

QSpacerItem * Ui_RecurrenceWidget::verticalSpacer
inherited

The documentation for this class was generated from the following files:
RecurrenceWidget::save
virtual bool save(bool intxn, RecurrenceChangePolicy cp, QString *msg=0)
Definition: recurrencewidget.cpp:502
RecurrenceWidget::NoPolicy
@ NoPolicy
Definition: recurrencewidget.h:38

Generated on Fri Apr 9 2021 xTuple ERP Programmer Reference, Version 5.1.0 doxygen 1.8.17