1 /*
2  * Copyright 2013-2015  Christian Dávid <christian-david@web.de>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef ONLINEJOB_H
19 #define ONLINEJOB_H
20 
21 #define STRINGIFY(x) #x
22 #define TOSTRING(x) STRINGIFY(x)
23 #define BADTASKEXCEPTION badTaskCast("Casted onlineTask with wrong type. " __FILE__ ":" TOSTRING(__LINE__))
24 #define EMPTYTASKEXCEPTION emptyTask("Requested onlineTask of onlineJob without any task. " __FILE__ ":" TOSTRING(__LINE__))
25 
26 #include <stdexcept>
27 #include <QMetaType>
28 #include <QString>
29 #include "mymoneyobject.h"
30 #include "onlinejobmessage.h"
31 
32 class onlineTask;
33 class MyMoneyAccount;
34 
35 namespace eMyMoney { namespace OnlineJob { enum class sendingState; } }
36 
37 /**
38  * @brief Class to share jobs which can be processed by an online banking plugin
39  *
40  * This class stores only the status information and a pointer to an @r onlineTask which stores
41  * the real data. So onlineJob is similar to an shared pointer.
42  *
43  * If you know the type of the onlineTask, @r onlineJobTyped is the first choice to use.
44  *
45  * It is save to use because accesses to pointers (e.g. task() ) throw an exception if onlineJob is null.
46  *
47  * Online jobs are usually not created directly but over @r onlineJobAdministration::createOnlineJob. This is
48  * required to allow loading of onlineTasks at runtime and only if needed.
49  *
50  * This class was created to help writing stable and reliable code. Before an unsafe structure (= pointer)
51  * is accessed it is checked. Exceptions are thrown if the content is unsafe.
52  *
53  * @see onlineTask
54  * @see onlineJobTyped
55  * @todo LOW make data implicitly shared
56  */
57 class onlineJobPrivate;
58 class KMM_MYMONEY_EXPORT onlineJob : public MyMoneyObject
59 {
60   Q_DECLARE_PRIVATE(onlineJob)
61 
62   KMM_MYMONEY_UNIT_TESTABLE
63 
64 public:
65    /**
66    * @brief Constructor for null onlineJobs
67    *
68    * A onlineJob which is null cannot become valid again.
69    * @see isNull()
70    */
71   onlineJob();
72   explicit onlineJob(const QString &id);
73 
74   /**
75    * @brief Default constructor
76    *
77    * The onlineJob takes ownership of the task. The task is deleted in the destructor.
78    */
79   onlineJob(onlineTask* task, const QString& id); // krazy:exclude=explicit
80   onlineJob(onlineTask* task); // krazy:exclude=explicit
81 
82   /**
83    * @brief Create new onlineJob as copy of other
84    *
85    * This constructor does not copy the status information but the task only.
86    */
87   onlineJob(const QString &id,
88             const onlineJob& other);
89 
90   onlineJob(const onlineJob & other);
91   onlineJob(onlineJob && other);
92   onlineJob & operator=(onlineJob other);
93   friend void swap(onlineJob& first, onlineJob& second);
94 
95   virtual ~onlineJob();
96 
97   void setTask(onlineTask *task);
98 
99   /**
100    * @brief Returns task attached to this onlineJob
101    *
102    * You should not store this pointer but use onlineJob::task() (or @r onlineJobTyped::task())
103    * every time you access it.
104    *
105    * @note The return type may change in future (e.g. to an atomic pointer). But you can always expect
106    * the operator @c -> to work like it does for onlineTask*.
107    *
108    * @throws emptyTask if isNull()
109    */
110   onlineTask* task();
111 
112   /** @copydoc task(); */
113   const onlineTask* task() const;
114 
115   /**
116    * @brief Returns task attached to this onlineJob as const
117    * @throws emptyTask if isNull()
118    */
119   const onlineTask* constTask() const;
120 
121   /**
122    * @brief Returns task of type T attached to this onlineJob
123    *
124    * Internally a dynamic_cast is done and the result is checked.
125    *
126    * @throws emptyTask if isNull()
127    * @throws badTaskCast if attached task cannot be casted to T
128    */
129   template<class T> T* task();
130 
131   /** @copydoc task() */
132   template<class T> const T* task() const;
constTask()133   template<class T> const T* constTask() const {
134     return task<T>();
135   }
136 
137   template<class T> bool canTaskCast() const;
138 
139   QString taskIid() const;
140 
141   /** @todo implement */
142   bool hasReferenceTo(const QString &id) const override;
143 
144   /**
145    * @brief Account this job is related to
146    *
147    * Each job must have an account on which the job operates. This is used to determine
148    * the correct onlinePlugin which can execute this job. If the job is related to more
149    * than one account (e.g. a password change) select a random one.
150    *
151    * @return accountId or QString() if none is set or job isNull.
152    */
153   virtual QString responsibleAccount() const;
154 
155   /**
156    * @brief Returns the MyMoneyAccount this job is related to
157    * @see responsibleAccount()
158    */
159   MyMoneyAccount responsibleMyMoneyAccount() const;
160 
161   /**
162    * @brief Check if this onlineJob is editable by the user
163    *
164    * A job is no longer editable by the user if it is used for documentary purposes
165    * e.g. the job was sent to the bank. In that case create a new job based on the
166    * old one.
167    *
168    * @todo make it possible to use onlineJobs as templates
169    */
170   virtual bool isEditable() const;
171 
172   /**
173    * @brief Checks if this onlineJob has an attached task
174    *
175    * @return true if no task is attached to this job
176    */
177   virtual bool isNull() const;
178 
179   /**
180    * @brief Checks if an valid onlineTask is attached
181    *
182    * @return true if task().isValid(), false if isNull() or !task.isValid()
183    */
184   virtual bool isValid() const;
185 
186   /**
187    * @brief DateTime the job was sent to the bank
188    *
189    * A valid return does not mean that this job was accepted by the bank.
190    *
191    * @return A valid QDateTime if send to bank, an QDateTime() if not send.
192    */
193   virtual QDateTime sendDate() const;
194 
195   /**
196    * @brief Mark this job as send
197    *
198    * To be used by online plugin only!
199    *
200    * Set dateTime to QDateTime to mark unsend.
201    */
202   virtual void setJobSend(const QDateTime &dateTime);
203   virtual void setJobSend();
204 
205   /**
206    * @brief The bank's answer to this job
207    *
208    * To be used by online plugin only!
209    *
210    * Set dateTime to QDateTime() and bankAnswer to noState to mark unsend. If bankAnswer == noState dateTime.isNull() must be true!
211    */
212   void setBankAnswer(const eMyMoney::OnlineJob::sendingState state, const QDateTime &dateTime);
213   void setBankAnswer(const eMyMoney::OnlineJob::sendingState state);
214 
215   /**
216    * @brief DateTime of the last status update by the bank
217    *
218    */
219   QDateTime bankAnswerDate() const;
220 
221   /**
222    * @brief Returns last status sand by bank
223    * @return
224    */
225   eMyMoney::OnlineJob::sendingState bankAnswerState() const;
226 
227   /**
228    * @brief locks the onlineJob for sending it
229    *
230    * Used when the job is in sending process by the online plugin.
231    *
232    * A locked onlineJob cannot be removed from the storage.
233    *
234    * @note The onlineJob can still be edited and stored. But it should be done by
235    * the one how owns the lock only.
236    *
237    * @todo Enforce the lock somehow? Note: the onlinePlugin must still be able to
238    * write to the job.
239    *
240    * @param enable true locks the job, false unlocks the job
241    */
242   virtual bool setLock(bool enable = true);
243 
244   /**
245    * @brief Get lock status
246    */
247   virtual bool isLocked() const;
248 
249   /**
250    * @brief Make this onlineJob a "new" onlineJob
251    *
252    * Removes all status information, log, and the id. Only
253    * the task is keept.
254    */
255   virtual void reset();
256 
257   /**
258    * @brief addJobMessage
259    *
260    * To be used by online plugin only.
261    * @param message
262    */
263   void addJobMessage(const onlineJobMessage &message);
264 
265   /**
266    * @brief Convenient method to set add a log message
267    */
268   void addJobMessage(const eMyMoney::OnlineJob::MessageType& type, const QString& sender, const QString& message, const QString& errorCode, const QDateTime& timestamp);
269   void addJobMessage(const eMyMoney::OnlineJob::MessageType& type, const QString& sender, const QString& message, const QString& errorCode);
270   void addJobMessage(const eMyMoney::OnlineJob::MessageType& type, const QString& sender, const QString& message);
271 
272 
273   /**
274    * @brief jobMessageList
275    * @return
276    */
277   virtual QList<onlineJobMessage> jobMessageList() const;
278 
279 
280   void clearJobMessageList();
281 
282   /**
283    * @brief Thrown if a cast of a task fails
284    *
285    * This is inspired by std::bad_cast
286    */
287   class badTaskCast : public std::runtime_error
288   {
289   public:
badTaskCast(const char * msg)290     explicit badTaskCast(const char *msg) : std::runtime_error(msg) {}  // krazy:exclude=inline
291   };
292 
293   /**
294    * @brief Thrown if a task of an invalid onlineJob is requested
295    */
296   class emptyTask : public std::runtime_error
297   {
298   public:
emptyTask(const char * msg)299     explicit emptyTask(const char *msg) : std::runtime_error(msg) {}  // krazy:exclude=inline
300   };
301 
302   /** @brief onlineTask attached to this job */
303   onlineTask* m_task;
304 
305 private:
306 
307   /** @brief Copies stored pointers (used by copy constructors) */
308   inline void copyPointerFromOtherJob(const onlineJob& other);
309 };
310 
swap(onlineJob & first,onlineJob & second)311 inline void swap(onlineJob& first, onlineJob& second) // krazy:exclude=inline
312 {
313   using std::swap;
314   swap(first.d_ptr, second.d_ptr);
315   swap(first.m_task, second.m_task);
316 }
317 
onlineJob(onlineJob && other)318 inline onlineJob::onlineJob(onlineJob && other) : onlineJob() // krazy:exclude=inline
319 {
320   swap(*this, other);
321 }
322 
323 inline onlineJob & onlineJob::operator=(onlineJob other) // krazy:exclude=inline
324 {
325   swap(*this, other);
326   return *this;
327 }
328 
329 template<class T>
task()330 T* onlineJob::task()
331 {
332   T* ret = dynamic_cast<T*>(m_task);
333   if (ret == 0)
334     throw EMPTYTASKEXCEPTION;
335   return ret;
336 }
337 
338 template<class T>
task()339 const T* onlineJob::task() const
340 {
341   const T* ret = dynamic_cast<const T*>(m_task);
342   if (ret == 0)
343     throw BADTASKEXCEPTION;
344   return ret;
345 }
346 
347 Q_DECLARE_METATYPE(onlineJob)
348 
349 #endif // ONLINEJOB_H
350