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.
566 lines
16 KiB
C++
566 lines
16 KiB
C++
#include "shopservice.h"
|
|
#include "numberseriesservice.h"
|
|
#include "isellableservice.h"
|
|
|
|
#include "settings/shopsettings.h"
|
|
|
|
#ifdef EET
|
|
#include <eetcpp.h>
|
|
#endif
|
|
#include <QEventLoop>
|
|
#include <seasonservice.h>
|
|
#include <cmath>
|
|
|
|
#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> 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());
|
|
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();
|
|
|
|
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::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<IService>() : nullptr);
|
|
ISellableService *selSrv = dynamic_cast<ISellableService*>(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<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 {};
|
|
}
|
|
|
|
void ShopService::moveItems(QList<VoucherItemPtr> 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);
|
|
}
|
|
|
|
calculate(target);
|
|
update(target, &session);
|
|
|
|
if (source->items().isEmpty())
|
|
{
|
|
erase(source, &session);
|
|
}
|
|
else
|
|
{
|
|
calculate(source);
|
|
update(source, &session);
|
|
}
|
|
}
|
|
|
|
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<IShopItemPtr> ShopService::allSellableItems(const QString& category/* = ""*/)
|
|
{
|
|
QList<QSharedPointer<IShopItem> > items;
|
|
foreach (IPlugin *plugin, Context::instance().plugins()) {
|
|
IService *srv = plugin->service<IService>();
|
|
ISellableService *selSrv = dynamic_cast<ISellableService*>(srv);
|
|
|
|
if (selSrv != nullptr)
|
|
{
|
|
items.append(selSrv->shopItems(category));
|
|
}
|
|
}
|
|
|
|
return items;
|
|
}
|
|
|
|
VoucherSum ShopService::unpaidSummary()
|
|
{
|
|
return summary("status = " + QString::number(Voucher::NOT_PAID));
|
|
}
|
|
|
|
VoucherSum ShopService::unsendEET()
|
|
{
|
|
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));
|
|
|
|
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::save(VoucherPtr entity, qx::QxSession* pSession/* = nullptr*/)
|
|
{
|
|
SeasonService seasonSrv;
|
|
SeasonPtr season = seasonSrv.active();
|
|
entity->setSeason(season);
|
|
|
|
addDateAndUser(entity, true);
|
|
Service::save(entity, pSession);
|
|
}
|
|
|
|
QMap<QString, QString> ShopService::allCategories() {
|
|
QMap<QString, QString> 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<qx::QxSession> ptrSession;
|
|
|
|
if (pSession == nullptr) {
|
|
ptrSession.reset(new qx::QxSession());
|
|
pSession = ptrSession.data();
|
|
}
|
|
|
|
Service::update(entity, pSession);
|
|
auto oldItems = entity->items();
|
|
|
|
Service<VoucherItem> 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);
|
|
}
|
|
}
|
|
|
|
VoucherSum ShopService::salesSummary(QDate date) {
|
|
return summary("payDateTime >= '" + date.toString("yyyy-MM-dd") + "'");
|
|
}
|
|
|
|
VoucherSum ShopService::summary(const QString& where) {
|
|
auto sums = all(where);
|
|
VoucherSum sum(sums.count(), 0);
|
|
|
|
for (const auto& item : sums) {
|
|
sum.addPrice(item->totalPrice());
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|