<?php
require_once (APPPATH."libraries/blockchyp/vendor/autoload.php");

use \BlockChyp\BlockChyp;

class Invoice extends CI_Model
{
	public function __construct()
	{
      parent::__construct();
	}
	
	public function get_info($type,$invoice_id)
	{
		$this->db->select('terms.name as term_name,terms.description as term_description,'.$type.'_'.'invoices.*,'.($type == 'customer' ? 'CONCAT(person.first_name, " ", person.last_name)' : 'company_name').' as person, person.zip as zip,person.address_1 as address_1, person.person_id as person_id', false);
		$this->db->from($type.'_'.'invoices');
		$this->db->join($type.'s', $type.'s.person_id = '.$type.'_'.'invoices.'.$type.'_id','left');
		$this->db->join('people as person', 'person.person_id = '.$type.'_'.'invoices.'.$type.'_id','left');
		$this->db->join('terms', 'terms.term_id = '.$type.'_'.'invoices.term_id','left');
		
		$this->db->where('invoice_id',$invoice_id);
		$query = $this->db->get();
		
		if($query->num_rows()==1)
		{
			return $query->row();
		}
		else
		{
			//Get empty base parent object, as $item_id is NOT an item
			$invoice_obj=new stdClass();

			//Get all the fields from items table
			$fields = $this->db->list_fields($type.'_'.'invoices');

			foreach ($fields as $field)
			{
				$invoice_obj->$field='';
			}			

			$invoice_obj->person = '';
			$invoice_obj->term_name = '';
			return $invoice_obj;
		}
	}
		
	
	/*
	Perform a search on invoices
	*/
	function search($type,$search, $days_past_due = NULL, $deleted = 0, $limit=20, $offset=0, $column='invoice_date', $orderby='desc',$status="",$location_id_override=null)
	{
		if (!$deleted)
		{
			$deleted = 0;
		}
		
		$location_id = $location_id_override ? $location_id_override : $this->Employee->get_logged_in_employee_current_location_id();
		
		$this->db->select('terms.name as terms,'.$type.'_'.'invoices.*,'.($type == 'customer' ? 'CONCAT(person.first_name, " ", person.last_name)' : 'company_name').' as person, person.last_name as person_last_name, terms.description as term_description', false);
		$this->db->from($type.'_'.'invoices');
		$this->db->join($type.'s', $type.'s.person_id = '.$type.'_'.'invoices.'.$type.'_id','left');
		$this->db->join('people as person', 'person.person_id = '.$type.'_'.'invoices.'.$type.'_id','left');
		$this->db->join('terms', 'terms.term_id = '.$type.'_'.'invoices.term_id','left');
		
		$this->db->where($type.'_'.'invoices.deleted', $deleted);
		$this->db->where($type.'_'.'invoices.location_id', $location_id);
		
		if ($status == 2) {
			$this->db->where($type.'_'.'invoices.balance >', 0);
		} elseif($status == 3) {
			$this->db->where($type.'_'.'invoices.balance <=', 0);
		} else {
			$this->db->where($type.'_'.'invoices.balance >', 0);
		}

		if ($days_past_due !== NULL)
		{
			if($days_past_due == 'current'){
				$this->db->where('datediff(NOW(),phppos_'.$type.'_'.'invoices.due_date) < 30', NULL, FALSE);
			}
			else{
				$this->db->where('datediff(NOW(),phppos_'.$type.'_'.'invoices.due_date) > '.$days_past_due, NULL, FALSE);
			}
			$this->db->where($type.'_invoices.balance > 0');
		}
		
		if ($search)
		{
			$this->db->where("(
			".($type == 'customer' ? 'person.full_name' : 'company_name')." LIKE '".$this->db->escape_like_str($search)."%' or
			".$type.'_invoices.'.$type."_po LIKE '".$this->db->escape_like_str($search)."%' or 
			person.first_name LIKE '".$this->db->escape_like_str($search)."%' or 
			person.last_name LIKE '".$this->db->escape_like_str($search)."%' or 
			person.email LIKE '".$this->db->escape_like_str($search)."%' or 
			person.phone_number LIKE '".$this->db->escape_like_str($search)."%' or 
			person.full_name LIKE '".$this->db->escape_like_str($search)."%') and ".$type."_invoices.deleted=$deleted");					
		}
				
		$this->db->order_by($column, $orderby);
		$this->db->limit($limit);
		$this->db->offset($offset);
		
		
	 return $this->db->get();
		
	}
	
	function search_count_all($type,$search, $days_past_due = NULL, $deleted = 0, $status="", $location_id_override=null)
	{
		if (!$deleted)
		{
			$deleted = 0;
		}
		
		$location_id = $location_id_override ? $location_id_override : $this->Employee->get_logged_in_employee_current_location_id();
		
				
		$this->db->select('terms.name as terms,'.$type.'_'.'invoices.*,'.($type == 'customer' ? 'CONCAT(person.first_name, " ", person.last_name)' : 'company_name').' as person, person.last_name as person_last_name', false);
		$this->db->from($type.'_'.'invoices');
		$this->db->join($type.'s', $type.'s.person_id = '.$type.'_'.'invoices.'.$type.'_id','left');
		
		$this->db->join('people as person', 'person.person_id = '.$type.'_'.'invoices.'.$type.'_id','left');
		
		$this->db->where($type.'_'.'invoices.deleted', $deleted);
		$this->db->where($type.'_'.'invoices.location_id', $location_id);
		
		if ($status == 2) {
			$this->db->where($type.'_'.'invoices.balance >', 0);
		} elseif($status == 3) {
			$this->db->where($type.'_'.'invoices.balance <=', 0);
		} elseif($status == 1) {
			
		} else {
			$this->db->where($type.'_'.'invoices.balance >', 0);
		}

		if ($days_past_due !== NULL)
		{
			if($days_past_due == 'current'){
				$this->db->where('datediff(NOW(),phppos_'.$type.'_'.'invoices.due_date) < 30', NULL, FALSE);
			}
			else{
				$this->db->where('datediff(NOW(),phppos_'.$type.'_'.'invoices.due_date) > '.$days_past_due, NULL, FALSE);
			}
			$this->db->where($type.'_invoices.balance > 0');
		}
				
		if ($search)
		{
			$this->db->where("(
			".($type == 'customer' ? 'person.full_name' : 'company_name')." LIKE '".$this->db->escape_like_str($search)."%' or
			".$type.'_invoices.'.$type."_po LIKE '".$this->db->escape_like_str($search)."%' or 
			person.first_name LIKE '".$this->db->escape_like_str($search)."%' or 
			person.last_name LIKE '".$this->db->escape_like_str($search)."%' or 
			person.email LIKE '".$this->db->escape_like_str($search)."%' or 
			person.phone_number LIKE '".$this->db->escape_like_str($search)."%' or 
			person.full_name LIKE '".$this->db->escape_like_str($search)."%') and ".$type."_invoices.deleted=$deleted");					
		}
								
		return $this->db->count_all_results();
		
	}
	
	/*
	Get search suggestions to find invoices
	*/
	function get_search_suggestions($type,$search,$deleted=0,$limit=5)
	{
		if (!trim($search))
		{
			return array();
		}
		if (!$deleted)
		{
			$deleted = 0;
		}
		
			$suggestions = array();
			$location_id = $this->Employee->get_logged_in_employee_current_location_id();
			
			$this->db->select($type.'_'.'invoices.*,'.($type == 'customer' ? 'CONCAT(person.first_name, " ", person.last_name)' : 'company_name').' as person, person.last_name as last_name, person.first_name as first_name', false);
			$this->db->from($type.'_'.'invoices');
		
			$this->db->join($type.'s', $type.'s.person_id = '.$type.'_'.'invoices.'.$type.'_id','left');
			$this->db->join('people as person', 'person.person_id = '.$type.'_'.'invoices.'.$type.'_id','left');
		
			$this->db->where($type.'_'.'invoices.deleted', $deleted);
				
			$this->db->where("(
			".($type == 'customer' ? 'person.full_name' : 'company_name')." LIKE '".$this->db->escape_like_str($search)."%' or
			".$type.'_invoices.'.$type."_po LIKE '".$this->db->escape_like_str($search)."%' or 
			person.first_name LIKE '".$this->db->escape_like_str($search)."%' or 
			person.last_name LIKE '".$this->db->escape_like_str($search)."%' or 
			person.email LIKE '".$this->db->escape_like_str($search)."%' or 
			person.phone_number LIKE '".$this->db->escape_like_str($search)."%' or 
			person.full_name LIKE '".$this->db->escape_like_str($search)."%') and ".$type."_invoices.deleted=$deleted");					
			
			$this->db->limit($limit);
			
			$query=$this->db->get();
			
			$temp_suggestions = array();
						
			foreach($query->result() as $row)
			{
				$data = array(
					'name' => $row->person,
					'subtitle' => '',
					'avatar' => base_url()."assets/img/user.png",
					 );
				$temp_suggestions[] = $data;
			}
		
		
			$this->load->helper('array');
			uasort($temp_suggestions, 'sort_assoc_array_by_name');
			
			foreach($temp_suggestions as $key => $value)
			{
				$suggestions[]=array('value'=> $key, 'label' => $value['name'],'avatar'=>$value['avatar'],'subtitle'=>$value['subtitle']);		
			}
				
			return $suggestions;
	}	
	
	
	function get_all($type,$days_past_due = NULL,$deleted=0,$limit=10000, $offset=0,$col='invoice_date',$order='desc',$status = null)
	{	
		if (!$deleted)
		{
			$deleted = 0;
		}
		
		$location_id = $this->Employee->get_logged_in_employee_current_location_id();
		
		$this->db->select('terms.name as terms,'.$type.'_'.'invoices.*,'.($type == 'customer' ? 'CONCAT(person.first_name, " ", person.last_name)' : 'company_name').' as person, person.last_name as person_last_name', false);
		$this->db->from($type.'_'.'invoices');
		$this->db->join($type.'s', $type.'s.person_id = '.$type.'_'.'invoices.'.$type.'_id','left');
		$this->db->join('people as person', 'person.person_id = '.$type.'_'.'invoices.'.$type.'_id','left');
		$this->db->join('terms', 'terms.term_id = '.$type.'_'.'invoices.term_id','left');
		$this->db->where($type.'_'.'invoices.deleted', $deleted);
		$this->db->where($type.'_'.'invoices.location_id', $location_id);
		
		if ($status == 2) {
			$this->db->where($type.'_'.'invoices.balance >', 0);
		} elseif($status == 3) {
			$this->db->where($type.'_'.'invoices.balance <=', 0);
		} elseif($status == 1) {
			
		} else {
			$this->db->where($type.'_'.'invoices.balance >', 0);
		}
		
		if ($days_past_due !== NULL)
		{
			if($days_past_due == 'current'){
				$this->db->where('datediff(NOW(),phppos_'.$type.'_'.'invoices.due_date) < 30', NULL, FALSE);
			}
			else{
				$this->db->where('datediff(NOW(),phppos_'.$type.'_'.'invoices.due_date) > '.$days_past_due, NULL, FALSE);
			}
			$this->db->where($type.'_invoices.balance > 0');
		}
			
		$this->db->order_by($col, $order);
		$this->db->limit($limit);
		$this->db->offset($offset);
  	  	return $this->db->get();
		
	}
	
	function count_all($type,$days_past_due = NULL,$deleted=0,$status=NULL)
	{
		if (!$deleted)
		{
			$deleted = 0;
		}
		
		$location_id = $this->Employee->get_logged_in_employee_current_location_id();
		
		
		$this->db->select($type.'_'.'invoices.*,CONCAT(person.first_name, " ", person.last_name) as person, person.last_name as person_last_name', false);
		$this->db->from($type.'_'.'invoices');
		$this->db->where($type.'_'.'invoices.deleted', $deleted);		
		$this->db->where($type.'_'.'invoices.location_id', $location_id);
		
		if ($status == 2) {
			$this->db->where($type.'_'.'invoices.balance >', 0);
		} elseif($status == 3) {
			$this->db->where($type.'_'.'invoices.balance <=', 0);
		} elseif($status == 1) {
			
		} else {
			$this->db->where($type.'_'.'invoices.balance >', 0);
		}

		if ($days_past_due !== NULL)
		{
			if($days_past_due == 'current'){
				$this->db->where('datediff(NOW(),phppos_'.$type.'_'.'invoices.due_date) < 30', NULL, FALSE);
			}
			else{
				$this->db->where('datediff(NOW(),phppos_'.$type.'_'.'invoices.due_date) > '.$days_past_due, NULL, FALSE);
			}
			$this->db->where($type.'_invoices.balance > 0');
		}
		

		return $this->db->count_all_results();		
	}
	

	function exists($type,$id)
	{
		$this->db->from($type.'_'.'invoices');
		$this->db->where('invoice_id',$id);
		$query = $this->db->get();

		return ($query->num_rows()==1);
	}
	
	/*
	Inserts or updates a invoice
	*/
	function save($type,&$invoice_data, $invoice_id = false, $store_account_sale = false)
	{
		
		//If the balance is 0 or less mark any store account sales as paid
		if ($invoice_id && isset($invoice_data['balance']) && $invoice_data['balance'] <= 1e-6 && $store_account_sale == false)
		{
			$this->mark_store_account_orders_as_paid_for_invoice($type, $invoice_id);
		}
		
		if (!$invoice_id or !$this->exists($type,$invoice_id))
		{
			if($this->db->insert($type.'_'.'invoices',$invoice_data))
			{
				$invoice_data['invoice_id']=$this->db->insert_id();
				return true;
			}
			return false;
		}
		
		$this->db->where('invoice_id', $invoice_id);
		return $this->db->update($type.'_'.'invoices',$invoice_data);
		
	}
	
	function delete($type,$id)
	{	
		$this->db->where('invoice_id', $id);
		return $this->db->update($type.'_'.'invoices', array('deleted' => 1));
	}
	
	function delete_list($type,$invoice_ids)
	{
		foreach($invoice_ids as $invoice_id)
		{
			$result = $this->delete($type,$invoice_id);
			
			if(!$result)
			{
				return false;
			}
		}
		
		return true;
 	}
		
	function undelete($type,$id)
	{	
		$this->db->where('invoice_id', $id);
		return $this->db->update($type.'_'.'invoices', array('deleted' => 0));
	}
	
	function undelete_list($type,$invoice_ids)
	{
		foreach($invoice_ids as $invoice_id)
		{
			$result = $this->undelete($type,$invoice_id);
			
			if(!$result)
			{
				return false;
			}
		}
		
		return true;
 	}
		
	function get_displayable_columns($type)
	{
		return array(
			'invoice_id' =>  array('sort_column' => $type.'_'.'invoices.invoice_id', 'label' => lang('invoices_invoice'),'format_function' => 'strsame'),
			
			'person' => array('sort_column' => 'last_name', 'label' => lang('invoices_'.$type), 'format_function' => 'strsame'),
			'terms' => array('sort_column' => 'terms', 'label' => lang('invoices_terms'), 'format_function' => 'strsame'),
			'invoice_date' => array('sort_column' => $type.'_'.'invoices.invoice_date', 'label' => lang('invoices_invoice_date'), 'format_function' => 'date_as_display_date'),
			'due_date' => array('sort_column' => $type.'_'.'invoices.due_date', 'label' => lang('invoices_due_date'), 'format_function' => 'date_as_display_date'),
			'total' => array('sort_column' => 'total', 'label' => lang('common_total'), 'format_function' => 'to_currency','html' => TRUE),
			'balance' => array('sort_column' => 'balance', 'label' => lang('common_balance'), 'format_function' => 'to_currency','html' => TRUE),
			'last_paid' => array('sort_column' => $type.'_'.'invoices.last_paid', 'label' => lang('invoices_last_paid'), 'format_function' => 'date_as_display_date'),
			"$type".'_po' => array('sort_column' => $type.'_'.'invoices.po', 'label' => lang('invoices_po_'.$type), 'format_function' => 'strsame'),
		);
	}
	
	function get_default_columns($type)
	{
		return array('invoice_id','person','terms','invoice_date','due_date','total','balance','last_paid');
	}
	
	
	function get_all_terms($limit=10000, $offset=0,$col='name',$order='asc')
	{
		$this->db->from('terms');
		$this->db->where('deleted', 0);
		
		$this->db->order_by($col, $order);
		
		$this->db->limit($limit);
		$this->db->offset($offset);
		
		$return = array();
		
		foreach($this->db->get()->result_array() as $result)
		{
			$return[$result['term_id']] = $result;
		}
		
		return $return;
	}
	
	function get_term($term_id)
	{
		$this->db->from('terms');
		$this->db->where('term_id', $term_id);
		
		$query = $this->db->get();
		
		if($query->num_rows()==1)
		{
			return $query->row();
		}
		else
		{
			//Get empty base parent object, as $item_id is NOT an item
			$term_obj=new stdClass();

			//Get all the fields from items table
			$fields = $this->db->list_fields('terms');

			foreach ($fields as $field)
			{
				$term_obj->$field='';
			}			

			return $term_obj;
		}
	}

	function get_term_by_name($name){
		$this->db->from('terms');
		$this->db->where('name', $name);
		
		$query = $this->db->get();
		
		if($query->num_rows()==1)
		{
			return $query->row();
		}
		else
		{
			//Get empty base parent object, as $item_id is NOT an item
			$term_obj=new stdClass();

			//Get all the fields from items table
			$fields = $this->db->list_fields('terms');

			foreach ($fields as $field)
			{
				$term_obj->$field='';
			}			

			return $term_obj;
		}
	}

	function save_term($term_data, $term_id = FALSE)
	{
		if ($term_id == FALSE)
		{
			if($this->db->insert('terms',$term_data))
			{
				$term_id = $this->db->insert_id();
			}
		}
		else
		{
			$this->db->where('term_id', $term_id);
			$this->db->update('terms',$term_data);
		}
				
		return $term_id;
	}
	
	/*
	Deletes one tag
	*/
	function delete_term($term_id)
	{		
		$this->db->where('term_id', $term_id);
		return $this->db->update('terms', array('deleted' => 1));
	}	
	
	function get_details($type,$invoice_id)
	{
		$this->db->from($type.'_invoice_details');
		$this->db->where('invoice_id',$invoice_id);
		
		return $this->db->get()->result_array();
	}
	
	function save_invoice_details($type,$details_data,$invoice_details_id = NULL)
	{
		if ($invoice_details_id)
		{
			$this->db->where('invoice_details_id',$invoice_details_id);
			$this->db->update($type.'_invoice_details',$details_data);
		}
		else
		{
			$type_prefix = $type == 'customer' ? 'sale' : 'receiving';
			
			if (!isset($details_data[$type_prefix.'_id']) || !$this->is_order_in_invoice($type,$details_data[$type_prefix.'_id']))
			{
				$this->db->insert($type.'_invoice_details',$details_data);			
			}
		}
	}
	
	function get_invoice_for_order_id($type,$order_id)
	{
		$type_prefix = $type == 'customer' ? 'sale' : 'receiving';
		
		$this->db->from($type.'_invoice_details');
		$this->db->where($type_prefix.'_id',$order_id);
		$query = $this->db->get();

		$row = $query->row();
		
		if ($row && $row->invoice_id)
		{
			return $row->invoice_id;
		}
		
		return FALSE;
	}
	
	function is_order_in_invoice($type,$order_id)
	{
		$type_prefix = $type == 'customer' ? 'sale' : 'receiving';
		
		$this->db->from($type.'_invoice_details');
		$this->db->where($type_prefix.'_id',$order_id);
		$query = $this->db->get();

		return ($query->num_rows()==1);
		
	}
	
	function get_invoice_id_for_detail($type,$invoice_details_id)
	{
		$this->db->from($type.'_invoice_details');
		$this->db->where('invoice_details_id',$invoice_details_id);
		$query = $this->db->get();
		
		if($query->num_rows()==1)
		{
			return $query->row()->invoice_id;
		}
		
		return NULL;
	}
	
	function get_total_from_invoice_details($type,$invoice_id)
	{
		$this->db->select("sum(total) as total_from_details",FALSE);
		$this->db->from($type.'_invoice_details');
		$this->db->where('invoice_id',$invoice_id);
		$query = $this->db->get();
		
		return $query->row()->total_from_details;
	}
	
	function delete_invoice_details($type,$invoice_details_id)
	{		
		$this->db->delete($type.'_invoice_details',array('invoice_details_id' => $invoice_details_id));
	}
	
	function delete_invoice_details_by_order($type,$order_id)
	{		
		$type_prefix = $type == 'customer' ? 'sale' : 'receiving';
		
		$this->db->delete($type.'_invoice_details',array($type_prefix.'_id' => $order_id));
	}
	
	
	public function process_payment($amount,$register,$cc_token,$cc_number = NULL, $cc_ccv = NULL,$expire_month = NULL,$expire_year = NULL,$address = NULL, $zip = NULL)
	{
		if ($this->Location->get_info_for_key('credit_card_processor') == 'coreclear')
		{
			return $this->process_mx($amount,$register,$cc_token,$cc_number,$cc_ccv,$expire_month,$expire_year,$address,$zip);
		}
		elseif ($this->Location->get_info_for_key('credit_card_processor') == 'coreclear2')
		{
			return $this->process_blockchyp($amount,$register,$cc_token,$cc_number,$cc_ccv,$expire_month,$expire_year,$address,$zip);			
		}
	}
	
	private function process_mx($amount,$register,$paymentToken = NULL, $card_number = NULL,$cvc = NULL, $expiryMonth = NULL,$expiryyear = NULL,$avsStreet=NULL, $avsZip=NULL)
	{		
		$amount = (double)$amount;
		$is_card_not_present = $register == -1;		
		$register_info = $this->Register->get_info($register);

		$terminal_id = $register_info->coreclear_terminal_id;
		$card_reader_type = $register_info->coreclear_card_reader_type;
		$cur_location_info = $this->Location->get_info($this->Employee->get_logged_in_employee_current_location_id());
		
		$test_mode = $cur_location_info->coreclear_sandbox;
		$merchant_id = $cur_location_info->coreclear_merchant_id;
		$authorization_key = $cur_location_info->coreclear_authorization_key;
		$authorization_key_created = $cur_location_info->coreclear_authorization_key_created;
		$coreclear_user = $cur_location_info->coreclear_user;
		$coreclear_password = $cur_location_info->coreclear_password;
		
		
		$authorization_key_created_timestamp = strtotime($authorization_key_created);
		$current_timestamp = time();

		//When processing cards the value of the config field "Token Create Date" is > 8 hours old from the current time then we need to get a new token before processing the credit cards
		if($current_timestamp-$authorization_key_created_timestamp > 28800){
			$get_coreclear_authorization_key = $this->Location->get_coreclear_authorization_key($test_mode,$merchant_id,$coreclear_user,$coreclear_password);
			$authorization_key = $get_coreclear_authorization_key['jwtToken'];
			$authorization_key_created = $get_coreclear_authorization_key['coreclear_authorization_key_created'];
			
			$authorization_key_update_data = array(
				'coreclear_authorization_key' => $authorization_key,
				'coreclear_authorization_key_created' => $authorization_key_created,
			);
			$this->Location->save($authorization_key_update_data,$this->Employee->get_logged_in_employee_current_location_id());
		}
		
		
		if ($is_card_not_present)
		{

			if($test_mode){
				$uri = "https://sandbox.api.mxmerchant.com";
			}
			else{
				$uri = "https://api.mxmerchant.com";
			}

			$method = "checkout/v3/payment?echo=true";
			$endpoint = $uri.'/'.$method;

			if(!$avsStreet)
			{
				$avsStreet = '';
			}
			
			if (!$avsZip)
			{
				$avsZip = '';
			}
			
			$data = array(
				'merchantId' => $merchant_id,
				'amount' => $amount,
				"tenderType" => "Card",
				"cardAccount" => array(
					"number"=>$card_number,
					"expiryMonth"=>$expiryMonth,
					"expiryyear"=>$expiryyear,
					"avsStreet"=>$avsStreet,
					"avsZip"=>$avsZip,
					"cvv"=>$cvc,
				),
			);						
			
			$data = json_encode($data);
			
			$curl = curl_init();
			curl_setopt_array($curl, array(
				CURLOPT_URL => $endpoint,
				CURLOPT_RETURNTRANSFER => true,
				CURLOPT_ENCODING => "",
				CURLOPT_MAXREDIRS => 10,
				CURLOPT_TIMEOUT => 360,
				CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
				CURLOPT_CUSTOMREQUEST => "POST",
				CURLOPT_POSTFIELDS => $data,
				CURLOPT_USERPWD => $coreclear_user.":".$coreclear_password,
				CURLOPT_HTTPHEADER => array(
					"Content-Type: application/json",
					"cache-control: no-cache"
				),
			));

			$response = curl_exec($curl);
			$err = curl_error($curl);

			$total_time = curl_getinfo($curl, CURLINFO_TOTAL_TIME)*1000;
			$response_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);

			curl_close($curl);

			$response_data = json_decode($response,TRUE);
	
			if($response_code == 200 || $response_code == 201)
			{
			
				if($response_data['status'] == 'Settled' || $response_data['status'] == 'Approved')
				{
					if (isset($response_data['amount']))
					{
						$Authorize = to_currency_no_money($response_data['amount']);
					}
					$charge_id = $response_data['id'];
					$masked_account = $response_data['cardAccount']['last4'];
					$card_brand = $response_data['cardAccount']['cardType'];
					$auth_code = $response_data['authCode'];
					$cc_token = $response_data['paymentToken'];
					$cc_exp = $response_data['cardAccount']['expiryMonth'].$response_data['cardAccount']['expiryYear'];
					$entry_method = $response_data['cardAccount']['entryMode'];
					$tran_type = $response_data['type'];
					$acq_ref_data = $response_data['reference'];
					$process_data = $response_data['authMessage'];
					
					
	 			   $payment_response_data = array(
	 				    'payment_date' => date('Y-m-d H:i:s'),	
	 				    'payment_type' => lang('common_credit'),
	 				    'payment_amount' => $amount,
	 				    'auth_code' => $auth_code,
	 				    'ref_no' => $charge_id,
	 				    'cc_token' => $cc_token,
	 				    'entry_method' => $entry_method,
	 				    'tran_type' => $tran_type,
	 				    'truncated_card' => $masked_account,
	 				    'card_issuer' => $card_brand,
						'acq_ref_data' => $acq_ref_data,
						'process_data' => $process_data,
	 				);
			
	 				return array('success' => TRUE, 'payment_response_data' => $payment_response_data);
					
				}
				else
				{
					
					return array('success' => FALSE,'payment_response_data' => NULL);
				}
			}
			else
			{
				return array('success' => FALSE,'payment_response_data' => NULL);
			}
		
		}
		else
		{
			if($test_mode){
				$uri = "https://sandbox-api2.mxmerchant.com";
			}
			else{
				$uri = "https://api2.mxmerchant.com";
			}
	
			$method = "terminal/v1/transaction/merchantid/".$merchant_id."/terminalid/".$terminal_id;
				
			
			//TODO need to do
			if($paymentToken != '')
			{
				
				return array('success' => FALSE,'payment_response_data' => NULL);
			}
			else{
				
				$data = array(
					'amount' => $amount,
					'type' => "Sale",
				);
					
				

				$replayId = $cur_register_id.date('ymdHis');
				$data['replayId'] = $replayId;
			
				$endpoint = $uri.'/'.$method;
				$data = json_encode($data);
		
				$curl = curl_init();
				curl_setopt_array($curl, array(
					CURLOPT_URL => $endpoint,
					CURLOPT_RETURNTRANSFER => true,
					CURLOPT_ENCODING => "",
					CURLOPT_MAXREDIRS => 10,
					CURLOPT_TIMEOUT => 360,
					CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
					CURLOPT_CUSTOMREQUEST => "POST",
					CURLOPT_POSTFIELDS => $data,
					CURLOPT_HTTPHEADER => array(
						"Authorization: Bearer ".$authorization_key,
						"Content-Type: application/json",
						"cache-control: no-cache"
					),
				));
		
				$response = curl_exec($curl);
				
				
				$err = curl_error($curl);
		
				$total_time = curl_getinfo($curl, CURLINFO_TOTAL_TIME)*1000;
		
				curl_close($curl);
		
				$response_data = json_decode($response,TRUE);
				
				if($response_data['status'] == 'SENTTOTERMINAL'){
					
					if($test_mode){
						$uri = "https://sandbox.api.mxmerchant.com";
					}
					else{
						$uri = "https://api.mxmerchant.com";
					}
	
					$method = "checkout/v3/payment?merchantId=".$merchant_id."&replayId=".$replayId;
	
					$get_transaction_result_start_time = $this->microtime_float();
	
					// we need a delay of 3 seconds
					sleep(3);
					
					//If the transaction is not complete we will get an error 404 (not found), so we need to keep calling every 2 seconds (I think we will have to time out after a certain amount of time, I'm thinking 1 minute).
					$get_transaction_result = $this->get_transaction_result($uri,$method,$coreclear_user,$coreclear_password,$get_transaction_result_start_time);
					$transaction_result_response_data = json_decode($get_transaction_result['response'],TRUE);
			
					if($get_transaction_result['success']){
						if (isset($transaction_result_response_data['amount']))
						{
							$Authorize = to_currency_no_money($transaction_result_response_data['amount']);						
						}
						
						$charge_id = $transaction_result_response_data['id'];
						$masked_account = $transaction_result_response_data['cardAccount']['last4'];
						$card_brand = $transaction_result_response_data['cardAccount']['cardType'];
						$auth_code = $transaction_result_response_data['authCode'];
						$cc_token = $transaction_result_response_data['paymentToken'];
						$cc_exp = $transaction_result_response_data['cardAccount']['expiryMonth'].$transaction_result_response_data['cardAccount']['expiryYear'];
						$entry_method = $transaction_result_response_data['cardAccount']['entryMode'];
						$tran_type = $preauth_completed_charge === 0 ? lang('sales_preauthorized_card') : $transaction_result_response_data['type'];
						$acq_ref_data = $transaction_result_response_data['reference'];
						$process_data = $transaction_result_response_data['authMessage'];
						
	 	 			   $payment_response_data = array(
	 	 				    'payment_date' => date('Y-m-d H:i:s'),	
	 	 				    'payment_type' => lang('common_credit'),
	 	 				    'payment_amount' => $amount,
	 	 				    'auth_code' => $auth_code,
	 	 				    'ref_no' => $charge_id,
	 	 				    'cc_token' => $cc_token,
	 	 				    'entry_method' => $entry_method,
	 	 				    'tran_type' => $tran_type,
	 	 				    'truncated_card' => $masked_account,
	 	 				    'card_issuer' => $card_brand,
	 						'acq_ref_data' => $acq_ref_data,
	 						'process_data' => $process_data,
	 	 				);
			
	 	 				return array('success' => TRUE, 'payment_response_data' => $payment_response_data);
						
					}
					else{
	
						if($get_transaction_result['timeout']){
							//If a timeout occurred we should call the following method:
							if($test_mode){
								$uri = "https://sandbox-api2.mxmerchant.com";
							}
							else{
								$uri = "https://api2.mxmerchant.com";
							}
					
							$method = "terminal/v1/transaction/merchantid/".$merchant_id."/terminalid/".$terminal_id;
	
							$endpoint = $uri.'/'.$method;
					
							$curl = curl_init();
							curl_setopt_array($curl, array(
								CURLOPT_URL => $endpoint,
								CURLOPT_RETURNTRANSFER => true,
								CURLOPT_ENCODING => "",
								CURLOPT_MAXREDIRS => 10,
								CURLOPT_TIMEOUT => 360,
								CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
								CURLOPT_CUSTOMREQUEST => "DELETE",
								CURLOPT_HTTPHEADER => array(
									"Authorization: Bearer ".$authorization_key,
									"Content-Type: application/json",
									"cache-control: no-cache"
								),
							));
					
							$response = curl_exec($curl);
							$err = curl_error($curl);
					
							$total_time = curl_getinfo($curl, CURLINFO_TOTAL_TIME)*1000;
					
							curl_close($curl);
					
							return array('success' => FALSE,'payment_response_data' => NULL);
							
						}
						else{
							return array('success' => FALSE,'payment_response_data' => NULL);
							
						}
					}
				}
				else{
		
					return array('success' => FALSE,'payment_response_data' => NULL);
					
				}
			}
		}		
	}
	
	private function process_blockchyp($amount,$register,$cc_token,$cc_number = NULL,$cc_ccv = NULL, $expire_month = NULL,$expire_year = NULL)
	{
		$is_card_not_present = $register == -1;
		$register_info = $this->Register->get_info($register);
		$emv_terminal_id = $register_info && property_exists($register_info,'emv_terminal_id') ? $register_info->emv_terminal_id : FALSE;
		
		try
		{
	    	BlockChyp::setApiKey($this->Location->get_info_for_key('blockchyp_api_key'));
	    	BlockChyp::setBearerToken($this->Location->get_info_for_key('blockchyp_bearer_token'));
	    	BlockChyp::setSigningKey($this->Location->get_info_for_key('blockchyp_signing_key'));
		
			if ($is_card_not_present)
			{
			
		        $charge_data = [
		            'pan' => $cc_number,
		            'expMonth' => $expire_month,
		            'expYear' => $expire_year,
					'amount' => to_currency_no_money($amount),
					'test' => (boolean)$this->Location->get_info_for_key('blockchyp_test_mode'),
					'enroll' => TRUE,
		        ];				
			}
			else
			{
				
				if ($cc_token)
				{
					$charge_data = array(
					'test' => (boolean)$this->Location->get_info_for_key('blockchyp_test_mode'),
					'token' => $cc_token,
					'amount' => to_currency_no_money($amount),
					'enroll' => TRUE,
					);	
					
				}
				else
				{
					$charge_data = array(
						'test' => (boolean)$this->Location->get_info_for_key('blockchyp_test_mode'),
						'terminalName' => $emv_terminal_id,
						'amount' => to_currency_no_money($amount),
						'enroll' => TRUE,
					);
				}
			}
		
			$response = BlockChyp::charge($charge_data);
			
			if ($response['success'] && $response['approved'])
			{
				
				if (isset($response['receiptSuggestions']))
				{
					@$CardType = $response['paymentType'];
					@$EntryMethod = $response['entryMethod'];
					@$ApplicationLabel = $response['receiptSuggestions']['applicationLabel'];

					@$AID = $response['receiptSuggestions']['aid'];
					@$TVR = $response['receiptSuggestions']['tvr'];
					@$IAD = $response['receiptSuggestions']['iad'];
					@$TSI = $response['receiptSuggestions']['tsi'];
			  	}
				else
				{
					@$EntryMethod = $is_card_not_present ? lang('sales_manual_entry') : lang('common_credit');
					@$ApplicationLabel = $EntryMethod;
					@$CardType =  $EntryMethod;
				}
			
			   $AcctNo = $response['maskedPan'];
			   $AuthCode = $response['authCode'];
			   $RefNo = $response['transactionId'];
		   
		   
			   $RecordNo = $response['token'];
				
			   $payment_response_data = array(
				    'payment_date' => date('Y-m-d H:i:s'),	
				    'payment_type' => lang('common_credit'),
				    'payment_amount' => $amount,
				    'auth_code' => $AuthCode,
				    'ref_no' => $RefNo,
				    'cc_token' => $RecordNo,
				    'entry_method' => $EntryMethod,
				    'aid'  => $AID,
				    'tvr' => $TVR,
				    'iad' => $IAD,
				    'tsi' => $TSI,
				    'tran_type' => lang('sales_card_transaction'),
				    'application_label' => $ApplicationLabel,
				    'truncated_card' => $AcctNo,
				    'card_issuer' => $CardType,
				);
			}
			
			return array('success' => ($response['success'] && $response['approved']), 'payment_response_data' => $payment_response_data);
		}
		catch(Exception $e)
		{
			return array('success' => FALSE,'payment_response_data' => NULL);
		}
	}
	
	function add_payment($type,$invoice_id,$payment_data)
	{
		$payment_data['invoice_id'] = $invoice_id;
		$this->db->insert($type.'_'.'invoice_payments',$payment_data);
		
		return $this->db->insert_id();
	}
	
	function get_payments($type, $invoice_id=null, $payment_id=null, $limit=null, $offset=null, $sort_col=null, $sort_dir=null)
	{
		$this->db->from($type.'_'.'invoice_payments');

		if($invoice_id){
			$this->db->where('invoice_id',$invoice_id);
		}

		if($payment_id){
			$this->db->where('payment_id', $payment_id);
		}

		if($sort_col && $sort_dir){
			$this->db->order_by($sort_col, $sort_dir);
		}else{
			$this->db->order_by('payment_date DESC, payment_id DESC');
		}
		
		if($limit){
			$this->db->limit($limit);
		}

		if($limit){
			$this->db->offset($offset);
		}

   	 	return $this->db->get();
	}
	
	private function microtime_float()
	{
			list($usec, $sec) = explode(" ", microtime());
			return ((float)$usec + (float)$sec);
	}
	
	private function get_transaction_result($uri,$method,$coreclear_user,$coreclear_password,$get_transaction_result_start_time){
		$endpoint = $uri.'/'.$method;

		$curl = curl_init();
		curl_setopt_array($curl, array(
			CURLOPT_URL => $endpoint,
			CURLOPT_RETURNTRANSFER => true,
			CURLOPT_ENCODING => "",
			CURLOPT_MAXREDIRS => 10,
			CURLOPT_TIMEOUT => 360,
			CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
			CURLOPT_CUSTOMREQUEST => "GET",
			CURLOPT_USERPWD => $coreclear_user.":".$coreclear_password,
			CURLOPT_HTTPHEADER => array(
				"Content-Type: application/json",
				"cache-control: no-cache"
			),
		));

		$response = curl_exec($curl);
		$err = curl_error($curl);

		$total_time = curl_getinfo($curl, CURLINFO_TOTAL_TIME)*1000;
		$response_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);

		curl_close($curl);

		if($response_code == 200){
			
			$response_data = json_decode($response,TRUE);
			if($response_data['status'] == 'Settled' || $response_data['status'] == 'Approved'){
				return array('success'=>true,'response'=>$response);
			}
			else if($response_data['status'] == 'Declined'){
				if($this->declined_index == 1){
					return array('success'=>false,'response'=>$response);
				}
				else{
					$this->declined_index++;
					sleep(2);
					return $this->get_transaction_result($uri,$method,$coreclear_user,$coreclear_password,$get_transaction_result_start_time);
				}
				
			}
			else{
				return array('success'=>false,'response'=>$response);
			}
		}
		else if($response_code == 404){
			$get_transaction_result_end_time = $this->microtime_float();
			
			//If the transaction is not complete we will get an error 404 (not found), so we need to keep calling every 2 seconds (I think we will have to time out after a certain amount of time, I'm thinking 1 minute).
			if(($get_transaction_result_end_time - $get_transaction_result_start_time)>60){
				return array('success'=>false,'timeout'=>true);
			}
			else{
				sleep(2);
				return $this->get_transaction_result($uri,$method,$coreclear_user,$coreclear_password,$get_transaction_result_start_time);
			}
				
		}
		else{
			return array('success'=>false,'response'=>$response);
		}
	}
	
	// 1: valor, 2: coreclear2
	function get_processor_payment_link($invoice_id)
	{

		$this->load->helper('text');
		$encrypted_invoice_id = do_encrypt($invoice_id,$this->Appconfig->get_secure_key());

		$store_url_base = site_url();
		return $store_url_base.'/i/'.$encrypted_invoice_id;
	}
	
	function mark_store_account_orders_as_paid_for_invoice($type,$invoice_id)
	{
		if ($type == 'customer')
		{
			foreach($this->get_details($type,$invoice_id) as $inv_detail)
			{
				if($sale_id_paid = $inv_detail['sale_id'])
				{
					$customer_id = $this->get_info($type,$invoice_id)->person_id;
					$cust_info = $this->Customer->get_info($customer_id);
				
					$new_balance = $cust_info->balance - $inv_detail['total'];
				
		 			$store_account_transaction = array(
			   		'customer_id'=>$customer_id,
			   		'sale_id'=>$inv_detail['sale_id'],
					 'comment'=>"INVOICE #$invoice_id PAYMENT",
			      	 'transaction_amount'=>$inv_detail['total']*-1,
					 'balance'=>$new_balance,
					 'date' => date('Y-m-d H:i:s')
					);
				
					$this->db->insert('store_accounts',$store_account_transaction);
					$this->db->insert('store_accounts_paid_sales',array('sale_id' => $sale_id_paid,'store_account_payment_sale_id' => NULL));
					
					$this->db->where('person_id', $customer_id);
					$this->db->update('customers', array('balance' => $new_balance));	
				}
			}
		}
		else
		{
			foreach($this->get_details($type,$invoice_id) as $inv_detail)
			{
				if($receiving_id_paid = $inv_detail['receiving_id'])
				{
					$supplier_id = $this->get_info($type,$invoice_id)->person_id;
					$sulp_info = $this->Supplier->get_info($supplier_id);
				
					$new_balance = $sulp_info->balance - $inv_detail['total'];
				
		 			$store_account_transaction = array(
			   		'supplier_id'=>$supplier_id,
			   		'receiving_id'=>$inv_detail['receiving_id'],
					 'comment'=>"INVOICE #$invoice_id PAYMENT",
			      	 'transaction_amount'=>$inv_detail['total']*-1,
					 'balance'=>$new_balance,
					 'date' => date('Y-m-d H:i:s')
					);
				
					$this->db->insert('supplier_store_accounts',$store_account_transaction);
					$this->db->insert('supplier_store_accounts_paid_receivings',array('receiving_id' => $receiving_id_paid,'store_account_payment_receiving_id' => NULL));
					
					$this->db->where('person_id', $supplier_id);
					$this->db->update('suppliers', array('balance' => $new_balance));	
					
				}
			}
		}
	}

	private function get_auto_match_payment($cart, $pos = 0){
		$cart->get_paid_store_account_ids();
		$calculated_amount_1 = 0;
		$pos_start_amount = 0;
		$pos_stop_amount = 0;
		foreach(array_keys($cart->get_paid_store_account_ids()) as $index => $sale_id_paid){
			$paid_amount = $cart->paid_store_account_amounts[$sale_id_paid];
			if($index == $pos){
				$pos_start_amount = $calculated_amount_1;
				$pos_stop_amount = $calculated_amount_1 + $paid_amount;
				break;
			}
			$calculated_amount_1 += $paid_amount;
		}

		$pos_start_amount_index = -1;
		$pos_stop_amount_index = -1;
		$calculated_amount_start = 0;
		$calculated_amount_end = 0;
		$pos_payment_list = array();

		$payment_temp = null;
		foreach($cart->get_payments() as $index => $payment) {
			$payment_temp = get_object_vars($payment);
			unset($payment_temp['ebt_voucher_no']);
			unset($payment_temp['ebt_auth_code']);
			$payment_amount = $payment_temp['payment_amount'];

			$calculated_amount_end += $payment_amount;

			if($pos_start_amount_index == -1 && $calculated_amount_end > $pos_start_amount){//start point
				$pos_start_amount_index = $index;
				$payment_temp['payment_amount'] -= ( $pos_start_amount - $calculated_amount_start );
			}

			if($pos_stop_amount_index == -1 && $calculated_amount_end >= $pos_stop_amount){
				$pos_stop_amount_index = $index;
				$payment_temp['payment_amount'] -= ( $calculated_amount_end - $pos_stop_amount );
				$pos_payment_list[] = $payment_temp;
				break;
			}else if($pos_start_amount_index > -1){
				$pos_payment_list[] = $payment_temp;
			}
			$calculated_amount_start += $payment_amount;
		}
		return $pos_payment_list;
	}

	function update_store_account_payment_invoice($type, $cart){
		if($type == "customer"){
			//store_accounts_paid_sales
			$paid_sales = $cart->get_paid_store_account_ids();
			if (!empty($paid_sales))
			{
				foreach(array_keys($cart->get_paid_store_account_ids()) as $index => $sale_id_paid)
				{
					$paid_amount = $cart->paid_store_account_amounts[$sale_id_paid];
					//check $sale_id_paid invoice exist
					$ref_invoice_detail = $this->get_invoice_details_by_type_id($type, $sale_id_paid);
					if($ref_invoice_detail){
						$ref_invoice_id = $ref_invoice_detail->invoice_id;
						$ref_invoice_info = $this->get_info($type, $ref_invoice_id);
						if($ref_invoice_info->deleted == 1) continue;

						$new_balance = $ref_invoice_info->balance - $paid_amount;
						$payment_list = $this->get_auto_match_payment($cart, $index);//get match payment

						$this->db->trans_start();
						foreach($payment_list as $payment){
							$this->add_payment($type, $ref_invoice_id, $payment);
						}
						$invoice_data = array('balance' => $new_balance, 'last_paid' => date('Y-m-d'));
						$this->save($type, $invoice_data, $ref_invoice_id, true);
						$this->db->trans_complete();
					}
				}
			}
		}

		if($type == "supplier"){
			//store_accounts_paid_receivings
			$paid_receivings = $cart->get_paid_store_account_ids();
			if (!empty($paid_receivings))
			{
				foreach(array_keys($cart->get_paid_store_account_ids()) as $index => $receiving_id_paid)
				{
					$paid_amount = $cart->paid_store_account_amounts[$receiving_id_paid];
					//check $receiving_id_paid invoice exist
					$ref_invoice_detail = $this->get_invoice_details_by_type_id($type, $receiving_id_paid);
					if($ref_invoice_detail){
						$ref_invoice_id = $ref_invoice_detail->invoice_id;
						$ref_invoice_info = $this->get_info($type, $ref_invoice_id);
						if($ref_invoice_info->deleted == 1) continue;

						$new_balance = $ref_invoice_info->balance - $paid_amount;
						$payment_list = $this->get_auto_match_payment($cart, $index);//get match payment
						$this->db->trans_start();
						foreach($payment_list as $payment){
							$this->add_payment($type, $ref_invoice_id, $payment);
						}

						$invoice_data = array('balance' => $new_balance, 'last_paid' => date('Y-m-d'));
						$this->save($type, $invoice_data, $ref_invoice_id, true);
						$this->db->trans_complete();
					}
				}
			}
		}
	}

	function get_balance_past_due($type,$days_past_due)
	{
		$location_id = $this->Employee->get_logged_in_employee_current_location_id();
		
		$this->db->select('SUM(balance) as balance_due', false);
		$this->db->from($type.'_'.'invoices');		
		$this->db->where($type.'_'.'invoices.deleted', 0);
		$this->db->where($type.'_'.'invoices.location_id', $location_id);
		if($days_past_due == 'current'){
			$this->db->where('datediff(NOW(),phppos_'.$type.'_'.'invoices.due_date) < 30', NULL, FALSE);
		}
		else{
			$this->db->where('datediff(NOW(),phppos_'.$type.'_'.'invoices.due_date) > '.$days_past_due, NULL, FALSE);
		}
		$this->db->where($type.'_invoices.balance > 0');
		return $this->db->get()->row()->balance_due;
	}

	function get_payment_receipt($id)
	{
		$this->db->from('customer_invoice_payments');
		$this->db->where('payment_id', $id);
		
		return $this->db->get()->row();
	}

	function get_invoice_detail($id)
	{
		$this->db->from('customer_invoices');
		$this->db->where('invoice_id', $id);
		
		return $this->db->get()->row();
	}

	function get_invoice_details_by_type_id($type, $type_id)
	{
		$this->db->from($type.'_invoice_details');
		$this->db->where(($type == 'customer'? 'sale' : 'receiving') . '_id', $type_id);
		return $this->db->get()->row();
	}

	function create_store_account_transaction($type, $invoice_info, $payment_data){

		$invoice_id = $invoice_info->invoice_id;

		$has_store_account_paid_amount = 0;

		$cart = NULL;
		if($type == "customer"){

			$invoice_details = $this->Invoice->get_details($type, $invoice_id);
			foreach($invoice_details as $invoice_detail){
				if($invoice_detail['sale_id'] > 0){
					//check store account payment exist
					$unpaid_sales = $this->Sale->get_unpaid_store_account_sales(array($invoice_detail['sale_id']));
					if($unpaid_sales[0]['payment_amount'] > 0) {
						$has_store_account_paid_amount += $unpaid_sales[0]['payment_amount'];
					}
				}
			}
	
			//check full payment
			$is_full_pay_invoice = false;
			if(( $invoice_info->balance - $payment_data['payment_amount']) <= 1e-6 ){
				$is_full_pay_invoice = true;
			}
	
			if(count($invoice_details) > 1 && $is_full_pay_invoice == false) {
				// can not match each store account payments and invoice amount
				return false;
			}
	
			if($has_store_account_paid_amount == 0){
				// don't need to make store account transaction
				return false;
			}
			// pass $is_full_pay_invoice == true

			//change_mode & set customer
			$store_account_payment_item_id = $this->Item->create_or_update_store_account_item();
	
			$cart = new PHPPOSCartSale();
			$cart->empty_items();
			$cart->set_mode('store_account_payment');
			$cart->employee_id = 1; //admin
			$cart->location_id = $invoice_info->location_id; //1
			$cart->select_customer($invoice_info->customer_id.'|FORCE_PERSON_ID|');
			$cart->add_item(new PHPPOSCartItemSale(array(
				'cost_price' => 0,
				'unit_price' => 0,
				'scan' => $store_account_payment_item_id.'|FORCE_ITEM_ID|',
				'cart' => $cart
			)));
			$cart->save();
			$invoice_details = $this->Invoice->get_details($type, $invoice_id);
			$invoice_sale_ids = array();
			foreach($invoice_details as $invoice_detail){
				if($invoice_detail['sale_id'] > 0){
					$invoice_sale_ids[] = $invoice_detail['sale_id'];
				}
			}
	
			$unpaid_store_account_sales = $this->Sale->get_unpaid_store_account_sales($invoice_sale_ids);
			$unpaid_store_account_sales_pay_amount = 0;
			$comment = "";
			foreach ($unpaid_store_account_sales as $unpaid_sale) {
				$unpaid_sale_pay_amount = $unpaid_sale['payment_amount'] < $payment_data['payment_amount'] ? $unpaid_sale['payment_amount'] : $payment_data['payment_amount'];
				$unpaid_store_account_sales_pay_amount += $unpaid_sale_pay_amount;
	
				//pay_store_account_sale
				$cart->add_paid_store_account_payment_id($unpaid_sale['sale_id'], $unpaid_sale_pay_amount );
				$cart_items = $cart->get_items();
				foreach($cart_items as $item)
				{
					if ($item->name == lang('common_store_account_payment'))
					{
						$item->unit_price += $unpaid_sale_pay_amount; 
						break;
					}
				}
				$comment = lang('sales_pays_sales'). ' - '.implode(', ',array_keys($cart->get_paid_store_account_ids()));
			}
	
			$cart->comment = $comment;
			$cart->save();
	
			$payment_data['payment_amount'] = $unpaid_store_account_sales_pay_amount;
			$cart->add_payment(new PHPPOSCartPaymentSale($payment_data));
			$cart->save();
	
			// complete
			$cart->change_cart_date = date('Y-m-d H:i:s');
			// $cart->suspended = 0;
			$sale_id_raw = $this->Sale->save($cart);
			return $sale_id_raw;
		}else if($type == "supplier") {
			$invoice_details = $this->Invoice->get_details($type, $invoice_id);
			foreach($invoice_details as $invoice_detail){
				if($invoice_detail['receiving_id'] > 0){
					//check store account payment exist
					$unpaid_receivings = $this->Receiving->get_unpaid_store_account_recvs(array($invoice_detail['receiving_id']));
					if($unpaid_receivings[0]['payment_amount'] > 0) {
						$has_store_account_paid_amount += $unpaid_receivings[0]['payment_amount'];
					}
				}
			}
	
			//check full payment
			$is_full_pay_invoice = false;
			if(( $invoice_info->balance - $payment_data['payment_amount']) <= 1e-6 ){
				$is_full_pay_invoice = true;
			}
	
			if(count($invoice_details) > 1 && $is_full_pay_invoice == false) {
				// can not match each sales store account payment and invoice amount
				return false;
			}
	
			if($has_store_account_paid_amount == 0){
				// don't need to make store account transaction
				return false;
			}
	
			//change_mode & set customer
			$store_account_payment_item_id = $this->Item->create_or_update_store_account_item();
	
			$cart = new PHPPOSCartRecv();
			$cart->empty_items();
			$cart->set_mode('store_account_payment');
			$cart->supplier_id = $invoice_info->supplier_id;

			$cart->add_item(new PHPPOSCartItemRecv(array(
				'cost_price' => 0,
				'unit_price' => 0,
				'scan' => $store_account_payment_item_id.'|FORCE_ITEM_ID|',
				'cart' => $cart
			)));
			$cart->save();
			$invoice_details = $this->Invoice->get_details($type, $invoice_id);
			$invoice_receiving_ids = array();
			foreach($invoice_details as $invoice_detail){
				if($invoice_detail['receiving_id'] > 0){
					$invoice_receiving_ids[] = $invoice_detail['receiving_id'];
				}
			}

			$unpaid_store_account_receivings = $this->Receiving->get_unpaid_store_account_recvs($invoice_receiving_ids);
			$unpaid_store_account_receivings_pay_amount = 0;
			$comment = "";
			foreach ($unpaid_store_account_receivings as $unpaid_receiving) {
				$unpaid_receiving_pay_amount = $unpaid_receiving['payment_amount'] < $payment_data['payment_amount'] ? $unpaid_receiving['payment_amount'] : $payment_data['payment_amount'];
				$unpaid_store_account_receivings_pay_amount += $unpaid_receiving_pay_amount;

				//pay_store_account_sale
				$cart->add_paid_store_account_payment_id($unpaid_receiving['receiving_id'], $unpaid_receiving_pay_amount );
				$cart_items = $cart->get_items();
				foreach($cart_items as $item)
				{
					if ($item->name == lang('common_store_account_payment'))
					{
						$item->unit_price += $unpaid_receiving_pay_amount; 
						break;
					}
				}
				$comment = lang('receivings_pays_receivings'). ' - '.implode(', ',array_keys($cart->get_paid_store_account_ids()));
			}

			$cart->comment = $comment;
			$cart->save();

			$payment_data['payment_amount'] = $unpaid_store_account_receivings_pay_amount;
			$cart->add_payment(new PHPPOSCartPaymentRecv($payment_data));
			$cart->save();

			// complete
			$cart->change_cart_date = date('Y-m-d H:i:s');
			// $cart->suspended = 0;
			$receiving_id_raw = $this->Receiving->save($cart);
			return $receiving_id_raw;
		}
		return 0;
	}

	function exist_sale_invoice($type, $location_id, $u_id, $type_id){
		$this->db->from($type.'_invoices');
		$this->db->join($type.'_invoice_details', $type.'_invoices.invoice_id = '.$type.'_invoice_details.invoice_id');
		$this->db->where($type.'_invoices.location_id', $location_id);
		$this->db->where($type.'_invoices.'.$type.'_id', $u_id);
		$this->db->where($type.'_invoice_details.'.($type=='customer'? 'sale' : 'receiving').'_id', $type_id);
		$query = $this->db->get();
		if($query->num_rows() > 0) return true;
		return false;
	}
}
