1 /***************************************************************************
2  * SPDX-FileCopyrightText: 2021 S. MANKOWSKI stephane@mankowski.fr
3  * SPDX-FileCopyrightText: 2021 G. DE BURE support@mankowski.fr
4  * SPDX-License-Identifier: GPL-3.0-or-later
5  ***************************************************************************/
6 /** @file
7  * This file implements classes SKGRecurrentOperationObject.
8  *
9  * @author Stephane MANKOWSKI / Guillaume DE BURE
10  */
11 #include "skgrecurrentoperationobject.h"
12 
13 #include <klocalizedstring.h>
14 
15 #include "skgdocumentbank.h"
16 #include "skgoperationobject.h"
17 #include "skgservices.h"
18 #include "skgsuboperationobject.h"
19 #include "skgtraces.h"
20 
SKGRecurrentOperationObject()21 SKGRecurrentOperationObject::SKGRecurrentOperationObject(): SKGRecurrentOperationObject(nullptr)
22 {}
23 
SKGRecurrentOperationObject(SKGDocument * iDocument,int iID)24 SKGRecurrentOperationObject::SKGRecurrentOperationObject(SKGDocument* iDocument, int iID): SKGObjectBase(iDocument, QStringLiteral("v_recurrentoperation"), iID)
25 {}
26 
27 SKGRecurrentOperationObject::~SKGRecurrentOperationObject()
28     = default;
29 
30 SKGRecurrentOperationObject::SKGRecurrentOperationObject(const SKGRecurrentOperationObject& iObject) = default;
31 
SKGRecurrentOperationObject(const SKGObjectBase & iObject)32 SKGRecurrentOperationObject::SKGRecurrentOperationObject(const SKGObjectBase& iObject)
33 {
34     if (iObject.getRealTable() == QStringLiteral("recurrentoperation")) {
35         copyFrom(iObject);
36     } else {
37         *this = SKGObjectBase(iObject.getDocument(), QStringLiteral("v_recurrentoperation"), iObject.getID());
38     }
39 }
40 
operator =(const SKGObjectBase & iObject)41 SKGRecurrentOperationObject& SKGRecurrentOperationObject::operator= (const SKGObjectBase& iObject)
42 {
43     copyFrom(iObject);
44     return *this;
45 }
46 
operator =(const SKGRecurrentOperationObject & iObject)47 SKGRecurrentOperationObject& SKGRecurrentOperationObject::operator= (const SKGRecurrentOperationObject& iObject)
48 {
49     copyFrom(iObject);
50     return *this;
51 }
52 
getParentOperation(SKGOperationObject & oOperation) const53 SKGError SKGRecurrentOperationObject::getParentOperation(SKGOperationObject& oOperation) const
54 {
55     SKGObjectBase objTmp;
56     SKGError err = getDocument()->getObject(QStringLiteral("v_operation"), "id=" % getAttribute(QStringLiteral("rd_operation_id")), objTmp);
57     oOperation = objTmp;
58     return err;
59 }
60 
setParentOperation(const SKGOperationObject & iOperation)61 SKGError SKGRecurrentOperationObject::setParentOperation(const SKGOperationObject& iOperation)
62 {
63     return setAttribute(QStringLiteral("rd_operation_id"), SKGServices::intToString(iOperation.getID()));
64 }
65 
setPeriodIncrement(int iIncrement)66 SKGError SKGRecurrentOperationObject::setPeriodIncrement(int iIncrement)
67 {
68     return setAttribute(QStringLiteral("i_period_increment"), SKGServices::intToString(iIncrement));
69 }
70 
getPeriodIncrement() const71 int SKGRecurrentOperationObject::getPeriodIncrement() const
72 {
73     return SKGServices::stringToInt(getAttribute(QStringLiteral("i_period_increment")));
74 }
75 
getPeriodUnit() const76 SKGRecurrentOperationObject::PeriodUnit SKGRecurrentOperationObject::getPeriodUnit() const
77 {
78     QString t_period_unit = getAttribute(QStringLiteral("t_period_unit"));
79     if (t_period_unit == QStringLiteral("D")) {
80         return SKGRecurrentOperationObject::DAY;
81     }
82     if (t_period_unit == QStringLiteral("W")) {
83         return SKGRecurrentOperationObject::WEEK;
84     }
85     if (t_period_unit == QStringLiteral("M")) {
86         return SKGRecurrentOperationObject::MONTH;
87     }
88     return SKGRecurrentOperationObject::YEAR;
89 }
90 
setPeriodUnit(SKGRecurrentOperationObject::PeriodUnit iPeriod)91 SKGError SKGRecurrentOperationObject::setPeriodUnit(SKGRecurrentOperationObject::PeriodUnit iPeriod)
92 {
93     return setAttribute(QStringLiteral("t_period_unit"), (iPeriod == SKGRecurrentOperationObject::DAY ? QStringLiteral("D") : (iPeriod == SKGRecurrentOperationObject::WEEK ? QStringLiteral("W") : (iPeriod == SKGRecurrentOperationObject::MONTH ? QStringLiteral("M") : QStringLiteral("Y")))));
94 }
95 
setAutoWriteDays(int iDays)96 SKGError SKGRecurrentOperationObject::setAutoWriteDays(int iDays)
97 {
98     return setAttribute(QStringLiteral("i_auto_write_days"), SKGServices::intToString(iDays));
99 }
100 
getAutoWriteDays() const101 int SKGRecurrentOperationObject::getAutoWriteDays() const
102 {
103     return SKGServices::stringToInt(getAttribute(QStringLiteral("i_auto_write_days")));
104 }
105 
setWarnDays(int iDays)106 SKGError SKGRecurrentOperationObject::setWarnDays(int iDays)
107 {
108     return setAttribute(QStringLiteral("i_warn_days"), SKGServices::intToString(iDays));
109 }
110 
getWarnDays() const111 int SKGRecurrentOperationObject::getWarnDays() const
112 {
113     return SKGServices::stringToInt(getAttribute(QStringLiteral("i_warn_days")));
114 }
115 
hasTimeLimit() const116 bool SKGRecurrentOperationObject::hasTimeLimit() const
117 {
118     return (getAttribute(QStringLiteral("t_times")) == QStringLiteral("Y"));
119 }
120 
timeLimit(bool iTimeLimit)121 SKGError SKGRecurrentOperationObject::timeLimit(bool iTimeLimit)
122 {
123     return setAttribute(QStringLiteral("t_times"), iTimeLimit ? QStringLiteral("Y") : QStringLiteral("N"));
124 }
125 
setTimeLimit(QDate iLastDate)126 SKGError SKGRecurrentOperationObject::setTimeLimit(QDate iLastDate)
127 {
128     // Get parameters
129     QDate firstDate = this->getDate();
130     if (iLastDate < firstDate) {
131         return setTimeLimit(0);
132     }
133     SKGRecurrentOperationObject::PeriodUnit period = this->getPeriodUnit();
134     int occu = qMax(this->getPeriodIncrement(), 1);
135 
136     // Compute nb time
137     int nbd = firstDate.daysTo(iLastDate);
138     if (period == SKGRecurrentOperationObject::DAY) {
139         nbd = nbd / occu;
140     } else if (period == SKGRecurrentOperationObject::WEEK) {
141         nbd = nbd / (7 * occu);
142     } else if (period == SKGRecurrentOperationObject::MONTH) {
143         nbd = (iLastDate.day() >= firstDate.day() ? 0 : -1) + (iLastDate.year() - firstDate.year()) * 12 + (iLastDate.month() - firstDate.month());
144     } else if (period == SKGRecurrentOperationObject::YEAR) {
145         nbd = nbd / (365 * occu);
146     }
147 
148     if (nbd < -1) {
149         nbd = -1;
150     }
151     return setTimeLimit(nbd + 1);
152 }
153 
setTimeLimit(int iTimeLimit)154 SKGError SKGRecurrentOperationObject::setTimeLimit(int iTimeLimit)
155 {
156     return setAttribute(QStringLiteral("i_nb_times"), SKGServices::intToString(iTimeLimit));
157 }
158 
getTimeLimit() const159 int SKGRecurrentOperationObject::getTimeLimit() const
160 {
161     return SKGServices::stringToInt(getAttribute(QStringLiteral("i_nb_times")));
162 }
163 
setDate(QDate iDate)164 SKGError SKGRecurrentOperationObject::setDate(QDate iDate)
165 {
166     return setAttribute(QStringLiteral("d_date"), SKGServices::dateToSqlString(iDate));
167 }
168 
getNextDate() const169 QDate SKGRecurrentOperationObject::getNextDate() const
170 {
171     QDate nextDate = getDate();
172     SKGRecurrentOperationObject::PeriodUnit punit = getPeriodUnit();
173     int p = getPeriodIncrement();
174     if (punit == SKGRecurrentOperationObject::DAY) {
175         nextDate = nextDate.addDays(p);
176     } else if (punit == SKGRecurrentOperationObject::WEEK) {
177         nextDate = nextDate.addDays(7 * p);
178     } else if (punit == SKGRecurrentOperationObject::MONTH) {
179         nextDate = nextDate.addMonths(p);
180     } else if (punit == SKGRecurrentOperationObject::YEAR) {
181         nextDate = nextDate.addYears(p);
182     }
183     return nextDate;
184 }
185 
getDate() const186 QDate SKGRecurrentOperationObject::getDate() const
187 {
188     return SKGServices::stringToTime(getAttribute(QStringLiteral("d_date"))).date();
189 }
190 
warnEnabled(bool iWarn)191 SKGError SKGRecurrentOperationObject::warnEnabled(bool iWarn)
192 {
193     return setAttribute(QStringLiteral("t_warn"), iWarn ? QStringLiteral("Y") : QStringLiteral("N"));
194 }
195 
isWarnEnabled() const196 bool SKGRecurrentOperationObject::isWarnEnabled() const
197 {
198     return (getAttribute(QStringLiteral("t_warn")) == QStringLiteral("Y"));
199 }
200 
autoWriteEnabled(bool iAutoWrite)201 SKGError SKGRecurrentOperationObject::autoWriteEnabled(bool iAutoWrite)
202 {
203     return setAttribute(QStringLiteral("t_auto_write"), iAutoWrite ? QStringLiteral("Y") : QStringLiteral("N"));
204 }
205 
isAutoWriteEnabled() const206 bool SKGRecurrentOperationObject::isAutoWriteEnabled() const
207 {
208     return (getAttribute(QStringLiteral("t_auto_write")) == QStringLiteral("Y"));
209 }
210 
getRecurredOperations(SKGListSKGObjectBase & oOperations) const211 SKGError SKGRecurrentOperationObject::getRecurredOperations(SKGListSKGObjectBase& oOperations) const
212 {
213     return getDocument()->getObjects(QStringLiteral("v_operation"), "r_recurrentoperation_id=" % SKGServices::intToString(getID()), oOperations);
214 }
215 
process(int & oNbInserted,bool iForce,QDate iDate)216 SKGError SKGRecurrentOperationObject::process(int& oNbInserted, bool iForce, QDate iDate)
217 {
218     SKGError err;
219     SKGTRACEINFUNCRC(10, err)
220     oNbInserted = 0;
221 
222     if (!hasTimeLimit() || getTimeLimit() > 0) {
223         if (isAutoWriteEnabled() || iForce) {
224             QDate nextDate = getDate();
225             if (nextDate.isValid() && iDate >= nextDate.addDays(-getAutoWriteDays())) {
226                 SKGOperationObject op;
227                 err = getParentOperation(op);
228                 IFOK(err) {
229                     // Create the duplicated operation
230                     SKGOperationObject newOp;
231                     err = op.duplicate(newOp, nextDate);
232 
233                     if (!op.isTemplate()) {
234                         // Set old op as recurrent
235                         IFOKDO(err, op.setAttribute(QStringLiteral("r_recurrentoperation_id"), SKGServices::intToString(getID())))
236                         IFOKDO(err, op.save())
237 
238                         // Set new operation as reference
239                         IFOKDO(err, setParentOperation(newOp))
240                     } else {
241                         // Set new op as recurrent
242                         IFOKDO(err, newOp.setAttribute(QStringLiteral("r_recurrentoperation_id"), SKGServices::intToString(getID())))
243                         IFOKDO(err, newOp.save())
244                     }
245 
246                     IFOKDO(err, setDate(getNextDate()))
247                     if (!err && hasTimeLimit()) {
248                         err = setTimeLimit(getTimeLimit() - 1);
249                     }
250                     IFOKDO(err, save())
251                     IFOKDO(err, load())
252 
253                     // Process again in case of multi insert needed
254                     int nbi = 0;
255                     IFOKDO(err, process(nbi, iForce, iDate))
256                     oNbInserted = oNbInserted + 1 + nbi;
257 
258                     // Send message
259                     IFOKDO(err, newOp.load())
260                     IFOK(err) {
261                         err = getDocument()->sendMessage(i18nc("An information message", "Operation '%1' has been inserted", newOp.getDisplayName()), SKGDocument::Positive);
262                     }
263                 }
264             }
265         }
266 
267         if (isWarnEnabled() && !err) {
268             QDate nextDate = getDate();
269             if (QDate::currentDate() >= nextDate.addDays(-getWarnDays())) {
270                 SKGOperationObject op;
271                 err = getParentOperation(op);
272                 IFOK(err) {
273                     int nbdays = QDate::currentDate().daysTo(nextDate);
274                     if (nbdays > 0) {
275                         err = getDocument()->sendMessage(i18np("Operation '%2' will be inserted in one day", "Operation '%2' will be inserted in %1 days", nbdays, getDisplayName()));
276                     }
277                 }
278             }
279         }
280     }
281     return err;
282 }
283 
process(SKGDocumentBank * iDocument,int & oNbInserted,bool iForce,QDate iDate)284 SKGError SKGRecurrentOperationObject::process(SKGDocumentBank* iDocument, int& oNbInserted, bool iForce, QDate iDate)
285 {
286     SKGError err;
287     oNbInserted = 0;
288 
289     // Get all operation with auto_write
290     SKGListSKGObjectBase recuOps;
291     if (iDocument != nullptr) {
292         err = iDocument->getObjects(QStringLiteral("v_recurrentoperation"), QLatin1String(""), recuOps);
293     }
294 
295     int nb = recuOps.count();
296     for (int i = 0; !err && i < nb; ++i) {
297         SKGRecurrentOperationObject recu(recuOps.at(i));
298         int nbi = 0;
299         err = recu.process(nbi, iForce, iDate);
300         oNbInserted += nbi;
301     }
302 
303     return err;
304 }
305 
306 
307 
308