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 SKGOperationObject.
0008  *
0009  * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgoperationobject.h"
0012 
0013 #include <klocalizedstring.h>
0014 
0015 #include "skgaccountobject.h"
0016 #include "skgdocument.h"
0017 #include "skgpayeeobject.h"
0018 #include "skgrecurrentoperationobject.h"
0019 #include "skgservices.h"
0020 #include "skgsuboperationobject.h"
0021 #include "skgtraces.h"
0022 #include "skgunitobject.h"
0023 
0024 SKGOperationObject::SKGOperationObject() : SKGOperationObject(nullptr)
0025 {}
0026 
0027 SKGOperationObject::SKGOperationObject(SKGDocument* iDocument, int iID) : SKGObjectBase(iDocument, QStringLiteral("v_operation"), iID)
0028 {}
0029 
0030 SKGOperationObject::~SKGOperationObject()
0031     = default;
0032 
0033 SKGOperationObject::SKGOperationObject(const SKGOperationObject& iObject)
0034     = default;
0035 
0036 SKGOperationObject::SKGOperationObject(const SKGObjectBase& iObject)
0037 {
0038     if (iObject.getRealTable() == QStringLiteral("operation")) {
0039         copyFrom(iObject);
0040     } else {
0041         *this = SKGObjectBase(iObject.getDocument(), QStringLiteral("v_operation"), iObject.getID());
0042     }
0043 }
0044 
0045 SKGOperationObject& SKGOperationObject::operator= (const SKGObjectBase& iObject)
0046 {
0047     copyFrom(iObject);
0048     return *this;
0049 }
0050 
0051 SKGOperationObject& SKGOperationObject::operator= (const SKGOperationObject& iObject)
0052 {
0053     copyFrom(iObject);
0054     return *this;
0055 }
0056 
0057 SKGError SKGOperationObject::duplicate(SKGOperationObject& oOperation, QDate iDate, bool iTemplateMode) const
0058 {
0059     SKGError err;
0060     SKGTRACEINFUNCRC(20, err)
0061     QDate previousDate = getDate();
0062 
0063     // Create the duplicated operation
0064     oOperation = SKGOperationObject(getDocument(), this->getID());  // To be sure the object is on v_operation
0065     IFOKDO(err, oOperation.load())
0066     IFOKDO(err, oOperation.resetID())
0067     IFOKDO(err, oOperation.setDate(iDate))
0068     IFOKDO(err, oOperation.setStatus(SKGOperationObject::NONE))
0069     IFOKDO(err, oOperation.setImported(false))
0070     IFOKDO(err, oOperation.setTemplate(iTemplateMode))
0071     IFOKDO(err, oOperation.setImportID(QLatin1String("")))
0072     IFOKDO(err, oOperation.bookmark(false))
0073     IFOKDO(err, oOperation.setNumber(QLatin1String("")))
0074     IFOKDO(err, oOperation.setGroupOperation(oOperation))
0075     IFOKDO(err, oOperation.setAttribute(QStringLiteral("d_createdate"), SKGServices::dateToSqlString(QDateTime::currentDateTime())))
0076     IFOKDO(err, oOperation.save(false, false))
0077 
0078     // Duplicate subop
0079     IFOK(err) {
0080         SKGListSKGObjectBase subops;
0081         err = getSubOperations(subops);
0082         int nbsupops = subops.count();
0083         for (int i = 0; !err && i < nbsupops; ++i) {
0084             SKGSubOperationObject subop(subops.at(i));
0085             err = subop.resetID();
0086             IFOKDO(err, subop.setParentOperation(oOperation))
0087             IFOKDO(err, subop.setDate(subop.getDate().addDays(previousDate.daysTo(iDate))))
0088             IFOKDO(err, subop.save(false))
0089         }
0090     }
0091 
0092     // Duplicate grouped transaction to support recurrent transfers
0093     IFOK(err) {
0094         SKGListSKGObjectBase goupops;
0095         err = getGroupedOperations(goupops);
0096         int nbgoupops = goupops.count();
0097         for (int i = 0; !err && i < nbgoupops; ++i) {
0098             SKGOperationObject groupop(goupops.at(i));
0099             if (groupop != *this) {
0100                 // Create the duplicated operation
0101                 SKGOperationObject newgroupop = groupop;
0102                 err = newgroupop.resetID();
0103                 IFOKDO(err, newgroupop.setDate(iDate))
0104                 IFOKDO(err, newgroupop.setStatus(SKGOperationObject::NONE))
0105                 IFOKDO(err, newgroupop.setImported(false))
0106                 IFOKDO(err, newgroupop.setTemplate(iTemplateMode))
0107                 IFOKDO(err, newgroupop.setImportID(QLatin1String("")))
0108                 IFOKDO(err, newgroupop.bookmark(false))
0109                 IFOKDO(err, newgroupop.setNumber(QLatin1String("")))
0110                 IFOKDO(err, newgroupop.setGroupOperation(newgroupop))
0111                 IFOKDO(err, newgroupop.setGroupOperation(oOperation))
0112                 IFOKDO(err, newgroupop.save(false))
0113 
0114                 // Duplicate subop
0115                 IFOK(err) {
0116                     SKGListSKGObjectBase subops;
0117                     err = groupop.getSubOperations(subops);
0118                     int nbsupops = subops.count();
0119                     for (int j = 0; !err && j < nbsupops; ++j) {
0120                         SKGSubOperationObject subop(subops.at(j));
0121                         err = subop.resetID();
0122                         IFOKDO(err, subop.setParentOperation(newgroupop))
0123                         IFOKDO(err, subop.setDate(subop.getDate().addDays(previousDate.daysTo(iDate))))
0124                         IFOKDO(err, subop.save(false))
0125                     }
0126                 }
0127             }
0128         }
0129     }
0130 
0131     IFOKDO(err, oOperation.load())
0132     return err;
0133 }
0134 
0135 
0136 SKGError SKGOperationObject::getParentAccount(SKGAccountObject& oAccount) const
0137 {
0138     SKGObjectBase objTmp;
0139     SKGError err = getDocument()->getObject(QStringLiteral("v_account"), "id=" % getAttribute(QStringLiteral("rd_account_id")), objTmp);
0140     oAccount = objTmp;
0141     return err;
0142 }
0143 
0144 SKGError SKGOperationObject::setParentAccount(const SKGAccountObject& iAccount, bool iForce)
0145 {
0146     SKGError err;
0147     QString currentAccount = getAttribute(QStringLiteral("rd_account_id"));
0148     QString newAccount = SKGServices::intToString(iAccount.getID());
0149     if (newAccount == QStringLiteral("0")) {
0150         err = SKGError(ERR_FAIL, i18nc("Error message",  "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGOperationObject::setParentAccount")));
0151     } else {
0152         if (newAccount != currentAccount) {
0153             if (iAccount.isClosed() && !iForce) {
0154                 err = SKGError(ERR_FAIL, i18nc("Error message",  "Impossible to add a transaction in a closed account"));
0155             } else {
0156                 err = setAttribute(QStringLiteral("rd_account_id"), newAccount);
0157             }
0158         }
0159     }
0160     return err;
0161 }
0162 
0163 SKGError SKGOperationObject::setMode(const QString& iMode)
0164 {
0165     return setAttribute(QStringLiteral("t_mode"), iMode);
0166 }
0167 
0168 QString SKGOperationObject::getMode() const
0169 {
0170     return getAttribute(QStringLiteral("t_mode"));
0171 }
0172 
0173 SKGError SKGOperationObject::setPayee(const SKGPayeeObject& iPayee)
0174 {
0175     return setAttribute(QStringLiteral("r_payee_id"), SKGServices::intToString(iPayee.getID()));
0176 }
0177 
0178 SKGError SKGOperationObject::getPayee(SKGPayeeObject& oPayee) const
0179 {
0180     SKGError err = getDocument()->getObject(QStringLiteral("v_payee"), "id=" % SKGServices::intToString(SKGServices::stringToInt(getAttribute(QStringLiteral("r_payee_id")))), oPayee);
0181     return err;
0182 }
0183 
0184 SKGError SKGOperationObject::setComment(const QString& iComment)
0185 {
0186     return setAttribute(QStringLiteral("t_comment"), iComment);
0187 }
0188 
0189 QString SKGOperationObject::getComment() const
0190 {
0191     return getAttribute(QStringLiteral("t_comment"));
0192 }
0193 
0194 SKGError SKGOperationObject::setNumber(const QString& iNumber)
0195 {
0196     return setAttribute(QStringLiteral("t_number"), iNumber);
0197 }
0198 
0199 QString SKGOperationObject::getNumber() const
0200 {
0201     return getAttribute(QStringLiteral("t_number"));
0202 }
0203 
0204 SKGOperationObject::OperationStatus SKGOperationObject::getStatus() const
0205 {
0206     QString t_status = getAttribute(QStringLiteral("t_status"));
0207     if (t_status == QStringLiteral("Y")) {
0208         return SKGOperationObject::CHECKED;
0209     }
0210     if (t_status == QStringLiteral("P")) {
0211         return SKGOperationObject::MARKED;
0212     }
0213     return SKGOperationObject::NONE;
0214 }
0215 
0216 SKGError SKGOperationObject::setStatus(SKGOperationObject::OperationStatus iStatus)
0217 {
0218     return setAttribute(QStringLiteral("t_status"), (iStatus == SKGOperationObject::CHECKED ? QStringLiteral("Y") : (iStatus == SKGOperationObject::MARKED ? QStringLiteral("P") : QStringLiteral("N"))));
0219 }
0220 
0221 SKGError SKGOperationObject::setDate(QDate iDate, bool iRefreshSubOperations)
0222 {
0223     SKGError err;
0224     // Compute delta of the change of date
0225     QDate previousDate = getDate();
0226     if (iRefreshSubOperations) {
0227         // Apply the delta on sub transactions
0228         SKGObjectBase::SKGListSKGObjectBase listSubOperations;
0229         getSubOperations(listSubOperations);  // Error is not manage to avoid error in case of first creation
0230         int nbSubOperations = listSubOperations.count();
0231         for (int i = 0; !err && i < nbSubOperations; ++i) {
0232             SKGSubOperationObject sop(listSubOperations.at(i));
0233             QDate previousSubDate = sop.getDate();
0234             if (previousSubDate.isValid()) {
0235                 if (previousDate.isValid()) {
0236                     int delta = previousDate.daysTo(iDate);
0237                     err = sop.setDate(previousSubDate.addDays(delta));
0238                     IFOKDO(err, sop.save(true, false))
0239                 }
0240             } else {
0241                 err = sop.setDate(iDate);
0242                 IFOKDO(err, sop.save(true, false))
0243             }
0244         }
0245     }
0246     IFOKDO(err, setAttribute(QStringLiteral("d_date"), SKGServices::dateToSqlString(iDate)))
0247     return err;
0248 }
0249 
0250 QDate SKGOperationObject::getDate() const
0251 {
0252     return SKGServices::stringToTime(getAttribute(QStringLiteral("d_date"))).date();
0253 }
0254 
0255 SKGError SKGOperationObject::getUnit(SKGUnitObject& oUnit) const
0256 {
0257     SKGError err = (getDocument() == nullptr ? SKGError(ERR_POINTER, i18nc("Error message", "Transaction impossible because the document is missing")) : getDocument()->getObject(QStringLiteral("v_unit"), "id=" % getAttribute(QStringLiteral("rc_unit_id")), oUnit));
0258     // SKGError err = getDocument()->getObject(QStringLiteral("v_unit"), "id=" % getAttribute(QStringLiteral("rc_unit_id")), oUnit);
0259     return err;
0260 }
0261 
0262 SKGError SKGOperationObject::setUnit(const SKGUnitObject& iUnit)
0263 {
0264     return setAttribute(QStringLiteral("rc_unit_id"), SKGServices::intToString(iUnit.getID()));
0265 }
0266 
0267 bool SKGOperationObject::isInGroup() const
0268 {
0269     return (getAttribute(QStringLiteral("i_group_id")) != QStringLiteral("0"));
0270 }
0271 
0272 bool SKGOperationObject::isTransfer(SKGOperationObject& oOperation) const
0273 {
0274     SKGTRACEINFUNC(10)
0275     SKGObjectBase::SKGListSKGObjectBase ops;
0276     getGroupedOperations(ops);
0277     if (ops.count() == 2) {
0278         oOperation = (*this == SKGOperationObject(ops.at(0)) ? ops.at(1) : ops.at(0));
0279     }
0280     return (getAttribute(QStringLiteral("t_TRANSFER")) == QStringLiteral("Y"));
0281 }
0282 
0283 SKGError SKGOperationObject::getGroupedOperations(SKGListSKGObjectBase& oGroupedOperations) const
0284 {
0285     SKGError err;
0286     QString gpId1 = getAttribute(QStringLiteral("i_group_id"));
0287     if (gpId1 == QStringLiteral("0") || gpId1.isEmpty()) {
0288         oGroupedOperations.clear();
0289     } else {
0290         err = getDocument()->getObjects(QStringLiteral("v_operation"), "i_group_id=" % gpId1, oGroupedOperations);
0291     }
0292     return err;
0293 }
0294 
0295 SKGError SKGOperationObject::getGroupOperation(SKGOperationObject& oOperation) const
0296 {
0297     SKGError err = getDocument()->getObject(QStringLiteral("v_operation"), "id=" % getAttribute(QStringLiteral("i_group_id")), oOperation);
0298     return err;
0299 }
0300 
0301 SKGError SKGOperationObject::setGroupOperation(const SKGOperationObject& iOperation)
0302 {
0303     SKGError err;
0304     SKGTRACEINFUNCRC(20, err)
0305 
0306     // Is it a remove group ?
0307     if (iOperation == *this) {
0308         // Yes
0309         err = setAttribute(QStringLiteral("i_group_id"), QStringLiteral("0"));
0310     } else {
0311         // Get previous groups
0312         QString group1 = getAttribute(QStringLiteral("i_group_id"));
0313         QString group2 = iOperation.getAttribute(QStringLiteral("i_group_id"));
0314 
0315         // Create a new group
0316         SKGStringListList result;
0317         err = getDocument()->executeSelectSqliteOrder(QStringLiteral("SELECT max(i_group_id) from operation"), result);
0318         IFOK(err) {
0319             // Compute new group id
0320             QString newIdGroup('1');
0321             if (result.count() == 2) {
0322                 newIdGroup = SKGServices::intToString(SKGServices::stringToInt(result.at(1).at(0)) + 1);
0323             }
0324 
0325             // Set group id
0326             SKGOperationObject op1 = SKGOperationObject(iOperation.getDocument(), iOperation.getID());
0327             err = op1.setAttribute(QStringLiteral("i_group_id"), newIdGroup);
0328             IFOKDO(err, op1.save(true, false))
0329 
0330             IFOKDO(err, setAttribute(QStringLiteral("i_group_id"), newIdGroup))
0331 
0332             // Update all objects of group2
0333             if (!err && !group1.isEmpty() && group1 != QStringLiteral("0")) {
0334                 err = getDocument()->executeSqliteOrder("UPDATE operation SET i_group_id=" % newIdGroup % " WHERE i_group_id=" % group1);
0335             }
0336 
0337             // Update all objects of group2
0338             if (!err && !group2.isEmpty() && group2 != QStringLiteral("0")) {
0339                 err = getDocument()->executeSqliteOrder("UPDATE operation SET i_group_id=" % newIdGroup % " WHERE i_group_id=" % group2);
0340             }
0341         }
0342     }
0343 
0344     return err;
0345 }
0346 
0347 SKGError SKGOperationObject::bookmark(bool iBookmark)
0348 {
0349     return setAttribute(QStringLiteral("t_bookmarked"), iBookmark ? QStringLiteral("Y") : QStringLiteral("N"));
0350 }
0351 
0352 bool SKGOperationObject::isBookmarked() const
0353 {
0354     return (getAttribute(QStringLiteral("t_bookmarked")) == QStringLiteral("Y"));
0355 }
0356 
0357 SKGError SKGOperationObject::setImported(bool iImported)
0358 {
0359     return setAttribute(QStringLiteral("t_imported"), iImported ? QStringLiteral("Y") : QStringLiteral("N"));
0360 }
0361 
0362 bool SKGOperationObject::isImported() const
0363 {
0364     return (getAttribute(QStringLiteral("t_imported")) != QStringLiteral("N"));
0365 }
0366 
0367 SKGError SKGOperationObject::setImportID(const QString& iImportID)
0368 {
0369     SKGError err = setAttribute(QStringLiteral("t_import_id"), iImportID);
0370     if (!err && !iImportID.isEmpty()) {
0371         err = setAttribute(QStringLiteral("t_imported"), QStringLiteral("T"));
0372     }
0373     return err;
0374 }
0375 
0376 QString SKGOperationObject::getImportID() const
0377 {
0378     return getAttribute(QStringLiteral("t_import_id"));
0379 }
0380 
0381 SKGError SKGOperationObject::setTemplate(bool iTemplate)
0382 {
0383     return setAttribute(QStringLiteral("t_template"), iTemplate ? QStringLiteral("Y") : QStringLiteral("N"));
0384 }
0385 
0386 bool SKGOperationObject::isTemplate() const
0387 {
0388     return (getAttribute(QStringLiteral("t_template")) != QStringLiteral("N"));
0389 }
0390 
0391 int SKGOperationObject::getNbSubOperations() const
0392 {
0393     return SKGServices::stringToInt(getAttribute(QStringLiteral("i_NBSUBOPERATIONS")));
0394 }
0395 
0396 SKGError SKGOperationObject::addSubOperation(SKGSubOperationObject& oSubOperation)
0397 {
0398     SKGError err;
0399     if (getID() == 0) {
0400         err = SKGError(ERR_FAIL, i18nc("Error message",  "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGOperationObject::addSubOperation")));
0401     } else {
0402         oSubOperation = SKGSubOperationObject(getDocument());
0403         err = oSubOperation.setParentOperation(*this);
0404         IFOKDO(err, oSubOperation.setDate(getDate()))
0405     }
0406     return err;
0407 }
0408 
0409 SKGError SKGOperationObject::getSubOperations(SKGListSKGObjectBase& oSubOperations) const
0410 {
0411     SKGError err;
0412     if (getID() == 0) {
0413         err = SKGError(ERR_FAIL, i18nc("Error message",  "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGOperationObject::getSubOperations")));
0414     } else {
0415         err = getDocument()->getObjects(QStringLiteral("v_suboperation"),
0416                                         "rd_operation_id=" % SKGServices::intToString(getID()) % " ORDER BY i_order", oSubOperations);
0417     }
0418     return err;
0419 }
0420 
0421 double SKGOperationObject::getCurrentAmount() const
0422 {
0423     return SKGServices::stringToDouble(getAttribute(QStringLiteral("f_CURRENTAMOUNT")));
0424 }
0425 
0426 double SKGOperationObject::getBalance() const
0427 {
0428     double output = 0.0;
0429     SKGStringListList result;
0430     SKGError err = getDocument()->executeSelectSqliteOrder("SELECT TOTAL(f_CURRENTAMOUNT) FROM v_operation WHERE t_template='N' AND "
0431                    "rd_account_id=" % getAttribute(QStringLiteral("rd_account_id")) % " AND (d_date<'" % getAttribute(QStringLiteral("d_date")) % "' OR "
0432                    "(d_date='" % getAttribute(QStringLiteral("d_date")) % "' AND id<=" % SKGServices::intToString(getID()) % "))", result);
0433     IFOK(err) {
0434         output = SKGServices::stringToDouble(result.at(1).at(0));
0435     }
0436 
0437     return output;
0438 }
0439 
0440 double SKGOperationObject::getAmount(QDate iDate) const
0441 {
0442     // Get quantity
0443     double quantity = SKGServices::stringToDouble(getAttribute(QStringLiteral("f_QUANTITY")));
0444 
0445     // Is the unit value already in cache ?
0446     double coef = 1;
0447     QString val = getDocument()->getCachedValue("unitvalue-" % getAttribute(QStringLiteral("rc_unit_id")));
0448     if (!val.isEmpty()) {
0449         // Yes
0450         coef = SKGServices::stringToDouble(val);
0451     } else {
0452         // No
0453         SKGUnitObject unit;
0454         if (getUnit(unit).isSucceeded()) {
0455             coef = unit.getAmount(iDate);
0456         }
0457     }
0458 
0459     return coef * quantity;
0460 }
0461 
0462 SKGError SKGOperationObject::addRecurrentOperation(SKGRecurrentOperationObject& oRecurrentOperation) const
0463 {
0464     SKGError err;
0465     if (getID() == 0) {
0466         err = SKGError(ERR_FAIL, i18nc("Error message",  "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGOperationObject::addRecurrentOperation")));
0467     } else {
0468         oRecurrentOperation = SKGRecurrentOperationObject(getDocument());
0469         err = oRecurrentOperation.setParentOperation(*this);
0470         IFOK(err) oRecurrentOperation.setDate(getDate());
0471     }
0472     return err;
0473 }
0474 
0475 SKGError SKGOperationObject::getRecurrentOperations(SKGListSKGObjectBase& oRecurrentOperation) const
0476 {
0477     SKGError err;
0478     if (getID() == 0) {
0479         err = SKGError(ERR_FAIL, i18nc("Error message",  "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGOperationObject::getRecurrentOperation")));
0480     } else {
0481         err = getDocument()->getObjects(QStringLiteral("v_recurrentoperation"),
0482                                         "rd_operation_id=" % SKGServices::intToString(getID()), oRecurrentOperation);
0483     }
0484     return err;
0485 }
0486 
0487 int SKGOperationObject::getRecurrentOperation() const
0488 {
0489     const auto recurrentId = SKGServices::stringToInt(getAttribute(QStringLiteral("r_recurrentoperation_id")));
0490     if (recurrentId != 0) {
0491         return recurrentId;
0492     } else {
0493         SKGObjectBase::SKGListSKGObjectBase recops;
0494         SKGError err = getRecurrentOperations(recops);
0495         IFOK(err) {
0496             if (!recops.isEmpty()) {
0497                 return recops.first().getID();
0498             }
0499         }
0500     }
0501 
0502     return 0;
0503 }
0504 
0505 SKGError SKGOperationObject::setRecurrentOperation(int recurrentId)
0506 {
0507     SKGError err;
0508     const auto currentRecurrentId = getRecurrentOperation();
0509     if (currentRecurrentId != 0 && recurrentId != 0 && currentRecurrentId == recurrentId) {
0510         err = SKGError(ERR_FAIL, i18nc("Error message", "Trying to set the same recurrent transaction with id=%1.", recurrentId));
0511         return err;
0512     } else if (isTemplate()) {
0513         err = SKGError(ERR_FAIL, i18nc("Error message", "Cannot set a recurrent for a template operation."));
0514         return err;
0515     }
0516 
0517     SKGRecurrentOperationObject oldRop(getDocument(), currentRecurrentId);
0518     SKGRecurrentOperationObject newRop(getDocument(), recurrentId);
0519     if (oldRop.exist()) {
0520         // Unlink from the current schedule
0521         SKGOperationObject oldRopParent;
0522         err = oldRop.getParentOperation(oldRopParent);
0523         if (oldRopParent == *this) {
0524             SKGObjectBase::SKGListSKGObjectBase transactions;
0525             IFOKDO(err, oldRop.getRecurredOperations(transactions))
0526             IFOK(err) {
0527                 if (!transactions.isEmpty()) {
0528                     SKGOperationObject lastObj(transactions.last());
0529                     IFOKDO(err, oldRop.setParentOperation(lastObj))
0530                     IFOKDO(err, oldRop.setDate(lastObj.getDate()))
0531                     IFOKDO(err, oldRop.setDate(oldRop.getNextDate()))
0532                     IFOKDO(err, oldRop.save())
0533                     IFOKDO(err, lastObj.setAttribute(QStringLiteral("r_recurrentoperation_id"), QString("0")))
0534                     IFOKDO(err, lastObj.save())
0535                 } else {
0536                     IFOKDO(err, oldRop.remove(true, true))
0537                 }
0538             }
0539         }
0540 
0541         IFOKDO(err, setAttribute(QStringLiteral("r_recurrentoperation_id"), QString("0")))
0542         IFOKDO(err, save())
0543     }
0544 
0545     if (newRop.exist()) {
0546         // Link to a new schedule
0547         const auto currentDate = getDate();
0548         QDate lastDate;
0549         SKGOperationObject newRopParent;
0550         err = newRop.getParentOperation(newRopParent);
0551         if (newRopParent.isTemplate()) {
0552             SKGObjectBase::SKGListSKGObjectBase transactions;
0553             IFOKDO(err, newRop.getRecurredOperations(transactions))
0554             IFOK(err) {
0555                 if (!transactions.isEmpty()) {
0556                     SKGOperationObject lastObj(transactions.last());
0557                     lastDate = lastObj.getDate();
0558                 }
0559             }
0560         } else {
0561             lastDate = newRopParent.getDate();
0562         }
0563         if (currentDate.isValid() && lastDate.isValid() && currentDate >= lastDate) {
0564             IFOK(err) {
0565                 if (!newRopParent.isTemplate()) {
0566                     // Set old parent op as recurrent
0567                     IFOKDO(err, newRopParent.setAttribute(QStringLiteral("r_recurrentoperation_id"), SKGServices::intToString(recurrentId)))
0568                     IFOKDO(err, newRopParent.save())
0569 
0570                     // Set this transaction as reference
0571                     IFOKDO(err, newRop.setParentOperation(*this))
0572                 } else {
0573                     // Set this op as recurrent
0574                     IFOKDO(err, setAttribute(QStringLiteral("r_recurrentoperation_id"), SKGServices::intToString(recurrentId)))
0575                     IFOKDO(err, save())
0576                 }
0577 
0578                 IFOKDO(err, newRop.setDate(currentDate))
0579                 IFOKDO(err, newRop.setDate(newRop.getNextDate()))
0580                 IFOKDO(err, newRop.save())
0581             }
0582         } else {
0583             IFOKDO(err, setAttribute(QStringLiteral("r_recurrentoperation_id"), SKGServices::intToString(recurrentId)))
0584             IFOKDO(err, save())
0585         }
0586     }
0587 
0588     return err;
0589 }
0590 
0591 SKGError SKGOperationObject::mergeAttribute(const SKGOperationObject& iDeletedOne, SKGOperationObject::AmountAlignmentMode iMode, bool iSendMessage)
0592 {
0593     // Merge operation
0594     SKGError err = setDate(iDeletedOne.getDate());
0595     IFOKDO(err, setImportID(iDeletedOne.getImportID()))
0596     IFOKDO(err, setAttribute(QStringLiteral("t_imported"), iDeletedOne.getAttribute(QStringLiteral("t_imported"))))
0597     if (!err && getComment().isEmpty()) {
0598         err = setComment(iDeletedOne.getComment());
0599     }
0600     SKGPayeeObject payee;
0601     getPayee(payee);
0602     if (!err && !payee.exist()) {
0603         iDeletedOne.getPayee(payee);
0604         err = setPayee(payee);
0605     }
0606     if (!err && getMode().isEmpty()) {
0607         err = setMode(iDeletedOne.getMode());
0608     }
0609     if (!err && !isBookmarked()) {
0610         err = bookmark(iDeletedOne.isBookmarked());
0611     }
0612     if (!err && getNumber().isEmpty()) {
0613         err = setNumber(iDeletedOne.getNumber());
0614     }
0615     IFOKDO(err, save())
0616 
0617     // Merge subtransactions
0618     double currentAmount = getCurrentAmount();
0619     double targettAmount = iDeletedOne.getCurrentAmount();
0620     if (qAbs(currentAmount - targettAmount) > 0.0001) {
0621         SKGObjectBase::SKGListSKGObjectBase subOps1;
0622         IFOKDO(err, getSubOperations(subOps1))
0623 
0624         SKGObjectBase::SKGListSKGObjectBase subOps2;
0625         IFOKDO(err, iDeletedOne.getSubOperations(subOps2))
0626 
0627         // Align amounts
0628         SKGOperationObject::AmountAlignmentMode mode = iMode;
0629         if (mode == DEFAULT) {
0630             if (subOps2.count() == 1 && subOps1.count() == 1) {
0631                 mode = PROPORTIONAL;
0632             } else if (subOps2.count() >= 1 && subOps1.count() >= 1) {
0633                 mode = ADDSUBOPERATION;
0634             }
0635         }
0636 
0637         if (mode == SKGOperationObject::ADDSUBOPERATION) {
0638             // Add sub transaction to align amount
0639             SKGSubOperationObject so1;
0640             IFOKDO(err, addSubOperation(so1))
0641             IFOKDO(err, so1.setQuantity(targettAmount - currentAmount))
0642             IFOKDO(err, so1.save())
0643         } else {
0644             // Keep ratio
0645             for (const auto& sopbase : qAsConst(subOps1)) {
0646                 SKGSubOperationObject sop(sopbase);
0647                 IFOKDO(err, sop.setQuantity(targettAmount * sop.getQuantity() / currentAmount))
0648                 IFOKDO(err, sop.save())
0649             }
0650         }
0651         IFOKDO(err, load())
0652         if (iSendMessage) {
0653             IFOK(err) getDocument()->sendMessage(i18nc("An information message",  "Amount has been changed to be aligned with the imported transaction"), SKGDocument::Positive);
0654         }
0655     }
0656 
0657     // transfers properties
0658     IFOKDO(err, getDocument()->executeSqliteOrder(QStringLiteral("DELETE FROM parameters WHERE t_uuid_parent='") % getUniqueID() % QStringLiteral("' AND t_name IN (SELECT t_name FROM parameters WHERE t_uuid_parent='") % iDeletedOne.getUniqueID() % QStringLiteral("')")))
0659     IFOKDO(err, getDocument()->executeSqliteOrder(QStringLiteral("UPDATE parameters SET t_uuid_parent='") % getUniqueID() % QStringLiteral("' WHERE t_uuid_parent='") % iDeletedOne.getUniqueID() % QStringLiteral("'")))
0660 
0661     // Delete useless operation
0662     IFOKDO(err, iDeletedOne.remove(false, true))
0663     return err;
0664 }
0665 
0666 SKGError SKGOperationObject::mergeSuboperations(const SKGOperationObject& iDeletedOne)
0667 {
0668     SKGError err;
0669     SKGObjectBase::SKGListSKGObjectBase subops;
0670     err = iDeletedOne.getSubOperations(subops);
0671     int nb = subops.count();
0672     for (int i = 0; !err && i < nb; ++i) {
0673         SKGSubOperationObject subop(subops.at(i));
0674         err = subop.setParentOperation(*this);
0675         IFOKDO(err, subop.save())
0676     }
0677     IFOKDO(err, iDeletedOne.remove(false))
0678     return err;
0679 }
0680 
0681 
0682