shippingRepository = $shippingRepository; $this->invoiceRepository = $invoiceRepository; $this->roleRepository = $roleRepository; $this->voucherRepository = $voucherRepository; $this->xendit = $xendit; } public function getList($params = []) { $customColumns = [ 'user.name' => 'users.name', ]; $limit = @$params["limit"] ? (int) @$params["limit"] : 10; $sortColumn = @$params["sort"]["column"] ? (@$customColumns[$params["sort"]["column"]] ? $customColumns[$params["sort"]["column"]] : $params["sort"]["column"]) : "id"; $sortDir = @$params["sort"]["dir"] ? $params["sort"]["dir"] : "desc"; $model = Transaction::select('transactions.*') ->leftJoin('address', 'address.id', 'transactions.address_id') ->leftJoin('users', 'users.id', 'transactions.user_id') ->when(@!$params['is_admin'], function ($query) { $query->where('transactions.user_id', auth()->user()->id); }) ->when(@$params['status'], function ($query) use ($params) { $query->where('transactions.status', $params['status']); }) ->when(@$params['filter']['multiple_status'] && !empty($params['filter']['multiple_status']), function($query) use ($params){ $query->whereIn("transactions.status", $params['filter']['multiple_status']); }) ->when(@$params['filter']['except'], function($query) use ($params){ $except = @$params['filter']['except']; if (is_array($except)){ $query->whereNotIn("status", $except); }else{ $query->where("status","<>", $except); } }) ->when(@$params['filter']['start'], function($query) use ($params){ $query->whereDate("transactions.time", ">=", $params['filter']['start']); }) ->when(@$params['filter']['end'], function($query) use ($params){ $query->whereDate("transactions.time", "<=", $params['filter']['end']); }) ->when(@$params['search'], function($query) use ($params) { $query->where(function($query) use ($params) { $query->where('transactions.number', 'ilike', '%' . $params['search'] . '%') ->orWhere('users.name', 'ilike', '%' . $params['search'] . '%') ->orWhere('address.name', 'ilike', '%' . $params['search'] . '%'); }); }) ->orderBy($sortColumn, $sortDir) ->paginate($limit); return $model; } public function getListForExport($params = []) { $customColumns = [ 'user.name' => 'users.name', ]; $limit = @$params["limit"] ? (int) @$params["limit"] : 10; $sortColumn = @$params["sort"]["column"] ? (@$customColumns[$params["sort"]["column"]] ? $customColumns[$params["sort"]["column"]] : $params["sort"]["column"]) : "transactions.id"; $sortDir = @$params["sort"]["dir"] ? $params["sort"]["dir"] : "desc"; return Transaction::selectRaw(" transactions.*, transaction_details.*, coalesce(item_variants.description, items.name) as product_name, item_reference.number as item_reference_number ") ->leftJoin('address', 'address.id', 'transactions.address_id') ->leftJoin('users', 'users.id', 'transactions.user_id') ->leftJoin('transaction_details', 'transaction_details.transaction_id', 'transactions.id') ->leftJoin('item_reference', 'item_reference.id', 'transaction_details.item_reference_id') ->leftJoin('items', 'items.id', 'item_reference.item_id') ->leftJoin('item_variants', 'item_variants.id', 'transaction_details.item_variant_id') ->when(@!$params['is_admin'], function ($query) { $query->where('transactions.user_id', auth()->user()->id); }) ->when(@$params['status'], function ($query) use ($params) { $query->where('transactions.status', $params['status']); }) ->when(@$params['filter']['multiple_status'] && !empty($params['filter']['multiple_status']), function($query) use ($params){ $query->whereIn("transactions.status", $params['filter']['multiple_status']); }) ->when(@$params['filter']['except'], function($query) use ($params){ $except = @$params['filter']['except']; if (is_array($except)){ $query->whereNotIn("status", $except); }else{ $query->where("status","<>", $except); } }) ->when(@$params['filter']['start'], function($query) use ($params){ $query->whereDate("transactions.time", ">=", $params['filter']['start']); }) ->when(@$params['filter']['end'], function($query) use ($params){ $query->whereDate("transactions.time", "<=", $params['filter']['end']); }) ->when(@$params['search'], function($query) use ($params) { $query->where(function($query) use ($params) { $query->where('transactions.number', 'ilike', '%' . $params['search'] . '%') ->orWhere('users.name', 'ilike', '%' . $params['search'] . '%') ->orWhere('address.name', 'ilike', '%' . $params['search'] . '%'); }); }) ->orderBy($sortColumn, $sortDir) ->get(); } public function getCount($params = []) { $model = Transaction::select('transactions.*') ->leftJoin('address', 'address.id', 'transactions.address_id') ->leftJoin('users', 'users.id', 'transactions.user_id') ->when(@!$params['is_admin'], function ($query) { $query->where('transactions.user_id', auth()->user()->id); }) ->when(@$params['status'], function ($query) use ($params) { $query->where('transactions.status', $params['status']); }) ->when(@$params['filter']['multiple_status'] && !empty($params['filter']['multiple_status']), function($query) use ($params){ $query->whereIn("transactions.status", $params['filter']['multiple_status']); }) ->when(@$params['filter']['except'], function($query) use ($params){ $except = @$params['filter']['except']; if (is_array($except)){ $query->whereNotIn("status", $except); }else{ $query->where("status","<>", $except); } }) ->select("status") ->addSelect(DB::raw("COUNT(*) as count")) ->groupBy("status") ->get(); return $model; } public function getlistItem(Transaction $transaction, $params = []) { $limit = @$params["limit"] ? (int) @$params["limit"] : 10; $sortColumn = @$params["sort"]["column"] ? $params["sort"]["column"] : "id"; $sortDir = @$params["sort"]["dir"] ? $params["sort"]["dir"] : "desc"; return TransactionDetail::select('transaction_details.*') ->leftJoin('item_reference', 'item_reference.id', 'transaction_details.item_reference_id') ->leftJoin('item_variants', 'item_variants.id', 'transaction_details.item_variant_id') ->where('transaction_id', $transaction->id) ->when(@$params['search'], function($query) use ($params) { $query->where(function($query) use ($params) { $query->where('item_variants.description', 'ilike', '%' . $params['search'] . '%') ->orWhere('item_reference.number', 'ilike', '%' . $params['search'] . '%'); }); }) ->orderBy($sortColumn, $sortDir) ->paginate($limit); } public function setStatusTransaction($transactionId, $status, $note = '') { $user = auth()->user(); TransactionStatus::create([ 'transaction_id' => $transactionId, 'time' => now(), 'note' => $note, 'status' => $status, 'user_id' => @$user->id ?? 0, ]); } public function getAmount($items) { $amount = 0; foreach ($items as $detail) { $itemReference = ItemReference::findOrFail($detail['item_reference_id']); $item = Items::findOrFail($itemReference->item_id); $amount += $detail['qty'] * $item->unit_price; } return $amount; } public function createPayment($model){ $fees = []; if ($model->shipping_price){ $fees[] = [ "type" => "SHIPPING", "value" => $model->shipping_price ]; } if ($model->discount){ $fees[] = [ "type" => "DISCOUNT", "value" => $model->discount ]; } $remaining_amount = ($model->subtotal + $model->shipping_price) - $model->discount; $xendit_response = $this->xendit->createPaymentLink([ "external_id" => $model->number, "amount" => $remaining_amount, "items" => $model->details->map(function($cart){ return [ "name" => $cart->item->name, "category" => @$cart->item->category->name ?? "GOODS", "price" => $cart->unit_price, "quantity" => $cart->qty ]; }), "fees" => $fees ]); if ($xendit_response){ $xendit_link = XenditLink::create([ "uid" => $xendit_response["id"], "invoice_url" => $xendit_response["invoice_url"], "user_id" => $xendit_response["user_id"], "external_id" => $xendit_response["external_id"], "status" => $xendit_response["status"], "amount" => $xendit_response["amount"], "expiry_date" => $xendit_response["expiry_date"] ]); $payment = TransactionPayment::create([ "amount" => $remaining_amount, "status" => "PENDING", "transaction_id" => $model->id, "method_type" => get_class($xendit_link), "method_id" => $xendit_link->id ]); } $this->setStatusTransaction($model->id, 'WAIT_PAYMENT'); return $xendit_response; } public function create($data) { $model = DB::transaction(function () use ($data) { $user = auth()->user(); $customer = Customer::where("user_id", $user->id)->first(); $location_id = $data["location_id"]; // validasi voucher if ( @$data["vouchers"]){ $used_voucher = Voucher::whereIn("id", $data["vouchers"])->whereNotNull("used_at")->count(); if ($used_voucher){ throw ValidationException::withMessages([ "voucher" => "Voucher Telah digunakan " ]); } } // get numbering $auto = new AutoNumbering([ "type" => "TRX", "location_id" => 0, "prefix" => "TRX", "pad" => 12 ]); $number = $auto->getCurrent(); $data["number"] = $number; $items = collect(@$data["items"] ?? []); $carts = Cart::where('user_id', $user->id) ->where("location_id", $data["location_id"]) ->when(count($items) > 0, function($query) use ($items) { $query->whereIn("item_reference_id", $items->pluck("item_reference_id")); }) ->get(); $carts = $carts->map(function($cart) use ($items){ $item = $items->firstWhere("item_reference_id", $cart->item_reference_id); if ($item){ $cart->new_qty = $cart->qty - $item["qty"]; $cart->qty = $item["qty"]; } return $cart; }); if (count($carts) == 0){ throw ValidationException::withMessages([ "items" => "Tidak ada item di keranjang" ]); } // detail data process $subtotal = 0; $details = []; foreach ($carts as $detail) { $convertion = 1; if (@$detail->item->display_unit and @$detail->item->display_unit != @$detail->item->unit){ $convertions = DB::select("select to_qty / from_qty as conv from item_convertions where from_unit = ? and to_unit = ?", [$detail->item->display_unit, $detail->item->unit] ); $convertion = max((float) @$convertions[0]->conv, 1); } $d_price = @$detail->itemReference->discount->price ?? 0; $s_price = @$detail->itemReference->price->price ?? 0; $price = ( $s_price ? $s_price : $detail->item->net_price) * $convertion; $price = ( $d_price ? $d_price : $price) * $convertion; $total = $detail->qty * $price; $details[] = [ 'item_id' => $detail->item_id, 'item_variant_id' => $detail->item_variant_id, 'item_reference_id' => $detail->item_reference_id, 'qty' => $detail->qty, 'unit' => ($detail->item->display_unit ?? $detail->item->unit), 'unit_price' => $price, 'unit_cost' => $detail->item->unit_cost, 'total' => $total ]; $subtotal += $total; } // validasi voucher if ( @$data["vouchers"]){ $used_vouchers = Voucher::whereIn("id", $data["vouchers"])->get(); foreach($used_vouchers as $v){ if ($subtotal < ((float) $v->min_sales)){ throw ValidationException::withMessages([ "voucher" => "Voucher minimal belanja ".$v->min_sales ]); } } } // get shipping_price $list_shipping = $this->shippingRepository->getList($data); $filtered_shipping = collect($list_shipping["pricing"])->filter(function($pricing) use ($data){ return $pricing["company"] == $data["courier_company"] && $pricing["type"] == $data["courier_type"]; })->values(); if (count($filtered_shipping) == 0){ throw ValidationException::withMessages([ "courier_company" => "Pilihan kurir tidak ditemukan" ]); } if ($subtotal >= 1000000){ $shipping_price = max($filtered_shipping[0]["price"] - 50000, 0); }else{ $shipping_price = $filtered_shipping[0]["price"]; } $valid_vouchers = $this->voucherRepository->getValidByItems($carts, $customer->id); $vouchers = @$data["vouchers"] ? Voucher::whereIn("id", $data["vouchers"]) ->whereIn("id",$valid_vouchers) ->get(): collect([]); // discount $discount = $vouchers->reduce(function($acc, $voucher){ return $acc + ($voucher->nominal); },0); $subtotal2 = $subtotal + $shipping_price; $amount = max(0, $subtotal2 - $discount); $discount = min($subtotal2, $discount); // discount points $discount_point = 0; $use_customer_points = @$data["use_customer_points"] ?? 0; if ($use_customer_points > 0){ $current_point = auth()->user()->customer->point; // use_customer_points if ($use_customer_points > $current_point){ throw ValidationException::withMessages([ "use_customer_points" => "Point tidak cukup. Poin anda " . number_format($current_point, 0, ",", ".") ]); } // convert point to amount $point_to_amount = $use_customer_points * 1000; $discount_point = min($point_to_amount, $amount); $amount = max(0, $amount - $discount_point); } $model = Transaction::create([ 'number' => $data['number'], 'address_id' => $data['address_id'], 'location_id' => $data['location_id'], 'courier_company' => $data['courier_company'], 'courier_type' => $data['courier_type'], 'shipping_price' => $shipping_price, 'user_id' => auth()->user()->id, 'customer_id' => @$customer->id, 'time' => now(), 'subtotal' => $subtotal, 'discount' => $discount, 'point' => $discount_point, 'amount' => $amount, 'status' => 'WAIT_PAYMENT', ]); $model->details()->createMany($details); $subtotal_remaining = $subtotal2; foreach ($vouchers as $voucher) { if ($subtotal_remaining <= 0){ continue; } $voucher->fill([ "used_at" => Carbon::now(), "reference_used_id" => $model->id, "reference_used_type" => get_class($model) ]); $voucher->save(); TransactionPayment::create([ "amount" => min($voucher->nominal, $subtotal_remaining), "status" => "PAID", "transaction_id" => $model->id, "method_type" => get_class($voucher), "method_id" => $voucher->id ]); $subtotal_remaining = max(0,$subtotal_remaining - $voucher->nominal); } if ($use_customer_points > 0){ $cp = CustomerPoint::create([ "customer_id" => $model->customer_id, "point" => -1 * $use_customer_points, "reference_type" => get_class($model), "reference_id" => $model->id, "description" => "Discount Point", ]); $current_point = auth()->user()->customer->point; if ($current_point < 0){ throw ValidationException::withMessages([ "use_customer_points" => "Point anda telah habis" ]); } TransactionPayment::create([ "amount" => $discount_point, "status" => "PAID", "transaction_id" => $model->id, "method_type" => get_class($cp), "method_id" => $cp->id ]); } // create payment $fees = [ [ "type" => "SHIPPING", "value" => $shipping_price ] ]; if ($discount){ $fees[] = [ "type" => "DISCOUNT", "value" => $discount ]; } $remaining_amount = $amount; if ($amount > 0){ $xendit_response = $this->xendit->createPaymentLink([ "external_id" => $model->number, "amount" => $remaining_amount, "items" => $carts->map(function($cart){ $detail = $cart; $convertion = 1; if (@$detail->item->display_unit and @$detail->item->display_unit != @$detail->item->unit){ $convertions = DB::select("select to_qty / from_qty as conv from item_convertions where from_unit = ? and to_unit = ?", [$detail->item->display_unit, $detail->item->unit] ); $convertion = max((float) @$convertions[0]->conv, 1); } $d_price = @$detail->itemReference->discount->price ?? 0; $s_price = @$detail->itemReference->price->price ?? 0; $price = ( $s_price ? $s_price : $detail->item->net_price) * $convertion; $price = ( $d_price ? $d_price : $price) * $convertion; return [ "name" => $cart->item->name, "category" => @$cart->item->category->name ?? "GOODS", "price" => $price, "quantity" => $cart->qty ]; }), "fees" => $fees ]); if ($xendit_response){ $xendit_link = XenditLink::create([ "uid" => $xendit_response["id"], "invoice_url" => $xendit_response["invoice_url"], "user_id" => $xendit_response["user_id"], "external_id" => $xendit_response["external_id"], "status" => $xendit_response["status"], "amount" => $xendit_response["amount"], "expiry_date" => $xendit_response["expiry_date"] ]); $payment = TransactionPayment::create([ "amount" => $remaining_amount, "status" => "PENDING", "transaction_id" => $model->id, "method_type" => get_class($xendit_link), "method_id" => $xendit_link->id ]); } $this->setStatusTransaction($model->id, 'WAIT_PAYMENT'); }else{ $model->status = 'WAIT_PROCESS'; $model->save(); $this->setStatusTransaction($model->id, 'WAIT_PROCESS'); } // clear cart foreach($carts as $cart){ if ($cart->new_qty <= 0){ $cart->delete(); }else{ $new_qty = $cart->new_qty; unset($cart->new_qty); $cart->update([ "qty" => $new_qty ]); } } return $model; }); return $model; } public function webhookPayment($data) { $paymentLink = XenditLink::where("uid", $data["id"])->firstOrFail(); $paymentLink->fill([ "payment_id" => @$data["payment_id"], "paid_amount" => @$data["paid_amount"], "payment_method" => @$data["payment_method"], "bank_code" => @$data["bank_code"], "payment_channel" => @$data["payment_channel"], "payment_destination" => @$data["payment_destination"], "received_amount" => @$data["adjusted_received_amount"], "status" => @$data["status"], "paid_at" => @$data["paid_at"] ]); $paymentLink->save(); $paymentLink->payment()->update([ "status" => $paymentLink->status, ]); $transaction = Transaction::find($paymentLink->payment->transaction_id); if ($transaction && @$data["status"] == "PAID"){ if ($transaction->status != "WAIT_PROCESS"){ // once notify for first $roleRepository = $this->roleRepository; $users = $roleRepository->findUserByPermission("transaction.online"); $notification = new NewOrder($transaction); Notification::send($users, $notification); $notification2 = new OrderPaid($transaction); $transaction->customer->user->notify($notification2); } $this->setStatusTransaction($transaction->id,'WAIT_PROCESS'); $transaction->status = 'WAIT_PROCESS'; $transaction->save(); }else if ($transaction && @$data["status"] == "EXPIRED"){ if ($transaction->status != "CANCELED"){ $this->cancel($transaction->id,"Oleh System"); } } return $paymentLink; } public function payment($id) { $model = DB::transaction(function () use ($id) { $model = Transaction::findOrfail($id); TransactionPayment::create([ 'transaction_id' => $model->id, 'amount' => $model->amount, 'status' => 'PAID', ]); $this->setStatusTransaction($model->id, 'WAIT_PROCESS'); $model->status = 'WAIT_PROCESS'; $model->save(); return $model; }); return $model; } public function process($id) { $repository = $this->invoiceRepository; $model = DB::transaction(function () use ($id, $repository) { $model = Transaction::findOrfail($id); $this->setStatusTransaction($model->id, 'ON_PROCESS'); $model->status = 'ON_PROCESS'; $model->save(); $invoice = $this->invoiceRepository->createFromOnline($model); $vouchers = $model->vouchers; foreach ($vouchers as $voucher) { $repository->checkAffiliatorVoucher($invoice, $voucher); } return $model; }); $user = @$model->customer->user; if ($user){ $notification2 = new OrderProcessed($model); $user->notify($notification2); } return $model; } public function deliver($id) { $model = DB::transaction(function () use ($id) { $model = Transaction::findOrfail($id); $shipping = TransactionShipping::where("transaction_id",$model->id)->first(); if ($shipping){ return $model; } // order shippping $data = $this->shippingRepository->order($model); if(empty($data)) { throw new Exception('error failed create order'); } $weight_total = collect($data["items"])->reduce(function($acc, $item){ $item = (object) $item; return $acc + ($item->weight * $item->quantity); },0); $shipping = TransactionShipping::create([ "transaction_id" => $model->id, "origin_address" => $data["origin"]["address"], "origin_name" => $data["origin"]["contact_name"], "origin_phone" => $data["origin"]["contact_phone"], "origin_postal_code" => $data["origin"]["postal_code"], "origin_latitude" => $data["origin"]["coordinate"]["latitude"], "origin_longitude" => $data["origin"]["coordinate"]["longitude"], "origin_note" => $data["origin"]["note"], "destination_address" => @$data["destination"]["address"], "destination_name" => @$data["destination"]["contact_name"], "destination_phone" => @$data["destination"]["contact_phone"], "destination_postal_code" => @$data["destination"]["postal_code"], "destination_latitude" => @$data["destination"]["coordinate"]["latitude"], "destination_longitude" => @$data["destination"]["coordinate"]["longitude"], "destination_note" => @$data["destination"]["note"], "shipper_name" => @$data["shipper"]["name"], "shipper_phone" => @$data["shipper"]["phone"], "shipper_email" => @$data["shipper"]["email"], "uid" => $data["id"], "tracking_id" => @$data["courier"]["tracking_id"], "waybill_id" => @$data["courier"]["waybill_id"], "company" => @$data["courier"]["company"], "driver_name" => @$data["courier"]["driver_name"], "driver_phone" => @$data["courier"]["driver_phone"], "driver_photo_url" => @$data["courier"]["driver_photo_url"], "driver_plate_number" => @$data["courier"]["driver_plate_number"], "type" => @$data["courier"]["type"], "insurance_amount" => @$data["insurance"]["amount"], "insurance_fee" => @$data["insurance"]["fee"], "weight_total" => $weight_total, "price" => @$data["price"], "note" => @$data["note"], "status" => @$data["status"] ]); $this->setStatusTransaction($model->id, 'ON_DELIVERY'); $model->status = 'ON_DELIVERY'; $model->save(); return $model; }); $user = @$model->customer->user; if ($user){ $notification2 = new OrderOnDelivery($model); $user->notify($notification2); } return $model; } public function close($id) { $model = DB::transaction(function () use ($id) { $model = Transaction::findOrfail($id); $this->setStatusTransaction($model->id, 'CLOSED'); $model->status = 'CLOSED'; $model->save(); return $model; }); return $model; } public function delivered($id) { $model = DB::transaction(function () use ($id) { $model = Transaction::findOrfail($id); $this->setStatusTransaction($model->id, 'DELIVERED'); $model->status = 'DELIVERED'; $model->save(); return $model; }); $user = @$model->customer->user; if ($user){ $notification2 = new OrderDelivered($model); $user->notify($notification2); } return $model; } public function cancel($id, $note = "") { $model = DB::transaction(function () use ($id, $note) { $model = Transaction::findOrfail($id); $model->vouchers()->update([ "used_at" => null, "reference_used_id" => null, "reference_used_type" => null ]); $this->setStatusTransaction($model->id, 'CANCELED', $note); $model->status = 'CANCELED'; $model->save(); $customer_point = CustomerPoint::where("customer_id", $model->customer_id) ->where("reference_id", $model->id) ->where("reference_type", get_class($model)) ->orderBy("id", "asc") ->first(); if ($customer_point){ CustomerPoint::create([ "customer_id" => $model->customer_id, "point" => -1 * $customer_point->point, "reference_type" => get_class($model), "reference_id" => $model->id, "description" => "Refund Discount Point", ]); } return $model; }); $user = @$model->customer->user; if ($user){ $notification2 = new OrderCanceled($model); $user->notify($notification2); } return $model; } }