示例#1
0
 /**
  * Model definition.
  */
 function define()
 {
     $this->auto_increment_start = 10000;
     // Fields.
     $this->fields = array('id', 'items', 'order', 'coupon_code', 'discounts', 'taxes', 'order_id', 'account_id', 'session_id', 'date_created', 'date_updated', 'items' => function ($cart) {
         return get("/products", array(':with' => $cart['items']));
     }, 'account' => function ($cart) {
         return get("/accounts/{$cart['account_id']}");
     }, 'shipping_methods' => function ($cart) {
         return Carts::get_shipping_methods($cart);
     }, 'taxes' => function ($cart) {
         return Carts::get_taxes($cart);
     }, 'sub_total' => function ($cart) {
         return Carts::get_sub_total($cart);
     }, 'sub_discount' => function ($cart) {
         return Carts::get_sub_total($cart, array('discount' => true));
     }, 'shipping_total' => function ($cart) {
         return Carts::get_shipping_total($cart);
     }, 'shipping_discount' => function ($cart) {
         return Carts::get_shipping_total($cart, array('discount' => true));
     }, 'tax_total' => function ($cart) {
         return Carts::get_tax_total($cart);
     }, 'discount_total' => function ($cart) {
         return Carts::get_discount_total($cart);
     }, 'grand_total' => function ($cart) {
         return Carts::get_grand_total($cart);
     }, 'credit_total' => function ($cart) {
         return Carts::get_credit_total($cart);
     }, 'billing_total' => function ($cart) {
         return Carts::get_billing_total($cart);
     }, 'product_cost' => function ($cart) {
         return Carts::get_sub_total($cart, array('cost' => true));
     }, 'quantity' => function ($cart) {
         return Carts::get_quantity($cart);
     }, 'weight' => function ($cart) {
         return Carts::get_weight($cart);
     }, 'abandoned' => function ($cart) {
         return Carts::abandoned($cart);
     });
     // Search fields.
     $this->search_fields = array('id', 'order.name', 'order.email');
     // Indexes.
     $this->indexes = array('id' => 'unique');
     // Validate.
     $this->validate = array('order', ':items' => array('required' => array('id', 'price', 'quantity')));
     // Cart item quantity limit.
     $this->item_quantity_limit = 99999999;
     // Event binds.
     $this->binds = array('GET' => function ($event) {
         $data =& $event['data'];
         // Reset cart session?
         if ($data[':reset']) {
             $data['items'] = null;
             $data['order'] = null;
             $data['discounts'] = null;
             return put("/carts/{$event['id']}", $data);
         }
     }, 'POST' => function ($event) {
         $data =& $event['data'];
         // Post item into cart (shortcut to POST.items)
         if ($event['id'] && $data['item']) {
             post("/carts/{$event['id']}/items", $data['item']);
             return get("/carts/{$event['id']}");
         }
     }, 'POST.items' => function ($event) {
         $data =& $event['data'];
         // Filter the item for update.
         $data = array_pop(Orders::get_items_for_update(array($data)));
         if ($cart = get("/carts/{$event['id']}")) {
             $product = get("/products/{$data['id']}", array('pricing' => array('roles' => $cart['account']['roles'], 'quantity' => $data['quantity'] ?: 1)));
             // Item has options?
             if ($data['options']) {
                 foreach ((array) $data['options'] as $key => $option) {
                     if (is_scalar($option)) {
                         $data['options'][$key] = $option = array('name' => $option);
                     }
                     // Matches product option?
                     if (is_array($product['options']) && is_array($product['options'][$key])) {
                         $data['options'][$key]['name'] = $option['name'] ?: $product['options'][$key]['name'];
                         $data['options'][$key]['price'] = $option['price'] ?: $product['options'][$key]['price'];
                     }
                 }
             }
             // Item variant?
             if ($product['variants']) {
                 $vid = $data['variant_id'];
                 if ($variant = $product['variants'][$vid]) {
                     $data['variant'] = $variant;
                     $data['price'] = $data['price'] ?: $variant['price'];
                 } else {
                     // Variant not found.
                     $model->error('not found', 'variant_id');
                 }
             } else {
                 $data['variant'] = null;
                 $data['variant_id'] = null;
             }
             // Prevent two of the same item in cart.
             foreach ((array) $cart['items'] as $key => $item) {
                 // Exists in cart?
                 if ($data['id'] == $item['id'] && $data['options'] == $item['options'] && $data['variant'] == $item['variant']) {
                     // Update quantity.
                     $item['quantity'] += $data['quantity'];
                     return put("{$cart}/items/{$key}", $item);
                 }
             }
         }
         // Defaults.
         $data['price'] = $data['price'] ?: $product['price'];
         $data['quantity'] = round($data['quantity'] ?: 1);
     }, 'PUT.items' => function ($event, $model) {
         $data =& $event['data'];
         // Defaults.
         if (isset($data['quantity'])) {
             $data['quantity'] = round($data['quantity'] ?: 1);
             // Upper limit on item quantity.
             if ($data['quantity'] > $model->item_quantity_limit) {
                 $data['quantity'] = $model->item_quantity_limit;
             }
         }
     }, 'PUT' => function ($event, $model) {
         $data =& $event['data'];
         // Cart already exists?
         if ($data && ($cart = get("/carts/{$event['id']}"))) {
             // Update items collection?
             if ($data['items']) {
                 $cart_items = $cart['items'];
                 foreach ((array) $data['items'] as $item_id => $item) {
                     // Update existing item quantity.
                     if (isset($item['quantity']) && $cart_items[$item_id]) {
                         $cart_items[$item_id]['quantity'] = round($item['quantity']);
                         // Upper limit on item quantity.
                         if ($cart_items[$item_id]['quantity'] > $model->item_quantity_limit) {
                             $cart_items[$item_id]['quantity'] = $model->item_quantity_limit;
                         }
                         // Check product for pricing update?
                         $product = get("/products/{$cart_items[$item_id]['id']}", array('pricing' => array('roles' => $cart['account']['roles'], 'quantity' => $cart_items[$item_id]['quantity'] ?: 1)));
                         // Update pricing?
                         if ($product['pricing']) {
                             $cart_items[$item_id]['price'] = $product['price'];
                         }
                     }
                     // Remove item?
                     if ($data['items'][$item_id]['quantity'] <= 0) {
                         unset($cart_items[$item_id]);
                     }
                 }
                 $data['items'] = $cart_items;
             }
             // Removed coupon code?
             if (isset($data['coupon_code']) && !$data['coupon_code']) {
                 // Remove coupon.
                 $data['discounts'] = $cart['discounts'];
                 unset($data['discounts']['coupon']);
             } elseif ($data['coupon_code'] && $data['coupon_code'] != $cart['discount']['coupon']['code']) {
                 $discount = get("/discounts", array('code' => $data['coupon_code'], 'is_valid' => true));
                 if ($discount === false) {
                     $model->error('already used', 'coupon_code');
                 } else {
                     if (!$discount) {
                         $model->error('invalid', 'coupon_code');
                     }
                 }
                 // Remember code.
                 $data['coupon_code'] = $discount['code'];
                 // Update items from coupon.
                 foreach ((array) $discount['rules'] as $rule) {
                     if ($rule['add'] && $rule['product_id']) {
                         // Exists in cart?
                         $exists = false;
                         foreach ((array) $cart['items'] as $item_id => $item) {
                             if ($item['id'] == $rule['product_id']) {
                                 $exists = $item_id;
                                 break;
                             }
                         }
                         // Update item quantity?
                         if ($exists) {
                             put("{$cart}/items/{$exists}", array('quantity' => $rule['quantity']));
                         } else {
                             // Post new item to cart.
                             post("{$cart}/items", array('id' => $rule['product_id'], 'quantity' => $rule['quantity']));
                         }
                     }
                 }
                 $data['discounts'] = $cart['discounts'];
                 $data['discounts']['coupon'] = $discount;
             } else {
                 // Don't update coupon code directly.
                 unset($data['coupon_code']);
             }
             // Extra discount updates.
             if ($data['discounts']) {
                 foreach ((array) $data['discounts'] as $did => $discount) {
                     // Unset some discount details.
                     unset($data['discounts'][$did]['codes']);
                     unset($data['discounts'][$did]['codes_used']);
                     unset($data['discounts'][$did]['code_history']);
                     unset($data['discounts'][$did]['conditions']);
                 }
             }
             // Update order data? Merge.
             if (isset($data['order']) && is_array($cart['order'])) {
                 if ($data['order']) {
                     if ($data['order']['shipping']) {
                         $data['order']['shipping'] = array_merge((array) $cart['order']['shipping'], (array) $data['order']['shipping']);
                     }
                     $data['order'] = $cart['order'] = array_merge($cart['order'], (array) $data['order']);
                 }
             }
             // Update shipping total?
             if ($data['order'] || $data['items']) {
                 // @TODO: Make sure we don't need this.
                 //$data['shipping_total'] = Carts::get_shipping_total($cart);
             }
             // Use credit?
             if ($data['credit_total']) {
                 // Validate.
                 $data['credit_total'] = Carts::get_credit_total(merge($cart, $data));
             }
         }
     }, 'validate:order' => function ($order, $field, $params, $model) {
         // Validate with orders collection.
         $order[':validate'] = true;
         $result = get("/orders", $order);
         // Apply errors to cart model?
         foreach ((array) $result['errors'] as $field => $error) {
             $model->error($error, 'order', $field);
         }
     });
 }
示例#2
0
 /**
  * Model definition.
  */
 function define()
 {
     $this->auto_increment_start = 10000;
     // Fields.
     $this->fields = array('id', 'account_id', 'name', 'email', 'phone', 'items', 'item_ids', 'shipping', 'billing', 'schedule', 'sub_total', 'sub_discount', 'tax_total', 'shipping_total', 'shipping_discount', 'discount_total', 'grand_total', 'credit_total', 'billing_total', 'product_cost', 'shipping_cost', 'coupon_code', 'discounts', 'discount_ids', 'status', 'error', 'parent_id', 'prev_id', 'next_id', 'date_created', 'date_updated', 'date_scheduled', 'date_shipped', 'date_cancelled', 'date_returned', 'account' => function ($order) {
         return get("/accounts/{$order['account_id']}");
     }, 'status' => function ($order) {
         return Orders::get_status($order);
     }, 'payment_total' => function ($order) {
         return Orders::get_payment_total($order);
     }, 'payment_balance' => function ($order) {
         return Orders::get_payment_balance($order);
     }, 'items' => function ($order) {
         return get("/products", array(':with' => $order['items']));
     }, 'items_out_of_stock' => function ($order) {
         return Orders::get_items_out_of_stock($order);
     }, 'discounts' => function ($order) {
         return get("/discounts", array(':with' => $order['discounts']));
     }, 'payments' => function ($order) {
         return get("/payments", array('order_id' => $order['id'], 'order' => 'date_created ASC', 'limit' => null));
     }, 'shipments' => function ($order) {
         return get("/shipments", array('order_id' => $order['id'], 'order' => 'date_created ASC', 'limit' => null));
     }, 'cart' => function ($order) {
         return Orders::get_cart_record($order);
     });
     // Search fields.
     $this->search_fields = array('id', 'name', 'email', 'shipping.name', 'billing.name', 'date_created', 'coupon_code');
     // Default query.
     $this->query = array('limit' => 50);
     // Indexes.
     $this->indexes = array('id' => 'unique', 'account_id');
     // Validate.
     $this->validate = array('required' => array('account_id', 'name', 'email', 'billing', 'shipping', 'sub_total', 'shipping_total', 'grand_total'), 'email-address' => array('email'), ':items' => array('required' => array('id', 'price', 'quantity')));
     // Event binds.
     $this->binds = array('POST' => function ($event, $model) {
         $data =& $event['data'];
         // Assemble order from cart?
         if ($cart = $data['cart']) {
             $order = array_merge((array) $cart['order'], array('id' => null, 'cart' => null, 'cart_id' => $cart['id'], 'account_id' => $cart['account_id'], 'items' => $cart['items'], 'coupon_code' => $cart['discounts']['coupon']['code'], 'discounts' => $cart['discounts'], 'sub_total' => $cart['sub_total'], 'sub_discount' => $cart['sub_discount'], 'shipping_total' => $cart['shipping_total'], 'shipping_discount' => $cart['shipping_discount'], 'tax_total' => $cart['tax_total'], 'discount_total' => $cart['discount_total'], 'grand_total' => $cart['grand_total'], 'credit_total' => $cart['credit_total'], 'billing_total' => $cart['billing_total'], 'product_cost' => $cart['product_cost']));
             // Remember discount IDs.
             foreach ((array) $order['discounts'] as $discount) {
                 $order['discount_ids'][] = $discount['id'];
             }
             // Default auto payment.
             $order['auto_payment'] = $order['auto_payment'] ?: true;
             // Send e-mail by default.
             if (!isset($data[':email'])) {
                 $data[':email'] = true;
             }
         } elseif ($data['order']) {
             $order = $data['order'];
             // Change certain properties.
             $order['parent_id'] = $order['parent_id'] ?: $order['id'];
             $order['prev_id'] = $order['id'];
             $order['status'] = 'pending';
             // Unset certain properties.
             unset($order['date_created']);
             unset($order['date_updated']);
             unset($order['date_scheduled']);
             unset($order['next_id']);
             unset($order['_id']);
             unset($order['id']);
         } elseif ($data['account_id']) {
             $order = $data;
             // New order by account id?
             if ($account = get("/accounts/{$order['account_id']}")) {
                 // Set defaults from account.
                 $order['name'] = $order['name'] ?: $account['name'];
                 $order['email'] = $order['email'] ?: $account['email'];
                 $order['phone'] = $order['phone'] ?: $account['phone'];
                 // Default to account shipping/billing, filter account arrays for validation.
                 $order['shipping'] = $order['shipping'] ?: array_filter((array) $account['shipping']);
                 $order['billing'] = $order['billing'] ?: array_filter((array) $account['billing']);
                 unset($order['shipping']['default']);
                 unset($order['billing']['default']);
             } else {
                 // Bad account.
                 return false;
             }
         } elseif ($data['account']) {
             $order = $data;
             // New account.
             $account = post("/accounts", $data['account']);
             if ($account['errors']) {
                 $model->errors['account'] = $account['errors'];
             } else {
                 unset($order['account']);
                 $order['account_id'] = $account['id'];
                 $order['name'] = $account['name'];
                 $order['email'] = $account['email'];
                 $order['phone'] = $account['phone'];
             }
         }
         // Default status.
         if (!isset($order['status'])) {
             $order['status'] = Orders::get_status($order);
         }
         // Default schedule date.
         $order['date_scheduled'] = Orders::get_date_scheduled($order);
         // Filter order items.
         // @TODO: Fix this to accept more but ignore :with stuff
         $order['items'] = Orders::get_items_for_update($order['items']);
         // Post it.
         $event['data'] = $order;
     }, 'PUT' => function ($event, $model) {
         $data =& $event['data'];
         if ($order = $model->get($event['id'])) {
             // Update billing info? Merge.
             if ($data['billing']) {
                 $data['billing'] = array_merge((array) $order['billing'], (array) $data['billing']);
             }
             // Update shipping info? Merge.
             if ($data['shipping']) {
                 $data['shipping'] = array_merge((array) $order['shipping'], (array) $data['shipping']);
             }
             // Update account info?
             if ($order['account']) {
                 if ($order['name'] != $order['account']['name']) {
                     $data['name'] = $order['account']['name'];
                 }
                 if ($order['email'] != $order['account']['email']) {
                     $data['email'] = $order['account']['email'];
                 }
                 if ($order['phone'] != $order['account']['phone']) {
                     $data['phone'] = $order['account']['phone'];
                 }
             }
             // Update schedule?
             if (isset($data['schedule'])) {
                 $data['prev_id'] = $order['prev_id'];
                 $data['date_scheduled'] = Orders::get_date_scheduled($data);
             }
             // Removed coupon code?
             if (isset($data['coupon_code']) && !$data['coupon_code']) {
                 // Remove coupon.
                 $data['discounts'] = $order['discounts'];
                 unset($data['discounts']['coupon']);
             } elseif ($data['coupon_code'] && ($data['coupon_code'] != $order['coupon_code'] || !$order['discounts']['coupon'])) {
                 $discount = get("/discounts", array('code' => $data['coupon_code'], 'is_valid' => true));
                 if ($discount === false) {
                     $model->error('already used', 'coupon_code');
                 } else {
                     if (!$discount) {
                         $model->error('invalid', 'coupon_code');
                     }
                 }
                 // Remember code.
                 $data['coupon_code'] = $discount['code'];
                 // Update items with discount rules.
                 // @TODO: shouldn't have to convert to array like this.
                 $data['items'] = $order['items'] instanceof Traversable ? iterator_to_array($order['items']) : $order['items'];
                 foreach ((array) $discount['rules'] as $rule) {
                     if ($rule['add'] && $rule['product_id']) {
                         // Exists in cart?
                         $exists = false;
                         foreach ((array) $data['items'] as $item_id => $item) {
                             if ($item['id'] == $rule['product_id']) {
                                 $exists = $item_id;
                                 break;
                             }
                         }
                         // Update item quantity?
                         if ($exists) {
                             $data['items'][$exists]['quantity'] = $rule['quantity'];
                         } else {
                             // New item.
                             $data['items'][] = array('id' => $rule['product_id'], 'quantity' => $rule['quantity']);
                             // @TODO: Find a beter way to make sure ID is never 0.
                             if ($data['items'][0]) {
                                 $data['items'][1] = $data['items'][0];
                                 unset($data['items'][0]);
                             }
                         }
                     }
                 }
                 $data['discounts'] = $order['discounts'];
                 $data['discounts']['coupon'] = $discount;
             } else {
                 // Don't update coupon code directly.
                 unset($data['coupon_code']);
             }
             // Extra discount updates.
             if ($data['discounts']) {
                 // Remember discount IDs for easy lookup.
                 $data['discount_ids'] = array();
                 foreach ((array) $data['discounts'] as $did => $discount) {
                     $data['discount_ids'][] = $discount['id'];
                     // Unset some discount details.
                     unset($data['discounts'][$did]['codes']);
                     unset($data['discounts'][$did]['codes_used']);
                     unset($data['discounts'][$did]['code_history']);
                     unset($data['discounts'][$did]['conditions']);
                 }
             }
             // Cancel order?
             if ($data[':cancel']) {
                 foreach ((array) $order['items'] as $id => $item) {
                     if (!$item['is_returned']) {
                         $item['is_cancelled'] = true;
                         $data['items'][$id] = $item;
                     }
                 }
                 $data['status'] = 'cancelled';
                 $data['date_cancelled'] = time();
             }
             // Filter order items?
             if ($data['items']) {
                 $data['items'] = Orders::get_items_for_update($data['items']);
             }
             // Recalculate order totals?
             if ($data[':recalc']) {
                 $order['shipping'] = $data['shipping'] ?: $order['shipping'];
                 $order['billing'] = $data['billing'] ?: $order['billing'];
                 // Allow shipping total to be set manually.
                 if (isset($data['shipping_total'])) {
                     if (is_numeric($data['shipping_total'])) {
                         $order['shipping_total'] = $data['shipping_total'];
                     } else {
                         $order['shipping_total'] = null;
                     }
                 }
                 // Allow tax total to be set manually.
                 if (isset($data['tax_total'])) {
                     if (is_numeric($data['tax_total'])) {
                         $order['set_tax_total'] = $data['set_tax_total'] = $data['tax_total'];
                     } else {
                         $order['set_tax_total'] = $data['set_tax_total'] = null;
                     }
                 }
                 // Use cart model to calc totals.
                 $data = array_merge($data, array('sub_total' => round($order['cart']['sub_total'], 2), 'sub_discount' => round($order['cart']['sub_discount'], 2), 'shipping_total' => round($order['cart']['shipping_total'], 2), 'shipping_discount' => round($order['cart']['shipping_discount'], 2), 'tax_total' => round($order['cart']['tax_total'], 2), 'discount_total' => round($order['cart']['discount_total'], 2), 'grand_total' => round($order['cart']['grand_total'], 2), 'billing_total' => round($order['cart']['grand_total'], 2), 'product_cost' => round($order['cart']['product_cost'], 2)));
             }
         }
     }, 'after:POST, after:PUT' => function ($result, $event, $model) {
         $data =& $event['data'];
         $orig =& $event['orig'];
         // Orig vs new coupon.
         $orig_coupon = array('id' => $orig['discounts']['coupon']['id'], 'code' => $orig['coupon_code']);
         $new_coupon = array('id' => $data['discounts']['coupon']['id'], 'code' => $data['coupon_code']);
         if ($event['id'] && isset($data['discounts']) && isset($data['coupon_code']) && $orig_coupon != $new_coupon) {
             // Undo orig coupon used?
             if ($orig['discounts']['coupon']) {
                 put("/discounts/{$orig_coupon['id']}", array('code_used' => $orig_coupon['code'], ':undo' => true));
             }
             // Used new coupon?
             if ($data['discounts']['coupon']) {
                 put("/discounts/{$new_coupon['id']}", array('code_used' => $new_coupon['code']));
             }
         }
         // Update order totals?
         if ($event['id']) {
             if (isset($data['discounts']) || isset($data['items'])) {
                 // Update order totals.
                 $model->put($event['id'], array(':recalc' => true));
             }
         }
         // Relate to cart?
         if ($cart_id = $data['cart_id']) {
             put("/carts/{$cart_id}/order_id", $result['id']);
         }
         // Send order receipt e-mail?
         if ($data[':email']) {
             $settings = get("/settings/emails/order");
             if ($settings !== false) {
                 $settings['order'] = $result;
                 $settings['to'] = $result['email'];
                 // Default subject?
                 if ($settings['subject']) {
                     $settings['subject'] .= ' #' . $result['id'];
                 }
                 // Override default email settings?
                 if (is_array($data[':email'])) {
                     $settings = array_merge($settings, $data[':email']);
                 }
                 post("/emails/order", $settings);
             }
         }
     }, 'POST.items, PUT.items' => function ($event, $model) {
         $data =& $event['data'];
         // Filter item.
         $items = array($data);
         $data = array_pop(Orders::get_items_for_update($items));
         if ($order = $model->get($event['id'])) {
             $product = get("/products/{$data['id']}", array('pricing' => array('roles' => $order['account']['roles'], 'quantity' => $data['quantity'] ?: 1)));
             // Default price?
             if ($product && isset($data['price']) && $data['price'] === "") {
                 $data['price'] = $product['price'];
             }
             // Item variant?
             if ($product['variants']) {
                 $vid = $data['variant_id'];
                 if ($variant = $product['variants'][$vid]) {
                     $data['variant'] = $variant;
                     $data['price'] = $data['price'] ?: $variant['price'];
                 } else {
                     // Variant not found.
                     $model->error('not found', 'variant_id');
                 }
             } else {
                 $data['variant'] = null;
                 $data['variant_id'] = null;
             }
         }
     }, 'after:POST.items, after:PUT.items, after:DELETE.items' => function ($result, $event, $model) {
         $data =& $event['data'];
         $order_id = $event['id'];
         $order = $model->get($order_id);
         // All items cancelled/returned?
         $all_items_cancelled = true;
         $all_items_returned = true;
         foreach ((array) $order['items'] as $i) {
             if (!$i['is_cancelled']) {
                 $all_items_cancelled = false;
             }
             if (!$i['is_returned']) {
                 $all_items_returned = false;
             }
         }
         if ($order['items'] && $all_items_cancelled && !$order['date_cancelled']) {
             $model->put($order_id, array('status' => 'cancelled', 'date_cancelled' => time()));
         } else {
             if (!$all_items_cancelled && $order['date_cancelled']) {
                 $model->put($order_id, array('date_cancelled' => null));
             }
         }
         if ($order['items'] && $all_items_returned && !$order['date_returned']) {
             $model->put($order_id, array('status' => 'cancelled', 'date_returned' => time()));
         } else {
             if (!$all_items_returned && $order['date_returned']) {
                 $model->put($order_id, array('date_returned' => null));
             }
         }
         if ($data['is_cancelled']) {
             // @TODO: Return item qty to stock.
         }
         if ($data['is_cancelled'] || $data['is_returned']) {
             // @TODO: Remove item price/cost from product, sub, cost, grand totals.
         }
         // Update order totals.
         $model->put($order_id, array(':recalc' => true));
     }, 'after:POST.items' => function ($result, $event, $model) {
         // Update order totals.
         //$model->put($event['id'], array(':recalc' => true));
         // @TODO: Remove/replace item qty from stock.
     }, 'after:DELETE.items' => function ($result, $event, $model) {
         // Update order totals.
         //$model->put($event['id'], array(':recalc' => true));
         // @TODO: Remove/replace item qty from stock.
     });
 }