New-Thanasoft/thanasoft-back/app/Http/Controllers/Api/PurchaseOrderController.php
nyavokevin 9cbc1bcbdb feat(ui): add price lists and group-based quote flows
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.
2026-04-02 12:07:11 +03:00

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);
}
}
}