#include "shopservice.h" #include "numberseriesservice.h" #include "isellableservice.h" #include "settings/shopsettings.h" #ifdef EET #include #endif #include #include #include #ifdef _WIN32 inline double round(double value) { return value < 0 ? -std::floor(0.5 - value) : std::floor(0.5 + value); } #endif VoucherPtr ShopService::createVoucher() { QSharedPointer voucher(new Voucher); loadSettings(); voucher->setVatRateHigh(m_gs->vatHigh()); voucher->setVatRateFirstLower(m_gs->vatFirstLower()); voucher->setVatRateSecondLower(m_gs->vatSecondLower()); voucher->setStatus(Voucher::NEW); return voucher; } void ShopService::addShopItem(VoucherPtr voucher, QSharedPointer item, int count) { QSharedPointer vItem(new VoucherItem); vItem->setName(item->name()); vItem->setUnitPrice(item->unitPrice()); vItem->setCount(count); vItem->setRefId(item->id()); vItem->setItemPlugin(item->pluginId()); vItem->setVatType(item->vatType()); vItem->setInsertDate(QDateTime::currentDateTime()); vItem->setVoucher(voucher); voucher->addItem(vItem); updateRelatedItem(vItem.data(), count); } void ShopService::calculate(VoucherPtr voucher) { QDecDouble total; voucher->setPriceNoVat(0); voucher->setPriceVatHigh(0); voucher->setPriceVatFirstLower(0); voucher->setPriceVatSecondLower(0); voucher->setTotalPriceVatHigh(0); voucher->setTotalPriceVatFirstLower(0); voucher->setTotalPriceVatSecondLower(0); loadSettings(); if (voucher->items().isEmpty()) { load(voucher); } foreach (QSharedPointer item, voucher->items()) { if (item->refId() == ROUNDING_ITEM) { voucher->removeItem(item); continue; } calculateItem(item); QDecDouble itemPrice; if (item->vatType() == Enums::NONE) { voucher->setPriceNoVat(voucher->priceNoVat() + item->priceWitouthVat()); } if (m_gs->pricesWithVAT()) { itemPrice = item->price(); switch (item->vatType()) { case Enums::HIGH: voucher->setTotalPriceVatHigh(voucher->totalPriceVatHigh() + itemPrice); break; case Enums::FIRST_LOWER: voucher->setTotalPriceVatFirstLower(voucher->totalPriceVatFirstLower() + itemPrice); break; case Enums::SECOND_LOWER: voucher->setTotalPriceVatSecondLower(voucher->totalPriceVatSecondLower() + itemPrice); break; default: break; } } else { itemPrice = item->priceWitouthVat(); switch (item->vatType()) { case Enums::HIGH: voucher->setPriceVatHigh(voucher->priceVatHigh() + itemPrice); break; case Enums::FIRST_LOWER: voucher->setPriceVatFirstLower(voucher->priceVatFirstLower() + itemPrice); break; case Enums::SECOND_LOWER: voucher->setPriceVatSecondLower(voucher->priceVatSecondLower() + itemPrice); break; default: break; } } total += item->price(); } if (m_gs->pricesWithVAT()) { voucher->setPriceVatHigh(excludeVat(voucher->totalPriceVatHigh(), Enums::HIGH)); voucher->setPriceVatFirstLower(excludeVat(voucher->totalPriceVatFirstLower(), Enums::FIRST_LOWER)); voucher->setPriceVatSecondLower(excludeVat(voucher->totalPriceVatSecondLower(), Enums::SECOND_LOWER)); } else { voucher->setTotalPriceVatHigh(includeVat(voucher->priceVatHigh(), Enums::HIGH)); voucher->setTotalPriceVatFirstLower(includeVat(voucher->priceVatFirstLower(), Enums::FIRST_LOWER)); voucher->setTotalPriceVatSecondLower(includeVat(voucher->priceVatSecondLower(), Enums::SECOND_LOWER)); } voucher->setTotalPrice(total); roundVoucher(voucher); } void ShopService::calculateItem(VoucherItemPtr item) { loadSettings(); if (m_gs->vatPayer()) { item->setVatRate(vatRate(item->vatType())); if (m_gs->pricesWithVAT()) { item->setPrice(item->unitPrice() * item->count()); item->setPriceWitouthVat(excludeVat(item->price(), item->vatType())); } else { item->setPriceWitouthVat(item->unitPrice() * item->count()); item->setPrice(includeVat(item->priceWitouthVat(), item->vatType())); } } else { item->setPrice(item->unitPrice() * item->count()); item->setPriceWitouthVat(item->price()); } } void ShopService::pay(VoucherPtr voucher) { NumberSeriesService srvNs; qx::QxSession session; voucher->setNumSer(srvNs.nextStrForPlugin("SHOP", &session)); voucher->setStatus(Voucher::PAID); voucher->setEetStatus(Voucher::EET_FOR_SEND); voucher->setPayDateTime(QDateTime::currentDateTime()); this->update(voucher, &session); if (!session.isValid()) { qDebug() << session.firstError().text(); } } void ShopService::updateRelatedItem(VoucherItem* item, int countAdded) { IPlugin *plugin = Context::instance().plugin(item->itemPlugin()); IService *srv = (plugin != nullptr ? plugin->service() : nullptr); ISellableService *selSrv = dynamic_cast(srv); if (selSrv != nullptr) { selSrv->addedToVoucher(item->refId(), countAdded); } } bool ShopService::processEet(VoucherPtr voucher, QString &message) { #ifdef EET if (voucher->eetStatus() == Voucher::EET_NOT_ENTERING) { return true; } SettingsService srvSettings("SHOP"); ShopSettingsPtr settings = srvSettings.loadSettings(); loadSettings(); EetRequest request; request.setCelkTrzba(voucher->totalPrice().toDouble()); request.setDatTrzby(voucher->payDateTime()); request.setIdPokl(settings->eetRegisterId()); request.setIdProvoz(settings->eetShopId()); request.setPrvniZaslani(voucher->eetStatus() == Voucher::EET_FOR_SEND); request.setDicPopl(m_gs->dic()); request.setPoradCis(voucher->numSer()); request.setDatOdesl(QDateTime::currentDateTime()); request.setRezim((EetRequest::EetRezim)settings->eetMode()); if (m_gs->vatPayer()) { request.setDan1(voucher->vatAmountHigh().toDouble()); request.setDan2(voucher->vatAmountFirstLower().toDouble()); request.setDan3(voucher->VatAmountSecondLower().toDouble()); request.setZaklDan1(voucher->priceVatHigh().toDouble()); request.setZaklDan2(voucher->priceVatFirstLower().toDouble()); request.setZaklDan3(voucher->priceVatSecondLower().toDouble()); request.setZaklNepodlDph(voucher->priceNoVat().toDouble()); } EetSender *sender = new EetSender(this); sender->setupSigner(settings->eetCertificate(), settings->eetKeyPassword()); sender->setPlayground(settings->eetPlayground()); QEventLoop loop; bool replyFinished = false; connect(sender, &EetSender::sendFinished, [this, voucher, sender, &loop, &replyFinished](){ Transaction tx; voucher->setEetBkp(sender->resut()->bkp()); voucher->setEetPkp(sender->resut()->pkp()); voucher->setEetFik(sender->resut()->fik()); if (sender->resut()->status() == EetResult::RESPONSE_OK) { voucher->setEetSendDateTime(QDateTime::currentDateTime()); voucher->setEetStatus(Voucher::EET_SENT); } else { voucher->setEetStatus(Voucher::EET_ERROR); } this->update(voucher); tx.commit(); sender->deleteLater(); loop.quit(); replyFinished = true; }); sender->sendRequest(&request); if (!replyFinished) { loop.exec(); } auto addMessage = [&message](EetMessage *msg, const QString &label){ if (message.isEmpty()) { message = label + "\n"; } message += QString::number(msg->code()) + ": " + msg->message(); }; foreach (EetMessage *msg, sender->resut()->errors()) { addMessage(msg, "Errors:"); } foreach (EetMessage *msg, sender->resut()->warnings()) { addMessage(msg, "Warnings:"); } return voucher->eetStatus() == Voucher::EET_SENT; #else return true; #endif } void ShopService::setEetOnline(bool online) { #ifdef EET EetSender::m_online = online; #endif } bool ShopService::isEetOnline() { #ifdef EET return EetSender::m_online; #else return false; #endif } bool ShopService::isEetEnabled() { SettingsService srvSettings("SHOP"); ShopSettingsPtr settings = srvSettings.loadSettings(); return settings->eetActive(); } void ShopService::roundVoucher(VoucherPtr voucher) { SettingsService setSrv("SHOP"); ShopSettingsPtr settings = setSrv.loadSettings(); QDecDouble totalPrice(voucher->totalPrice()); switch (settings->rounding()) { case Enums::R_UP: totalPrice = QDecDouble(ceil(voucher->totalPrice().toDouble() * pow(10, settings->decimalPlaces())) / pow(10, settings->decimalPlaces())); break; case Enums::R_DOWN: totalPrice = QDecDouble(floor(voucher->totalPrice().toDouble() * pow(10, settings->decimalPlaces())) / pow(10, settings->decimalPlaces())); break; case Enums::R_MATH: totalPrice = QDecDouble(round(voucher->totalPrice().toDouble() * pow(10, settings->decimalPlaces())) / pow(10, settings->decimalPlaces())); break; case Enums::R_NONE: break; } VoucherItemPtr roundingItem(new VoucherItem); roundingItem->setCount(1); roundingItem->setUnitPrice(totalPrice - voucher->totalPrice()); roundingItem->setVatType(Enums::NONE); roundingItem->setName(settings->roundingItem()); roundingItem->setRefId(ROUNDING_ITEM); roundingItem->setVoucher(voucher); calculateItem(roundingItem); if (roundingItem->price() != QDecDouble(0)) { voucher->addItem(roundingItem); voucher->setPriceNoVat(voucher->priceNoVat() + roundingItem->price()); voucher->setTotalPrice(totalPrice); } } VoucherItemPtr ShopService::roundingItem(VoucherPtr voucher) { foreach (VoucherItemPtr item, voucher->items()) { if (item->refId() == ROUNDING_ITEM) { return item; } } return {}; } void ShopService::moveItems(QList items, VoucherPtr source, VoucherPtr target) { if (source->items().isEmpty()) { load(source); } if (target->items().isEmpty()) { load(target); } qx::QxSession session; if (target->status() == Voucher::NEW && target->id() == 0) { this->save(target, &session); } for (const auto& it : items) { target->addItem(it); source->removeItem(it); it->setVoucher(target); } if (source->items().isEmpty()) { erase(source, &session); } else { calculate(source); update(source, &session); } } QList ShopService::savedVouchers() { return all(QString("status = %1").arg(QString::number(Voucher::NOT_PAID))); } QList ShopService::tempVouchers() { return all(QString("status = %1").arg(QString::number(Voucher::TEMPORARY))); } QList ShopService::paiedVouchers() { SeasonService seasonSrv; SeasonPtr season = seasonSrv.active(); return all(QString("status = %1 AND season = %2").arg(QString::number(Voucher::PAID), QString::number(season->id()))); } QList ShopService::vouchersForEet() { return all(QString("status = %1 AND eetStatus <> %2 AND eetStatus <> %3") .arg(QString::number(Voucher::PAID), QString::number(Voucher::EET_SENT), QString::number(Voucher::EET_NOT_ENTERING))); } QList ShopService::allSellableItems(const QString& category/* = ""*/) { QList > items; foreach (IPlugin *plugin, Context::instance().plugins()) { IService *srv = plugin->service(); ISellableService *selSrv = dynamic_cast(srv); if (selSrv != nullptr) { items.append(selSrv->shopItems(category)); } } return items; } VoucherSum ShopService::unpaidSummary() { VoucherSum sum; //= db->query_value("status = " + query::_ref((int)Voucher::NOT_PAID)); return sum; } VoucherSum ShopService::unsendEET() { VoucherSum sum; //= db->query_value("(eetStatus = " + query::_ref((int)Voucher::EET_FOR_SEND) //+ " OR eetStatus = " + query::_ref((int)Voucher::EET_ERROR) //+ ") AND status = " + query::_ref((int)Voucher::PAID)); return sum; } QDecDouble ShopService::includeVat(QDecDouble price, Enums::VatType vatType) { return price * ((vatRate(vatType) / 100) + QDecDouble(1)); } QDecDouble ShopService::excludeVat(QDecDouble price, Enums::VatType vatType) { return price / ((vatRate(vatType) / 100) + QDecDouble(1)); } void ShopService::loadSettings() { if (m_gs.isNull()) { SettingsService settings("CORE"); m_gs = settings.loadSettings(); } } QDecDouble ShopService::vatRate(Enums::VatType vatType) { QDecDouble vatRate; loadSettings(); switch (vatType) { case Enums::NONE: vatRate = 0; break; case Enums::HIGH: vatRate = m_gs->vatHigh(); break; case Enums::FIRST_LOWER: vatRate = m_gs->vatFirstLower(); break; case Enums::SECOND_LOWER: vatRate = m_gs->vatSecondLower(); break; default: break; } return vatRate; } void ShopService::save(VoucherPtr entity, qx::QxSession* pSession/* = nullptr*/) { SeasonService seasonSrv; SeasonPtr season = seasonSrv.active(); entity->setSeason(season); addDateAndUser(entity, true); Service::save(entity, pSession); } QMap ShopService::allCategories() { QMap ret; for (const auto& item : allSellableItems()) { if (!item->category().isEmpty() && !ret.contains(item->category())) { ret[item->category()] = item->color(); } } return ret; } void ShopService::update(VoucherPtr entity, qx::QxSession* pSession) { bool reloadItems = pSession == nullptr; { QScopedPointer ptrSession; if (pSession == nullptr) { ptrSession.reset(new qx::QxSession()); pSession = ptrSession.data(); } Service::update(entity, pSession); auto oldItems = entity->items(); Service srvItem; auto items = srvItem.all("voucher = " + QString::number(entity->id())); for (auto item : items) { auto newItem = std::find_if(oldItems.begin(), oldItems.end(), [item](auto it){ return item->id() == it->id(); }); if (newItem == oldItems.end()) { qx::dao::delete_by_id(item, pSession->database()); } } } if (reloadItems) { entity->clearItems(); load(entity); } }