purchasePriceGateway = $purchasePriceGateway; $this->rawMaterialGateway = $rawMaterialGateway; $this->documentPositionWrapper = $documentPositionWrapper; $this->documentService = $documentService; $this->config = $copperSurchargeConfig; $this->documentGateway = $documentGateway; } /** * @param string $docType * @param int $docId * @param array|DocumentPositionData[] $possibleCopperPositions * @param array $copperPositionsInPartsList * * @return int */ public function handleCopperSurchargePositions( string $docType, int $docId, array $possibleCopperPositions, array $copperPositionsInPartsList ): int { $calcDate = $this->evaluateCalcDate($docType, $docId); if ($this->config->getSurchargePositionType() === CopperSurchargeData::POSITION_TYPE_ALWAYS) { $this->createManyPositions( $docType, $docId, $possibleCopperPositions, $copperPositionsInPartsList, $calcDate ); return 0; } elseif ($this->config->getSurchargePositionType() === CopperSurchargeData::POSITION_TYPE_ONETIME) { $this->createSinglePosition( $docType, $docId, $possibleCopperPositions, $copperPositionsInPartsList, $calcDate ); return 1; } else { $this->createGroupPositions($docType, $docId, $copperPositionsInPartsList, $calcDate); return 2; } } /** * Because there is no connection between positions, * all surcharge positions get deleted and recreated in the next step * * @param string $docType * @param int $docId */ public function resetDocument(string $docType, int $docId): void { $this->resetBetweenPositions($docId, $docType, $this->config->getCopperSurchargeArticleId()); $this->documentService->deleteCopperSurchargePositions( $docType, $docId, $this->config->getCopperSurchargeArticleId() ); $this->documentService->updatePositionSorts($docType, $docId); } /** * @param string $docType * @param int $docId * @param array|DocumentPositionData[] $copperPositions * @param array $copperPositionsInPartsList * @param DateTimeInterface $calcDate * */ private function createManyPositions( string $docType, int $docId, array $copperPositions, array $copperPositionsInPartsList, DateTimeInterface $calcDate ): void { foreach ($copperPositions as $position) { $amount = $this->calcAmount($docType, $position->getPositionId(), $position->getArticleId()); $copperBase = $this->getCopperBase($position->getArticleId()); $price = $this->calculateCopperSurchargePrice( $copperBase, $amount, $calcDate ); $newPosId = $this->addCopperSurchargePosition( $docType, $docId, $price, $amount, $copperBase, $position->getCurrency(), $calcDate ); $this->documentService->updatePositionSort($docType, $docId, $position->getPositionId(), $newPosId); } if (empty($copperPositionsInPartsList)) { return; } foreach ($copperPositionsInPartsList as $partListPosition) { $partListData = $this->evaluateSurchargeDataForPartList( $partListPosition['article_id'], $partListPosition['pos_id'], $calcDate, $docType, $partListPosition['amount'] ); $newPosId = $this->addCopperSurchargePosition( $docType, $docId, $partListData['price'], $partListData['amount'], $partListData['copper_base'], $partListPosition['currency'], $calcDate ); $this->documentService->updatePositionSort($docType, $docId, $partListData['position_id'], $newPosId); } } /** * @param int $partListHeadId * @param int $positionId * @param DateTimeInterface $calcDate * @param string $docType * @param float $partListPositionAmount * * @return array */ private function evaluateSurchargeDataForPartList( int $partListHeadId, int $positionId, DateTimeInterface $calcDate, string $docType, float $partListPositionAmount ): array { $copperArticles = $this->getCopperArticlesFromPartList( $partListHeadId, $this->config->getSurchargeMaintenanceType(), $this->config->getCopperNumberOption(), $this->config->getCopperSurchargeArticleId() ); $amount = 0; $price = 0.0; $copperBase = 0.0; foreach ($copperArticles as $copperArticle) { $amount += $copperArticle['amount']; $copperBase = $this->getCopperBase($copperArticle['article_id']); $price += $this->calculateCopperSurchargePrice( $copperBase, $amount, $calcDate ); } return [ 'amount' => $amount * $partListPositionAmount, 'price' => $price * $partListPositionAmount, 'copper_base' => $copperBase, 'position_id' => $this->documentGateway->evaluatePartListLastPositionId($docType, $positionId), ]; } /** * @param string $docType * @param int $docId * @param float $price * @param float $amount * @param float $copperBase * @param $currency * @param DateTimeInterface $calcDate * * @return int */ private function addCopperSurchargePosition( string $docType, int $docId, float $price, float $amount, float $copperBase, $currency, DateTimeInterface $calcDate ): int { $copperSurchargeArticleId = $this->config->getCopperSurchargeArticleId(); $articleData = $this->documentGateway->getArticleData($copperSurchargeArticleId); $description = $this->findCopperSurchargeArticleDescription( $amount, $copperBase, $price, $articleData, $calcDate ); return $this->documentPositionWrapper->addPositionManuallyWithPrice( $docType, $docId, $copperSurchargeArticleId, $articleData, 1, $price, $currency, $description ); } /** * @param float $amount * @param float $copperBase * @param float $price * @param array $articleData * @param DateTimeInterface $calcDate * * @return string */ private function findCopperSurchargeArticleDescription( float $amount, float $copperBase, float $price, array $articleData, DateTimeInterface $calcDate ): string { $description = $articleData['description']; $delPrice = $this->purchasePriceGateway->getDelCopperPriceByDate( $calcDate, $this->config->getCopperSurchargeArticleId() ); $price = number_format($price, 2, ",", "."); $delPrice = number_format($delPrice, 2, ",", "."); $copperBase = number_format($copperBase, 2, ",", "."); $amount = str_replace('.', ',', $amount); $description = str_replace('{NETPRICE}', $price, $description); $description = str_replace('{ARTIKELNUMMER}', $articleData['number'], $description); $description = str_replace('{ARTIKELNAME}', $articleData['name_de'], $description); $description = str_replace('{COPPERBASIS}', $copperBase, $description); $description = str_replace('{COPPERNUMBER}', $amount, $description); $description = str_replace('{DELVALUE}', $delPrice, $description); return $description; } /** * @param string $docType * @param int $docId * @param array|DocumentPositionData[] $copperPositions * @param array $copperPositionsInPartsList * @param DateTimeInterface $calcDate * * @return int */ private function createSinglePosition( string $docType, int $docId, array $copperPositions, array $copperPositionsInPartsList, DateTimeInterface $calcDate ): int { $price = 0.0; $totalAmount = 0.0; $currency = 'EUR'; $copperBase = 0.0; foreach ($copperPositions as $position) { $currency = $position->getCurrency(); $amount = $this->calcAmount($docType, $position->getPositionId(), $position->getArticleId()); $totalAmount += $amount; $copperBase = $this->getCopperBase($position->getArticleId()); $price += $this->calculateCopperSurchargePrice( $copperBase, $amount, $calcDate ); } if (!empty($copperPositionsInPartsList)) { foreach ($copperPositionsInPartsList as $position) { $partListData = $this->evaluateSurchargeDataForPartList( $position['article_id'], $position['pos_id'], $calcDate, $docType, $position['amount'] ); $totalAmount += $partListData['amount']; $price += $partListData['price']; $copperBase = $partListData['copper_base']; } } $newPosId = 0; if ($price > 0) { $newPosId = $this->addCopperSurchargePosition( $docType, $docId, $price, $totalAmount, $copperBase, $currency, $calcDate ); } return $newPosId; } /** * @param string $docType * @param int $docId * @param array $copperPositionsInPartsList * @param DateTimeInterface $calcDate */ private function createGroupPositions( string $docType, int $docId, array $copperPositionsInPartsList, DateTimeInterface $calcDate ): void { if ($this->config->getSurchargeMaintenanceType() === CopperSurchargeData::SURCHARGE_MAINTENANCE_TYPE_APP) { $positions = $this->rawMaterialGateway->findAllPositionsForGrouped( $docType, $docId, $this->config->getCopperSurchargeArticleId() ); } else { $positions = $this->documentGateway->findAllPositionsForGrouped( $docType, $docId, $this->config->getCopperNumberOption() ); } $price = 0.0; $totalAmount = 0.0; $currency = 'EUR'; $prev = null; $copperBase = 0.0; $lastSort = 0; foreach ($positions as $key => $position) { if ((bool)$position['is_copper']) { $amount = $this->calcAmount($docType, (int)$position['pos_id'], (int)$position['article_id']); $copperBase = $this->getCopperBase($position['article_id']); $price += $this->calculateCopperSurchargePrice( $copperBase, $amount, $calcDate ); $currency = $position['currency']; $totalAmount += $amount; } if ($position['between_type'] === 'gruppe') { if (!empty($copperPositionsInPartsList)) { $partListElements = $this->getElementsFromPartListBetweenSorts( $copperPositionsInPartsList, $lastSort, $position['sort'] ); foreach ($partListElements as $partListElement) { $partListKey = $partListElement['part_list_key']; $partListData = $this->evaluateSurchargeDataForPartList( $partListElement['article_id'], $position['pos_id'], $calcDate, $docType, $partListElement['amount'] ); $totalAmount += $partListData['amount']; $price += $partListData['price']; $copperBase = $partListData['copper_base']; unset($copperPositionsInPartsList[$partListKey]); } } if ($price > 0.0) { $newPosId = $this->addCopperSurchargePosition( $docType, $docId, $price, $totalAmount, $copperBase, $currency, $calcDate ); if (!empty($prev)) { $this->documentService->updatePositionSort($docType, $docId, (int)$prev['pos_id'], $newPosId); } $price = 0.0; $totalAmount = 0.0; $lastSort = $position['sort']; } } $prev = $position; } if (!empty($copperPositionsInPartsList)) { foreach ($copperPositionsInPartsList as $partListElement) { $partListData = $this->evaluateSurchargeDataForPartList( $partListElement['article_id'], $partListElement['pos_id'], $calcDate, $docType, $partListElement['amount'] ); $totalAmount += $partListData['amount']; $price += $partListData['price']; $copperBase = $partListData['copper_base']; } } if ($price > 0.0) { $this->addCopperSurchargePosition( $docType, $docId, $price, $totalAmount, $copperBase, $currency, $calcDate ); } } /** * @param float $copperBase * @param float $amount * @param DateTimeInterface $calcDate * * @return float */ private function calculateCopperSurchargePrice( float $copperBase, float $amount, DateTimeInterface $calcDate ): float { $delPrice = $this->purchasePriceGateway->getDelCopperPriceByDate( $calcDate, $this->config->getCopperSurchargeArticleId() ); $perCent = $this->config->getSurchargeDeliveryCosts() / 100; return (($delPrice + ($delPrice * $perCent)) - $copperBase) * $amount / 100; } /** * @param string $docType * @param int $positionId * @param int $positionArticleId * * @return float */ private function calcAmount(string $docType, int $positionId, int $positionArticleId): float { if ($this->config->getSurchargeMaintenanceType() === CopperSurchargeData::SURCHARGE_MAINTENANCE_TYPE_APP) { $amount = $this->rawMaterialGateway->getRawMaterialAmount( $positionArticleId, $this->config->getCopperSurchargeArticleId() ); } else { $articleId = $this->documentGateway->getArticleIdByPositionId($docType, $positionId); $amount = $this->documentGateway->getArticleCopperNumber( $articleId, $this->config->getCopperNumberOption() ); } $documentAmount = $this->documentGateway->getPositionAmount($docType, $positionId); $amount *= $documentAmount; return (float)$amount; } /** * @param int $positionArticleId * * @return float */ private function getCopperBase(int $positionArticleId): float { $copperBase = $this->config->getSurchargeCopperBaseStandard(); $articleCopperBaseField = $this->config->getSurchargeCopperBase(); if (!empty($articleCopperBaseField)) { $copperBaseTemp = $this->documentGateway->getArticleCopperBase($positionArticleId, $articleCopperBaseField); if (!empty($copperBaseTemp)) { $copperBase = $copperBaseTemp; } } return $copperBase; } /** * @param string $doctype * @param int $docId * * @throws InvalidDateFormatException * @return DateTimeInterface */ private function evaluateCalcDate(string $doctype, int $docId): DateTimeInterface { $orderOfferId = 0; if ($doctype === 'auftrag') { $orderOfferId = $this->documentGateway->findOrderOfferId($docId); } if ( $doctype === 'rechnung' && $this->config->getSurchargeInvoice() === CopperSurchargeData::INVOICE_CREATE_POS_BY_DELIVERY_DATE ) { $calcDate = $this->documentGateway->findDeliveryDate($docId); if (empty($calcDate)) { $calcDate = new DateTimeImmutable(); } } elseif ( $doctype === 'rechnung' && $this->config->getSurchargeInvoice() === CopperSurchargeData::INVOICE_CREATE_POS_BY_ORDER_DATE ) { $orderId = $this->documentGateway->findInvoiceOrderId($docId); if (empty($orderId)) { $calcDate = new DateTimeImmutable(); } else { $calcDate = $this->documentGateway->getCalcDate('auftrag', $orderId); } } elseif ($doctype === 'rechnung' && $this->config->getSurchargeInvoice() === CopperSurchargeData::INVOICE_CREATE_POS_BY_INVOICE_DATE) { $calcDate = $this->documentGateway->getCalcDate($doctype, $docId); } elseif ($doctype === 'rechnung' && $this->config->getSurchargeInvoice() === CopperSurchargeData::INVOICE_CREATE_POS_BY_OFFER_DATE) { $offerId = $this->documentGateway->findInvoiceOfferId($docId); if (!empty($offerId)) { $calcDate = $this->documentGateway->getCalcDate('angebot', $offerId); } else { $calcDate = new DateTimeImmutable(); } } elseif ( $doctype === 'auftrag' && $orderOfferId !== 0 && $this->config->getSurchargeDocumentConversion() === CopperSurchargeData::DOCUMENT_CONVERSION_FROM_OFFER ) { $calcDate = $this->documentGateway->getCalcDate('angebot', $orderOfferId); } else { $calcDate = new DateTimeImmutable(); } return $calcDate; } /** * @param int $docId * @param string $docType * @param int $copperSurchargeArticleId */ private function resetBetweenPositions(int $docId, string $docType, int $copperSurchargeArticleId): void { if ($this->config->getSurchargeMaintenanceType() === CopperSurchargeData::SURCHARGE_MAINTENANCE_TYPE_APP) { $positions = $this->rawMaterialGateway->findAllPositionsForGrouped( $docType, $docId, $copperSurchargeArticleId ); } else { $positions = $this->documentGateway->findAllPositionsForGrouped( $docType, $docId, $this->config->getCopperNumberOption() ); } $offset = 0; foreach ($positions as $position) { if ((int)$position['article_id'] === $copperSurchargeArticleId) { $offset--; } if ((int)$position['pos_type'] === 2 && $offset < 0) { $this->documentService->updateBetweenSort( (int)$position['between_id'], (int)$position['sort'] + $offset ); } } } /** * @param string $docType * @param int $docId */ public function deleteRemainingCopperSurchargeArticles(string $docType, int $docId): void { if ($this->config->getSurchargeMaintenanceType() === CopperSurchargeData::SURCHARGE_MAINTENANCE_TYPE_ARTICLE) { $hasCopperArticles = $this->documentGateway->hasCopperArticles( $docType, $docId, $this->config->getCopperNumberOption() ); } else { $hasCopperArticles = $this->rawMaterialGateway->hasCopperArticles( $docType, $docId, $this->config->getCopperSurchargeArticleId() ); } if (!$hasCopperArticles) { $this->documentService->deleteCopperSurchargePositions( $docType, $docId, $this->config->getCopperSurchargeArticleId() ); } } /** * @param string $docType * @param int $posId */ public function updatePositionContributionMargin(string $docType, int $posId) { $this->documentService->updatePositionContributionMargin($docType, $posId, 0); } /** * @param string $docType * @param int $posId */ public function updatePositionPurchasePrice(string $docType, int $posId) { $this->documentService->updatePositionPurchasePrice($docType, $posId, 0.0); } /** * @param string $docType * * @param int $docTypeId * * @return array|DocumentPositionData[] */ public function findPositionsForMaintenanceApp(string $docType, int $docTypeId): array { $data = $this->rawMaterialGateway->findPositions( $docType, $docTypeId, $this->config->getCopperSurchargeArticleId() ); return $this->transformToDocumentPositionData($data); } /** * @param string $docType * * @param int $docTypeId * * @return array|DocumentPositionData[] */ public function findPositionsForMaintenanceArticle(string $docType, int $docTypeId): array { $data = $this->documentGateway->findPositions( $docType, $docTypeId, $this->config->getCopperNumberOption() ); return $this->transformToDocumentPositionData($data); } /** * @param array $copperPositionsRaw * * @return array|DocumentPositionData[] */ private function transformToDocumentPositionData(array $copperPositionsRaw): array { $documentPositions = []; foreach ($copperPositionsRaw as $position) { $data = new DocumentPositionData( (int)$position['pos_id'], (int)$position['article_id'], (string)$position['currency'] ); $documentPositions[] = $data; } return $documentPositions; } /** * @param int $doctypeId * @param string $doctype */ public function updateCopperSurchargeArticles(int $doctypeId, string $doctype) { $copperSurchargeArticleId = $this->config->getCopperSurchargeArticleId(); $surchargePositions = $this->documentGateway->findCopperSurchargeArticlePositionIds( $doctype, $doctypeId, $copperSurchargeArticleId ); if (!empty($surchargePositions)) { foreach ($surchargePositions as $surchargePosition) { $this->updatePositionContributionMargin($doctype, (int)$surchargePosition['pos_id']); $this->updatePositionPurchasePrice($doctype, (int)$surchargePosition['pos_id']); } } } /** * @param array $copperPositionsInPartsList * @param int $lastSort * @param int $nextSort * * @return array */ private function getElementsFromPartListBetweenSorts( array $copperPositionsInPartsList, int $lastSort, int $nextSort ): array { $result = []; foreach ($copperPositionsInPartsList as $partListKey => $position) { $currentSort = $position['sort']; if ($currentSort > $lastSort && $currentSort <= $nextSort) { $position['part_list_key'] = $partListKey; $result[] = $position; } } return $result; } /** * @param int $partListHeadId * @param int $surchargeMaintenanceType * @param string $copperNumberOption * @param int $copperArticleId * * @return array */ private function getCopperArticlesFromPartList( int $partListHeadId, int $surchargeMaintenanceType, string $copperNumberOption, int $copperArticleId ): array { $result = []; $childElements = $this->documentGateway->getAllPartListChildElements($partListHeadId); foreach ($childElements as $childElement) { if ($surchargeMaintenanceType === CopperSurchargeData::SURCHARGE_MAINTENANCE_TYPE_ARTICLE) { $possibleArticle = $this->documentGateway->findPossibleCopperArticle( $childElement['id'], $copperNumberOption ); } else { $possibleArticle = $this->rawMaterialGateway->findPossibleCopperArticle( $childElement['id'], $copperArticleId ); } if (empty($possibleArticle)) { continue; } $result[] = [ 'article_id' => $possibleArticle['article_id'], 'amount' => $possibleArticle['amount'] * $childElement['amount'], ]; } return $result; } /** * @param string $docType * @param int $docTypeId * * @return array */ public function findPositionsForMaintenanceAppInPartsList( string $docType, int $docTypeId ): array { $result = []; $copperArticleId = $this->config->getCopperSurchargeArticleId(); $headArticles = $this->documentGateway->findPartListHeadArticles($docType, $docTypeId); foreach ($headArticles as $headArticle) { $childElements = $this->documentGateway->getAllPartListChildElements($headArticle['id']); $hasCopper = false; foreach ($childElements as $childElement) { if (!$hasCopper) { $hasCopper = !empty( $this->rawMaterialGateway->findPossibleCopperArticle( $childElement['id'], $copperArticleId ) ); } } if ($hasCopper) { $result[] = [ 'article_id' => $headArticle['id'], 'sort' => $headArticle['sort'], 'pos_id' => $headArticle['pos_id'], 'currency' => $headArticle['currency'], 'amount' => (float)$headArticle['amount'], ]; } } return $result; } /** * @param string $docType * @param int $docTypeId * * @return array */ public function findPositionsForMaintenanceArticleInPartsList( string $docType, int $docTypeId ): array { $result = []; $copperNumberOption = $this->config->getCopperNumberOption(); $headArticles = $this->documentGateway->findPartListHeadArticles($docType, $docTypeId); foreach ($headArticles as $headArticle) { $childElements = $this->documentGateway->getAllPartListChildElements((int)$headArticle['id']); $hasCopper = false; foreach ($childElements as $childElement) { if (!$hasCopper) { $hasCopper = !empty( $this->documentGateway->findPossibleCopperArticle( $childElement['id'], $copperNumberOption ) ); } } if ($hasCopper) { $result[] = [ 'article_id' => $headArticle['id'], 'sort' => $headArticle['sort'], 'pos_id' => $headArticle['pos_id'], 'currency' => $headArticle['currency'], 'amount' => (float)$headArticle['amount'], ]; } } return $result; } }