<?php
namespace Entity;

class SupplierInvoicePayment extends \App\Entity\GeneralMtAbstract
{

	/**
	 * Indicate if the operations available for the current entity must be done inside a transaction or not.
	 * @var Boolean
	 */
	protected $saveInsideTransaction = true;

	protected $addAndUpdateDateTimeFormat = 'timestamp'; // TODO: to be eliminated after the table columns are renamed and replaced with datetime

	/**
	 * Indicates which will be the new invoice payed status (used on `insert`/`update` operation).
	 * @var String
	 */
	protected $newSupplierInvoicePaidStatus = null;

	/**
	 * Stores the document value in supplier invoice currency (to determine the newCashedValue on `insert`/`update` operation).
	 * @var Double
	 */
	protected $docValueInvCurr = null;

	/**
	 * (non-PHPdoc)
	 * @see \App\Entity\GeneralMtAbstract::setTableGroup()
	 */
	protected function setTableGroup(): void
	{
		global $DBTSupplierInvoicePayment;
		$this->tableGroup = $DBTSupplierInvoicePayment;
	}

	/**
	 * Static method for blocking (confirming) an invoice cashing document.
	 * @return InvoiceCashing
	 */
	static public function block($id)
	{
		return new self('block', $id);
	}

	/**
	 * Static method for unblocking (de-confirming) an invoice cashing document.
	 * @return InvoiceCashing
	 */
	static public function unblock($id)
	{
		return new self('unblock', $id);
	}


	/**
	 * (non-PHPdoc)
	 * @see \App\Entity\SelectAbstract::getAttribute()
	 */
	protected function getAttribute($var)
	{

		// CHECKS EACH FIELD THAT CAN BE EXTRACTED FOR THIS INVOICE ///////////////////////////////
		switch ($var) {

			// NORMAL FIELDS ----------------------------------------------------------------------
			case 'deleted': case 'idPaymentSeries': case 'invoiceNumber':
			case 'docDate': case 'docNumber': case 'value': case 'currency': case 'invoicePayedValue':
			case 'idBankAccount': case 'explanations': case 'comments': case 'idImportedDoc': case 'remarks':
			case 'idUser': case 'idOffice': case 'idAddUser': case 'addTime':
			case 'idUpdateUser': case 'lastUpdateTime': case 'updatesCount': case 'idBlockUser': case 'blockTime': {
				return $this->fields[$var];
			}

			// supplier invoice
			case 'idSupplierInvoice': {
				$this->fields['idSupplierInvoice'] = $this->fields['idInvoice'];
				return $this->fields['idSupplierInvoice'];
			}
			case 'supplierInvoice': {
				return SupplierInvoice::select($this->fields['idInvoice'], $this->selectForUpdate);
			}

			// the imported document object
			case 'importedDocument': {
				return \Entity\ImportedDocument::select($this->fields['idImportedDoc'], $this->selectForUpdate);
			}

			// payment series
			case 'paymentSeries': {
				return \Entity\DocumentSeries::select($this->fields['idPaymentSeries'], $this->selectForUpdate);
			}

			// document type
			case 'docTypeCode': {
				$this->fields['docTypeCode'] = $this->fields['docType'];
				return $this->fields['docTypeCode'];
			}
			case 'docTypeName': {
				if (!array_key_exists('docTypeName', $this->fields)) {
					$this->fields['docTypeName'] = \App\Config\PaymentDocuments::getInstance()->getName( $this->fields['docType'] );
				}
				return $this->fields['docTypeName'];
			}

			case 'bankAccount': {
				return AgencyBankAccount::select($this->fields['idBankAccount'], $this->selectForUpdate);
			}

			// gets an associative array of list of documents (indexed by their id) which contains all the documents generated on this pivot
			case 'documentsList': {
				if (!array_key_exists($var, $this->fields)) {
					$dlObj = new \App\Entity\Utils\PivotDocumentsList('payment', $this->id);
					$this->fields[$var] = $dlObj->getDocumentsList($this->selectForUpdate);
				}
				return $this->fields[$var];
			}

			// extra info for the document
			case 'extraInfo': {
				if (!is_array($this->fields['extraInfo'])) {
					if ($this->fields['extraInfo']) {
						$this->fields['extraInfo'] = unserialize($this->fields['extraInfo']);
					} else {
						$this->fields['extraInfo'] = [];
					}
				}
				return $this->fields['extraInfo'];
			}

			// processed remarks
			case 'processedRemarks': {
				if (!is_array($this->fields['processedRemarks'])) {
					$this->fields['processedRemarks']=$this->getUnserializedProcessedRemarks();
				}
				return $this->fields['processedRemarks'];
			}

			// responsible user/office, add/update/block user
			case 'user': {
				return User::select($this->fields['idUser'], $this->selectForUpdate);
			}
			case 'office': {
				return Office::select($this->fields['idOffice'], $this->selectForUpdate);
			}
			case 'addUser': {
				return User::select($this->fields['idAddUser'], $this->selectForUpdate);
			}
			case 'updateUser': {
				return User::select($this->fields['idUpdateUser'], $this->selectForUpdate);
			}
			case 'blockUser': {
				return User::select($this->fields['idBlockUser'], $this->selectForUpdate);
			}
			case 'issueUser': {
				return \Entity\User::select($this->fields['idIssueUser'], $this->selectForUpdate);
			}

			// $var IS NOT VALID - throws Exception -----------------------------------------------
			default: {
				return parent::getAttribute($var);
			}
		}
	}



	/**
	 * (non-PHPdoc)
	 * @see \App\Entity\GeneralAbstract::setAttribute()
	 */
	protected function setAttribute($var, $value)
	{
		// check that operation is okay
		if ($this->operation != 'insert' and $this->operation != 'update') {
			throw new \Exception('You can use __set magic method only with insert/update operation in `'.__CLASS__.'` class.');
		}

		// CHECKS EACH FIELD THAT CAN BE SET FOR AN INVOICE PAYMENT ///////////////////////////////
		switch ($var) {

			// SIMPLE FIELDS ------------------------------------------------------------------
			case 'idPaymentSeries':	case 'docType':  case 'docDate':  case 'docNumber': case 'value':
			case 'currency': case 'idBankAccount': case 'explanations': case 'comments': case 'idUser': case 'idOffice': {
				$this->fields[$var]=$value;
				break;
			}

			// invoice id - can only be set for insert operations
			case 'idInvoice': {
				if ($this->operation != 'insert') {
					throw new \Exception('The supplier invoice id cannot be set for payment document update operation.');
				}
				$this->fields[$var] = $value;
				break;
			}

			// process remarks
			case 'remarks': {
				$this->fields['remarks'] = $value;
				$this->fields['processedRemarks'] = \App\Config\Remarks::process($value, 'supplierInvoicePayment');
				break;
			}

			// extra info - updates only the keys that are not null (the other keys in the old array are not touched; the null keys are eliminated and he other keys are added/changed)
			case 'extraInfo': {
				if (!is_array($value)) {
					throw new \Exception("The field `extraInfo` can only be set as an array for the `".__CLASS__."` entity.");
				}
				if (!$value) {
					throw new \Exception("The field `extraInfo` cannot be set as an empty array for the `".__CLASS__."` entity.");
				}
				$this->fields[$var] = $value;
				break;
			}

			// the imported document from where the value will be used
			case 'idImportedDoc': {
				if ($this->operation != 'insert') {
					throw new \Exception('The `idImportedDoc` can only be set for payment document `insert` operation.');
				}
				$this->fields[$var] = $value;
				break;
			}

			// throw exception if bad attribute
			default: {
				throw new \Exception("Wrong var ({$var}) for the '".__CLASS__."' class.");
			}
		}
	}

	/**
	 * On 'insert' operation - this will set the table month from the reservation id.
	 * @see \App\Entity\GeneralAbstract::save_preCheck()
	 */
	protected function save_preCheck()
	{
		// sets the table month
		if ($this->operation == 'insert' and $this->fields['idInvoice']) {
			$this->setTableMonth($this->fields['idInvoice']);
		}

	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Entity\GeneralAbstract::save_filterSaveData()
	 */
	protected function save_filterSaveData(Array $saveData)
	{
		// completely unset the fields that are set as blank and may create confusion
		if (!$saveData['idPaymentSeries']) {
			unset($saveData['idPaymentSeries']);
		}

		// processed remarks
		if (array_key_exists('remarks', $saveData)) {
			$saveData['processedRemarks'] = $this->getSerializedProcessedRemarks();
		}

		// OPERATION = insert (prepare some extra fields) /////////////////////////////////////////
		if ($this->operation == 'insert') {

			// determine 'invoicePayedValue'
			$saveData['invoicePayedValue'] = \App\CurrencyExchange\Rates::getInstance($this->fields['docDate'])->convert($this->fields['value'], $this->fields['currency'], \Entity\SupplierInvoice::selectForUpdate($this->fields['idInvoice'])->currency, null);
		}

		// OPERATION = update //////////////////////////////////////////////////////////////////////
		if ($this->operation == 'update') {
			if ($this->fields['docDate']) {
				$docDate = $this->fields['docDate'];
			}
			else {
				$docDate = self::selectForUpdate($this->id)->docDate;
			}

			if ($this->fields['value']) {
				$docValue = $this->fields['value'];
			}
			else {
				$docValue = self::selectForUpdate($this->id)->value;
			}

			if ($this->fields['currency']) {
				$docCurrency = $this->fields['currency'];
			}
			else {
				$docCurrency = self::selectForUpdate($this->id)->currency;
			}

			// determine 'invoicePayedValue'
			$saveData['invoicePayedValue'] = \App\CurrencyExchange\Rates::getInstance($docDate)->convert($docValue, $docCurrency, self::selectForUpdate($this->getId())->supplierInvoice->currency, null);

			// check series and number (only one of them must be set)
			if ($saveData['idPaymentSeries']) {
				$saveData['docNumber'] = 0;
			}
			if ($saveData['docNumber']) {
				$saveData['idPaymentSeries'] = 0;
			}
		}

		// OPERATION = block //////////////////////////////////////////////////////////////////////////
		if ($this->operation == 'block') {
			$saveData['blockTime'] = 	TIMESTAMP;
			$saveData['idBlockUser'] = 	CURRENT_ID_USER;
		}

		// OPERATION = block //////////////////////////////////////////////////////////////////////////
		if ($this->operation == 'unblock') {
			$saveData['blockTime'] = 	0;
			$saveData['idBlockUser'] = 	0;
		}

		// clean extra info
		if ($this->fields['extraInfo']) {
			$extraInfo = self::selectForUpdate($this->id)->extraInfo;
			foreach($this->fields['extraInfo'] as $key => $val) {
				if ($val === null) {
					unset($extraInfo[$key]);
				}
				else $extraInfo[$key] = $val;
			}
			if ($extraInfo) {
				$saveData['extraInfo'] = serialize($extraInfo);
			}
			else {
				$saveData['extraInfo'] = '';
			}
		}

		return $saveData;
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Entity\GeneralAbstract::save_postProcess()
	 */
	protected function save_postProcess(Array $saveData)
	{

		// DO THE MAIN OPERATIONS /////////////////////////////////////////////////////////////////
		// for insert operation
		if ($this->operation == 'insert') {
			$this->save_postProcess_insert();
		}

		// for update operation
		if ($this->operation == 'update') {
			$this->save_postProcess_update();
		}

		// for delete operation
		if ($this->operation == 'delete') {
			$this->save_postProcess_delete();
		}

		// UPDATE THE VIRTUAL MONTH FOR THIS CASHING DOCUMENT /////////////////////////////////////
		// docDate is mandatory for 'insert' and optional for 'update'; if 'nodate is set, then we have 'update' without date setting so we do NOT have to run this
		if (($this->operation == 'insert' or $this->operation == 'update' ) and $saveData['docDate']) {
			\App\Utils\Db\TablesUtils::getInstance()->insertRealYmFromVirtualYm('supplierInvoicePayment', $this->tableMonth, $this->fields['docDate']);
		}
	}

	/**
	 * Post processing data for insert operation.
	 * @param array $saveData
	 */
	private function save_postProcess_insert()
	{

		global $DBTSupplierInvoices, $DBTImportedDocuments_documents;

		// determine the class attribute $this->newSupplierInvoicePaidStatus
		$this->save_postProcess_insert_storePaidDetails();

		// UPDATE SUPPLIER DEBIT DETAILS //////////////////////////////////////////////////////////
		$this->save_postProcess_insert_refreshClientDetails();

		// SUBSTRACT IMPORTED DOCUMENT ////////////////////////////////////////////////////////////
		if ($this->fields['idImportedDoc']) {

			$importedDoc = \Entity\ImportedDocument::selectForUpdate($this->fields['idImportedDoc']);

			// determine the imported document status
			if ( round(abs($importedDoc->usedValue + $this->fields['value'] - $importedDoc->docValue), 3) < 0.001 ) { // rounds as to overcome floating point substraction
				$impDocSts = 'processed';
			}
			else {
				$impDocSts = 'partial';
			}

			// UPDATE IMPORTED DOCUMENT ---------------------------------------------------------------
			$fields = ['sql:usedValue'=>"`usedValue`+{$this->fields['value']}", 'status'=>$impDocSts, 'sql:idDocumentsTina'=>"CONCAT(`idDocumentsTina`, '{$this->getId()}', '|')"];

			$this->save_executeSql_update($DBTImportedDocuments_documents, $fields, 'id', $this->fields['idImportedDoc']);

			// empty cache
			$this->save_updateCache_addEntry('ImportedDocument', $this->fields['idImportedDoc']);

		}

		// UPDATE INVOICE PAID DETAILS //////////////////////////////////////////////////////
		$fields = ['sql:payedValue'=>"`payedValue`+{$this->docValueInvCurr}", 'statusFin'=>$this->newSupplierInvoicePaidStatus];
		$this->save_executeSql_update($DBTSupplierInvoices.$this->getTableMonth(), $fields, 'id', $this->fields['idInvoice']);

		// clean the invoice cache
		$this->save_updateCache_addEntry('SupplierInvoice', $this->fields['idInvoice']);
		$inv = \Entity\SupplierInvoice::selectForUpdate($this->fields['idInvoice']);
		$this->save_updateCache_addEntry('Supplier', $inv->idSupplier);

		$this->save_updateCache_addEntry('User', $this->fields['idUser']);
		$this->save_updateCache_addEntry('Office', $this->fields['idOffice']);
	}

	/**
	 * Determines and stores the class attributes $this->newSupplierInvoicePaidStatus and $this->docValueInvCurr for 'insert' operation.
	 */
	private function save_postProcess_insert_storePaidDetails()
	{

		// check input
		if ($this->operation != 'insert') {
			throw new \Exception('The method `'.__METHOD__.'` can only be used with `insert` operation.');
		}

		// DOCUMENT CASHED VALUE ------------------------------------------------------------------
		$supplierInvoice = \Entity\SupplierInvoice::selectForUpdate($this->fields['idInvoice']);
		$this->docValueInvCurr = \App\CurrencyExchange\Rates::getInstance($this->fields['docDate'])->convert($this->fields['value'], $this->fields['currency'], $supplierInvoice->currency, null);

		// DOCUMENT CASHED STATUS -----------------------------------------------------------------
		// the supplier invoice new paid value
		$newPaidValue = $supplierInvoice->payedValue + $this->docValueInvCurr;

		// determine and store the status
		$determineStatusObj = new \App\Utils\Invoices\DetermineSupplierInvoicePayedStatus($supplierInvoice->totalValue);
		$this->newSupplierInvoicePaidStatus = $determineStatusObj->getStatus($newPaidValue);

	}


	private function save_postProcess_insert_refreshClientDetails()
	{

		// check input
		if ($this->operation!='insert')
			throw new \Exception('The method `'.__METHOD__.'` can only be used with `insert` operation.');
		if (!$this->newSupplierInvoicePaidStatus)
			throw new \Exception('The method `'.__METHOD__.'` cannot be executed as the protected class attribute `newSupplierInvoicePaidStatus` is not set.');

		$invoice = \Entity\SupplierInvoice::selectForUpdate($this->fields['idInvoice']);

		// DETERMINE HOW THIS DOCUMENT WILL INFLUENCE THE SUPPLIER BALANCE ------------------------
		// initial and final payed value
		if ($invoice->statusFin=='t' or $invoice->statusFin=='o')
			$initialPayedValue = $invoice->totalValue;
		elseif ($invoice->statusFin=='p')
			$initialPayedValue = $invoice->payedValue;
		else
			$initialPayedValue = 0;

		// final payed value
		if ($this->newSupplierInvoicePaidStatus=='t' or $this->newSupplierInvoicePaidStatus=='o')
			$finalPayedValue = $invoice->totalValue;
		elseif ($this->newSupplierInvoicePaidStatus=='p')
			$finalPayedValue = $invoice->payedValue+$this->docValueInvCurr;
		else
			$finalPayedValue = 0;

		// UPDATE CUSTOMER CURRENT BALANCE --------------------------------------------------------
		\App\PartnerBalance\Supplier::getInstance($invoice->idSupplier)->updateBalance_updatePaymentDoc($invoice->currency, $initialPayedValue, $finalPayedValue);

		// UPDATE CUSTOMER HISTORY BALANCE (DELETE THE NEWER BALANCES) ----------------------------
		\App\PartnerBalance\History\Supplier::getInstance($invoice->idSupplier, $invoice->currency)->flushBalances($this->fields['docDate']);

		if ($this->fields['idImportedDoc']) {
			$impDocEnt = \Entity\ImportedDocument::selectForUpdate($this->fields['idImportedDoc']);
			if ($impDocEnt->idSupplier) {
				// UPDATE SUPPLIER CURRENT CREDIT BALANCE -------------------------------------------------
				\App\PartnerBalance\ImpDocBalance\Supplier::getInstance($impDocEnt->idSupplier)->updateBalance_addPaymentDoc($this->fields['currency'], $this->fields['value']);
			}
		}

	}

	/**
	 * Post processing data for update operation
	 */
	private function save_postProcess_update()
	{
		global $DBTSupplierInvoices;

		// determine the class attribute $this->newSupplierInvoicePaidStatus
		$this->save_postProcess_update_storePaidDetails();

		// UPDATE SUPPLIER DEBIT DETAILS //////////////////////////////////////////////////////////
		$this->save_postProcess_update_refreshClientDetails();

		// UPDATE INVOICE PAID DETAILS

		// determine new value for invoice payed value
		$supplierInvoicePaymentEntity = self::selectForUpdate($this->getId());
		$supplierInvoiceEntity = $supplierInvoicePaymentEntity->supplierInvoice;

		$newPaidValue = $supplierInvoiceEntity->payedValue - $supplierInvoicePaymentEntity->invoicePayedValue + $this->docValueInvCurr;

		// update supplier invoice details
		$fields = ['payedValue' => $newPaidValue, 'statusFin' => $this->newSupplierInvoicePaidStatus];
		$this->save_executeSql_update($DBTSupplierInvoices.$this->getTableMonth(), $fields, 'id', $supplierInvoiceEntity->getId());

		// clean the invoice cache
		$this->save_updateCache_addEntry('SupplierInvoice', $supplierInvoiceEntity->getId());
		$this->save_updateCache_addEntry('Supplier', $supplierInvoiceEntity->idSupplier);

		$idUser = $this->fields['idUser'];
		if (!$idUser) {
			$idUser = $supplierInvoicePaymentEntity->idUser;
		}

		$idOffice = $this->fields['idOffice'];
		if (!$idOffice) {
			$idOffice = $supplierInvoicePaymentEntity->idOffice;
		}

		$this->save_updateCache_addEntry('User', $idUser);
		$this->save_updateCache_addEntry('Office', $idOffice);

	}

	private function save_postProcess_update_storePaidDetails()
	{

		// check input
		if ($this->operation != 'update') {
			throw new \Exception('The method `'.__METHOD__.'` can only be used with `update` operation.');
		}

		// get entities
		$supplierInvoicePaymentEntity = self::selectForUpdate($this->getId());
		$supplierInvoiceEntity = $supplierInvoicePaymentEntity->supplierInvoice;

		// determine doc date
		if (array_key_exists('docDate', $this->fields)) {
			$docDate = $this->fields['docDate'];
		}
		else {
			$docDate = $supplierInvoicePaymentEntity->docDate;
		}

		// doc value and currency haven't changes
		if (array_key_exists('value', $this->fields)) {
			$docValue = $this->fields['value'];
			$docCurrency = $this->fields['currency'];
		}
		else {
			$docValue = 	$supplierInvoicePaymentEntity->value;
			$docCurrency = 	$supplierInvoicePaymentEntity->currency;
		}

		// DOCUMENT PAID STATUS
		$this->docValueInvCurr = \App\CurrencyExchange\Rates::getInstance($docDate)->convert($docValue, $docCurrency, $supplierInvoiceEntity->currency, null);

		$newPaidValue = $supplierInvoiceEntity->payedValue - $supplierInvoicePaymentEntity->invoicePayedValue + $this->docValueInvCurr;

		// determine and store the status
		$determineStatusObj = new \App\Utils\Invoices\DetermineSupplierInvoicePayedStatus($supplierInvoiceEntity->totalValue);
		$this->newSupplierInvoicePaidStatus = $determineStatusObj->getStatus($newPaidValue);

	}

	private function save_postProcess_update_refreshClientDetails()
	{

		// check input
		if ($this->operation!='update') throw new \Exception('The method `'.__METHOD__.'` can only be used with `update` operation.');
		if (!$this->newSupplierInvoicePaidStatus) throw new \Exception('The method `'.__METHOD__.'` cannot be executed as the protected class attribute `newSupplierInvoicePaidStatus` is not set.');

		// the payed invoice
		$doc=self::selectForUpdate($this->id);
		$invoice=$doc->supplierInvoice;

		// new doc date
		if (array_key_exists('docDate', $this->fields)) $newDocDate=$this->fields['docDate'];
		else $newDocDate=$doc->docDate;

		// DETERMINE HOW THIS DOCUMENT WILL INFLUENCE THE CLIENT BALANCE --------------------------
		// initial/final payed value
		if ($invoice->statusFin=='t' or $invoice->statusFin=='o') $initialPayedValue = $invoice->totalValue;
		elseif ($invoice->statusFin=='p') $initialPayedValue = $invoice->payedValue;
		else $initialPayedValue = 0;
		if ($this->newSupplierInvoicePaidStatus=='t' or $this->newSupplierInvoicePaidStatus=='o') $finalPayedValue = $invoice->totalValue;
		elseif ($this->newSupplierInvoicePaidStatus=='p') $finalPayedValue = $invoice->payedValue-$doc->invoicePayedValue+$this->docValueInvCurr;
		else $finalPayedValue = 0;

		// UPDATE SUPPLIER CURRENT BALANCE --------------------------------------------------------
		\App\PartnerBalance\Supplier::getInstance($invoice->idSupplier)->updateBalance_updatePaymentDoc($invoice->currency, $initialPayedValue, $finalPayedValue);

		// UPDATE SUPPLIER HISTORY BALANCE (DELETE THE STALE BALANCES) ----------------------------
		$flushFromDate = min($newDocDate, $doc->docDate);
		\App\PartnerBalance\History\Supplier::getInstance($invoice->idSupplier, $invoice->currency)->flushBalances($flushFromDate);

	}

	private function save_postProcess_delete()
	{
		global $DBTSupplierInvoices, $DBTImportedDocuments_documents;

		// get entities
		$supplierInvoicePaymentEntity = self::selectForUpdate($this->getId());

		// determine the class attribute `newSupplierInvoicePaidStatus`
		$this->save_postProcess_delete_storePaidDetails();

		// UPDATE SUPPLIER DEBIT DETAILS //////////////////////////////////////////////////////////
		$this->save_postProcess_delete_refreshClientDetails();

		// UPDATE IMPORTED DOCUMENT
		if ($supplierInvoicePaymentEntity->idImportedDoc) {
			$importedDoc = $supplierInvoicePaymentEntity->importedDocument;

			// determine the imported document status
			if (abs($importedDoc->usedValue - $supplierInvoicePaymentEntity->value) < 0.001) {
				$importedDocStatus = 'new';
			}
			else {
				$importedDocStatus = 'partial';
			}

			// determine the new id docs tina (eliminate the current deleting document from the list)
			$newIdDocsTina = str_replace($this->getId().'|', '', $importedDoc->idDocumentsTina);

			// UPDATE IMPORTED DOCUMENT
			$fields = ['sql:usedValue'=>"`usedValue`-".(float)$supplierInvoicePaymentEntity->value, 'status' => $importedDocStatus, 'idDocumentsTina'=>$newIdDocsTina];
			$this->save_executeSql_update($DBTImportedDocuments_documents, $fields, 'id', $supplierInvoicePaymentEntity->idImportedDoc);

			// empty cache
			$this->save_updateCache_addEntry('ImportedDocument', $supplierInvoicePaymentEntity->idImportedDoc);
		}

		// UPDATE INVOICE PAID DETAILS
		$fields = ['sql:payedValue'=>"`payedValue`-{$supplierInvoicePaymentEntity->invoicePayedValue}", 'statusFin'=>$this->newSupplierInvoicePaidStatus];
		$this->save_executeSql_update($DBTSupplierInvoices.$this->getTableMonth(), $fields, 'id', $supplierInvoicePaymentEntity->idInvoice);

		// empty cache
		$this->save_updateCache_addEntry('SupplierInvoice', $supplierInvoicePaymentEntity->idInvoice);
		$this->save_updateCache_addEntry('Supplier', $supplierInvoicePaymentEntity->supplierInvoice->idSupplier);

		$this->save_updateCache_addEntry('User', $supplierInvoicePaymentEntity->idUser);
		$this->save_updateCache_addEntry('Office', $supplierInvoicePaymentEntity->idOffice);
	}

	private function save_postProcess_delete_storePaidDetails()
	{

		// check input
		if ($this->operation != 'delete') {
			throw new \Exception('The method `'.__METHOD__.'` can only be used with `delete` operation.');
		}

		// the current document details
		$supplierInvoicePaymentEntity = self::selectForUpdate($this->getId());
		$supplierInvoiceEntity = $supplierInvoicePaymentEntity->supplierInvoice;

		// DOCUMENT PAID STATUS -----------------------------------------------------------------
		// the invoice new paid value
		$newPaidValue = $supplierInvoiceEntity->payedValue - $supplierInvoicePaymentEntity->invoicePayedValue;

		// determine and store the status
		$determineStatusObj = new \App\Utils\Invoices\DetermineSupplierInvoicePayedStatus($supplierInvoiceEntity->totalValue);
		$this->newSupplierInvoicePaidStatus = $determineStatusObj->getStatus($newPaidValue);

	}


	private function save_postProcess_delete_refreshClientDetails()
	{

		// check input
		if ($this->operation!='delete') throw new \Exception('The method `'.__METHOD__.'` can only be used with `delete` operation.');
		if (!$this->newSupplierInvoicePaidStatus) throw new \Exception('The method `'.__METHOD__.'` cannot be executed as the protected class attribute `newSupplierInvoicePaidStatus` is not set.');

		$doc=self::selectForUpdate($this->id);
		$invoice=$doc->supplierInvoice;

		// DETERMINE HOW THIS DOCUMENT WILL INFLUENCE THE CLIENT BALANCE --------------------------
		// initial/final payed value
		if ($invoice->statusFin=='t' or $invoice->statusFin=='o') $initialPayedValue = $invoice->totalValue;
		elseif ($invoice->statusFin=='p') $initialPayedValue = $invoice->payedValue;
		else $initialPayedValue = 0;
		if ($this->newSupplierInvoicePaidStatus=='t' or $this->newSupplierInvoicePaidStatus=='o') $finalPayedValue = $invoice->totalValue;
		elseif ($this->newSupplierInvoicePaidStatus=='p') $finalPayedValue = $invoice->payedValue-$doc->invoicePayedValue;
		else $finalPayedValue = 0;

		// UPDATE CUSTOMER CURRENT BALANCE ----------------------------------------------------
		\App\PartnerBalance\Supplier::getInstance($invoice->idSupplier)->updateBalance_updatePaymentDoc($invoice->currency, $initialPayedValue, $finalPayedValue);

		// UPDATE CUSTOMER HISTORY BALANCE (DELETE THE STALE BALANCES) ------------------------
		\App\PartnerBalance\History\Supplier::getInstance($invoice->idSupplier, $invoice->currency)->flushBalances($doc->docDate);

		if ($doc->idImportedDoc) {
			// UPDATE SUPPLIER CURRENT CREDIT BALANCE -------------------------------------------------
			if ($doc->importedDocument->idSupplier) {
				\App\PartnerBalance\ImpDocBalance\Supplier::getInstance($doc->importedDocument->idSupplier)->updateBalance_deletePaymentDoc($doc->currency, $doc->value);
			}
		}

	}


	protected function save_finishOperation()
	{
		parent::save_finishOperation();

		// get document type
		$doc = self::select($this->id);

		// EXPORT DATA FOR OPERATION `block` and `unblock`
		$operation = null;
		if ($this->operation == 'block') {
			$operation = 'confirm';
		}
		if ($this->operation == 'unblock') {
			$operation = 'unconfirm';
		}
		if ($this->operation == 'delete' and $doc->issueTime) {
			$operation = 'void';
		}

		if ($operation) {
			try {
				$supplierInvoicePaymentExport = new \App\ExportData\ExportFile\SupplierInvoicePayment($this->getId(), $operation);
				$supplierInvoicePaymentExport->writeFile();
			}
			catch (\App\MessagesException $ex) {
				$this->messages->removeSuccess();
				\Log::getInstance()->critical(sprintf('The supplier invoice payment XML was not generated. Error: %s', $ex->getMessagesCollector()->getMessageBulk()));
				$this->messages->addErrorCode('block_exportXmlFailed');
			}
		}
	}
}