Product Orders Between 2 Users

Product orders between 2 users

Warning: here comes a small novel.

Part 1: setting up the associations

I'd recommend reading the Rails guide on associations thoroughly, bookmark it, and read it again, because this is a key thing to understand properly, and can be a bit tricky - there are lots of options once you go beyond basic associations.

One thing to notice about your app is that your users have two roles, buyers and sellers. You're going to need to be careful with the names of your associations - Does @user.offers return the offers the user has made, or the offers the user has received? You might want to be able to put lists of both these things in the user's profile.

The basic relationships you're describing are fairly simple:

  • A user can sell many products, so User has_many :products and Product belongs_to :user

  • A user can make many offers, so User has_many :offers and Offer belongs_to :user

  • A product may receive many offers so Product has_many :offers and Offer belongs_to :product

That's all well and good, and you could certainly get by just doing this - in which case you can skip down to Part 2 :)

However, as soon as you start trying to add the through relationships the waters are going to get muddy. After all,

  • Offer belongs_to :user (the buyer), but it also has a user through product (the seller)

  • User has_many :products (that they are selling), but they also have many products through offers (that they are buying - well, trying to buy).

Aargh, confusing!

This is the point when you need the :class_name option, which lets you name an association differently to the class it refers to, and the :source option, which lets you name associations on the 'from' model differently to the 'through' model.

So you might then form your associations like this:

# User
has_many :products_selling, class_name: 'Product'
has_many :offers_received, class_name: 'Offer',
through: :products_selling, source: :offers

has_many :offers_made, class_name: 'Offer'
has_many :products_buying, class_name: 'Product',
through: :offers_made, source: :product

# Product
belongs_to :seller, class_name: 'User', foreign_key: :user_id
has_many :offers
has_many :buyers, class_name: 'User', through: :offers

# Offer
belongs_to :product
belongs_to :buyer, class_name: 'User', foreign_key: :user_id
has_one :seller, class_name: 'User', through: :product

Although if you renamed your user_id columns to seller_id in the products table, and buyer_id in the offers table, you wouldn't need those :foreign_key options.

Part 2: accepting/rejecting offers

There's a number of ways to tackle this. I would put a boolean field accepted on Offer and then you could have something like

# Offer
def accept
self.accepted = true
save
end

def reject
self.accepted = false
save
end

and you could find the outstanding offers (where accepted is null)

scope :outstanding, where(accepted: nil)

To get the accept/reject logic happening in the controller, you might consider adding new RESTful actions (the linked guide is another one worth reading thoroughly!). You should find a line like

resources :offers

in config/routes.rb, which provides the standard actions index, show, edit, etc. You can change it to

resources :offers do
member do
post :accept
post :reject
end
end

and put something like this in your OffersController

def accept
offer = current_user.offers_received.find(params[:id])
offer.accept
end

# similarly for reject

Then you can issue a POST request to offers/3/accept and it will cause the offer with id 3 to be accepted. Something like this in a view should do it:

link_to "Accept this offer", accept_offer_path(@offer), method: :post 

Note that I didn't just write Offer.find(params[:id]) because then a crafty user could accept offers on the behalf of the seller. See Rails Best Practices.

Associating orders with products and customers in Rails

I think the best way to go about it would be:

class User
has_many :orders
has_many :products, through: :orders
end

class Order
belongs_to :user
belongs_to :product
end

class Product
belongs_to :category
has_many :orders
has_many :users, through: :orders
end

More information about associations can be found here: http://guides.rubyonrails.org/association_basics.html

Limit customers to buy a particular product multiple times within a certain time frame in WooCommerce

For a restriction for a specific product per week you could use:

  • Based on the billing email address
  • wc_get_orders provide a standard way of retrieving orders - wc_get_orders and WC_Order_Query
  • Will work for guest users or logged in users
function action_woocommerce_checkout_process() {
// Initialize
$customer_email = '';

// Get email
if ( is_user_logged_in() ) {
// Get current user
$user = wp_get_current_user();

// Get email
$customer_email = $user->user_email;
} elseif ( isset( $_POST['billing_email'] ) && ! empty ( $_POST['billing_email'] ) ) {
$customer_email = $_POST['billing_email'];
} else {
// Get billing_email
$customer_email = WC()->customer->get_billing_email();
}

// NOT empty
if ( ! empty ( $customer_email ) ) {
// Time in seconds (1 week)
$time_in_seconds = 604800;

// Set limit per week
$limit = 2;

// Specific product id
$specific_product_id = 30;

// Get orders from last week from customer by email
$orders_last_week_by_customer_email = wc_get_orders( array(
'date_created' => '>' . (time() - $time_in_seconds ),
'customer' => $customer_email,
));

// Total (counter)
$total = 0;

// Iterating through each order
foreach ( $orders_last_week_by_customer_email as $order ) {
// Going through order items
foreach ( $order->get_items() as $item ) {
// Get product ID
$product_id = $item->get_product_id();

// Compare
if ( $specific_product_id == $product_id ) {
// Get quantity
$quantity = $item->get_quantity();

// Add to total
$total += $quantity;
}
}
}

// Show error when total >= limit
if ( $total >= $limit ) {
wc_add_notice( sprintf( __( 'You are not allowed to buy more than %d pieces of product with ID = %d in one week', 'woocommerce' ), $limit, $specific_product_id ), 'error' );
}
}
}
add_action( 'woocommerce_checkout_process', 'action_woocommerce_checkout_process', 10, 0 );

To apply this only for guest users:

Replace

// Get email
if ( is_user_logged_in() ) {
// Get current user
$user = wp_get_current_user();

// Get email
$customer_email = $user->user_email;
} elseif ( isset( $_POST['billing_email'] ) && ! empty ( $_POST['billing_email'] ) ) {
$customer_email = $_POST['billing_email'];
}

With

// Only for guests
if ( ! is_user_logged_in() ) return;

if ( isset( $_POST['billing_email'] ) && ! empty ( $_POST['billing_email'] ) ) {
$customer_email = $_POST['billing_email'];
}

Count when a user order a product

You have to try this its working properly

$count = order::where('id_user',auth()->user()->id)->count();

OR

$count = order::where('id_user',auth()->id())->count();

Restrict guest users to buy a specific product multiple times in the same week based on previous orders and phone number in WooCommerce

customer-phone simply does not exist, you should use billing_phone instead.

See: wc_get_orders and WC_Order_Query

Address and Name

billing_first_name, billing_last_name, billing_company, billing_address_1, billing_address_2, billing_city, billing_state, billing_postcode, billing_country, billing_email, billing_phone, shipping_first_name, shipping_last_name, shipping_company, shipping_address_1, shipping_address_2, shipping_city, shipping_state, shipping_postcode, shipping_country, customer_ip_address


Customer

Accepts a string or an integer: The order's billing email or customer id.




Note: If it is purely about the number of times the product appears in an order (1x per order) versus the quantity of a product per order, there is no need to use get_quantity() in the foreach loop.


So you get:

function action_woocommerce_checkout_process() {
// Only for guests
if ( is_user_logged_in() ) return;

// Isset
if ( isset( $_POST['billing_phone'] ) ) {
// NOT empty
if ( ! empty ( $_POST['billing_phone'] ) ) {
$customer_phone = $_POST['billing_phone'];
}
}

// Isset
if ( isset ( $customer_phone ) ) {
// Time in seconds (1 week)
$time_in_seconds = 604800;

// Set limit per week
$limit = 2;

// Specific product name
$specific_product_name = 'Buy product';

// Get orders from last week from customer by phone
$orders_last_week_by_customer_phone = wc_get_orders( array(
'date_created' => '>' . (time() - $time_in_seconds ),
'billing_phone' => $customer_phone,
));

// Total (counter)
$total = 0;

// Iterating through each order
foreach ( $orders_last_week_by_customer_phone as $order ) {
// Going through order items
foreach ( $order->get_items() as $item ) {
// Name of the product
$product_name = $item->get_name();

// Compare
if ( $specific_product_name == $product_name ) {
// Add to total
$total += 1;
}
}
}

// Show error when total >= limit
if ( $total >= $limit ) {
wc_add_notice( sprintf( __( 'You are not allowed to buy more than %d products. For more information please contact support.', 'woocommerce' ), $limit ), 'error' );
}
}
}
add_action( 'woocommerce_checkout_process', 'action_woocommerce_checkout_process', 10, 0 );

How to Query database to grab product orders to user profile

In your user Model :

public function orders(){
return $this->hasMany(Order::class)->latest();
}

in your Order Model

//For the listing of product in an order
public function products(){
return $this->belongsToMany(Product::class);
}

In your controller

public function index(User $user)
{
$user->load('orders.products');
return view('profile.index', compact('user'));
}

In your view

@forelse($user->orders as $order)

<h4>{{$order->total}}</h4>
@forelse($order->products as $product)
{{ $product->name }}
@empty
no product
@endforelse

@empty

<h5>No items</h5>

@endforelse

Find users who have made two or more orders with ActiveRecord

You can query to find all the users with more than 2 orders in the past along with their latest order's created_at. And then select users with their latest order's created_at less than a month. Like this:

repeat_users = User.joins(:orders).group("(users.id) having count(users.id) > 2").select("users.*", "max(orders.created_at) as last_order_time")

past_month_users = repeat_users.select{|user| user.last_order_time > Time.now - 1.month}


Related Topics



Leave a reply



Submit