File indexing completed on 2024-04-28 16:30:12

0001 /***************************************************************************
0002  * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
0003  * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  ***************************************************************************/
0006 /** @file
0007  * This file implements classes SKGRecurrentOperationObject.
0008  *
0009  * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgrecurrentoperationobject.h"
0012 
0013 #include <klocalizedstring.h>
0014 
0015 #include "skgdocumentbank.h"
0016 #include "skgoperationobject.h"
0017 #include "skgservices.h"
0018 #include "skgsuboperationobject.h"
0019 #include "skgtraces.h"
0020 
0021 SKGRecurrentOperationObject::SKGRecurrentOperationObject(): SKGRecurrentOperationObject(nullptr)
0022 {}
0023 
0024 SKGRecurrentOperationObject::SKGRecurrentOperationObject(SKGDocument* iDocument, int iID): SKGObjectBase(iDocument, QStringLiteral("v_recurrentoperation"), iID)
0025 {}
0026 
0027 SKGRecurrentOperationObject::~SKGRecurrentOperationObject()
0028     = default;
0029 
0030 SKGRecurrentOperationObject::SKGRecurrentOperationObject(const SKGRecurrentOperationObject& iObject) = default;
0031 
0032 SKGRecurrentOperationObject::SKGRecurrentOperationObject(const SKGObjectBase& iObject)
0033 {
0034     if (iObject.getRealTable() == QStringLiteral("recurrentoperation")) {
0035         copyFrom(iObject);
0036     } else {
0037         *this = SKGObjectBase(iObject.getDocument(), QStringLiteral("v_recurrentoperation"), iObject.getID());
0038     }
0039 }
0040 
0041 SKGRecurrentOperationObject& SKGRecurrentOperationObject::operator= (const SKGObjectBase& iObject)
0042 {
0043     copyFrom(iObject);
0044     return *this;
0045 }
0046 
0047 SKGRecurrentOperationObject& SKGRecurrentOperationObject::operator= (const SKGRecurrentOperationObject& iObject)
0048 {
0049     copyFrom(iObject);
0050     return *this;
0051 }
0052 
0053 SKGError SKGRecurrentOperationObject::getParentOperation(SKGOperationObject& oOperation) const
0054 {
0055     SKGObjectBase objTmp;
0056     SKGError err = getDocument()->getObject(QStringLiteral("v_operation"), "id=" % getAttribute(QStringLiteral("rd_operation_id")), objTmp);
0057     oOperation = objTmp;
0058     return err;
0059 }
0060 
0061 SKGError SKGRecurrentOperationObject::setParentOperation(const SKGOperationObject& iOperation)
0062 {
0063     return setAttribute(QStringLiteral("rd_operation_id"), SKGServices::intToString(iOperation.getID()));
0064 }
0065 
0066 SKGError SKGRecurrentOperationObject::setTemplate(bool iTemplate)
0067 {
0068     SKGError err;
0069 
0070     if (iTemplate == isTemplate()) {
0071         err.addError(ERR_UNEXPECTED, i18nc("Error message", "Trying to set the same template status \"%1\" to a recurrent operation", iTemplate));
0072         return err;
0073     }
0074 
0075     SKGOperationObject parentOp;
0076     err = getParentOperation(parentOp);
0077     if (iTemplate) {
0078         // Convert to template
0079         SKGOperationObject operationObjOrig = parentOp;
0080         IFOKDO(err, operationObjOrig.duplicate(parentOp, operationObjOrig.getDate(), true))
0081 
0082         IFOKDO(err, setParentOperation(parentOp))
0083         IFOKDO(err, save())
0084 
0085         IFOKDO(err, operationObjOrig.setAttribute(QStringLiteral("r_recurrentoperation_id"), SKGServices::intToString(getID())))
0086         IFOKDO(err, operationObjOrig.save())
0087     } else {
0088         // Convert to non-template
0089         SKGObjectBase::SKGListSKGObjectBase transactions;
0090         IFOKDO(err, getRecurredOperations(transactions))
0091         IFOK(err) {
0092             if (!transactions.isEmpty()) {
0093                 SKGOperationObject lastObj(transactions.last());
0094                 IFOKDO(err, setParentOperation(lastObj))
0095                 IFOKDO(err, save())
0096                 IFOKDO(err, lastObj.setAttribute(QStringLiteral("r_recurrentoperation_id"), QString()))
0097                 IFOKDO(err, lastObj.save())
0098 
0099                 SKGObjectBase::SKGListSKGObjectBase goupops;
0100                 IFOKDO(err, parentOp.getGroupedOperations(goupops))
0101                 IFOK(err) {
0102                     int nbgoupops = goupops.count();
0103                     for (int i = 0; !err && i < nbgoupops; ++i) {
0104                         SKGOperationObject groupop(goupops.at(i));
0105                         if (groupop != parentOp) {
0106                             IFOKDO(err, groupop.remove(true))
0107                         }
0108                     }
0109                 }
0110 
0111                 IFOKDO(err, parentOp.remove(true))
0112             } else {
0113                 err.addError(ERR_FAIL, i18nc("Error message", "Need at least one transaction to convert a schedule to a non-template one"));
0114             }
0115         }
0116     }
0117 
0118     return err;
0119 }
0120 
0121 bool SKGRecurrentOperationObject::isTemplate() const
0122 {
0123     SKGOperationObject op;
0124     SKGError err = getParentOperation(op);
0125     IFOK(err) return op.isTemplate();
0126     return false;
0127 }
0128 
0129 SKGError SKGRecurrentOperationObject::setPeriodIncrement(int iIncrement)
0130 {
0131     return setAttribute(QStringLiteral("i_period_increment"), SKGServices::intToString(iIncrement));
0132 }
0133 
0134 int SKGRecurrentOperationObject::getPeriodIncrement() const
0135 {
0136     return SKGServices::stringToInt(getAttribute(QStringLiteral("i_period_increment")));
0137 }
0138 
0139 SKGRecurrentOperationObject::PeriodUnit SKGRecurrentOperationObject::getPeriodUnit() const
0140 {
0141     QString t_period_unit = getAttribute(QStringLiteral("t_period_unit"));
0142     if (t_period_unit == QStringLiteral("D")) {
0143         return SKGRecurrentOperationObject::DAY;
0144     }
0145     if (t_period_unit == QStringLiteral("W")) {
0146         return SKGRecurrentOperationObject::WEEK;
0147     }
0148     if (t_period_unit == QStringLiteral("M")) {
0149         return SKGRecurrentOperationObject::MONTH;
0150     }
0151     return SKGRecurrentOperationObject::YEAR;
0152 }
0153 
0154 SKGError SKGRecurrentOperationObject::setPeriodUnit(SKGRecurrentOperationObject::PeriodUnit iPeriod)
0155 {
0156     return setAttribute(QStringLiteral("t_period_unit"), (iPeriod == SKGRecurrentOperationObject::DAY ? QStringLiteral("D") : (iPeriod == SKGRecurrentOperationObject::WEEK ? QStringLiteral("W") : (iPeriod == SKGRecurrentOperationObject::MONTH ? QStringLiteral("M") : QStringLiteral("Y")))));
0157 }
0158 
0159 SKGError SKGRecurrentOperationObject::setAutoWriteDays(int iDays)
0160 {
0161     return setAttribute(QStringLiteral("i_auto_write_days"), SKGServices::intToString(iDays));
0162 }
0163 
0164 int SKGRecurrentOperationObject::getAutoWriteDays() const
0165 {
0166     return SKGServices::stringToInt(getAttribute(QStringLiteral("i_auto_write_days")));
0167 }
0168 
0169 SKGError SKGRecurrentOperationObject::setWarnDays(int iDays)
0170 {
0171     return setAttribute(QStringLiteral("i_warn_days"), SKGServices::intToString(iDays));
0172 }
0173 
0174 int SKGRecurrentOperationObject::getWarnDays() const
0175 {
0176     return SKGServices::stringToInt(getAttribute(QStringLiteral("i_warn_days")));
0177 }
0178 
0179 bool SKGRecurrentOperationObject::hasTimeLimit() const
0180 {
0181     return (getAttribute(QStringLiteral("t_times")) == QStringLiteral("Y"));
0182 }
0183 
0184 SKGError SKGRecurrentOperationObject::timeLimit(bool iTimeLimit)
0185 {
0186     return setAttribute(QStringLiteral("t_times"), iTimeLimit ? QStringLiteral("Y") : QStringLiteral("N"));
0187 }
0188 
0189 SKGError SKGRecurrentOperationObject::setTimeLimit(QDate iLastDate)
0190 {
0191     // Get parameters
0192     QDate firstDate = this->getDate();
0193     if (iLastDate < firstDate) {
0194         return setTimeLimit(0);
0195     }
0196     SKGRecurrentOperationObject::PeriodUnit period = this->getPeriodUnit();
0197     int occu = qMax(this->getPeriodIncrement(), 1);
0198 
0199     // Compute nb time
0200     int nbd = firstDate.daysTo(iLastDate);
0201     if (period == SKGRecurrentOperationObject::DAY) {
0202         nbd = nbd / occu;
0203     } else if (period == SKGRecurrentOperationObject::WEEK) {
0204         nbd = nbd / (7 * occu);
0205     } else if (period == SKGRecurrentOperationObject::MONTH) {
0206         nbd = (iLastDate.day() >= firstDate.day() ? 0 : -1) + (iLastDate.year() - firstDate.year()) * 12 + (iLastDate.month() - firstDate.month());
0207     } else if (period == SKGRecurrentOperationObject::YEAR) {
0208         nbd = nbd / (365 * occu);
0209     }
0210 
0211     if (nbd < -1) {
0212         nbd = -1;
0213     }
0214     return setTimeLimit(nbd + 1);
0215 }
0216 
0217 SKGError SKGRecurrentOperationObject::setTimeLimit(int iTimeLimit)
0218 {
0219     return setAttribute(QStringLiteral("i_nb_times"), SKGServices::intToString(iTimeLimit));
0220 }
0221 
0222 int SKGRecurrentOperationObject::getTimeLimit() const
0223 {
0224     return SKGServices::stringToInt(getAttribute(QStringLiteral("i_nb_times")));
0225 }
0226 
0227 SKGError SKGRecurrentOperationObject::setDate(QDate iDate)
0228 {
0229     return setAttribute(QStringLiteral("d_date"), SKGServices::dateToSqlString(iDate));
0230 }
0231 
0232 QDate SKGRecurrentOperationObject::getNextDate() const
0233 {
0234     QDate nextDate = getDate();
0235     SKGRecurrentOperationObject::PeriodUnit punit = getPeriodUnit();
0236     int p = getPeriodIncrement();
0237     if (punit == SKGRecurrentOperationObject::DAY) {
0238         nextDate = nextDate.addDays(p);
0239     } else if (punit == SKGRecurrentOperationObject::WEEK) {
0240         nextDate = nextDate.addDays(7 * p);
0241     } else if (punit == SKGRecurrentOperationObject::MONTH) {
0242         nextDate = nextDate.addMonths(p);
0243     } else if (punit == SKGRecurrentOperationObject::YEAR) {
0244         nextDate = nextDate.addYears(p);
0245     }
0246     return nextDate;
0247 }
0248 
0249 QDate SKGRecurrentOperationObject::getDate() const
0250 {
0251     return SKGServices::stringToTime(getAttribute(QStringLiteral("d_date"))).date();
0252 }
0253 
0254 SKGError SKGRecurrentOperationObject::warnEnabled(bool iWarn)
0255 {
0256     return setAttribute(QStringLiteral("t_warn"), iWarn ? QStringLiteral("Y") : QStringLiteral("N"));
0257 }
0258 
0259 bool SKGRecurrentOperationObject::isWarnEnabled() const
0260 {
0261     return (getAttribute(QStringLiteral("t_warn")) == QStringLiteral("Y"));
0262 }
0263 
0264 SKGError SKGRecurrentOperationObject::autoWriteEnabled(bool iAutoWrite)
0265 {
0266     return setAttribute(QStringLiteral("t_auto_write"), iAutoWrite ? QStringLiteral("Y") : QStringLiteral("N"));
0267 }
0268 
0269 bool SKGRecurrentOperationObject::isAutoWriteEnabled() const
0270 {
0271     return (getAttribute(QStringLiteral("t_auto_write")) == QStringLiteral("Y"));
0272 }
0273 
0274 SKGError SKGRecurrentOperationObject::getRecurredOperations(SKGListSKGObjectBase& oOperations) const
0275 {
0276     return getDocument()->getObjects(QStringLiteral("v_operation"), "r_recurrentoperation_id=" % SKGServices::intToString(getID()) % " ORDER BY d_date", oOperations);
0277 }
0278 
0279 SKGError SKGRecurrentOperationObject::process(int& oNbInserted, bool iForce, QDate iDate)
0280 {
0281     SKGError err;
0282     SKGTRACEINFUNCRC(10, err)
0283     oNbInserted = 0;
0284 
0285     if (!hasTimeLimit() || getTimeLimit() > 0) {
0286         if (isAutoWriteEnabled() || iForce) {
0287             QDate nextDate = getDate();
0288             if (nextDate.isValid() && iDate >= nextDate.addDays(-getAutoWriteDays())) {
0289                 SKGOperationObject op;
0290                 err = getParentOperation(op);
0291                 IFOK(err) {
0292                     // Create the duplicated operation
0293                     SKGOperationObject newOp;
0294                     err = op.duplicate(newOp, nextDate);
0295                     IFOKDO(err, newOp.setRecurrentOperation(getID()))
0296                     IFOKDO(err, load())
0297 
0298                     if (!err && hasTimeLimit()) {
0299                         err = setTimeLimit(getTimeLimit() - 1);
0300                     }
0301                     IFOKDO(err, save())
0302 
0303                     // Process again in case of multi insert needed
0304                     int nbi = 0;
0305                     IFOKDO(err, process(nbi, iForce, iDate))
0306                     oNbInserted = oNbInserted + 1 + nbi;
0307 
0308                     // Send message
0309                     IFOKDO(err, newOp.load())
0310                     IFOK(err) {
0311                         err = getDocument()->sendMessage(i18nc("An information message", "Transaction '%1' has been inserted", newOp.getDisplayName()), SKGDocument::Positive);
0312                     }
0313                 }
0314             }
0315         }
0316 
0317         if (isWarnEnabled() && !err) {
0318             QDate nextDate = getDate();
0319             if (QDate::currentDate() >= nextDate.addDays(-getWarnDays())) {
0320                 SKGOperationObject op;
0321                 err = getParentOperation(op);
0322                 IFOK(err) {
0323                     int nbdays = QDate::currentDate().daysTo(nextDate);
0324                     if (nbdays > 0) {
0325                         err = getDocument()->sendMessage(i18np("Transaction '%2' will be inserted in one day", "Transaction '%2' will be inserted in %1 days", nbdays, getDisplayName()));
0326                     }
0327                 }
0328             }
0329         }
0330     }
0331     return err;
0332 }
0333 
0334 SKGError SKGRecurrentOperationObject::process(SKGDocumentBank* iDocument, int& oNbInserted, bool iForce, QDate iDate)
0335 {
0336     SKGError err;
0337     oNbInserted = 0;
0338 
0339     // Get all transaction with auto_write
0340     SKGListSKGObjectBase recuOps;
0341     if (iDocument != nullptr) {
0342         err = iDocument->getObjects(QStringLiteral("v_recurrentoperation"), QLatin1String(""), recuOps);
0343     }
0344 
0345     int nb = recuOps.count();
0346     for (int i = 0; !err && i < nb; ++i) {
0347         SKGRecurrentOperationObject recu(recuOps.at(i));
0348         int nbi = 0;
0349         err = recu.process(nbi, iForce, iDate);
0350         oNbInserted += nbi;
0351     }
0352 
0353     return err;
0354 }
0355 
0356 
0357