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.
Related
I need to recreate a resquest so that it behaves like a call via api to go through the validator, but my $request->input('rps.number') always arrives empty, although I can see the data in the debug
I also couldn't get it to go through the laravel validator
I can't use a technique to make an http call, because I need to put this call in a transaction
<?php
$nota = new stdClass();
$rps = new stdClass();
$rps->numero = (int)$xml->Rps->IdentificacaoRps->Numero;
$rps->serie = (string)$xml->Rps->IdentificacaoRps->Serie;
$rps->tipo = (int)$xml->Rps->IdentificacaoRps->Tipo;
$nota->rps = $rps;
$controller = new NotaController(new Nota());
$content = new StoreNotaRequest();
$content->request->add($nota);
$result = $controller->store($content);
StoreNotaRequest
<?php
class StoreNotaRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
$request = $this->request;
return [
'rps.numero' => 'required_with:rps|numeric|between:1,999999999999999',
'rps.serie' => 'required_with:rps|string|min:1|max:5',
'rps.tipo' => 'required_with:rps|integer|in:1,2,3'
];
}
}
NotaController
<?php
class NotaController extends Controller
{
private Nota $nota;
public function __construct(Nota $nota)
{
$this->nota = $nota;
}
public function store(StoreNotaRequest $request): JsonResponse
{
// $validated = $request->validated();
try {
$nota = DB::transaction(function () use ($request) {
$request->input('rps.numero');
});
return response()->json($nota);
} catch (Throwable $e) {
return response()->json($data, 409);
}
}
}
Solution
the solution was a little too verbose, I believe it is possible to solve with less code.
more does what it needs to go through the validation of the data contained in the StoreNotaRequest
and it returns an http response, in addition to being able to put all these isolated calls in a single transaction
DB::beginTransaction();
$errors = [];
foreach ($itens as $item) {
$controller = new NotaController(new Nota());
$request = new StoreNotaRequest();
$request->setMethod('POST');
$request->request->add($nota);
$request
->setContainer(app())
->setRedirector(app(Redirector::class))
->validateResolved();
$response = $controller->store($request);
if ($response->statusText() !== 'OK') {
$errors[$item->id] = 'ERROR';
}
}
if (count($errors) === 0) {
DB::commit();
} else {
DB::rollBack();
}
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
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();
I am trying to update the popularity count of Magento's Tag module by interacting with this core function in Mage_Tag_Model_API
public function update($tagId, $data, $store)
{
$data = $this->_prepareDataForUpdate($data);
$storeId = $this->_getStoreId($store);
/** #var $tag Mage_Tag_Model_Tag */
$tag = Mage::getModel('tag/tag')->setStoreId($storeId)->setAddBasePopularity()->load($tagId);
if (!$tag->getId()) {
$this->_fault('tag_not_exists');
}
// store should be set for 'base_popularity' to be saved in Mage_Tag_Model_Resource_Tag::_afterSave()
$tag->setStore($storeId);
if (isset($data['base_popularity'])) {
$tag->setBasePopularity($data['base_popularity']);
}
if (isset($data['name'])) {
$tag->setName(trim($data['name']));
}
if (isset($data['status'])) {
// validate tag status
if (!in_array($data['status'], array(
$tag->getApprovedStatus(), $tag->getPendingStatus(), $tag->getDisabledStatus()))) {
$this->_fault('invalid_data');
}
$tag->setStatus($data['status']);
}
try {
$tag->save();
} catch (Mage_Core_Exception $e) {
$this->_fault('save_error', $e->getMessage());
}
return true;
}
In my controller I have this :
public function clickAction()
{
$tagString = $this->getRequest()->getParam('tag');
$tagByName = Mage::getModel('tag/tag')->loadByName($tagString);
$tagId = $tagByName->getTagId();
$basePopularity = ['base_popularity' => '13']; // hard coding while testing
Mage::getModel('tag/api')->update($tagId, $basePopularity, 1);
}
If I put a log statement in this part of the update function :
try {
// log stuff
$tag->save();
}
I can see it makes it to that try but there is no change in the data. What did I screw up? Any other ideas on how I can update the popularity of a tag through a controller? Using this same method and adding 'name' => 'blah' to that $data array parameter works fine..
I also found in Mage_Tag_Model_Indexer_Summary.php this method defined in the PHPdoc * #method Mage_Tag_Model_Indexer_Summary setPopularity(int $value) Maybe that is what I need... can someone provide an example showing how I could use that magic setter?
Try adding Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID); at the start of your clickAction function. base_popularity can only be updated from admin store.
The following code runs fine under Magento 1.6 but raises a Mage_Core_Exception (message: 'Cannot retrieve entity config: sales/Array') when run under 1.5.0.1. What do I need to do to get this code running under Magento 1.5.0.1?
$results = Mage::getResourceModel('sales/order_collection');
$results->join(
array('status_key_table' => 'order_status'),
'main_table.status = status_key_table.status',
array('status_key_table.label')
);
Thank you,
Ben
If you compare the join() methods between 1.5.0.1 and 1.6.2.0:
1.5.0.1: Mage_Core_Model_Mysql4_Collection_Abstract::join()
public function join($table, $cond, $cols='*')
{
if (!isset($this->_joinedTables[$table])) {
$this->getSelect()->join(array($table=>$this->getTable($table)), $cond, $cols);
$this->_joinedTables[$table] = true;
}
return $this;
}
1.6.2.0: Mage_Core_Model_Resource_Db_Collection_Abstract::join()
public function join($table, $cond, $cols = '*')
{
if (is_array($table)) {
foreach ($table as $k => $v) {
$alias = $k;
$table = $v;
break;
...
You can see that 1.5.0.1 doesn't support aliases. Instead, it calls $this->getTable() on the first parameter you pass in -- which should be a string. So, in your case, you'll need to pass in 'sales/order_status' as the first parameter.