Add price list management across the API, store, services, routes, navigation, and sales views. Support quotes for either a client or a client group, including PDF download and nullable client validation for group-based recipients. Extend client groups to manage assigned clients directly from the form and detail views, and refresh supplier, intervention, stock, and order screens with updated interactions and layouts.
226 lines
7.9 KiB
PHP
226 lines
7.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\StorePurchaseOrderRequest;
|
|
use App\Http\Requests\UpdatePurchaseOrderRequest;
|
|
use App\Http\Resources\Fournisseur\PurchaseOrderResource;
|
|
use App\Models\GoodsReceipt;
|
|
use App\Models\Warehouse;
|
|
use App\Repositories\GoodsReceiptRepositoryInterface;
|
|
use App\Repositories\PurchaseOrderRepositoryInterface;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class PurchaseOrderController extends Controller
|
|
{
|
|
public function __construct(
|
|
protected PurchaseOrderRepositoryInterface $purchaseOrderRepository,
|
|
protected GoodsReceiptRepositoryInterface $goodsReceiptRepository
|
|
)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Display a listing of purchase orders.
|
|
*/
|
|
public function index(): AnonymousResourceCollection|JsonResponse
|
|
{
|
|
try {
|
|
$purchaseOrders = $this->purchaseOrderRepository->all();
|
|
return PurchaseOrderResource::collection($purchaseOrders);
|
|
}
|
|
catch (\Exception $e) {
|
|
Log::error('Error fetching purchase orders: ' . $e->getMessage(), [
|
|
'exception' => $e,
|
|
'trace' => $e->getTraceAsString(),
|
|
]);
|
|
|
|
return response()->json([
|
|
'message' => 'Une erreur est survenue lors de la récupération des commandes fournisseurs.',
|
|
'error' => config('app.debug') ? $e->getMessage() : null,
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Store a newly created purchase order.
|
|
*/
|
|
public function store(StorePurchaseOrderRequest $request): PurchaseOrderResource|JsonResponse
|
|
{
|
|
try {
|
|
$purchaseOrder = $this->purchaseOrderRepository->create($request->validated());
|
|
|
|
// If PO is created directly as validated/delivered, ensure a draft goods receipt exists.
|
|
if ($purchaseOrder && in_array($purchaseOrder->status, ['confirmee', 'livree'], true)) {
|
|
$this->createGoodsReceiptFromValidatedPurchaseOrder($purchaseOrder);
|
|
}
|
|
|
|
return new PurchaseOrderResource($purchaseOrder);
|
|
}
|
|
catch (\Exception $e) {
|
|
Log::error('Error creating purchase order: ' . $e->getMessage(), [
|
|
'exception' => $e,
|
|
'trace' => $e->getTraceAsString(),
|
|
'data' => $request->validated(),
|
|
]);
|
|
|
|
return response()->json([
|
|
'message' => 'Une erreur est survenue lors de la création de la commande fournisseur.',
|
|
'error' => config('app.debug') ? $e->getMessage() : null,
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display the specified purchase order.
|
|
*/
|
|
public function show(string $id): PurchaseOrderResource|JsonResponse
|
|
{
|
|
try {
|
|
$purchaseOrder = $this->purchaseOrderRepository->find($id);
|
|
|
|
if (!$purchaseOrder) {
|
|
return response()->json([
|
|
'message' => 'Commande fournisseur non trouvée.',
|
|
], 404);
|
|
}
|
|
|
|
return new PurchaseOrderResource($purchaseOrder);
|
|
}
|
|
catch (\Exception $e) {
|
|
Log::error('Error fetching purchase order: ' . $e->getMessage(), [
|
|
'exception' => $e,
|
|
'trace' => $e->getTraceAsString(),
|
|
'purchase_order_id' => $id,
|
|
]);
|
|
|
|
return response()->json([
|
|
'message' => 'Une erreur est survenue lors de la récupération de la commande fournisseur.',
|
|
'error' => config('app.debug') ? $e->getMessage() : null,
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the specified purchase order.
|
|
*/
|
|
public function update(UpdatePurchaseOrderRequest $request, string $id): PurchaseOrderResource|JsonResponse
|
|
{
|
|
try {
|
|
$updated = $this->purchaseOrderRepository->update($id, $request->validated());
|
|
|
|
if (!$updated) {
|
|
return response()->json([
|
|
'message' => 'Commande fournisseur non trouvée ou échec de la mise à jour.',
|
|
], 404);
|
|
}
|
|
|
|
$purchaseOrder = $this->purchaseOrderRepository->find($id);
|
|
|
|
// Ensure draft goods receipt exists when PO is validated/delivered.
|
|
// Idempotent: guarded by purchase_order_id existence check in helper.
|
|
if ($purchaseOrder && in_array($purchaseOrder->status, ['confirmee', 'livree'], true)) {
|
|
$this->createGoodsReceiptFromValidatedPurchaseOrder($purchaseOrder);
|
|
}
|
|
|
|
return new PurchaseOrderResource($purchaseOrder);
|
|
}
|
|
catch (\Exception $e) {
|
|
Log::error('Error updating purchase order: ' . $e->getMessage(), [
|
|
'exception' => $e,
|
|
'trace' => $e->getTraceAsString(),
|
|
'purchase_order_id' => $id,
|
|
'data' => $request->validated(),
|
|
]);
|
|
|
|
return response()->json([
|
|
'message' => 'Une erreur est survenue lors de la mise à jour de la commande fournisseur.',
|
|
'error' => config('app.debug') ? $e->getMessage() : null,
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a draft goods receipt when a purchase order is validated.
|
|
*/
|
|
protected function createGoodsReceiptFromValidatedPurchaseOrder($purchaseOrder): void
|
|
{
|
|
$alreadyExists = GoodsReceipt::query()
|
|
->where('purchase_order_id', $purchaseOrder->id)
|
|
->exists();
|
|
|
|
if ($alreadyExists) {
|
|
return;
|
|
}
|
|
|
|
$warehouseId = Warehouse::query()->value('id');
|
|
if (!$warehouseId) {
|
|
throw new \RuntimeException('Aucun entrepôt disponible pour créer la réception de marchandise.');
|
|
}
|
|
|
|
$receiptNumber = 'GR-' . now()->format('Ym') . '-' . str_pad((string)$purchaseOrder->id, 4, '0', STR_PAD_LEFT);
|
|
|
|
$lines = collect($purchaseOrder->lines ?? [])
|
|
->filter(fn($line) => !empty($line->product_id))
|
|
->map(function ($line) {
|
|
return [
|
|
'product_id' => (int)$line->product_id,
|
|
'packaging_id' => null,
|
|
'packages_qty_received' => null,
|
|
'units_qty_received' => (float)$line->quantity,
|
|
'qty_received_base' => (float)$line->quantity,
|
|
'unit_price' => (float)$line->unit_price,
|
|
'unit_price_per_package' => null,
|
|
'tva_rate_id' => null,
|
|
];
|
|
})
|
|
->values()
|
|
->all();
|
|
|
|
$this->goodsReceiptRepository->create([
|
|
'purchase_order_id' => $purchaseOrder->id,
|
|
'warehouse_id' => (int)$warehouseId,
|
|
'receipt_number' => $receiptNumber,
|
|
'receipt_date' => now()->toDateString(),
|
|
'status' => 'draft',
|
|
'lines' => $lines,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Remove the specified purchase order.
|
|
*/
|
|
public function destroy(string $id): JsonResponse
|
|
{
|
|
try {
|
|
$deleted = $this->purchaseOrderRepository->delete($id);
|
|
|
|
if (!$deleted) {
|
|
return response()->json([
|
|
'message' => 'Commande fournisseur non trouvée ou échec de la suppression.',
|
|
], 404);
|
|
}
|
|
|
|
return response()->json([
|
|
'message' => 'Commande fournisseur supprimée avec succès.',
|
|
], 200);
|
|
}
|
|
catch (\Exception $e) {
|
|
Log::error('Error deleting purchase order: ' . $e->getMessage(), [
|
|
'exception' => $e,
|
|
'trace' => $e->getTraceAsString(),
|
|
'purchase_order_id' => $id,
|
|
]);
|
|
|
|
return response()->json([
|
|
'message' => 'Une erreur est survenue lors de la suppression de la commande fournisseur.',
|
|
'error' => config('app.debug') ? $e->getMessage() : null,
|
|
], 500);
|
|
}
|
|
}
|
|
} |