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
| 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 : |
<?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
}
}