Magento 2 update invoice state through REST API giving error - magento

I am having project in magento enterprise version :2.3.7-p3
I'm trying to update Magento 2 invoice state via Rest API call : {{magento_api_url}}/V1/invoices/ and method is post below are payload for api
{
"entity": {
"entity_id": 8147,
"state": 2
}
}
but I am getting this error :
Fatal error: Uncaught Error: Call to a member function getId() on null in
/var/www/html/vendor/magento/module-sales/Model/ResourceModel/Order/Invoice.php:58
Could some one help me?

As by default megento issue, So we can not make changes in core file, So for this I have make one alternative solution...
I have created one custom invoice interface in my custom module with same API end point : {{magento_api_url}}/V1/invoices/ in webapi.xml file and defined the our custom model with preference in di.xml file and update the invoice state successfully.
Below are the code snippet
Custom Interface
interface InvoiceCustomInterface
{
/**
* save api
* #param \Magento\Sales\Api\Data\InvoiceInterface $entity Invoice interface
* #return \Magento\Sales\Api\Data\InvoiceInterface Invoice interface
*/
public function save($entity);
}
Webapi.xml
<route url="/V1/invoices/" method="POST">
<service class="Vendor\Module_Nmae\Api\InvoiceCustomInterface" method="save"/>
<resources>
<resource ref="Vendor_Module_Nmae::Module_Nmae_invoice" />
</resources>
</route>
di.xml
<preference for="Vendor\Module_Nmae\Api\InvoiceCustomInterface" type="Vendor\Module_Nmae\Model\Api\Invoice"/>
Model file
class Invoice implements InvoiceCustomInterface
{
protected $logger;
/**
* #var InvoiceRepositoryInterface
*/
private $invoiceRepository;
public function __construct(
LoggerInterface $logger,
InvoiceRepositoryInterface $invoiceRepository
)
{
$this->invoiceRepository = $invoiceRepository;
$this->logger = $logger;
}
/**
* #inheritdoc
* #param $entity
*/
public function save($entity)
{
try {
$invoiceRepo = $this->invoiceRepository->get($entity->getEntityId());
$invoiceRepo->setState($entity->getState());
$this->invoiceRepository->save($invoiceRepo);
} catch (\Exception $e) {
$this->logger->info($e->getMessage());
}
return $invoiceRepo;
}
}
So this solution will resolved the issue.

Related

Policies not hitting / working in Laravel 9 with Spatie Permission Package 5

Greetings. I'm having difficulties to use Policy Laravel 9. I have tried all day to figuring this out why POLICY class is not hitting by controller / route but could not find solution that. All possible solutions which I understand already tried.
Here is my code.
Policy class:
class PacketPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can create models.
*
* #param \App\Models\User $user
* #return \Illuminate\Auth\Access\Response|bool
*/
public function create(User $user)
{
dd('hit packet create');
}
}
And controller class
class PacketController extends Controller
{
/**
* Create the controller instance.
* Also check authorization on route level as per associated policies
*
* #return void
*/
public function __construct()
{
$this->authorizeResource(Packet::class, 'packet'); // Option-1 tried. Not working
}
public function create()
{
//request()->user()->can('create', Packet::class); // Option-2 tried. Not working
return inertia('Packet/Create', [
'item' => [],
]);
}
}
and even register policy classes in AuthServiceProvider class like so.
class AuthServiceProvider extends ServiceProvider
{
/**
* The model to policy mappings for the application.
*
* #var array<class-string, class-string>
*/
protected $policies = [
'App\Models\Packet' => 'App\Policies\PacketPolicy', // Not working
OutboundOrder::class => OutboundOrderPolicy::class, // Not working either
];
}
Route is
Route::resource('packet', PacketController::class);
Please help me out what i'm missing here. I don't understand why my Policy class not hitting when in visit domain/packet/create route

Laravel Components not getting my methods in shared host

I've just migrated a project that was working great on my localhost to a shared hosting and my components suddently are not getting the methods that i gave them and i'm getting errors in my views like so :
Undefined variable: CatPromo
this is my Component :
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use App\Categories;
class promo extends Component
{
/**
* Create a new component instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Get the view / contents that represent the component.
*
* #return \Illuminate\View\View|string
*/
public function render()
{
return view('components.promo');
}
public function CatPromo()
{
$Categories = Categories::all();
return $Categories;
}
}
Update : I removed the App\View\Components\promo.php to see if it can help me by throwing an error and it seems that he don't even detect the controller.
The documentation says: You should define the component's required data in its class constructor.
public function __construct($CatPromo)
{
// use as variable
$this->CatPromo = $CatPromo;
}
// use as method
public function CatPromo()
{
$Categories = Categories::all();
return $Categories;
}
And in blade template:
#foreach($CatPromo() as $key => $Categorie)

Laravel Nova - Change Tool to Resource Tool

I'm trying to convert a build Nova tool to a resource tool. I've tried to change my Tool to extend ResourceTool instead of Tool and add the resource tool to the Resource in the fields, but it's not showing up. I also changed the code of ToolServiceProvider to match that of a ResourceTool but it does not work unfortunately.
I've searched the internet but I seem to be the only one having this issue, anyone ever experienced this and know what to do? I'm not getting any error message, the resource tool just is not showing up.
Here is my code, I removed some of it for readability and confidentiality.
Product (Resource)
<?php
namespace App\Nova;
use confidentiality\SalesHistory\SalesHistory;
class Product extends Resource
{
/**
* Get the fields displayed by the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function fields(Request $request)
{
return [
ID::make()->sortable(),
SalesHistory::make(),
];
}
}
SalesHistory (Resource tool)
<?php
namespace confidentiality\SalesHistory;
use Laravel\Nova\ResourceTool;
class SalesHistory extends ResourceTool
{
/**
* Get the displayable name of the resource tool.
*
* #return string
*/
public function name()
{
return 'Sales History';
}
/**
* Get the component name for the resource tool.
*
* #return string
*/
public function component()
{
return 'sales-history';
}
}
ToolServiceProvider (Resource Tool)
<?php
namespace confidentiality\SalesHistory;
use Laravel\Nova\Nova;
use Laravel\Nova\Events\ServingNova;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
class ToolServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
$this->app->booted(function () {
$this->routes();
});
Nova::serving(function (ServingNova $event) {
Nova::script('sales-history', __DIR__.'/../dist/js/tool.js');
Nova::style('sales-history', __DIR__.'/../dist/css/tool.css');
});
}
/**
* Register the tool's routes.
*
* #return void
*/
protected function routes()
{
if ($this->app->routesAreCached()) {
return;
}
Route::middleware(['nova'])
->prefix('nova-vendor/sales-history')
->group(__DIR__.'/../routes/api.php');
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
I managed to fix it finally. I also had to change the tool.js file to match that of a Resource Tool.
Nova.booting((Vue, router, store) => {
Vue.component('sales-history', require('./components/Tool'))
})
Try this command from the root of project: composer update

What event should I use to detect an entity is being persisted again but is not being changed?

I want to trigger an event when updating an entity even if the data didn't change. So, what event should I use?
Remind: I want to access the entity data, so I can not use onFlush. Besides, since the entity data is not being changed, preUpate or postUpdate are not triggered.
Doctrine events docs
You can centralise the persist of the entity in service manager.
in this manager try to dispatch a general event
use Symfony\Component\EventDispatcher\GenericEvent;
...
...
public function persistAndFlush($entity)
{
$this->em->persist($entity);
$this->em->flush();
//dispatch an event
$event = new GenericEvent($entity, array('course_history' => 1));
$this->dispatcher->dispatch(YourClassEvents::UPDATE_ENTITY, $event);
}
Then you can create a listner to listen this generic event
Edit
More details
assume we have entity Product
namespace Domain\YourBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class Product
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $productName
*
* #ORM\Column(name="productName", type="string", length=255, nullable=false)
*/
private $productName;
...
...
...
//setters and getters
}
Create service manager
service.xml
<service id="yourdomain.product_manager" class="Domain\YourBundle\Service\ProductManager">
<argument type="service" id="doctrine.orm.entity_manager"/>
<argument type="service" id="event_dispatcher"/>
</service>
Create Our manager
ProductManager class
namespace Domain\YourBundle\Manager;
use Doctrine\ORM\EntityManager;
use Domain\YourBundle\Entity\Product;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
class ProductManager
{
/**
* #var EntityManager
*/
protected $em;
/**
* #var EventDispatcherInterface
*/
protected $dispatcher;
/**
* #param EntityManager $em
*/
public function __construct(EntityManager $em, EventDispatcherInterface $dispatcher)
{
$this->em = $em;
$this->dispatcher = $dispatcher;
}
public function persistAndFlush($product)
{
$this->em->persist($product);
$this->em->flush();
//The entity is updated then dispatch an event
$event = new GenericEvent($product, array('product_name' => 1));
$this->dispatcher->dispatch('product', $event);
}
}
Add definition of listener in service.xml
<service id="yourdomain.event.update_product" class="Domain\YourBundle\EventListener\UpdateProductListener">
<argument type="service" id="logger"/>
<tag name="kernel.event_listener" event="product" method="onUpdateProduct"/>
</service>
class UpdateProductListener
namespace Domain\YourBundle\EventListener;
use Symfony\Component\EventDispatcher\GenericEvent;
use Domain\YourBundle\Entity\Product;
use Psr\Log\LoggerInterface;
class UpdateProductListener
{
private $logger;
/**
* #param LoggerInterface $logger
*/
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
/**
* Listen the generic event dispatched when the product name is updated
*
* #param GenericEvent $event
*
* #throws RuntimeException
* #throws \ErrorException
*/
public function onUpdateProduct(GenericEvent $event)
{
if ($event->getSubject() instanceof Product) {
$product = $event->getSubject();
if ($event->hasArgument('product_name') && $event->getArgument('product_name') == 1) {
/*
Make your logic (send mail, add log, notification....)
*/
$this->logger->notice('UpdateProductListener : event update product is occured');
} else {
$this->logger->notice('UpdateProductListener : event other than update product is occured.');
}
}
}
}
I see the problem here. You want to access entity after update, but the entity is not really updates. So you obviously use DQL query here, and this disallows you to access pre\postUpdate as you correctly mentioned. The problem is that doctrine does not really knows that the object was changed - the query could be a bit complicated so the object identifier could be hidden to it. Or even not known (in case of MySQL)
Some RDBMs have the RETURNING clause, allowing you to see the results of update, but it's not DB abstract, thus could not be accessed from the DQL out of the box.
You can use the native SQL query or do other tricks to fetch updated rows.
If the query is not performance critical I'd rather suggest it to some software (php-side) processing with simple doctrine flush.
If it is - I think it is not possible. If you privde more detail on the use case, the workaround could be discussed.
I.e. you can use versioning iterate all the entities cached at the EM and check the vesrion is changed.

Laravel 4 - Extend Pagination Class

Is there a way to extend the Pagination Class of Laravel 4 ?
I tried some things but nothing good...
I'm here :
PaginationServiceProvider.php
class PaginationServiceProvider extends \Illuminate\Pagination\PaginationServiceProvider {
/**
* Indicates if loading of the provider is deferred.
* #var bool
*/
protected $defer = false;
/**
* Bootstrap the application events.
* #return void
*/
public function boot(){
$this->package('thujohn/pagination');
}
/**
* Register the service provider.
* #return void
*/
public function register(){
$this->app['paginator'] = $this->app->share(function($app){
$paginator = new Environment($app['request'], $app['view'], $app['translator']);
$paginator->setViewName($app['config']['view.pagination']);
return $paginator;
});
}
/**
* Get the services provided by the provider.
* #return array
*/
public function provides(){
return array();
}
}
Environment.php
class Environment extends \Illuminate\Pagination\Environment {
public function hello(){
return 'hello';
}
}
I replaced 'Illuminate\Pagination\PaginationServiceProvider', by 'Thujohn\Pagination\PaginationServiceProvider',
When I call $test->links() it's ok
When I call $test->hello() it fails
When I call Paginator::hello() it's ok
Any idea ?
Everyting is fine except that Paginator::make() returns Paginator instance, not Environment.
You should move Your method to Paginator class.
Today I've posted on GH my extension for Paginator. You can check it as a reference desmart/pagination

Resources