diff --git a/thanasoft-back/app/Http/Controllers/Api/GoodsReceiptController.php b/thanasoft-back/app/Http/Controllers/Api/GoodsReceiptController.php index 419af92..db6ffa2 100644 --- a/thanasoft-back/app/Http/Controllers/Api/GoodsReceiptController.php +++ b/thanasoft-back/app/Http/Controllers/Api/GoodsReceiptController.php @@ -8,6 +8,7 @@ use App\Http\Controllers\Controller; use App\Http\Requests\StoreGoodsReceiptRequest; use App\Http\Requests\UpdateGoodsReceiptRequest; use App\Http\Resources\GoodsReceiptResource; +use App\Models\PurchaseOrder; use App\Repositories\GoodsReceiptRepositoryInterface; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; @@ -45,7 +46,32 @@ class GoodsReceiptController extends Controller public function store(StoreGoodsReceiptRequest $request): JsonResponse { try { - $goodsReceipt = $this->goodsReceiptRepository->create($request->validated()); + $payload = $request->validated(); + + if (empty($payload['lines']) && !empty($payload['purchase_order_id'])) { + $purchaseOrder = PurchaseOrder::query() + ->with('lines') + ->find($payload['purchase_order_id']); + + if ($purchaseOrder) { + $payload['lines'] = $purchaseOrder->lines + ->filter(fn($line) => !empty($line->product_id)) + ->map(fn($line) => [ + '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(); + } + } + + $goodsReceipt = $this->goodsReceiptRepository->create($payload); return response()->json([ 'data' => new GoodsReceiptResource($goodsReceipt), 'message' => 'Réception de marchandise créée avec succès.', diff --git a/thanasoft-back/app/Http/Controllers/Api/PurchaseOrderController.php b/thanasoft-back/app/Http/Controllers/Api/PurchaseOrderController.php index 0d39d71..dd7eb69 100644 --- a/thanasoft-back/app/Http/Controllers/Api/PurchaseOrderController.php +++ b/thanasoft-back/app/Http/Controllers/Api/PurchaseOrderController.php @@ -8,6 +8,9 @@ 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; @@ -16,7 +19,8 @@ use Illuminate\Support\Facades\Log; class PurchaseOrderController extends Controller { public function __construct( - protected PurchaseOrderRepositoryInterface $purchaseOrderRepository + protected PurchaseOrderRepositoryInterface $purchaseOrderRepository, + protected GoodsReceiptRepositoryInterface $goodsReceiptRepository ) { } @@ -98,6 +102,9 @@ class PurchaseOrderController extends Controller public function update(UpdatePurchaseOrderRequest $request, string $id): PurchaseOrderResource|JsonResponse { try { + $existingPurchaseOrder = $this->purchaseOrderRepository->find($id); + $previousStatus = $existingPurchaseOrder?->status; + $updated = $this->purchaseOrderRepository->update($id, $request->validated()); if (!$updated) { @@ -107,6 +114,16 @@ class PurchaseOrderController extends Controller } $purchaseOrder = $this->purchaseOrderRepository->find($id); + + // On validation/delivery (status => confirmee|livree), create a draft goods receipt automatically. + if ( + $purchaseOrder + && in_array($purchaseOrder->status, ['confirmee', 'livree'], true) + && !in_array($previousStatus, ['confirmee', 'livree'], true) + ) { + $this->createGoodsReceiptFromValidatedPurchaseOrder($purchaseOrder); + } + return new PurchaseOrderResource($purchaseOrder); } catch (\Exception $e) { Log::error('Error updating purchase order: ' . $e->getMessage(), [ @@ -123,6 +140,53 @@ class PurchaseOrderController extends Controller } } + /** + * 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. */ diff --git a/thanasoft-back/app/Http/Resources/GoodsReceiptResource.php b/thanasoft-back/app/Http/Resources/GoodsReceiptResource.php index 0a01d15..8e775bf 100644 --- a/thanasoft-back/app/Http/Resources/GoodsReceiptResource.php +++ b/thanasoft-back/app/Http/Resources/GoodsReceiptResource.php @@ -2,6 +2,7 @@ namespace App\Http\Resources; +use App\Http\Resources\Fournisseur\PurchaseOrderResource; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; diff --git a/thanasoft-front/src/components/Organism/Commande/CommandeDetailPresentation.vue b/thanasoft-front/src/components/Organism/Commande/CommandeDetailPresentation.vue index 5efd717..ef305d7 100644 --- a/thanasoft-front/src/components/Organism/Commande/CommandeDetailPresentation.vue +++ b/thanasoft-front/src/components/Organism/Commande/CommandeDetailPresentation.vue @@ -7,178 +7,208 @@
- Créée le {{ formatDate(goodsReceipt.created_at) }} -
-
{{ goodsReceipt.receipt_number }}
-{{ formatDate(goodsReceipt.receipt_date) }}
-- {{ goodsReceipt.purchase_order?.po_number || goodsReceipt.purchase_order_id }} -
-{{ goodsReceipt.warehouse?.name || '-' }}
-{{ goodsReceipt.notes || 'Aucune note' }}
-| Produit | -Conditionnement | -Colis | -Unités | -Prix Unitaire | -TVA | -
|---|---|---|---|---|---|
|
-
-
-
-
- - {{ line.product?.nom || 'Produit ' + line.product_id }} - -- {{ line.product?.reference || '' }} - - |
- - {{ line.packaging?.name || 'Unité' }} - | -- {{ line.packages_qty_received || '-' }} - | -- {{ line.units_qty_received || '-' }} - | -- {{ line.unit_price ? formatCurrency(line.unit_price) : '-' }} - | -- {{ line.tva_rate ? line.tva_rate.name + ' (' + line.tva_rate.rate + '%)' : '-' }} - | -
| - Aucune ligne dans cette réception. - | -|||||
| Produit | +Conditionnement | +Colis | +Unités | +Prix Unitaire | +TVA | +
|---|---|---|---|---|---|
|
+
+ {{ line.product?.nom || `Produit #${line.product_id}` }}
+ {{ line.product?.reference || "-" }}
+
+ |
+ {{ line.packaging?.name || "Unité" }} | +{{ line.packages_qty_received || "-" }} | +{{ line.units_qty_received || "-" }} | +{{ line.unit_price ? formatCurrency(line.unit_price) : "-" }} | ++ {{ line.tva_rate ? `${line.tva_rate.name} (${line.tva_rate.rate}%)` : "-" }} + | +
| + Aucune ligne dans cette réception. + | +|||||
+ {{ + isEditMode + ? "Modifiez les informations de l'entrepôt ci-dessous." + : "Informations détaillées, produits stockés et mouvements." + }} +
+Chargement des informations de l'entrepôt...
++ {{ warehouse.name }} +
++ {{ warehouse.country_code || "-" }} +
++ {{ warehouse.address_line1 || "-" }} +
++ {{ warehouse.address_line2 || "-" }} +
++ {{ warehouse.postal_code || "-" }} +
++ {{ warehouse.city || "-" }} +
+| + Produit + | ++ Quantité + | ++ Stock de sécurité + | ++ Actions + | +
|---|---|---|---|
|
+
+ {{
+ item.product?.nom || `Produit #${item.product_id}`
+ }}
+ Réf: {{ item.product?.reference || "-" }}
+
+ |
+ {{ item.qty_on_hand_base }} | +{{ item.safety_stock_base }} | ++ + | +
| + Date + | ++ Type + | ++ Produit + | ++ Entrée / Sortie + | ++ Quantité + | +
|---|---|---|---|---|
| + {{ + formatDate(movement.moved_at || movement.created_at) + }} + | +{{ movement.move_type || "-" }} | ++ {{ + movement.product?.nom || + `Produit #${movement.product_id}` + }} + | ++ + {{ isIncoming(movement) ? "Entrée" : "Sortie" }} + + | +{{ movement.qty_base }} | +