You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
prodejna/shop/shopservice.cpp

559 lines
15 KiB
C++

#include "shopservice.h"
#include "numberseriesservice.h"
#include "isellableservice.h"
#include "shop-odb.hxx"
#include "settings/shopsettings.h"
#include <eetcpp.h>
#include <QEventLoop>
#include <seasonservice.h>
#include <math.h>
#ifdef _WIN32
inline double round(double value) { return value < 0 ? -std::floor(0.5 - value) : std::floor(0.5 + value); }
#endif
ShopService::ShopService()
{
}
VoucherPtr ShopService::createVoucher()
{
QSharedPointer<Voucher> 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<IShopItem> item, int count)
{
QSharedPointer<VoucherItem> 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());
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();
foreach (QSharedPointer<VoucherItem> 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::loadItems(VoucherPtr voucher)
{
Service<VoucherItem> srv;
voucher->setItems(srv.all(QString("voucher = %1").arg(voucher->id())));
}
void ShopService::pay(VoucherPtr voucher)
{
Transaction tx;
NumberSeriesService srvNs;
voucher->setNumSer(srvNs.nextStrForPlugin("SHOP"));
voucher->setStatus(Voucher::PAID);
voucher->setEetStatus(Voucher::EET_FOR_SEND);
voucher->setPayDateTime(QDateTime::currentDateTime());
this->update(voucher);
tx.commit();
}
void ShopService::updateRelatedItem(VoucherItem* item, int countAdded)
{
IPlugin *plugin = Context::instance().plugin(item->itemPlugin());
IService *srv = (plugin != NULL ? plugin->service<IService>() : NULL);
ISellableService *selSrv = dynamic_cast<ISellableService*>(srv);
if (selSrv != NULL)
{
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<ShopSettings>();
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<ShopSettings>();
return settings->eetActive();
}
void ShopService::roundVoucher(VoucherPtr voucher)
{
SettingsService setSrv("SHOP");
ShopSettingsPtr settings = setSrv.loadSettings<ShopSettings>();
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 VoucherItemPtr();
}
void ShopService::moveItems(QList<VoucherItemPtr> items, VoucherPtr source, VoucherPtr target)
{
Transaction tx;
if (target->status() == Voucher::NEW && target->id() == 0)
{
this->saveVoucher(target);
}
odb::database *db = Context::instance().db();
foreach (VoucherItemPtr item, items) {
QString sql = QString("update VoucherItem set voucher = %1 where id = %2").arg(QString::number(target->id()), QString::number(item->id()));
db->execute(sql.toStdString());
}
loadItems(source);
loadItems(target);
if (source->items().isEmpty())
{
erase(source);
}
else
{
calculate(source);
update(source);
}
tx.commit();
}
QList<VoucherPtr> ShopService::savedVouchers()
{
return all(QString("status = %1").arg(QString::number(Voucher::NOT_PAID)));
}
QList<VoucherPtr> ShopService::tempVouchers()
{
return all(QString("status = %1").arg(QString::number(Voucher::TEMPORARY)));
}
QList<VoucherPtr> 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<VoucherPtr> 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<ShopItemPtr> ShopService::allSellableItems()
{
QList<QSharedPointer<ShopItem> > items;
foreach (IPlugin *plugin, Context::instance().plugins()) {
IService *srv = plugin->service<IService>();
ISellableService *selSrv = dynamic_cast<ISellableService*>(srv);
if (selSrv != NULL)
{
items.append(selSrv->shopItems());
}
}
return items;
}
VoucherSum ShopService::unpaidSummary()
{
Transaction tr;
odb::database *db = Context::instance().db();
typedef odb::query<VoucherSum> query;
VoucherSum sum = db->query_value<VoucherSum>("status = " + query::_ref((int)Voucher::NOT_PAID));
tr.commit();
return sum;
}
VoucherSum ShopService::unsendEET()
{
Transaction tr;
odb::database *db = Context::instance().db();
typedef odb::query<VoucherSum> query;
VoucherSum sum = db->query_value<VoucherSum>("(eetStatus = " + query::_ref((int)Voucher::EET_FOR_SEND)
+ " OR eetStatus = " + query::_ref((int)Voucher::EET_ERROR)
+ ") AND status = " + query::_ref((int)Voucher::PAID));
tr.commit();
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<GlobalSettings>();
}
}
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::saveVoucher(VoucherPtr entity)
{
SeasonService seasonSrv;
SeasonPtr season = seasonSrv.active();
entity->setSeason(season);
Transaction tr;
odb::database *db = Context::instance().db();
addDateAndUser(entity, true);
db->persist(entity);
foreach (QSharedPointer<VoucherItem> item, entity->items()) {
item->setVoucher(entity.toWeakRef());
db->persist(item);
}
tr.commit();
}
void ShopService::updateVoucher(VoucherPtr entity)
{
Transaction tr;
odb::database *db = Context::instance().db();
db->execute(QString("DELETE FROM VoucherItem WHERE voucher = %1").arg(entity->id()).toStdString());
foreach (QSharedPointer<VoucherItem> item, entity->items()) {
item->setVoucher(entity.toWeakRef());
db->persist(item);
}
addDateAndUser(entity, false);
db->update(entity);
tr.commit();
}
void ShopService::eraseVoucher(VoucherPtr entity)
{
Transaction tr;
odb::database *db = Context::instance().db();
db->execute(QString("DELETE FROM VoucherItem WHERE voucher = %1").arg(entity->id()).toStdString());
db->erase(entity);
tr.commit();
}