first commit

This commit is contained in:
2025-08-02 16:30:27 +02:00
commit 23646bfcee
14851 changed files with 1750626 additions and 0 deletions

View File

@@ -0,0 +1,396 @@
<?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();
});
}
}