397 lines
18 KiB
PHP
397 lines
18 KiB
PHP
<?php
|
|
|
|
namespace KupShop\POSBundle\Util;
|
|
|
|
use KupShop\CatalogBundle\ProductList\ProductCollection;
|
|
use KupShop\GraphQLBundle\ApiPos\Types\Collection\PosSerialNumberCollection;
|
|
use KupShop\GraphQLBundle\ApiPos\Types\Order\PosInvalidDiscountCoupon;
|
|
use KupShop\GraphQLBundle\ApiPos\Types\Purchase\PosPurchaseCharge;
|
|
use KupShop\GraphQLBundle\ApiPos\Types\Purchase\PosPurchaseDiscount;
|
|
use KupShop\GraphQLBundle\ApiPos\Types\Purchase\PosPurchaseItem;
|
|
use KupShop\GraphQLBundle\ApiPos\Types\Purchase\PosSerialNumber;
|
|
use KupShop\KupShopBundle\Context\CountryContext;
|
|
use KupShop\KupShopBundle\Context\CurrencyContext;
|
|
use KupShop\KupShopBundle\Context\LanguageContext;
|
|
use KupShop\KupShopBundle\Context\UserContext;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use KupShop\KupShopBundle\Util\Database\QueryHint;
|
|
use KupShop\KupShopBundle\Util\Price\Price;
|
|
use KupShop\OrderDiscountBundle\OrderDiscountLocator;
|
|
use KupShop\OrderDiscountBundle\Util\DiscountManager;
|
|
use KupShop\OrderingBundle\Entity\Purchase\ChargePurchaseItem;
|
|
use KupShop\OrderingBundle\Entity\Purchase\PurchaseState;
|
|
use KupShop\OrderingBundle\Entity\Purchase\TextPurchaseItem;
|
|
use KupShop\OrderingBundle\Util\Order\OrderInfo;
|
|
use KupShop\OrderingBundle\Util\Purchase\PurchaseUtil;
|
|
use KupShop\WarehouseBundle\Util\StoreItemWorker;
|
|
use Query\Operator;
|
|
|
|
class PosPurchaseStateUtil
|
|
{
|
|
public function __construct(
|
|
// Contexts
|
|
protected UserContext $userContext,
|
|
protected CurrencyContext $currencyContext,
|
|
protected LanguageContext $languageContext,
|
|
protected OrderDiscountLocator $orderDiscountLocator,
|
|
|
|
// Utils
|
|
protected DiscountManager $discountManager,
|
|
protected PosOrderDiscounts $posOrderDiscounts,
|
|
protected PurchaseUtil $purchaseUtil,
|
|
protected ?PosStoreUtil $posStoreUtil = null,
|
|
protected ?PosWarehouseUtil $posWarehouseUtil = null,
|
|
protected ?StoreItemWorker $storeItemWorker = null,
|
|
) {
|
|
}
|
|
|
|
public function createStateFromApi(
|
|
$purchaseItems,
|
|
?array $couponItems,
|
|
$user = null,
|
|
$methodPayment = null,
|
|
$paidPrice = null,
|
|
$bonusPoints = null,
|
|
$isOrderNew = false,
|
|
): PurchaseState {
|
|
if ($user && $user->getId() ?? false) {
|
|
$userDB = \User::createFromId($user->getId());
|
|
$userDB->activateUser();
|
|
$this->userContext->activate($userDB);
|
|
}
|
|
|
|
$purchaseState = new PurchaseState();
|
|
$purchaseState->setSource(OrderInfo::ORDER_SOURCE_POS);
|
|
|
|
foreach ($purchaseItems as $key => $inputItem) {
|
|
$rawItem = $inputItem->item->getItem();
|
|
$isProduct = $rawItem['idProduct'] ?? false;
|
|
$isCharge = $rawItem['idCharge'] ?? false;
|
|
|
|
if ($isProduct) {
|
|
$price = null;
|
|
if (!empty($rawItem['idOrderItem'])) {
|
|
$price = new Price(
|
|
toDecimal($rawItem['priceWithoutVat'])->mul(toDecimal($rawItem['pieces'])),
|
|
$this->currencyContext->getActive(),
|
|
$rawItem['vat']
|
|
);
|
|
}
|
|
|
|
$newProductItem = $this->purchaseUtil->createProductPurchaseItem(
|
|
$rawItem['idProduct'],
|
|
$rawItem['idVariation'],
|
|
$rawItem['pieces'],
|
|
$price,
|
|
['uuid' => $rawItem['uuid'] ?? null]
|
|
);
|
|
|
|
if ($rawItem['discount'] ?? false) {
|
|
$this->purchaseUtil->applyDiscountToPurchaseItem($newProductItem, $rawItem['discount']->getDiscount());
|
|
}
|
|
|
|
if (findModule(\Modules::PRODUCTS_SERIAL_NUMBERS) && !empty($rawItem['serialNumbers'] ?? []) && $isOrderNew) {
|
|
$availableSerialNumbers = sqlQueryBuilder()
|
|
->select('serial_number, id')
|
|
->from('products_serial_numbers', 'psn')
|
|
->andWhere(Operator::inStringArray($rawItem['serialNumbers'], 'psn.serial_number'))
|
|
->andWhere('id_order_item IS NULL')
|
|
->execute()
|
|
->fetchAllAssociativeIndexed();
|
|
|
|
$checkedNumbers = [];
|
|
foreach ($rawItem['serialNumbers'] as $sNumber) {
|
|
$checkedNumbers[$sNumber] = new PosSerialNumber($sNumber, array_key_exists($sNumber, $availableSerialNumbers));
|
|
}
|
|
$newProductItem->addNote('serial_numbers', $checkedNumbers);
|
|
}
|
|
|
|
$newProductItem->setId($rawItem['idOrderItem'] ?? $key);
|
|
|
|
$purchaseState->addProduct($newProductItem);
|
|
} elseif ($isCharge) {
|
|
$newChargeItem = new ChargePurchaseItem(
|
|
name: $rawItem['title'],
|
|
price: new Price(toDecimal($rawItem['priceWithoutVat'])->mul(toDecimal($rawItem['pieces'])),
|
|
$this->currencyContext->getActive(),
|
|
$rawItem['vat']
|
|
),
|
|
id_charge: $rawItem['idCharge'],
|
|
note: ['uuid' => $rawItem['uuid'] ?? null]
|
|
);
|
|
|
|
$newChargeItem->setId($rawItem['idOrderItem'] ?? $key);
|
|
|
|
$purchaseState->addCharge($newChargeItem);
|
|
} else {
|
|
$newTextItem = new TextPurchaseItem(
|
|
$rawItem['title'],
|
|
$rawItem['pieces'],
|
|
new Price(toDecimal($rawItem['priceWithoutVat'])->mul(toDecimal($rawItem['pieces'])),
|
|
$this->currencyContext->getActive(),
|
|
$rawItem['vat']
|
|
),
|
|
$rawItem['vat'],
|
|
['uuid' => $rawItem['uuid'] ?? null]
|
|
);
|
|
|
|
if ($rawItem['discount'] ?? false) {
|
|
$this->purchaseUtil->applyDiscountToPurchaseItem($newTextItem, $rawItem['discount']->getDiscount());
|
|
}
|
|
|
|
$newTextItem->setId($rawItem['idOrderItem'] ?? $key);
|
|
|
|
$purchaseState->addProduct($newTextItem);
|
|
}
|
|
}
|
|
|
|
$filterForDiscounts = [];
|
|
$purchaseState = $this->purchaseUtil->recalculateTotalPrices($purchaseState);
|
|
|
|
$order_discounts = [];
|
|
// Nenačítaly se kupony v pokladně
|
|
foreach ($couponItems ?? [] as $item) {
|
|
$purchaseState->addCoupon($item->getCode());
|
|
$this->discountManager->couponNumberExists($item->getCode(), $order_discounts);
|
|
$filterForDiscounts = array_merge($filterForDiscounts, array_keys($order_discounts));
|
|
}
|
|
|
|
$this->discountManager->setPurchaseState($purchaseState);
|
|
|
|
$this->posOrderDiscounts->applyBonusDataToPurchaseState(
|
|
purchaseState: $purchaseState,
|
|
filter: $filterForDiscounts,
|
|
bonusPoints: $bonusPoints,
|
|
);
|
|
|
|
if ($discounts = $this->discountManager->getDiscountForCheck()) {
|
|
// Při editaci uplatním pouze slevy pro kupony a bonusové body
|
|
if (!$isOrderNew) {
|
|
$discounts = array_intersect_key($discounts, array_flip($filterForDiscounts));
|
|
}
|
|
$purchaseState = $this->discountManager->recalculate($discounts);
|
|
if ($handlers = $this->discountManager->getActionHandlers()) {
|
|
$purchaseState->setDiscountHandlers($handlers);
|
|
}
|
|
$this->purchaseUtil->ensureTotalIsNotNegative($purchaseState);
|
|
}
|
|
|
|
$data['order'] = [
|
|
'order_no' => '_'.rand(0, 999999),
|
|
'status' => 0,
|
|
'date_created' => date('Y-m-d H:i:s'),
|
|
'date_updated' => date('Y-m-d H:i:s'),
|
|
'status_payed' => 0,
|
|
'status_dispatch' => 0,
|
|
'status_storno' => 0,
|
|
];
|
|
if (isset($userDB)) {
|
|
$data['order'] = array_merge($data['order'], $this->getUserDBArray($userDB));
|
|
} elseif ($isOrderNew) {
|
|
$countryContext = Contexts::get(CountryContext::class);
|
|
$data['order'] = array_merge($data['order'], $this->getUserDBArray(), [
|
|
'invoice_name' => 'Maloobchodní prodej',
|
|
'invoice_country' => $countryContext->getActiveId() ?? '',
|
|
'delivery_country' => $countryContext->getActiveId() ?? '',
|
|
]);
|
|
}
|
|
|
|
if (findModule(\Modules::CURRENCIES)) {
|
|
$data['order'] = array_merge($data['order'], [
|
|
'id_language' => $this->languageContext->getActiveId(),
|
|
'currency' => $this->currencyContext->getActiveId(),
|
|
'currency_rate' => $this->currencyContext->getActive()->getRate()->asString(),
|
|
]);
|
|
}
|
|
if ($methodPayment) {
|
|
$data['payment'] = true;
|
|
$data['methodPayment'] = $methodPayment;
|
|
$data['paidPrice'] = $paidPrice;
|
|
$data['order']['delivery_complete'] = 0;
|
|
}
|
|
$purchaseState->setCustomData($data);
|
|
|
|
return $this->purchaseUtil->recalculateTotalPrices($purchaseState);
|
|
}
|
|
|
|
private function getUserDBArray($userDB = null): array
|
|
{
|
|
return [
|
|
'id_user' => $userDB->id ?? null,
|
|
'invoice_name' => $userDB['name'] ?? 'Maloobchodní prodej',
|
|
'invoice_surname' => $userDB['surname'] ?? '',
|
|
'invoice_firm' => $userDB['firm'] ?? '',
|
|
'invoice_ico' => $userDB['ico'] ?? '',
|
|
'invoice_dic' => $userDB['dic'] ?? '',
|
|
'invoice_street' => $userDB['street'] ?? '',
|
|
'invoice_city' => $userDB['city'] ?? '',
|
|
'invoice_zip' => $userDB['zip'] ?? '',
|
|
'invoice_country' => $userDB['country'] ?? '',
|
|
'invoice_phone' => $userDB['phone'] ?? '',
|
|
'invoice_email' => $userDB['email'] ?? '',
|
|
'invoice_state' => $userDB['state'] ?? '',
|
|
'invoice_custom_address' => $userDB['custom_address'] ?? '',
|
|
'delivery_name' => $userDB['delivery_name'] ?? '',
|
|
'delivery_surname' => $userDB['delivery_surname'] ?? '',
|
|
'delivery_firm' => $userDB['delivery_firm'] ?? '',
|
|
'delivery_street' => $userDB['delivery_street'] ?? '',
|
|
'delivery_city' => $userDB['delivery_city'] ?? '',
|
|
'delivery_zip' => $userDB['delivery_zip'] ?? '',
|
|
'delivery_country' => $userDB['delivery_country'] ?? '',
|
|
'delivery_state' => $userDB['delivery_state'] ?? '',
|
|
'delivery_custom_address' => $userDB['delivery_custom_address'] ?? '',
|
|
'delivery_phone' => $userDB['delivery_phone'] ?? '',
|
|
'note_user' => '',
|
|
];
|
|
}
|
|
|
|
public function getChargesFromPurchaseState(PurchaseState $purchaseState): array
|
|
{
|
|
$charges = [];
|
|
foreach ($purchaseState->getCharges() as $charge) {
|
|
$charges[] = new PosPurchaseCharge($charge);
|
|
}
|
|
|
|
return $charges;
|
|
}
|
|
|
|
public function getDiscountItemsFromPurchaseState(PurchaseState $purchaseState): array
|
|
{
|
|
$discounts = [];
|
|
// valid discounts
|
|
foreach ($purchaseState->getDiscounts() as $discount) {
|
|
$discounts[] = new PosPurchaseDiscount($discount, true, note: $discount->getNote());
|
|
}
|
|
|
|
if (($invalidCoupons = $purchaseState->getCustomData('invalidCoupons')) && is_array($invalidCoupons)) {
|
|
foreach ($invalidCoupons as $invalidCoupon) {
|
|
$discounts[] = new PosInvalidDiscountCoupon($invalidCoupon);
|
|
}
|
|
}
|
|
|
|
return $discounts;
|
|
}
|
|
|
|
public function getPurchaseItemsFromOrder(PosEntity $pos, \Order $order): array
|
|
{
|
|
$order->fetchItemsPhoto();
|
|
|
|
$items = [];
|
|
foreach ($order->items ?? [] as $item) {
|
|
// Map item to purchase state, then load actual uuid
|
|
$mappedPurchaseItem = array_reduce($this->getPurchaseStateProducts($order->getPurchaseState()), static function ($carry, $purchaseItem) use ($item) {
|
|
return $carry ?? ($purchaseItem->getId() === $item['id'] ? $purchaseItem : $carry);
|
|
}, null);
|
|
|
|
$photo = null;
|
|
$stores = null;
|
|
$warehouse = null;
|
|
$stockIn = null;
|
|
$serialNumberRequire = null;
|
|
$serialNumberCheck = null;
|
|
if ($item['id_product'] ?? false) {
|
|
if ($mappedPurchaseItem && $mappedPurchaseItem->getIdProduct()) {
|
|
$product = $mappedPurchaseItem->getProduct();
|
|
} else {
|
|
// Vytvoří se produkt například dárek, v pstatu je pod discount a teda nemám nafetchované informace o skladech atd..,
|
|
// v dalším přepočítání už přijde v products a musí se namapovat takže se tohle nestane
|
|
$collection = new ProductCollection([$item['product']['id'] => $item['product']]);
|
|
$collection->fetchVariations();
|
|
$collection->fetchStoresInStore(findModule(\Modules::PRODUCTS_VARIATIONS));
|
|
$product = $collection->get($item['product']['id']);
|
|
$product->fetchImages(1);
|
|
}
|
|
|
|
$photo = $product->image['src'] ?? null;
|
|
$stores = $this->posStoreUtil->getStoresCollection($product);
|
|
$warehouse = $this->posWarehouseUtil->getWarehouseItem($pos, $product);
|
|
$stockIn = $this->posWarehouseUtil->loadOverallInStoreForPos($product, $pos);
|
|
$serialNumberRequire = $product->serial_number_require ?? null;
|
|
$serialNumberCheck = new PosSerialNumberCollection($mappedPurchaseItem?->getNote()['serial_numbers'] ?? []);
|
|
}
|
|
|
|
$items[] = new PosPurchaseItem([
|
|
'uuid' => !empty($mappedPurchaseItem) ? $mappedPurchaseItem->getNote()['uuid'] ?? null : $item['note']['uuid'] ?? null,
|
|
'id_charge' => $item['note']['id_charge'] ?? null,
|
|
'id' => $item['id'],
|
|
'title' => $item['descr'],
|
|
'id_product' => $item['id_product'],
|
|
'id_variation' => $item['id_variation'],
|
|
'pieces' => $item['pieces'],
|
|
'vat' => $item['vat'],
|
|
'piece_price' => $item['piece_price']['value_without_vat']->asString(),
|
|
'code' => $item['code'] ?? $item['note']['coupon'] ?? null,
|
|
'photo' => $photo,
|
|
'ean' => $item['ean'],
|
|
'stores' => $stores,
|
|
'warehouse' => $warehouse,
|
|
'stockIn' => $stockIn,
|
|
'serial_number_require' => $serialNumberRequire,
|
|
'serial_number_check' => $serialNumberCheck,
|
|
'variationTitle' => $item['variation_title'] ?? null,
|
|
]);
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
public function getPurchaseItemsFromPurchaseState(PosEntity $pos, PurchaseState $purchaseState): array
|
|
{
|
|
$items = [];
|
|
|
|
foreach ($this->getPurchaseStateProducts($purchaseState) as $purchaseItem) {
|
|
$code = null;
|
|
$photo = null;
|
|
$ean = null;
|
|
$stores = null;
|
|
$warehouse = null;
|
|
$stockIn = null;
|
|
$serialNumberRequire = null;
|
|
if ($purchaseItem->getIdProduct()) {
|
|
$code = ($purchaseItem->getIdVariation()) ? $purchaseItem->getProduct()['variations'][$purchaseItem->getIdVariation()]['code'] : $purchaseItem->getProduct()->code;
|
|
$photo = $purchaseItem->getProduct()->image['src'] ?? null;
|
|
$ean = ($purchaseItem->getIdVariation()) ? ($purchaseItem->getProduct()['variations'][$purchaseItem->getIdVariation()]['ean'] ?? '') : $purchaseItem->getProduct()->ean;
|
|
$stores = $this->posStoreUtil->getStoresCollection($purchaseItem->getProduct());
|
|
$warehouse = $this->posWarehouseUtil->getWarehouseItem($pos, $purchaseItem->getProduct());
|
|
$stockIn = $this->posWarehouseUtil->loadOverallInStoreForPos($purchaseItem->getProduct(), $pos);
|
|
$serialNumberRequire = $purchaseItem->getProduct()->serial_number_require ?? null;
|
|
}
|
|
|
|
$items[] = new PosPurchaseItem([
|
|
'uuid' => $purchaseItem->getNote()['uuid'] ?? null,
|
|
'id' => $purchaseItem->getId() ?? null,
|
|
'title' => $purchaseItem->getName() ?? null,
|
|
'id_product' => ($purchaseItem->id_product ?? false) ? strval($purchaseItem->getIdProduct()) : null,
|
|
'id_variation' => ($purchaseItem->id_variation ?? false) ? strval($purchaseItem->getIdVariation()) : null,
|
|
'pieces' => $purchaseItem->getPieces(),
|
|
'vat' => $purchaseItem->getPrice()->getVat(),
|
|
'piece_price' => $purchaseItem->getPiecePriceWithoutVat(),
|
|
'code' => $code ?? null,
|
|
'photo' => $photo ?? null,
|
|
'ean' => $ean ?? null,
|
|
'stores' => $stores ?? null,
|
|
'warehouse' => $warehouse ?? null,
|
|
'stockIn' => $stockIn ?? null,
|
|
'serial_number_require' => $serialNumberRequire ?? null,
|
|
'serial_number_check' => new PosSerialNumberCollection($purchaseItem->getNote()['serial_numbers'] ?? []),
|
|
'variationTitle' => ($purchaseItem->id_variation ?? false) ? $purchaseItem->getProduct()->variations[$purchaseItem->getProduct()->variationId]['title'] : null,
|
|
]);
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
public function getPurchaseStateProducts(PurchaseState $purchaseState): array
|
|
{
|
|
return QueryHint::withRouteToMaster(function () use ($purchaseState) {
|
|
$purchaseState->getProductList()
|
|
->fetchVariations(true, false)
|
|
->fetchImages(1)
|
|
->getProducts()
|
|
->fetchStoresInStore(findModule(\Modules::PRODUCTS_VARIATIONS));
|
|
|
|
return $purchaseState->getProducts();
|
|
});
|
|
}
|
|
}
|