<?php
namespace Modules\Bsp\Listing;

class ModelGenerateSupplierInvoice
{
	public $lang = [];

	protected $cache = [];

	/**
	 * Generate the supplier invoice object, with its rows populated
	 * @param int $bspPeriodId
	 * @param array $formData
	 * @param \App\MessagesCollector $msgs
	 * @return Array
	 */
	public function generateSupplierInvoice($formData, $selectedPeriods, $msgs)
	{
		set_time_limit(10*60);	// 10 minutes timelimit, in case we need to parse the file

		// validate BSP module that the settings are filled in
		$selectedSupplierInvoiceVats = (\App\Bsp\Config::getInstance()->get("supplierInvoiceVat")?unserialize(\App\Bsp\Config::getInstance()->get("supplierInvoiceVat")):[]);
		if (!$selectedSupplierInvoiceVats) {
			throw new \Engine\Mvc\ModelException(sprintf("Please configure service VAT to use in BSP settings module"));
		}

		\Log::getInstance()->info(sprintf("[%s] start generating supplier invoice", __METHOD__), [
			'formData' => new \Engine\Logger\Context\ArrayAssoc($formData)
		]);

		$supplierInvoice = \Entity\SupplierInvoice::insert();
		$supplierInvoiceRows = [];

		$gatherer = new \ModulesAbstract\PluginReports\Report\Model\Gatherer\BspPeriodRowsCached();
		list($idOffice, $iataCode) = explode(":", $formData['filters']['idOffice']['id']);
		$gatherer->addFilter('office:id', $idOffice);
		$gatherer->addFilter('office:iataCode', $iataCode);

		if ($selectedPeriods) {
			$gatherer->addFilter('period:idList', $selectedPeriods);
		}

		if ($formData['filters']['period']['start'] && $formData['filters']['period']['end']) {
			$gatherer->addFilter('ticketDate:start', $formData['filters']['period']['start']);
			$gatherer->addFilter('ticketDate:end', $formData['filters']['period']['end']);
		}

		// NOTE: used for debuging / uTests
		if (SHOW_DEBUGS) {
			if ($formData['filters']['ticketNumber']) {
				$gatherer->addFilter('ticketNumber', $formData['filters']['ticketNumber']);
			}
		}

		$this->cache['suppliersList']['tktCode'] = [];
		foreach ($formData['filters']['supliersList'] as $supplier) {
			if ($supplier['id']) {
				$this->cache['suppliersList']['tktCode'][] = \Entity\Supplier::select($supplier['id'])->codeTktCode;
			}
		}

		\Log::getInstance()->info(sprintf("[%s] setup done", __METHOD__), ['this->cache'=>new \Engine\Logger\Context\ArrayAssoc($this->cache), ]);

		while ($bspTicketRawData = $gatherer->next()) {
			/* @var $bspTicket \App\Bsp\BspRow */
			// process bspTicket data
			$rows = [];
			// this ticket is paid with CC and the user chose that they should be reported in duplicate form

			$bspTicket = $bspTicketRawData['data'];
			if ($formData['filters']['extraRowOperations']=='reportComissionCashCcRevCashRevCcForCc' && $bspTicket->isPaidWith('cc')) {
				$rows = $this->generateSupplierInvoiceRows_reportComissionCashCcRevCashRevCcForCc($bspTicket, $formData);
			}
			else {
				$rows = $this->generateSupplierInvoiceRows_standard($bspTicket, $formData);
			}


			if ($rows) {
				$this->cache['DebugInvoiceStats:bspTicket']['tickets']++;
				$this->cache['DebugInvoiceStats:bspTicket']['byCurrency'][$bspTicket->priceBalancePayable->currency]++;
				$this->cache['DebugInvoiceStats:bspTicket']['bySuplierServices'][$bspTicket->supplierCode3]++;
				$this->cache['DebugInvoiceStats:bspTicket']['bySuplierPrices'][$bspTicket->supplierCode3]['total']+=$bspTicket->priceBalancePayable->amount;
				$this->cache['DebugInvoiceStats:bspTicket']['bySuplierPrices'][$bspTicket->supplierCode3]['tax']+= 	$bspTicket->priceTaxCc->amount + $bspTicket->priceTaxCash->amount;
				$this->cache['DebugInvoiceStats:bspTicket']['bySuplierPrices'][$bspTicket->supplierCode3]['iss']+= 	$bspTicket->priceBalancePayable->amount - ($bspTicket->priceTaxCc->amount + $bspTicket->priceTaxCash->amount);

				$this->cache['DebugInvoiceStats:bspTicket']['byTicket'][$bspTicket->ticketNumber]['total']+=$bspTicket->priceBalancePayable->amount;
				$this->cache['DebugInvoiceStats:bspTicket']['byTicket'][$bspTicket->ticketNumber]['tax']+= 	$bspTicket->priceTaxCc->amount + $bspTicket->priceTaxCash->amount;
				$this->cache['DebugInvoiceStats:bspTicket']['byTicket'][$bspTicket->ticketNumber]['iss']+= 	$bspTicket->priceBalancePayable->amount - ($bspTicket->priceTaxCc->amount + $bspTicket->priceTaxCash->amount);

				// add the generated rows to the final list
				foreach ($rows as $row) {
					$fields = $row->getFields();

					if (abs($fields['priceValue'] + $fields['vatValue'])>0.000001) {
						$this->cache['DebugInvoiceStats:invoiceRows']['rows']++;
						$this->cache['DebugInvoiceStats:invoiceRows']['byAmounts']['priceValue']+= 	$fields['priceValue'];
						$this->cache['DebugInvoiceStats:invoiceRows']['byAmounts']['vatValue']+= 	$fields['vatValue'];
						$this->cache['DebugInvoiceStats:invoiceRows']['byAmounts']['vatText'][$fields['vatText']]++;

						$this->cache['DebugInvoiceStats:invoiceRows']['byTicket'][$fields['ticketNo']]['priceValue']+= 	$fields['priceValue'];
						$this->cache['DebugInvoiceStats:invoiceRows']['byTicket'][$fields['ticketNo']]['vatValue']+= 	$fields['vatValue'];

						$supplierInvoiceRows[]= $row;
					}
				}
			}
		}

		if ($this->cache['DebugInvoiceStats:bspTicket']['bySuplierServices']) {
			ksort($this->cache['DebugInvoiceStats:bspTicket']['bySuplierServices']);
		}

		if ($this->cache['DebugInvoiceStats:bspTicket']['bySuplierPrices']) {
			ksort($this->cache['DebugInvoiceStats:bspTicket']['bySuplierPrices']);
		}

		\Log::getInstance()->info(sprintf("[%s] rows generated", __METHOD__), ['this->cache'=>new \Engine\Logger\Context\ArrayAssoc($this->cache)]);

		if (!$supplierInvoiceRows) {
			throw new \Engine\Mvc\ModelException(sprintf("There are no invoice rows to be added on this invoice. Please check the filters."));
		}

		\Log::getInstance()->info(sprintf("[%s] start populating supplier invoice", __METHOD__));

		$invoiceCurrency =					($formData['filters']['currency']?:$this->cache['currency']);
		$supplierInvoice->idSupplier = 		$formData['invoice']['supplier']['id'];
		$supplierInvoice->invoiceDate = 	(string)$formData['invoice']['date'];
		$supplierInvoice->dueDate = 		$formData['invoice']['dueDate'] ? $formData['invoice']['dueDate'] : NULL;
		$supplierInvoice->invoiceSeries = 	$formData['invoice']['series'] ? $formData['invoice']['series'] : NULL;
		$supplierInvoice->invoiceNumber = 	$formData['invoice']['number'];

		$comments = [];
		if ($selectedPeriods) {
			$selectedPeriodNames = [];
			foreach ($selectedPeriods as $id) {
				$selectedPeriodNames[] = \Entity\BspPeriod::select($id)->filename;
			}
			$comments[] = sprintf($this->lang['generateSupplierInvoice']['comments_autogeneratedFromFiles'], implode(",", $selectedPeriodNames));
		}
		else {
			$comments[] = sprintf($this->lang['generateSupplierInvoice']['comments_autogeneratedGeneral']);
		}

		if ($formData['filters']['period']['start'] || $formData['filters']['period']['end']) {
			$comments[] = sprintf($this->lang['generateSupplierInvoice']['comments_period'], $formData['filters']['period']['start'], $formData['filters']['period']['end']);
		}
		if ($idOffice) {
			$comments[] = sprintf($this->lang['generateSupplierInvoice']['comments_idOffice'], $idOffice);
		}
		if ($iataCode) {
			$comments[] = sprintf($this->lang['generateSupplierInvoice']['comments_idOfficeIataCode'], $iataCode);
		}
		$comments[] = sprintf($this->lang['generateSupplierInvoice']['comments_destinationType'], $formData['filters']['destinationType']);
		$comments[] = sprintf($this->lang['generateSupplierInvoice']['comments_currency'], $invoiceCurrency);
		if ($this->cache['suppliersList']['tktCode']) {
			if ($formData['filters']['suppliersListType']=='excludeList') {
				$comments[] = sprintf($this->lang['generateSupplierInvoice']['comments_supplierFilterExcludeList'], implode(",", $this->cache['suppliersList']['tktCode']));
			}
			elseif ($formData['filters']['suppliersListType']=='includeList') {
				$comments[] = sprintf($this->lang['generateSupplierInvoice']['comments_supplierFilterIncludeList'], implode(",", $this->cache['suppliersList']['tktCode']));
			}
		}
		$comments[] = sprintf($this->lang['generateSupplierInvoice']['comments_rowOptions'], $this->lang['generateSupplierInvoice']['extraRowOperations_'.$formData['filters']['extraRowOperations']]);
		$supplierInvoice->invoiceComments = implode(", ", $comments);

		$supplierInvoice->currency = 		($formData['filters']['currency']?:$this->cache['currency']);
		$supplierInvoice->remarks = 		$formData['invoice']['remarks'];

		$supplierInvoice->idUser = 			$formData['invoice']['user']['id'];
		$supplierInvoice->idOffice = 		\Entity\User::select($formData['invoice']['user']['id'])->idOffice;

		$supplierInvoice->rows = 			$supplierInvoiceRows;
		$supplierInvoice->save();

		\Log::getInstance()->info(sprintf("[%s] supplier invoice inserted", __METHOD__), ['id'=>$supplierInvoice->getId()]);

		return [
			'insertedInvoiceId' => $supplierInvoice->getId(),
		];
	}

	/**
	 * Check if this row should get reported in the supplier invoice
	 * @param \App\Bsp\BspRow $bspTicket
	 * @param array $formData
	 * @throws \Engine\Mvc\ModelException
	 * @return boolean
	 */
	protected function shouldReportRow(\App\Bsp\BspRow $bspTicket, $formData)
	{
		$ret = true;

		// filter by supplier
		if ($ret) {
			if ($this->cache['suppliersList']['tktCode']) {
				if ($formData['filters']['suppliersListType']=='excludeList') {
					$ret = !in_array($bspTicket->supplierCode3, $this->cache['suppliersList']['tktCode']);
				}
				elseif ($formData['filters']['suppliersListType']=='includeList') {
					$ret = in_array($bspTicket->supplierCode3, $this->cache['suppliersList']['tktCode']);
				}
			}
		}

		// filter by currency
		if ($ret) {
			if ($formData['filters']['currency']=='') {	// auto currency, but only one type of currency is allowed, so stop if multiple currencies are found
				if (!$this->cache['currency']) {
					$this->cache['currency'] = $bspTicket->priceBalancePayable->currency;
				}

				if ($this->cache['currency']!=$bspTicket->priceBalancePayable->currency) {
					// $ret = false;
					throw new \Engine\Mvc\ModelException(sprintf("There are multiple currencies in this file. Cannot automatically determine wich one to use (found %s, %s)", $this->cache['currency'], $bspTicket->priceBalancePayable->currency));
				}
			}
			else {
				if ($formData['filters']['currency']!=$bspTicket->priceBalancePayable->currency) {
					$ret = false;
				}
			}
		}

		// filter by type (internaltional/domestic)
		if ($ret) {
			switch ($formData['filters']['destinationType']) {
				case 'all':
					// do nothing, all destination types are allowed
				break;
				case 'international':
					$ret = ($bspTicket->locationType=='I');
				break;
				case 'domestic':
					$ret = ($bspTicket->locationType=='D');
				break;
				case 'undefined':
					$ret = ($bspTicket->locationType=='');
				break;
				default:
					throw new \Engine\Mvc\ModelException(sprintf("Invalid filter value used for ticket destination type: %s", $formData['filters']['destinationType']));
			}
		}

		return $ret;
	}

	/**
	 * Standard invoice
	 * @param \App\Bsp\BspRow $bspTicket
	 * @param Array $formData
	 * @return multitype:\App\Entity\Entity
	 */
	protected function generateSupplierInvoiceRows_standard(\App\Bsp\BspRow $bspTicket, $formData)
	{
		$rows = [];

		if ($this->shouldReportRow($bspTicket, $formData)) {
			$ticketNo = $bspTicket->ticketNumber;
			$currencyDecimals = \App\Config\CurrencyDecimals::getCurrencyDecimals($bspTicket->priceBalancePayable->currency);
			if ($bspTicket->extraData->isRefund) {
				$ticketNo = $ticketNo.'R';
			}

			// add supplier price
			$rowPrice = 0.0;
			if ($formData['filters']['extraRowOperations']=='reportCashCc') {
				$rowPrice =   ($bspTicket->priceSupplierCash->amount + $bspTicket->priceSupplierCc->amount)
							- ($bspTicket->priceCommission->amount + $bspTicket->priceCommissionVat->amount + $bspTicket->priceCommissionSupplementary->amount);
			}
			else {
				// by default, the total price doesn't contain the cc components
				$rowPrice =   ($bspTicket->priceSupplierCash->amount)
							- ($bspTicket->priceCommission->amount + $bspTicket->priceCommissionVat->amount + $bspTicket->priceCommissionSupplementary->amount);
			}
			if ($rowPrice) {
				list($serviceCode, $priceVat) = $this->chooseRowVAT_supplier($bspTicket, $formData);
				$priceComponents = $this->splitPrice($rowPrice, $priceVat->percent, $currencyDecimals);

				$row = \Entity\SupplierInvoiceRow::insert();
				$row->ticketNo =		$ticketNo;
				$row->serviceCode = 	$serviceCode;
				if ($bspTicket->extraData->isEMD) {
					$row->description = 	sprintf("EMD\n%s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
				}
				elseif ($bspTicket->extraData->isAdm) {
					$row->description = 	sprintf("ADM/ACM\n%s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
				}
				else {
					// ticketing, refund, void. Assume we're still reporting ticketing services
					if ($bspTicket->isPaidWith('cc')) {
						$row->description = 	sprintf("Ticket\n%s-%s\n%s; FP=CC", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
					}
					else {
						$row->description = 	sprintf("Ticket\n%s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
					}
				}

				$row->externalReservationDate = $bspTicket->ticketDate;
				$row->quantity = 		1;
				$row->measureUnit = 	$this->lang['generateSupplierInvoice']['rows']['measureUnit'];
				$row->priceValue = 		$priceComponents->value;
				$row->vatValue = 		$priceComponents->vat;
				$row->vatText = 		$priceVat->label;
				$rows[] = $row;
			}


			// add tax
			$rowPrice = 0.0;
			if ($formData['filters']['extraRowOperations']=='reportCashCc') {
				$rowPrice = $bspTicket->priceTaxCash->amount + $bspTicket->priceTaxCc->amount;
			}
			else {
				$rowPrice = $bspTicket->priceTaxCash->amount;
			}
			if ($rowPrice) {
				list($serviceCode, $priceVat) = $this->chooseRowVAT_tax($bspTicket, $formData);
				$priceComponents = $this->splitPrice($rowPrice, $priceVat->percent, $currencyDecimals);

				$row = \Entity\SupplierInvoiceRow::insert();
				$row->ticketNo =		$ticketNo;
				$row->serviceCode = 	$serviceCode;
				if ($bspTicket->extraData->isEMD) {
					$row->description = 	sprintf("Tax for EMD\n%s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
				}
				elseif ($bspTicket->extraData->isAdm) {
					$row->description = 	sprintf("Tax for ADM/ACM\n%s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
				}
				else {
					// ticketing, refund, void. Assume we're still reporting ticketing services
					if ($bspTicket->isPaidWith('cc')) {
						$row->description = 	sprintf("Tax for ticket\n%s-%s\n%s; FP=CC", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
					}
					else {
						$row->description = 	sprintf("Tax for ticket\n%s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
					}
				}

				$row->externalReservationDate = $bspTicket->ticketDate;
				$row->quantity = 		1;
				$row->measureUnit = 	$this->lang['generateSupplierInvoice']['rows']['measureUnit'];
				$row->priceValue = 		$priceComponents->value;
				$row->vatValue = 		$priceComponents->vat;
				$row->vatText = 		$priceVat->label;
				$rows[] = $row;
			}
		}

		return $rows;
	}

	/**
	 * Report some of the tickets as duplicate and negated rows, so the final total is still the same,
	 * @see DAMA/TST:3262(add option to bsp invoice generator to duplicate rows for CC tickets) for HAPPY TOUR RO / METROPOLIS CENTER, by Stan Octav
	 * @param \App\Bsp\BspRow $bspTicket
	 * @param array $formData
	 * @return multitype:\App\Entity\Entity
	 */
	protected function generateSupplierInvoiceRows_reportComissionCashCcRevCashRevCcForCc(\App\Bsp\BspRow $bspTicket, $formData)
	{
		$rows = [];

		if ($this->shouldReportRow($bspTicket, $formData)) {
			$currencyDecimals = \App\Config\CurrencyDecimals::getCurrencyDecimals($bspTicket->priceBalancePayable->currency);

			// row 1
			$rowPrice = $bspTicket->priceCommission->amount + $bspTicket->priceCommissionVat->amount + $bspTicket->priceCommissionSupplementary->amount;
			list($serviceCode, $priceVat) = $this->chooseRowVAT_supplier($bspTicket, $formData);
			$priceComponents = $this->splitPrice($rowPrice, $priceVat->percent, $currencyDecimals);

			$baseRow = \Entity\SupplierInvoiceRow::insert();

			$ticketNo = $bspTicket->ticketNumber;
			if ($bspTicket->extraData->isRefund) {
				$ticketNo = $ticketNo.'R';
			}

			$baseRow->ticketNo =		$ticketNo;
			$baseRow->serviceCode = 	$serviceCode;


			$baseRow->externalReservationDate = $bspTicket->ticketDate;
			$baseRow->quantity = 		1;
			$baseRow->measureUnit = 	$this->lang['generateSupplierInvoice']['rows']['measureUnit'];

			$row = clone $baseRow;
			$row->description = 	sprintf("CC/R1:%s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
			$row->priceValue = 		-1*$priceComponents->value;
			$row->vatValue = 		-1*$priceComponents->vat;
			$row->vatText = 		$priceVat->label;
			/*
			 * // $bspTicket['balancePayable'] should always be negative, and the oposite of $newRow['totalPrice']
			if (abs($rowPrice+$bspTicket['balancePayable'])>0.01) {	// i'm comparing things in this way because of floating comparison point discrepancies
				\Log::getInstance()->warning(sprintf("[%s] the row total price is different from the balance payable. totalPrice should be positive and equal to the negated balancePayable", __METHOD__), Array(
					'ticketNo' => 						$ticketNo,
					'totalPrice(calculated)' => 		$rowPrice,
					'totalPrice(BSP->balancePayable)' => $bspTicket['balancePayable'],
				));
			}
			*/
			if ($rowPrice) {
				$rows[] = $row;
			}

			// row 1-1
			$rowPrice = $bspTicket->priceSupplierCash->amount;
			list(, $priceVat) = $this->chooseRowVAT_supplier($bspTicket, $formData);
			$priceComponents = $this->splitPrice($rowPrice, $priceVat->percent, $currencyDecimals);

			$row = clone $baseRow;
			$row->description = 	sprintf("CC/R1-1:%s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
			$row->priceValue = 		$priceComponents->value;
			$row->vatValue = 		$priceComponents->vat;
			$row->vatText = 		$priceVat->label;
			if ($rowPrice) {
				$rows[] = $row;
			}

			// row 1-2
			$rowPrice = $bspTicket->priceTaxCash->amount;
			list(, $priceVat) = $this->chooseRowVAT_tax($bspTicket, $formData);
			$priceComponents = $this->splitPrice($rowPrice, $priceVat->percent, $currencyDecimals);

			$row = clone $baseRow;
			$row->description = 	sprintf("CC/R1-2:%s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
			$row->priceValue = 		$priceComponents->value;
			$row->vatValue = 		$priceComponents->vat;
			$row->vatText = 		$priceVat->label;
			if ($rowPrice) {
				$rows[] = $row;
			}

			// row 2
			$rowPrice = $bspTicket->priceSupplierCc->amount;
			list(, $priceVat) = $this->chooseRowVAT_supplier($bspTicket, $formData);
			$priceComponents = $this->splitPrice($rowPrice, $priceVat->percent, $currencyDecimals);

			$row = clone $baseRow;
			$row->description = 	sprintf("CC/R2:%s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
			$row->priceValue = 		$priceComponents->value;
			$row->vatValue = 		$priceComponents->vat;
			$row->vatText = 		$priceVat->label;
			if ($rowPrice) {
				$rows[] = $row;
			}

			// row 3
			$rowPrice = $bspTicket->priceTaxCc->amount;
			list(, $priceVat) = $this->chooseRowVAT_tax($bspTicket, $formData);
			$priceComponents = $this->splitPrice($rowPrice, $priceVat->percent, $currencyDecimals);

			$row = clone $baseRow;
			$row->description = 	sprintf("CC/R3:Tax for ticket %s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
			$row->priceValue = 		$priceComponents->value;
			$row->vatValue = 		$priceComponents->vat;
			$row->vatText = 		$priceVat->label;
			if ($rowPrice) {
				$rows[] = $row;
			}

			// row 4 (reverse of row 2)
			$rowPrice = $bspTicket->priceSupplierCc->amount;
			list(, $priceVat) = $this->chooseRowVAT_supplier($bspTicket, $formData);
			$priceComponents = $this->splitPrice($rowPrice, $priceVat->percent, $currencyDecimals);

			$row = clone $baseRow;
			$row->description = 	sprintf("CC/R4:%s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
			$row->priceValue = 		-1*$priceComponents->value;
			$row->vatValue = 		-1*$priceComponents->vat;
			$row->vatText = 		$priceVat->label;
			if ($rowPrice) {
				$rows[] = $row;
			}

			// row 5 (reverse of row 3)
			$rowPrice = $bspTicket->priceTaxCc->amount;
			list(, $priceVat) = $this->chooseRowVAT_tax($bspTicket, $formData);
			$priceComponents = $this->splitPrice($rowPrice, $priceVat->percent, $currencyDecimals);

			$row = clone $baseRow;
			$row->description = 	sprintf("CC/R5:Tax for ticket %s-%s\n%s", $bspTicket->supplierCode3, $ticketNo, $bspTicket->ticketDate);
			$row->priceValue = 		-1*$priceComponents->value;
			$row->vatValue = 		-1*$priceComponents->vat;
			$row->vatText = 		$priceVat->label;
			if ($rowPrice) {
				$rows[] = $row;
			}

		}

		return $rows;
	}


	/**
	 * Split the price into VAT and value components
	 * Taken from old bspBalanceHelper
	 * @param Float $amount
	 * @param Float $vatPercent
	 * @return \Modules\Bsp\Listing\Types\PriceWithVat
	 */
	protected function splitPrice($amount, $vatPercent, int $currencyDecimals)
	{
		$price = new Types\PriceWithVat();

		$price->value = 0 + round($amount*100/(100+$vatPercent), $currencyDecimals);
		$price->vat = 	0 + round($amount-$price->value, $currencyDecimals);

		$price->total = $amount;

		return $price;
	}

	/**
	 * Determine the VAT supplier data according to the ticket type(location)
	 * @param \App\Bsp\BspRow $bspTicket
	 * @param Array $formData
	 * @return \Modules\Bsp\Listing\Types\PercentWithLabel
	 */
	protected function chooseRowVAT_supplier(\App\Bsp\BspRow $bspTicket, $formData)
	{
		$cacheKey = implode(",", [$bspTicket->extraData->isEMD, $bspTicket->extraData->isAdm, $bspTicket->extraData->isTicketing, $bspTicket->locationType]);
		if ($this->cache[__METHOD__][$cacheKey]) {
			return $this->cache[__METHOD__][$cacheKey];
		}

		// assume these are filled in correctly
		$selectedSupplierInvoiceVats = (\App\Bsp\Config::getInstance()->get("supplierInvoiceVat")?unserialize(\App\Bsp\Config::getInstance()->get("supplierInvoiceVat")):[]);

		$ticketingVatArr = null;
		if ($bspTicket->extraData->isEMD) {
			$ticketingVatArr = $selectedSupplierInvoiceVats['EMD'];
		}
		elseif ($bspTicket->extraData->isAdm) {
			$ticketingVatArr = $selectedSupplierInvoiceVats['ADM'];
		}
		else {
			// ticketing, refund, void. Assume we're still reporting ticketing services
			$ticketingVatArr = $selectedSupplierInvoiceVats['ticketing'];
		}

		$TINASupplierInvoicesVats = \App\Config\SupplierInvoice_Vats::getInstance()->getAll();
		$ret = new Types\PercentWithLabel();
		switch ($bspTicket->locationType) {
			case 'I':
				list($srv, $v) = explode("|", $ticketingVatArr['international']['supplier']);
			break;
			case 'D':
				list($srv, $v) = explode("|", $ticketingVatArr['domestic']['supplier']);
			break;
			case '':
				list($srv, $v) = explode("|", $ticketingVatArr['undefined']['supplier']);
			break;
		}

		$ret->percent = $TINASupplierInvoicesVats[$srv]['list'][$v];
		$ret->label = $v;

		$this->cache[__METHOD__][$cacheKey] = [$srv, $ret];

		return $this->cache[__METHOD__][$cacheKey];
	}

	/**
	 * Determine the VAT tax data according to the ticket type(location)
	 * @param \App\Bsp\BspRow $bspTicket
	 * @param array $formData
	 * @return \Modules\Bsp\Listing\Types\PercentWithLabel
	 */
	protected function chooseRowVAT_tax(\App\Bsp\BspRow $bspTicket, $formData)
	{
		$cacheKey = implode(",", [$bspTicket->extraData->isEMD, $bspTicket->extraData->isAdm, $bspTicket->extraData->isTicketing, $bspTicket->locationType]);
		if ($this->cache[__METHOD__][$cacheKey]) {
			return $this->cache[__METHOD__][$cacheKey];
		}

		// assume these are filled in correctly
		$selectedSupplierInvoiceVats = (\App\Bsp\Config::getInstance()->get("supplierInvoiceVat")?unserialize(\App\Bsp\Config::getInstance()->get("supplierInvoiceVat")):[]);

		$ticketingVatArr = null;
		if ($bspTicket->extraData->isEMD) {
			$ticketingVatArr = $selectedSupplierInvoiceVats['EMD'];
		}
		elseif ($bspTicket->extraData->isAdm) {
			$ticketingVatArr = $selectedSupplierInvoiceVats['ADM'];
		}
		else {
			// ticketing, refund, void. Assume we're still reporting ticketing services
			$ticketingVatArr = $selectedSupplierInvoiceVats['ticketing'];
		}

		$TINASupplierInvoicesVats = \App\Config\SupplierInvoice_Vats::getInstance()->getAll();
		$ret = new Types\PercentWithLabel();

		switch ($bspTicket->locationType) {
			case 'I':
				list($srv, $v) = explode("|", $ticketingVatArr['international']['tax']);
			break;
			case 'D':
				list($srv, $v) = explode("|", $ticketingVatArr['domestic']['tax']);
			break;
			case '':
				list($srv, $v) = explode("|", $ticketingVatArr['undefined']['tax']);
			break;
		}

		$ret->percent = $TINASupplierInvoicesVats[$srv]['list'][$v];
		$ret->label = $v;

		$this->cache[__METHOD__][$cacheKey] = [$srv, $ret];

		return $this->cache[__METHOD__][$cacheKey];
	}
}
