get round grand total in cart & invoice - magento

I want a rounding grand total; I have created a custom module and rewritten the core models to achieve this.
My rewrite Model code is below
1. Mage_Sales_Model_Quote_Address_Total_Grand
<?php
class Lr_Roundtotal_Model_Quote_Address_Total_Grand extends Mage_Sales_Model_Quote_Address_Total_Grand
{
public function collect(Mage_Sales_Model_Quote_Address $address)
{
$grandTotal = $address->getGrandTotal();
$baseGrandTotal = $address->getBaseGrandTotal();
$totals = array_sum($address->getAllTotalAmounts());
$baseTotals = array_sum($address->getAllBaseTotalAmounts());
$address->setGrandTotal(round($grandTotal+$totals)); //Modificated
$address->setBaseGrandTotal(round($baseGrandTotal+$baseTotals)); //Modificated
//$address->setGrandTotal($grandTotal+$totals); --Original
//$address->setBaseGrandTotal($baseGrandTotal+$baseTotals); --Original
return $this;
}
public function fetch(Mage_Sales_Model_Quote_Address $address)
{
$address->addTotal(array(
'code' => $this->getCode(),
'title' => Mage::helper('sales')->__('Grand Total'),
'value' => round($address->getGrandTotal()),
'netvalue' => round($address->getGrandTotal()),
'area' => 'footer',
));
return $this;
}
}
and second one is
2.Mage_Sales_Model_Order_Invoice
<?php
class Lr_Roundtotal_Model_Order_Invoice extends Mage_Sales_Model_Order_Invoice
{
public function pay()
{
if ($this->_wasPayCalled) {
return $this;
}
$this->_wasPayCalled = true;
$invoiceState = self::STATE_PAID;
if ($this->getOrder()->getPayment()->hasForcedState()) {
$invoiceState = $this->getOrder()->getPayment()->getForcedState();
}
$this->setState($invoiceState);
$this->getOrder()->getPayment()->pay($this);
$this->getOrder()->setTotalPaid(
round($this->getOrder()->getTotalPaid()+$this->getGrandTotal()) //Modificated
// $this->getOrder()->getTotalPaid()+$this->getGrandTotal() --Original
);
$this->getOrder()->setBaseTotalPaid(
round($this->getOrder()->getBaseTotalPaid()+$this->getBaseGrandTotal()) //Modificated
// $this->getOrder()->getBaseTotalPaid()+$this->getBaseGrandTotal() --Original
);
Mage::dispatchEvent('sales_order_invoice_pay', array($this->_eventObject=>$this));
return $this;
}
}
For example
Cart
sub-total : 990.00
discount : 120.70
Grand Total: 869.00(rounded)
Invoice
sub-total : 990.00
discount : 120.70
Grand Total: 869.30(not-rounded)
I want same grand total in cart and Invoice

Once you overwrite the core file in local then implement below code.
First create one function in the HELPER file like below(no need to create new module but you can put this function in any module helper file):
Namespace_module_helper_data extends Mage_Core_Helper_Abstract
{
public function getFormatedPrice($price)
{
return Mage::getModel('directory/currency')->format($price, array('display'=>Zend_Currency::NO_SYMBOL), false);
}
}
then you can just use this function where you need to round the price in any where in Magento.
You can use helper function like below:
$helper = Mage::helper('modulename'); // module name means name of the module in which you have create helper
Use function for round price like below:
$price = 120.12456;
echo $helper->getFormatedPrice($price); // you can get round price as per your store.

Related

Duplicated request, need an advice please

I made a system to calculate the total price of a basket, it works but I saw that there were duplicate requests, I would like to share the code with you to know if you would have done otherwise?
For the moment I have not found any other solution.
Userscontroller.php :
$carts =\Auth::user()->carts;
User.php (model) :
public function carts()
{
return $this->hasMany('App\Models\Cart')->with('product');
}
Cart.php (model) :
public function product()
{
return $this->belongsTo('App\Models\Product', 'product_id')->with('productCat');
}
public function getTotalPrice()
{
$price = 0;
foreach($this->user->carts as $carts)
{
$price += $carts->getPriceByQuantity();
}
return $price;
}
public function getPriceByQuantity()
{
$quantity = $this->quantity;
$price = ($this->product->product_total_price == '') ? $this->product->priceRender() : $this->product->product_total_price;
return $price * $quantity;
}
The problem is that i declare user->carts one time in userscontroller for the view, and one time in getTotalPrice. Before, i wanted to loop over $this instead of $this->user->carts, but the loop was repeated by the quantity number of a product.
If someone knows the best way to do this ? Thank you

Laravel - How to update Input Array without deleting Sales Detail

In my Laravel-8 project, I have this controller for Input Field Array Update.
Controller:
public function update(UpdateSaleRequest $request, $id)
{
try {
$sale = Sale::find($id);
$data = $request->all();
$update['date'] = date('Y-m-d', strtotime($data['date']));
$update['company_id'] = $data['company_id'];
$update['name'] = $data['name'];
$update['remarks'] = $data['remarks'];
$sale->update($update);
SaleDetail::where('sale_id', $sale->id)->delete();
foreach ($data['invoiceItems'] as $item) {
$details = [
'sale_id' => $sale->id,
'item_id' => $item['item_id'],
'employee_id' => $item['employee_id'],
'quantity' => $item['qty'],
'price' => $item['cost'],
'total_price' => $item['cost'] * $item['qty'],
'sale_type_id'=>$item['sale_type_id']
];
$saleDetail = new SaleDetail($details );
$saleDetail->save();
}
} catch (JWTException $e) {
throw new HttpException(500);
}
return response()->json($sale);
}
In the form, the user can add more Sales Detail or remove.
Some of the SaleDetail fields are being used somewhere else.
Is there a way to update the input field array without deleting the SaleDetail as shown in what I did here:
SaleDetail::where('sale_id', $sale->id)->delete();
Thanks
I've tried to restructure your code so that's easier to edit. I've left some comments. I can really recommend refactoring.guru. There you will find many ways to improve your code so that it is more extensible, maintainable and testable. If you have any questions, please feel free to ask.
class Sale extends Model
{
// Use a relationship instead of building your own query
public function details() {
return $this->hasMany(SaleDetail::class);
}
}
class SaleDetail extends Model
{
// Use a computed property instead of manually calculating total price
// You can access it with $saleDetail->totalPrice
public function getTotalPriceAttribute() {
return $this->price * $this->quantity;
}
}
class UpdateSaleRequest extends Request
{
public function authorize() {
return true;
}
protected function prepareForValidation() {
$this->merge([
// Create a Carbon instance by string
'date' => Carbon::make($this->date)
]);
}
public function rules() {
// Your validation rules
// Please also validate your invoice items!
// See https://laravel.com/docs/8.x/validation#validating-arrays
}
}
// We let Laravel solve the sale by dependency injection
// You have to rename the variable name in ihr web.php
public function update(UpdateSaleRequest $request, Sale $sale)
{
// At this point, all inputs are validated!
// See https://laravel.com/docs/8.x/validation#creating-form-requests
$sale->update($request->validated());
// Please ensure, that all properties have the same name
// In your current implementation you have price = cost, be consistent!
foreach($request->input('invoiceItems') as $invoiceItem) {
// How we can consider that a detail is already created?
// I assume that each item_id will only occur once, otherwise you'll
// place the id of each detail in your update form (e.g. in a hidden input)
$candidate = $sale->details()
->where('item_id', $properties['item_id'])
->first();
if($candidate) {
$candidate->update($properties);
} else {
$sale->details()->create($properties);
}
}
// A JWT-Exception should not be necessary, since your authentication
// will be handled by a middleware.
return response()->json($sale);
}
I have not tested the code, few adjustments may be needed.
Laravel has a method called updateOrCreate as follow
/**
* Create or update a record matching the attributes, and fill it with values.
*
* #param array $attributes
* #param array $values
* #return \Illuminate\Database\Eloquent\Model|static
*/
public function updateOrCreate(array $attributes, array $values = [])
{
return tap($this->firstOrNew($attributes), function ($instance) use ($values) {
$instance->fill($values)->save();
});
}
That means you could do some thing like
public function update(UpdateSaleRequest $request, $id)
{
try {
$sale = Sale::find($id);
$data = $request->all();
$update['date'] = date('Y-m-d', strtotime($data['date']));
$update['company_id'] = $data['company_id'];
$update['name'] = $data['name'];
$update['remarks'] = $data['remarks'];
$sale->update($update);
foreach ($data['invoiceItems'] as $item) {
$details = [
'item_id' => $item['item_id'],
'employee_id' => $item['employee_id'],
'quantity' => $item['qty'],
'price' => $item['cost'],
'total_price' => $item['cost'] * $item['qty'],
'sale_type_id'=>$item['sale_type_id']
];
$sale->saleDetail()->updateOrCreate([
'sale_id' => $sale->id
], $details);
}
} catch (JWTException $e) {
throw new HttpException(500);
}
return response()->json($sale);
}
I would encourage you to refactor and clean up your code.You can also read more about it here https://laravel.com/docs/8.x/eloquent#upserts

How to apply the promotion code only once for a product in laravel

I need to apply the promotion code only once for a product, when I apply the same code for the same product it should not get applied.
Find below the code :
public function apply_promo(Request $request)
{
$name = $request->input();
$code=$name['code'];
$code_flags=0;
$p_id='';
$discount_value=0;
$discount_type='';
$Object = new Promotion();
$response = $Object->GetPromotion();
//print_r($response);exit;
foreach($response['promotions']['promotion'] as $value)
{
if($value['code']==$code)
{
$code_flags=1;
$p_id=$value['requires'];
$discount_value=$value['value'];
$discount_type=$value['type'];
}
}
if($code_flags==1)
{
$p_id_array=explode(',',$p_id);
$flags=0;
$data=json_encode(Cart::content());
$cartdata=json_decode($data);
//echo "<pre>";print_r($cartdata);exit;
foreach($cartdata as $key => $value)
{
if($value->options->package != 'domain')
{
if(in_array($value->options->gid,$p_id_array))
{
$flags=0;
$price=$value->price;
if($discount_type=='Percentage')
{
$discount_p=(($price*$discount_value)/100);
$price=$price-$discount_p;
$value->options->discount_code = $code;
$value->options->discount_price = $discount_p;
$value->options->discount_percentage = $discount_value;
Cart::update($value->rowId, ['price' => $price]);
}
}
}
}
if($flags==0)
{
session()->put('promo_code', $code);
\Session::flash('message');
return redirect('cart?success');
}
}
else{
session()->put('promo_code', '');
\Session::flash('message_error');
return redirect('cart?error');
}
}
I have tried using the above code, but here the code getting applied as many times as i submit the code. Kindly suggest me a solution to solve this.
you can make table the have used promotions and their product , say we name it product_promotions have ( promo_id , product_id ) .
then in product model create relation called promocodes (many to many)
public function promocodes(){
return $this->belongsToMany(Promo::class)->withPivot(['promo_id']);
}
this relation will return all promos used by this products so we now able to check if promo used before by it using this code
$product->promocodes()->where('promo_id' , $your_promo_code_id)->exists();

Magento - steps checkout and additional module

Thanks for this forum. I often read it and this is my first question.
The question concerns Magento (1.9.1.0). I wanted to add an additional module that allows customer to write a comment on the order. I've been able to find a module that suits my need on the web. It works perfectly, but there is a big problem: it joins in the final step of the checkout (in particular, after the payment phase). And so, if the customer pays by bank transfer, marking or similar payments, the problem doesn't arise: Magento passes to the next step (order comment) and then order completion.
The problem arises in case of Paypal payment, as Magento redirects customer to Paypal.com and, after making the payment, returns to the store by the end of the order (without going to the next step of the comment). The comment form consisted of 19 files that, once installed in Magento, don't modify any existing files.
I would like to include this form after shipping_method step (and not after payment step) and, therefore, I think I have to modify the file (of the module) that is called Abstract.php (app/code/community/Brainvire/Ordercomment/Controller/Onepage/Abstract.php).
I would be very grateful if you could help me edit that file.
<?php
require_once 'Mage/Checkout/controllers/OnepageController.php';
class Brainvire_Ordercomment_Controller_Onepage_Abstract extends
Mage_Checkout_OnepageController {
/*
* Saving the Payment at Checkout
*/
public function savePaymentAction()
{
$this->_expireAjax();
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost('payment', array());
try {
$result = $this->getOnepage()->savePayment($data);
}
catch (Mage_Payment_Exception $e) {
if ($e->getFields()) {
$result['fields'] = $e->getFields();
}
$result['error'] = $e->getMessage();
}
catch (Exception $e) {
$result['error'] = $e->getMessage();
}
$redirectUrl = $this->getOnePage()->getQuote()->getPayment()->getCheckoutRedirectUrl();
if (empty($result['error']) && !$redirectUrl) {
$this->loadLayout('checkout_onepage_ordercomment');
$result['goto_section'] = 'ordercomment';
}
if ($redirectUrl) {
$result['redirect'] = $redirectUrl;
}
$this->getResponse()->setBody(Zend_Json::encode($result));
}
}
/*
* Saving the order comment
*/
public function saveOrdercommentAction()
{
$this->_expireAjax();
if ($this->getRequest()->isPost()) {
$_brainvire_Ordercomment = $this->getRequest()->getPost('ordercomment');
Mage::getSingleton('core/session')->setBrainvireOrdercomment($_brainvire_Ordercomment);
$result = array();
$redirectUrl = $this->getOnePage()->getQuote()->getPayment()->getCheckoutRedirectUrl();
if (!$redirectUrl) {
$this->loadLayout('checkout_onepage_review');
$result['goto_section'] = 'review';
$result['update_section'] = array(
'name' => 'review',
'html' => $this->_getReviewHtml()
);
}
if ($redirectUrl) {
$result['redirect'] = $redirectUrl;
}
$this->getResponse()->setBody(Zend_Json::encode($result));
}
}
}
How do I edit this php code to insert Ordercomment after shipping_method step and before payment step?
Thanks
GS
I was able to move the order-comment section by editing two files:
First: Brainvire\Ordercomment\Block\Onepage.php
class Brainvire_Ordercomment_Block_Onepage extends Mage_Checkout_Block_Onepage
{
/**
* getting the steps of Checkout page
*/
public function getSteps()
{
...
$stepCodes = array('billing', 'shipping', 'shipping_method', 'ordercomment', 'payment', 'review'); // moved 'ordercomment' before 'payment'
...
}
}
And then the file you mentioned: Brainvire\Ordercomment\Controller\Onepage\Abstract.php
Remove the savePaymentAction() method and replace with this:
class Brainvire_Ordercomment_Controller_Onepage_Abstract extends Mage_Checkout_OnepageController {
/**
* Shipping method save action
*/
public function saveShippingMethodAction()
{
if ($this->_expireAjax()) {
return;
}
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost('shipping_method', '');
$result = $this->getOnepage()->saveShippingMethod($data);
/*
$result will have erro data if shipping method is empty
*/
if(!$result) {
Mage::dispatchEvent('checkout_controller_onepage_save_shipping_method',
array('request'=>$this->getRequest(),
'quote'=>$this->getOnepage()->getQuote()));
$this->getOnepage()->getQuote()->collectTotals();
$this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
if (empty($result['error']) && !$redirectUrl) {
$this->loadLayout('checkout_onepage_ordercomment');
$result['goto_section'] = 'ordercomment';
}
}
$this->getOnepage()->getQuote()->collectTotals()->save();
$this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
}
}
/*
* Saving the order comment
*/
public function saveOrdercommentAction()
{
$this->_expireAjax();
if ($this->getRequest()->isPost()) {
$_brainvire_Ordercomment = $this->getRequest()->getPost('ordercomment');
Mage::getSingleton('core/session')->setBrainvireOrdercomment($_brainvire_Ordercomment);
$result = array();
$result['goto_section'] = 'payment';
$result['update_section'] = array(
'name' => 'payment-method',
'html' => $this->_getPaymentMethodsHtml()
);
$this->getResponse()->setBody(Zend_Json::encode($result));
}
}
}
Once you get this working, you should move these customizations into an override module, or at least copy the modified files to app/code/local, so the original code stays intact.

Better way for pagination

If I want to do the pagination, I have to fetch data twice, one for get total rows, one for get the rows with limit, for example
<?php
class Admins extends CI_Model
{
public function dataTotal()
{
$total = $this->db->get('admins')->num_rows();
return $total;
}
public function data()
{
return $this->db->limit(10, $this->start)->get('admins')->result();
}
}
Then assign total to pagination and assign the data to view, it's quite make sense, but if there are a lot of conditions, I need to do it twice, for example:
<?php
class Admins extends CI_Model
{
public function dataTotal()
{
$db = $this->db->from('admins')
->where('id >', 1)
->like('name', 'abc', 'both');
return $db->get()->num_rows();
}
public function data()
{
$data = $this->db->from('admins')
->where('id >', 1)
->like('name', 'abc', 'both')
->limit(10, $this->start);
return $data->get()->result();
}
}
More conditions means more duplicated code, any way to make condition filter as one?
You could make a function and use an SQL query as a parameter, that would be the most recommended option. If that's not an option you could do something like this:
public function data($option = "default")
{
if($option == 'default')
{
$data = $this->db->from('admins')
->where('id >', 1)
->like('name', 'abc', 'both')
}
else if($option == 'other')
{
$data = $this->db->from('admins')
->where('id >', 1)
->like('name', 'abc', 'both')
->limit(10, $this->start);
}
return $data->get()->result();
}
And then calling it:
data();
data("other");
That's the most efficient way I can come up with.

Resources