diff --git a/thanasoft-back/app/Http/Controllers/Api/GoodsReceiptController.php b/thanasoft-back/app/Http/Controllers/Api/GoodsReceiptController.php new file mode 100644 index 0000000..419af92 --- /dev/null +++ b/thanasoft-back/app/Http/Controllers/Api/GoodsReceiptController.php @@ -0,0 +1,133 @@ +goodsReceiptRepository->all(); + return response()->json([ + 'data' => GoodsReceiptResource::collection($goodsReceipts), + 'status' => 'success' + ]); + } catch (\Exception $e) { + Log::error('Error fetching goods receipts: ' . $e->getMessage()); + return response()->json([ + 'message' => 'Une erreur est survenue lors de la récupération des réceptions de marchandises.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } + + /** + * Store a newly created goods receipt. + */ + public function store(StoreGoodsReceiptRequest $request): JsonResponse + { + try { + $goodsReceipt = $this->goodsReceiptRepository->create($request->validated()); + return response()->json([ + 'data' => new GoodsReceiptResource($goodsReceipt), + 'message' => 'Réception de marchandise créée avec succès.', + 'status' => 'success' + ], 201); + } catch (\Exception $e) { + Log::error('Error creating goods receipt: ' . $e->getMessage()); + return response()->json([ + 'message' => 'Une erreur est survenue lors de la création de la réception de marchandise.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } + + /** + * Display the specified goods receipt. + */ + public function show(string $id): JsonResponse + { + try { + $goodsReceipt = $this->goodsReceiptRepository->find((int) $id); + if (!$goodsReceipt) { + return response()->json(['message' => 'Réception de marchandise non trouvée.'], 404); + } + return response()->json([ + 'data' => new GoodsReceiptResource($goodsReceipt), + 'status' => 'success' + ]); + } catch (\Exception $e) { + Log::error('Error fetching goods receipt: ' . $e->getMessage()); + return response()->json([ + 'message' => 'Une erreur est survenue lors de la récupération de la réception de marchandise.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } + + /** + * Update the specified goods receipt. + */ + public function update(UpdateGoodsReceiptRequest $request, string $id): JsonResponse + { + try { + $updated = $this->goodsReceiptRepository->update((int) $id, $request->validated()); + if (!$updated) { + return response()->json(['message' => 'Réception de marchandise non trouvée ou échec de la mise à jour.'], 404); + } + $goodsReceipt = $this->goodsReceiptRepository->find((int) $id); + return response()->json([ + 'data' => new GoodsReceiptResource($goodsReceipt), + 'message' => 'Réception de marchandise mise à jour avec succès.', + 'status' => 'success' + ]); + } catch (\Exception $e) { + Log::error('Error updating goods receipt: ' . $e->getMessage()); + return response()->json([ + 'message' => 'Une erreur est survenue lors de la mise à jour de la réception de marchandise.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } + + /** + * Remove the specified goods receipt. + */ + public function destroy(string $id): JsonResponse + { + try { + $deleted = $this->goodsReceiptRepository->delete((int) $id); + if (!$deleted) { + return response()->json(['message' => 'Réception de marchandise non trouvée ou échec de la suppression.'], 404); + } + return response()->json([ + 'message' => 'Réception de marchandise supprimée avec succès.', + 'status' => 'success' + ]); + } catch (\Exception $e) { + Log::error('Error deleting goods receipt: ' . $e->getMessage()); + return response()->json([ + 'message' => 'Une erreur est survenue lors de la suppression de la réception de marchandise.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } +} diff --git a/thanasoft-back/app/Http/Controllers/Api/TvaRateController.php b/thanasoft-back/app/Http/Controllers/Api/TvaRateController.php new file mode 100644 index 0000000..b4c4d36 --- /dev/null +++ b/thanasoft-back/app/Http/Controllers/Api/TvaRateController.php @@ -0,0 +1,133 @@ +tvaRateRepository->all(); + return response()->json([ + 'data' => TvaRateResource::collection($tvaRates), + 'status' => 'success' + ]); + } catch (\Exception $e) { + Log::error('Error fetching TVA rates: ' . $e->getMessage()); + return response()->json([ + 'message' => 'Une erreur est survenue lors de la récupération des taux de TVA.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } + + /** + * Store a newly created TVA rate. + */ + public function store(StoreTvaRateRequest $request): JsonResponse + { + try { + $tvaRate = $this->tvaRateRepository->create($request->validated()); + return response()->json([ + 'data' => new TvaRateResource($tvaRate), + 'message' => 'Taux de TVA créé avec succès.', + 'status' => 'success' + ], 201); + } catch (\Exception $e) { + Log::error('Error creating TVA rate: ' . $e->getMessage()); + return response()->json([ + 'message' => 'Une erreur est survenue lors de la création du taux de TVA.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } + + /** + * Display the specified TVA rate. + */ + public function show(string $id): JsonResponse + { + try { + $tvaRate = $this->tvaRateRepository->find((int) $id); + if (!$tvaRate) { + return response()->json(['message' => 'Taux de TVA non trouvé.'], 404); + } + return response()->json([ + 'data' => new TvaRateResource($tvaRate), + 'status' => 'success' + ]); + } catch (\Exception $e) { + Log::error('Error fetching TVA rate: ' . $e->getMessage()); + return response()->json([ + 'message' => 'Une erreur est survenue lors de la récupération du taux de TVA.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } + + /** + * Update the specified TVA rate. + */ + public function update(UpdateTvaRateRequest $request, string $id): JsonResponse + { + try { + $updated = $this->tvaRateRepository->update((int) $id, $request->validated()); + if (!$updated) { + return response()->json(['message' => 'Taux de TVA non trouvé ou échec de la mise à jour.'], 404); + } + $tvaRate = $this->tvaRateRepository->find((int) $id); + return response()->json([ + 'data' => new TvaRateResource($tvaRate), + 'message' => 'Taux de TVA mis à jour avec succès.', + 'status' => 'success' + ]); + } catch (\Exception $e) { + Log::error('Error updating TVA rate: ' . $e->getMessage()); + return response()->json([ + 'message' => 'Une erreur est survenue lors de la mise à jour du taux de TVA.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } + + /** + * Remove the specified TVA rate. + */ + public function destroy(string $id): JsonResponse + { + try { + $deleted = $this->tvaRateRepository->delete((int) $id); + if (!$deleted) { + return response()->json(['message' => 'Taux de TVA non trouvé ou échec de la suppression.'], 404); + } + return response()->json([ + 'message' => 'Taux de TVA supprimé avec succès.', + 'status' => 'success' + ]); + } catch (\Exception $e) { + Log::error('Error deleting TVA rate: ' . $e->getMessage()); + return response()->json([ + 'message' => 'Une erreur est survenue lors de la suppression du taux de TVA.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } +} diff --git a/thanasoft-back/app/Http/Controllers/Api/WarehouseController.php b/thanasoft-back/app/Http/Controllers/Api/WarehouseController.php index 56abd83..e57f37b 100644 --- a/thanasoft-back/app/Http/Controllers/Api/WarehouseController.php +++ b/thanasoft-back/app/Http/Controllers/Api/WarehouseController.php @@ -62,7 +62,7 @@ class WarehouseController extends Controller } /** - * Display the specified warehouse. + * Display specified warehouse. */ public function show(string $id): JsonResponse { @@ -85,7 +85,7 @@ class WarehouseController extends Controller } /** - * Update the specified warehouse. + * Update specified warehouse. */ public function update(UpdateWarehouseRequest $request, string $id): JsonResponse { @@ -110,7 +110,7 @@ class WarehouseController extends Controller } /** - * Remove the specified warehouse. + * Remove specified warehouse. */ public function destroy(string $id): JsonResponse { @@ -131,4 +131,44 @@ class WarehouseController extends Controller ], 500); } } + + /** + * Search warehouses by name. + */ + public function searchBy(Request $request): JsonResponse + { + try { + $name = $request->query('name'); + $exactMatch = $request->query('exact_match', false); + + if (empty($name)) { + return response()->json([ + 'data' => [], + 'count' => 0, + 'message' => 'Le paramètre de recherche est requis.' + ], 400); + } + + $warehouses = $this->warehouseRepository->all(); + + $filtered = $warehouses->filter(function ($warehouse) use ($name, $exactMatch) { + if ($exactMatch) { + return strtolower($warehouse->name) === strtolower($name); + } + return stripos($warehouse->name, $name) !== false; + }); + + return response()->json([ + 'data' => WarehouseResource::collection($filtered), + 'count' => $filtered->count(), + 'message' => 'Recherche effectuée avec succès.' + ]); + } catch (\Exception $e) { + Log::error('Error searching warehouses: ' . $e->getMessage()); + return response()->json([ + 'message' => 'Une erreur est survenue lors de la recherche des entrepôts.', + 'error' => config('app.debug') ? $e->getMessage() : null, + ], 500); + } + } } diff --git a/thanasoft-back/app/Http/Requests/StoreGoodsReceiptRequest.php b/thanasoft-back/app/Http/Requests/StoreGoodsReceiptRequest.php new file mode 100644 index 0000000..2973073 --- /dev/null +++ b/thanasoft-back/app/Http/Requests/StoreGoodsReceiptRequest.php @@ -0,0 +1,79 @@ +|string> + */ + public function rules(): array + { + return [ + 'purchase_order_id' => 'required|exists:purchase_orders,id', + 'warehouse_id' => 'required|exists:warehouses,id', + 'receipt_number' => 'required|string|max:191', + 'receipt_date' => 'required|date', + 'status' => 'nullable|in:draft,posted', + 'notes' => 'nullable|string', + 'lines' => 'nullable|array', + 'lines.*.product_id' => 'required_with:lines|exists:products,id', + 'lines.*.packaging_id' => 'nullable|exists:product_packagings,id', + 'lines.*.packages_qty_received' => 'nullable|numeric|min:0', + 'lines.*.units_qty_received' => 'nullable|numeric|min:0', + 'lines.*.qty_received_base' => 'nullable|numeric|min:0', + 'lines.*.unit_price' => 'nullable|numeric|min:0', + 'lines.*.unit_price_per_package' => 'nullable|numeric|min:0', + 'lines.*.tva_rate_id' => 'nullable|exists:tva_rates,id', + ]; + } + + /** + * Get the error messages for the defined validation rules. + * + * @return array + */ + public function messages(): array + { + return [ + 'purchase_order_id.required' => 'La commande fournisseur est requise.', + 'purchase_order_id.exists' => 'La commande fournisseur spécifiée n\'existe pas.', + 'warehouse_id.required' => 'L\'entrepôt est requis.', + 'warehouse_id.exists' => 'L\'entrepôt spécifié n\'existe pas.', + 'receipt_number.required' => 'Le numéro de réception est requis.', + 'receipt_number.string' => 'Le numéro de réception doit être une chaîne de caractères.', + 'receipt_number.max' => 'Le numéro de réception ne peut pas dépasser 191 caractères.', + 'receipt_date.required' => 'La date de réception est requise.', + 'receipt_date.date' => 'La date de réception doit être une date valide.', + 'status.in' => 'Le statut doit être "draft" ou "posted".', + 'notes.string' => 'Les notes doivent être une chaîne de caractères.', + 'lines.array' => 'Les lignes doivent être un tableau.', + 'lines.*.product_id.required_with' => 'Le produit est requis pour chaque ligne.', + 'lines.*.product_id.exists' => 'Le produit spécifié dans une ligne n\'existe pas.', + 'lines.*.packaging_id.exists' => 'Le conditionnement spécifié dans une ligne n\'existe pas.', + 'lines.*.packages_qty_received.numeric' => 'La quantité de colis doit être un nombre.', + 'lines.*.packages_qty_received.min' => 'La quantité de colis ne peut pas être négative.', + 'lines.*.units_qty_received.numeric' => 'La quantité d\'unités doit être un nombre.', + 'lines.*.units_qty_received.min' => 'La quantité d\'unités ne peut pas être négative.', + 'lines.*.qty_received_base.numeric' => 'La quantité de base doit être un nombre.', + 'lines.*.qty_received_base.min' => 'La quantité de base ne peut pas être négative.', + 'lines.*.unit_price.numeric' => 'Le prix unitaire doit être un nombre.', + 'lines.*.unit_price.min' => 'Le prix unitaire ne peut pas être négatif.', + 'lines.*.unit_price_per_package.numeric' => 'Le prix par colis doit être un nombre.', + 'lines.*.unit_price_per_package.min' => 'Le prix par colis ne peut pas être négatif.', + 'lines.*.tva_rate_id.exists' => 'Le taux de TVA spécifié dans une ligne n\'existe pas.', + ]; + } +} diff --git a/thanasoft-back/app/Http/Requests/StoreTvaRateRequest.php b/thanasoft-back/app/Http/Requests/StoreTvaRateRequest.php new file mode 100644 index 0000000..d9acb93 --- /dev/null +++ b/thanasoft-back/app/Http/Requests/StoreTvaRateRequest.php @@ -0,0 +1,47 @@ +|string> + */ + public function rules(): array + { + return [ + 'name' => 'required|string|max:50', + 'rate' => 'required|numeric|min:0|max:999.99', + ]; + } + + /** + * Get the error messages for the defined validation rules. + * + * @return array + */ + public function messages(): array + { + return [ + 'name.required' => 'Le nom du taux de TVA est requis.', + 'name.string' => 'Le nom doit être une chaîne de caractères.', + 'name.max' => 'Le nom ne peut pas dépasser 50 caractères.', + 'rate.required' => 'Le taux de TVA est requis.', + 'rate.numeric' => 'Le taux doit être un nombre.', + 'rate.min' => 'Le taux ne peut pas être négatif.', + 'rate.max' => 'Le taux ne peut pas dépasser 999.99.', + ]; + } +} diff --git a/thanasoft-back/app/Http/Requests/UpdateGoodsReceiptRequest.php b/thanasoft-back/app/Http/Requests/UpdateGoodsReceiptRequest.php new file mode 100644 index 0000000..a7566c2 --- /dev/null +++ b/thanasoft-back/app/Http/Requests/UpdateGoodsReceiptRequest.php @@ -0,0 +1,75 @@ +|string> + */ + public function rules(): array + { + return [ + 'purchase_order_id' => 'sometimes|exists:purchase_orders,id', + 'warehouse_id' => 'sometimes|exists:warehouses,id', + 'receipt_number' => 'sometimes|string|max:191', + 'receipt_date' => 'sometimes|date', + 'status' => 'nullable|in:draft,posted', + 'notes' => 'nullable|string', + 'lines' => 'nullable|array', + 'lines.*.product_id' => 'required_with:lines|exists:products,id', + 'lines.*.packaging_id' => 'nullable|exists:product_packagings,id', + 'lines.*.packages_qty_received' => 'nullable|numeric|min:0', + 'lines.*.units_qty_received' => 'nullable|numeric|min:0', + 'lines.*.qty_received_base' => 'nullable|numeric|min:0', + 'lines.*.unit_price' => 'nullable|numeric|min:0', + 'lines.*.unit_price_per_package' => 'nullable|numeric|min:0', + 'lines.*.tva_rate_id' => 'nullable|exists:tva_rates,id', + ]; + } + + /** + * Get the error messages for the defined validation rules. + * + * @return array + */ + public function messages(): array + { + return [ + 'purchase_order_id.exists' => 'La commande fournisseur spécifiée n\'existe pas.', + 'warehouse_id.exists' => 'L\'entrepôt spécifié n\'existe pas.', + 'receipt_number.string' => 'Le numéro de réception doit être une chaîne de caractères.', + 'receipt_number.max' => 'Le numéro de réception ne peut pas dépasser 191 caractères.', + 'receipt_date.date' => 'La date de réception doit être une date valide.', + 'status.in' => 'Le statut doit être "draft" ou "posted".', + 'notes.string' => 'Les notes doivent être une chaîne de caractères.', + 'lines.array' => 'Les lignes doivent être un tableau.', + 'lines.*.product_id.required_with' => 'Le produit est requis pour chaque ligne.', + 'lines.*.product_id.exists' => 'Le produit spécifié dans une ligne n\'existe pas.', + 'lines.*.packaging_id.exists' => 'Le conditionnement spécifié dans une ligne n\'existe pas.', + 'lines.*.packages_qty_received.numeric' => 'La quantité de colis doit être un nombre.', + 'lines.*.packages_qty_received.min' => 'La quantité de colis ne peut pas être négative.', + 'lines.*.units_qty_received.numeric' => 'La quantité d\'unités doit être un nombre.', + 'lines.*.units_qty_received.min' => 'La quantité d\'unités ne peut pas être négative.', + 'lines.*.qty_received_base.numeric' => 'La quantité de base doit être un nombre.', + 'lines.*.qty_received_base.min' => 'La quantité de base ne peut pas être négative.', + 'lines.*.unit_price.numeric' => 'Le prix unitaire doit être un nombre.', + 'lines.*.unit_price.min' => 'Le prix unitaire ne peut pas être négatif.', + 'lines.*.unit_price_per_package.numeric' => 'Le prix par colis doit être un nombre.', + 'lines.*.unit_price_per_package.min' => 'Le prix par colis ne peut pas être négatif.', + 'lines.*.tva_rate_id.exists' => 'Le taux de TVA spécifié dans une ligne n\'existe pas.', + ]; + } +} diff --git a/thanasoft-back/app/Http/Requests/UpdateTvaRateRequest.php b/thanasoft-back/app/Http/Requests/UpdateTvaRateRequest.php new file mode 100644 index 0000000..7403c57 --- /dev/null +++ b/thanasoft-back/app/Http/Requests/UpdateTvaRateRequest.php @@ -0,0 +1,45 @@ +|string> + */ + public function rules(): array + { + return [ + 'name' => 'sometimes|string|max:50', + 'rate' => 'sometimes|numeric|min:0|max:999.99', + ]; + } + + /** + * Get the error messages for the defined validation rules. + * + * @return array + */ + public function messages(): array + { + return [ + 'name.string' => 'Le nom doit être une chaîne de caractères.', + 'name.max' => 'Le nom ne peut pas dépasser 50 caractères.', + 'rate.numeric' => 'Le taux doit être un nombre.', + 'rate.min' => 'Le taux ne peut pas être négatif.', + 'rate.max' => 'Le taux ne peut pas dépasser 999.99.', + ]; + } +} diff --git a/thanasoft-back/app/Http/Resources/GoodsReceiptLineResource.php b/thanasoft-back/app/Http/Resources/GoodsReceiptLineResource.php new file mode 100644 index 0000000..4df6241 --- /dev/null +++ b/thanasoft-back/app/Http/Resources/GoodsReceiptLineResource.php @@ -0,0 +1,35 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'goods_receipt_id' => $this->goods_receipt_id, + 'product_id' => $this->product_id, + 'product' => new ProductResource($this->whenLoaded('product')), + 'packaging_id' => $this->packaging_id, + 'packaging' => new ProductPackagingResource($this->whenLoaded('packaging')), + 'packages_qty_received' => $this->packages_qty_received, + 'units_qty_received' => $this->units_qty_received, + 'qty_received_base' => $this->qty_received_base, + 'unit_price' => $this->unit_price, + 'unit_price_per_package' => $this->unit_price_per_package, + 'tva_rate_id' => $this->tva_rate_id, + 'tva_rate' => new TvaRateResource($this->whenLoaded('tvaRate')), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} diff --git a/thanasoft-back/app/Http/Resources/GoodsReceiptResource.php b/thanasoft-back/app/Http/Resources/GoodsReceiptResource.php new file mode 100644 index 0000000..0a01d15 --- /dev/null +++ b/thanasoft-back/app/Http/Resources/GoodsReceiptResource.php @@ -0,0 +1,32 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'purchase_order_id' => $this->purchase_order_id, + 'purchase_order' => new PurchaseOrderResource($this->whenLoaded('purchaseOrder')), + 'warehouse_id' => $this->warehouse_id, + 'warehouse' => new WarehouseResource($this->whenLoaded('warehouse')), + 'receipt_number' => $this->receipt_number, + 'receipt_date' => $this->receipt_date, + 'status' => $this->status, + 'notes' => $this->notes, + 'lines' => GoodsReceiptLineResource::collection($this->whenLoaded('lines')), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} diff --git a/thanasoft-back/app/Http/Resources/TvaRateResource.php b/thanasoft-back/app/Http/Resources/TvaRateResource.php new file mode 100644 index 0000000..834434c --- /dev/null +++ b/thanasoft-back/app/Http/Resources/TvaRateResource.php @@ -0,0 +1,25 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'rate' => $this->rate, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} diff --git a/thanasoft-back/app/Models/GoodsReceipt.php b/thanasoft-back/app/Models/GoodsReceipt.php new file mode 100644 index 0000000..326ba59 --- /dev/null +++ b/thanasoft-back/app/Models/GoodsReceipt.php @@ -0,0 +1,41 @@ + 'date', + ]; + + public function purchaseOrder(): BelongsTo + { + return $this->belongsTo(PurchaseOrder::class); + } + + public function warehouse(): BelongsTo + { + return $this->belongsTo(Warehouse::class); + } + + public function lines(): HasMany + { + return $this->hasMany(GoodsReceiptLine::class); + } +} diff --git a/thanasoft-back/app/Models/GoodsReceiptLine.php b/thanasoft-back/app/Models/GoodsReceiptLine.php new file mode 100644 index 0000000..f9e48a3 --- /dev/null +++ b/thanasoft-back/app/Models/GoodsReceiptLine.php @@ -0,0 +1,53 @@ + 'decimal:3', + 'units_qty_received' => 'decimal:3', + 'qty_received_base' => 'decimal:3', + 'unit_price' => 'decimal:2', + 'unit_price_per_package' => 'decimal:2', + ]; + + public function goodsReceipt(): BelongsTo + { + return $this->belongsTo(GoodsReceipt::class); + } + + public function product(): BelongsTo + { + return $this->belongsTo(Product::class); + } + + public function packaging(): BelongsTo + { + return $this->belongsTo(ProductPackaging::class); + } + + public function tvaRate(): BelongsTo + { + return $this->belongsTo(TvaRate::class); + } +} diff --git a/thanasoft-back/app/Models/Product.php b/thanasoft-back/app/Models/Product.php index 895352e..0f46b60 100644 --- a/thanasoft-back/app/Models/Product.php +++ b/thanasoft-back/app/Models/Product.php @@ -123,6 +123,14 @@ class Product extends Model return $this->hasMany(StockMove::class); } + /** + * Get the goods receipt lines for the product. + */ + public function goodsReceiptLines(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(GoodsReceiptLine::class); + } + /** * Boot the model */ diff --git a/thanasoft-back/app/Models/PurchaseOrder.php b/thanasoft-back/app/Models/PurchaseOrder.php index 88a73b0..28598cc 100644 --- a/thanasoft-back/app/Models/PurchaseOrder.php +++ b/thanasoft-back/app/Models/PurchaseOrder.php @@ -30,7 +30,7 @@ class PurchaseOrder extends Model $newNumber = 1; } - $purchaseOrder->po_number = $prefix . str_pad((string)$newNumber, 4, '0', STR_PAD_LEFT); + $purchaseOrder->po_number = $prefix . str_pad((string) $newNumber, 4, '0', STR_PAD_LEFT); } }); } @@ -66,4 +66,12 @@ class PurchaseOrder extends Model { return $this->hasMany(PurchaseOrderLine::class); } + + /** + * Get the goods receipts for this purchase order. + */ + public function goodsReceipts(): HasMany + { + return $this->hasMany(GoodsReceipt::class); + } } diff --git a/thanasoft-back/app/Models/TvaRate.php b/thanasoft-back/app/Models/TvaRate.php new file mode 100644 index 0000000..14c850b --- /dev/null +++ b/thanasoft-back/app/Models/TvaRate.php @@ -0,0 +1,20 @@ + 'decimal:2', + ]; +} diff --git a/thanasoft-back/app/Models/Warehouse.php b/thanasoft-back/app/Models/Warehouse.php index 6559b8c..1cc8479 100644 --- a/thanasoft-back/app/Models/Warehouse.php +++ b/thanasoft-back/app/Models/Warehouse.php @@ -41,4 +41,12 @@ class Warehouse extends Model { return $this->hasMany(StockMove::class, 'to_warehouse_id'); } + + /** + * Get the goods receipts for this warehouse. + */ + public function goodsReceipts(): HasMany + { + return $this->hasMany(GoodsReceipt::class); + } } diff --git a/thanasoft-back/app/Providers/RepositoryServiceProvider.php b/thanasoft-back/app/Providers/RepositoryServiceProvider.php index 03d0595..344e400 100644 --- a/thanasoft-back/app/Providers/RepositoryServiceProvider.php +++ b/thanasoft-back/app/Providers/RepositoryServiceProvider.php @@ -28,6 +28,8 @@ class RepositoryServiceProvider extends ServiceProvider $this->app->bind(\App\Repositories\StockItemRepositoryInterface::class, \App\Repositories\StockItemRepository::class); $this->app->bind(\App\Repositories\StockMoveRepositoryInterface::class, \App\Repositories\StockMoveRepository::class); $this->app->bind(\App\Repositories\ProductPackagingRepositoryInterface::class, \App\Repositories\ProductPackagingRepository::class); + $this->app->bind(\App\Repositories\TvaRateRepositoryInterface::class, \App\Repositories\TvaRateRepository::class); + $this->app->bind(\App\Repositories\GoodsReceiptRepositoryInterface::class, \App\Repositories\GoodsReceiptRepository::class); } /** diff --git a/thanasoft-back/app/Repositories/GoodsReceiptRepository.php b/thanasoft-back/app/Repositories/GoodsReceiptRepository.php new file mode 100644 index 0000000..c5705ae --- /dev/null +++ b/thanasoft-back/app/Repositories/GoodsReceiptRepository.php @@ -0,0 +1,87 @@ +model->with(['purchaseOrder', 'warehouse', 'lines.product', 'lines.packaging', 'lines.tvaRate'])->get($columns); + } + + public function find(int|string $id, array $columns = ['*']): ?GoodsReceipt + { + return $this->model->with(['purchaseOrder', 'warehouse', 'lines.product', 'lines.packaging', 'lines.tvaRate'])->find($id, $columns); + } + + public function create(array $attributes): GoodsReceipt + { + return DB::transaction(function () use ($attributes) { + try { + $lines = $attributes['lines'] ?? []; + unset($attributes['lines']); + + $goodsReceipt = parent::create($attributes); + + if (!empty($lines)) { + foreach ($lines as $line) { + $goodsReceipt->lines()->create($line); + } + } + + return $goodsReceipt->load('lines.product', 'lines.packaging', 'lines.tvaRate'); + } catch (\Exception $e) { + Log::error('Error creating GoodsReceipt with lines: ' . $e->getMessage(), [ + 'attributes' => $attributes, + 'exception' => $e + ]); + throw $e; + } + }); + } + + public function update(int|string $id, array $attributes): bool + { + return DB::transaction(function () use ($id, $attributes) { + try { + $goodsReceipt = $this->find($id); + if (!$goodsReceipt) { + return false; + } + + $lines = $attributes['lines'] ?? null; + unset($attributes['lines']); + + $updated = parent::update($id, $attributes); + + if ($lines !== null && $updated) { + $goodsReceipt->lines()->delete(); + foreach ($lines as $line) { + $goodsReceipt->lines()->create($line); + } + } + + return $updated; + } catch (\Exception $e) { + Log::error('Error updating GoodsReceipt with lines: ' . $e->getMessage(), [ + 'id' => $id, + 'attributes' => $attributes, + 'exception' => $e + ]); + throw $e; + } + }); + } +} diff --git a/thanasoft-back/app/Repositories/GoodsReceiptRepositoryInterface.php b/thanasoft-back/app/Repositories/GoodsReceiptRepositoryInterface.php new file mode 100644 index 0000000..1f76efa --- /dev/null +++ b/thanasoft-back/app/Repositories/GoodsReceiptRepositoryInterface.php @@ -0,0 +1,9 @@ +model->get($columns); + } + + public function find(int|string $id, array $columns = ['*']): ?TvaRate + { + return $this->model->find($id, $columns); + } +} diff --git a/thanasoft-back/app/Repositories/TvaRateRepositoryInterface.php b/thanasoft-back/app/Repositories/TvaRateRepositoryInterface.php new file mode 100644 index 0000000..f633087 --- /dev/null +++ b/thanasoft-back/app/Repositories/TvaRateRepositoryInterface.php @@ -0,0 +1,9 @@ +id(); $table->unsignedBigInteger('fournisseur_id'); // Use existing fournisseurs table - + $table->string('po_number', 191); $table->enum('status', ['brouillon', 'confirmee', 'livree', 'facturee', 'annulee'])->default('brouillon'); $table->date('order_date')->useCurrent(); @@ -26,13 +25,13 @@ return new class extends Migration $table->decimal('total_ttc', 14, 2)->default(0); $table->text('notes')->nullable(); $table->string('delivery_address')->nullable(); - + $table->timestamps(); $table->unique('po_number', 'uq_po_number'); $table->index('status', 'idx_po_status'); $table->index('order_date', 'idx_po_order_date'); - + $table->foreign('fournisseur_id', 'fk_po_fournisseur')->references('id')->on('fournisseurs')->onDelete('cascade'); }); @@ -41,61 +40,28 @@ return new class extends Migration $table->id(); $table->unsignedBigInteger('purchase_order_id'); $table->unsignedBigInteger('product_id')->nullable(); - + $table->text('description'); $table->decimal('quantity', 14, 3)->default(1); $table->decimal('unit_price', 12, 2); $table->decimal('tva_rate', 5, 2)->default(0); // Use rate directly, no FK to tva_rates $table->decimal('discount_pct', 5, 2)->default(0); $table->decimal('total_ht', 14, 2); - + $table->timestamps(); $table->foreign('purchase_order_id', 'fk_pol_po')->references('id')->on('purchase_orders')->onDelete('cascade'); $table->foreign('product_id', 'fk_pol_product')->references('id')->on('products')->onDelete('set null'); }); - // Goods Receipts (Réceptions de marchandises) - Schema::create('goods_receipts', function (Blueprint $table) { - $table->id(); - $table->unsignedBigInteger('purchase_order_id'); - - $table->string('receipt_number', 191); - $table->date('receipt_date')->useCurrent(); - $table->enum('status', ['brouillon', 'valide', 'annule'])->default('brouillon'); - $table->text('notes')->nullable(); - - $table->timestamps(); - $table->unique(['purchase_order_id', 'receipt_number'], 'uq_gr_po_number'); - - $table->foreign('purchase_order_id', 'fk_gr_po')->references('id')->on('purchase_orders')->onDelete('cascade'); - }); - - // Goods Receipt Lines - Schema::create('goods_receipt_lines', function (Blueprint $table) { - $table->id(); - $table->unsignedBigInteger('goods_receipt_id'); - $table->unsignedBigInteger('product_id'); - $table->unsignedBigInteger('purchase_order_line_id')->nullable(); // Link to original order line - - $table->decimal('quantity_received', 14, 3); - $table->decimal('unit_price', 12, 2)->nullable(); - $table->text('notes')->nullable(); - - $table->timestamps(); - - $table->foreign('goods_receipt_id', 'fk_grl_gr')->references('id')->on('goods_receipts')->onDelete('cascade'); - $table->foreign('product_id', 'fk_grl_product')->references('id')->on('products'); - $table->foreign('purchase_order_line_id', 'fk_grl_pol')->references('id')->on('purchase_order_lines')->onDelete('set null'); - }); // Supplier Invoices (Factures Fournisseurs) Schema::create('supplier_invoices', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('fournisseur_id'); $table->unsignedBigInteger('purchase_order_id')->nullable(); // Optional link to purchase order - + $table->string('invoice_number', 191); $table->date('invoice_date')->useCurrent(); $table->date('due_date')->nullable(); @@ -105,13 +71,13 @@ return new class extends Migration $table->decimal('total_tva', 14, 2)->default(0); $table->decimal('total_ttc', 14, 2)->default(0); $table->text('notes')->nullable(); - + $table->timestamps(); $table->unique(['fournisseur_id', 'invoice_number'], 'uq_supplier_invoice'); $table->index('status', 'idx_si_status'); $table->index('invoice_date', 'idx_si_invoice_date'); - + $table->foreign('fournisseur_id', 'fk_si_fournisseur')->references('id')->on('fournisseurs'); $table->foreign('purchase_order_id', 'fk_si_po')->references('id')->on('purchase_orders')->onDelete('set null'); }); @@ -122,13 +88,13 @@ return new class extends Migration $table->unsignedBigInteger('supplier_invoice_id'); $table->unsignedBigInteger('product_id')->nullable(); $table->unsignedBigInteger('purchase_order_line_id')->nullable(); // Link to original order line - + $table->text('description'); $table->decimal('quantity', 14, 3)->default(1); $table->decimal('unit_price', 12, 2); $table->decimal('tva_rate', 5, 2)->default(0); $table->decimal('total_ht', 14, 2); - + $table->timestamps(); $table->foreign('supplier_invoice_id', 'fk_sil_si')->references('id')->on('supplier_invoices')->onDelete('cascade'); @@ -144,8 +110,7 @@ return new class extends Migration { Schema::dropIfExists('supplier_invoice_lines'); Schema::dropIfExists('supplier_invoices'); - Schema::dropIfExists('goods_receipt_lines'); - Schema::dropIfExists('goods_receipts'); + Schema::dropIfExists('purchase_order_lines'); Schema::dropIfExists('purchase_orders'); } diff --git a/thanasoft-back/database/migrations/2026_02_03_123000_create_goods_receipt_tables.php b/thanasoft-back/database/migrations/2026_02_03_123000_create_goods_receipt_tables.php new file mode 100644 index 0000000..93cbf24 --- /dev/null +++ b/thanasoft-back/database/migrations/2026_02_03_123000_create_goods_receipt_tables.php @@ -0,0 +1,82 @@ +id(); + $table->string('name', 50); + $table->decimal('rate', 5, 2); + $table->timestamps(); + }); + } + + // Goods Receipts (Réceptions de marchandises) + Schema::create('goods_receipts', function (Blueprint $table) { + $table->id(); + $table->unsignedBigInteger('purchase_order_id'); + $table->unsignedBigInteger('warehouse_id'); // entrepôt de réception + + $table->string('receipt_number', 191); + $table->date('receipt_date')->useCurrent(); // Default handled by DB if possible or model + $table->enum('status', ['draft', 'posted'])->default('draft'); + $table->timestamp('created_at')->useCurrent(); + // Note: Laravel default timestamps are created_at and updated_at. + // The user asked for created_at DEFAULT CURRENT_TIMESTAMP. + // Laravel's $table->timestamps() creates both created_at and updated_at nullable. + // We will stick to standard Laravel behavior but can add default if strictly needed. + // For now, let's use standard timestamps + specific columns requested. + $table->timestamp('updated_at')->nullable(); + + $table->unique(['purchase_order_id', 'receipt_number'], 'uq_gr_po_number'); + + $table->foreign('purchase_order_id', 'fk_gr_po')->references('id')->on('purchase_orders')->onDelete('cascade'); + $table->foreign('warehouse_id', 'fk_gr_wh')->references('id')->on('warehouses'); + }); + + // Goods Receipt Lines + Schema::create('goods_receipt_lines', function (Blueprint $table) { + $table->id(); + $table->unsignedBigInteger('goods_receipt_id'); + $table->unsignedBigInteger('product_id'); + $table->unsignedBigInteger('packaging_id')->nullable(); + + $table->decimal('packages_qty_received', 14, 3)->nullable(); // nb de colis reçus + $table->decimal('units_qty_received', 14, 3)->nullable(); // nb d’unités reçues (si pas de colis) + $table->decimal('qty_received_base', 14, 3)->nullable(); // quantité base reçue (calculable) + $table->decimal('unit_price', 12, 2)->nullable(); // par unité produit + $table->decimal('unit_price_per_package', 12, 2)->nullable();// par colis + $table->unsignedBigInteger('tva_rate_id')->nullable(); + + $table->timestamps(); + + $table->foreign('goods_receipt_id', 'fk_grl_gr')->references('id')->on('goods_receipts')->onDelete('cascade'); + $table->foreign('product_id', 'fk_grl_product')->references('id')->on('products'); + $table->foreign('packaging_id', 'fk_grl_packaging')->references('id')->on('product_packagings')->onDelete('set null'); + $table->foreign('tva_rate_id', 'fk_grl_tva')->references('id')->on('tva_rates')->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('goods_receipt_lines'); + Schema::dropIfExists('goods_receipts'); + // We do not drop tva_rates here blindly as it might have been created elsewhere if we decide to separate later. + // But since we created it conditionally, we can leave it or drop it if we are sure we own it. + // Given the instructions, I'll drop it if it's safe, but usually safe to leave shared tables or manage in separate migration. + // For this task, I'll only drop what I definitely created exclusively. + Schema::dropIfExists('tva_rates'); + } +}; diff --git a/thanasoft-back/routes/api.php b/thanasoft-back/routes/api.php index 3525b07..57a96b1 100644 --- a/thanasoft-back/routes/api.php +++ b/thanasoft-back/routes/api.php @@ -21,6 +21,8 @@ use App\Http\Controllers\Api\FileAttachmentController; use App\Http\Controllers\Api\QuoteController; use App\Http\Controllers\Api\ClientActivityTimelineController; use App\Http\Controllers\Api\PurchaseOrderController; +use App\Http\Controllers\Api\TvaRateController; +use App\Http\Controllers\Api\GoodsReceiptController; /* @@ -99,12 +101,19 @@ Route::middleware('auth:sanctum')->group(function () { Route::patch('/products/{id}/stock', [ProductController::class, 'updateStock']); // Warehouse management + Route::get('/warehouses/searchBy', [\App\Http\Controllers\Api\WarehouseController::class, 'searchBy']); Route::apiResource('warehouses', \App\Http\Controllers\Api\WarehouseController::class); // Stock management Route::apiResource('stock-items', \App\Http\Controllers\Api\StockItemController::class); Route::apiResource('stock-moves', \App\Http\Controllers\Api\StockMoveController::class); + // TVA Rates management + Route::apiResource('tva-rates', TvaRateController::class); + + // Goods Receipts management + Route::apiResource('goods-receipts', GoodsReceiptController::class); + // Product Category management Route::get('/product-categories/search', [ProductCategoryController::class, 'search']); Route::get('/product-categories/active', [ProductCategoryController::class, 'active']); diff --git a/thanasoft-front/src/components/Organism/Stock/GoodsReceiptListPresentation.vue b/thanasoft-front/src/components/Organism/Stock/GoodsReceiptListPresentation.vue new file mode 100644 index 0000000..cbbaa49 --- /dev/null +++ b/thanasoft-front/src/components/Organism/Stock/GoodsReceiptListPresentation.vue @@ -0,0 +1,112 @@ + + + diff --git a/thanasoft-front/src/components/Organism/Stock/NewReceptionPresentation.vue b/thanasoft-front/src/components/Organism/Stock/NewReceptionPresentation.vue new file mode 100644 index 0000000..39c477f --- /dev/null +++ b/thanasoft-front/src/components/Organism/Stock/NewReceptionPresentation.vue @@ -0,0 +1,71 @@ + + + diff --git a/thanasoft-front/src/components/Organism/Stock/ReceptionDetailPresentation.vue b/thanasoft-front/src/components/Organism/Stock/ReceptionDetailPresentation.vue new file mode 100644 index 0000000..1f6b507 --- /dev/null +++ b/thanasoft-front/src/components/Organism/Stock/ReceptionDetailPresentation.vue @@ -0,0 +1,189 @@ + + + diff --git a/thanasoft-front/src/components/molecules/Stock/NewReceptionForm.vue b/thanasoft-front/src/components/molecules/Stock/NewReceptionForm.vue new file mode 100644 index 0000000..85eacd7 --- /dev/null +++ b/thanasoft-front/src/components/molecules/Stock/NewReceptionForm.vue @@ -0,0 +1,723 @@ + + + + + diff --git a/thanasoft-front/src/components/molecules/Tables/Stock/GoodsReceiptTable.vue b/thanasoft-front/src/components/molecules/Tables/Stock/GoodsReceiptTable.vue new file mode 100644 index 0000000..4ddf58d --- /dev/null +++ b/thanasoft-front/src/components/molecules/Tables/Stock/GoodsReceiptTable.vue @@ -0,0 +1,198 @@ + + + diff --git a/thanasoft-front/src/router/index.js b/thanasoft-front/src/router/index.js index 8bc929e..75fef2b 100644 --- a/thanasoft-front/src/router/index.js +++ b/thanasoft-front/src/router/index.js @@ -588,7 +588,27 @@ const routes = [ name: "Statistiques ventes", component: () => import("@/views/pages/Ventes/Statistiques.vue"), }, - // Stock + // Stock - Reception + { + path: "/stock/receptions", + name: "Réceptions de marchandises", + component: () => import("@/views/pages/Stock/Reception.vue"), + }, + { + path: "/stock/receptions/new", + name: "Nouvelle Réception", + component: () => import("@/views/pages/Stock/NewReception.vue"), + }, + { + path: "/stock/receptions/:id", + name: "Détails Réception", + component: () => import("@/views/pages/Stock/ReceptionDetail.vue"), + }, + { + path: "/stock/receptions/:id/edit", + name: "Modifier Réception", + component: () => import("@/views/pages/Stock/EditReception.vue"), + }, { path: "/stock/reception", name: "Reception stock", diff --git a/thanasoft-front/src/services/goodsReceipt.ts b/thanasoft-front/src/services/goodsReceipt.ts index 2be2a3a..df78fd8 100644 --- a/thanasoft-front/src/services/goodsReceipt.ts +++ b/thanasoft-front/src/services/goodsReceipt.ts @@ -4,25 +4,50 @@ export interface GoodsReceiptLine { id: number; goods_receipt_id: number; product_id: number; - purchase_order_line_id: number | null; - quantity_received: number; + packaging_id: number | null; + packages_qty_received: number | null; + units_qty_received: number | null; + qty_received_base: number | null; unit_price: number | null; - notes: string | null; + unit_price_per_package: number | null; + tva_rate_id: number | null; created_at: string; updated_at: string; - product?: any; + product?: { + id: number; + nom: string; + reference: string; + }; + packaging?: { + id: number; + name: string; + qty_base: number; + }; + tva_rate?: { + id: number; + name: string; + rate: number; + }; } export interface GoodsReceipt { id: number; purchase_order_id: number; + warehouse_id: number; receipt_number: string; receipt_date: string; - status: 'brouillon' | 'valide' | 'annule'; + status: 'draft' | 'posted'; notes: string | null; created_at: string; updated_at: string; - purchase_order?: any; + purchase_order?: { + id: number; + po_number: string; + }; + warehouse?: { + id: number; + name: string; + }; lines?: GoodsReceiptLine[]; } @@ -42,14 +67,18 @@ export interface GoodsReceiptResponse { export interface CreateGoodsReceiptLinePayload { product_id: number; - purchase_order_line_id?: number | null; - quantity_received: number; + packaging_id?: number | null; + packages_qty_received?: number | null; + units_qty_received?: number | null; + qty_received_base?: number | null; unit_price?: number | null; - notes?: string | null; + unit_price_per_package?: number | null; + tva_rate_id?: number | null; } export interface CreateGoodsReceiptPayload { purchase_order_id: number; + warehouse_id: number; receipt_number?: string; receipt_date?: string; status?: string; @@ -111,14 +140,6 @@ export const GoodsReceiptService = { }); return response; }, - - async getByPurchaseOrder(purchaseOrderId: number): Promise { - const response = await request({ - url: `/api/purchase-orders/${purchaseOrderId}/goods-receipts`, - method: "get", - }); - return response; - }, }; export default GoodsReceiptService; diff --git a/thanasoft-front/src/services/tvaRate.ts b/thanasoft-front/src/services/tvaRate.ts new file mode 100644 index 0000000..07578da --- /dev/null +++ b/thanasoft-front/src/services/tvaRate.ts @@ -0,0 +1,73 @@ +import { request } from "./http"; + +export interface TvaRate { + id: number; + name: string; + rate: number; + created_at: string; + updated_at: string; +} + +export interface TvaRateListResponse { + data: TvaRate[]; +} + +export interface TvaRateResponse { + data: TvaRate; +} + +export interface CreateTvaRatePayload { + name: string; + rate: number; +} + +export interface UpdateTvaRatePayload extends Partial { + id: number; +} + +export const TvaRateService = { + async getAllTvaRates(): Promise { + const response = await request({ + url: "/api/tva-rates", + method: "get", + }); + return response; + }, + + async getTvaRate(id: number): Promise { + const response = await request({ + url: `/api/tva-rates/${id}`, + method: "get", + }); + return response; + }, + + async createTvaRate(payload: CreateTvaRatePayload): Promise { + const response = await request({ + url: "/api/tva-rates", + method: "post", + data: payload, + }); + return response; + }, + + async updateTvaRate(payload: UpdateTvaRatePayload): Promise { + const { id, ...updateData } = payload; + const response = await request({ + url: `/api/tva-rates/${id}`, + method: "put", + data: updateData, + }); + return response; + }, + + async deleteTvaRate(id: number): Promise<{ success: boolean; message: string }> { + const response = await request<{ success: boolean; message: string }>({ + url: `/api/tva-rates/${id}`, + method: "delete", + }); + return response; + }, +}; + +export default TvaRateService; diff --git a/thanasoft-front/src/services/warehouse.ts b/thanasoft-front/src/services/warehouse.ts index ecfdc3e..2292e72 100644 --- a/thanasoft-front/src/services/warehouse.ts +++ b/thanasoft-front/src/services/warehouse.ts @@ -58,6 +58,30 @@ class WarehouseService { method: "delete", }); } + + /** + * Search warehouses by name + */ + async searchWarehouses( + query: string, + params?: { + exact_match?: boolean; + } + ): Promise { + const response = await request<{ + data: Warehouse[]; + count: number; + message: string; + }>({ + url: "/api/warehouses/searchBy", + method: "get", + params: { + name: query, + exact_match: params?.exact_match || false, + }, + }); + return response.data; + } } export default WarehouseService; diff --git a/thanasoft-front/src/stores/goodsReceiptStore.ts b/thanasoft-front/src/stores/goodsReceiptStore.ts index 0236e33..54b8589 100644 --- a/thanasoft-front/src/stores/goodsReceiptStore.ts +++ b/thanasoft-front/src/stores/goodsReceiptStore.ts @@ -1,8 +1,6 @@ import { defineStore } from "pinia"; import { ref, computed } from "vue"; -import GoodsReceiptService from "@/services/goodsReceipt"; - -import type { +import GoodsReceiptService, { GoodsReceipt, CreateGoodsReceiptPayload, UpdateGoodsReceiptPayload, @@ -23,13 +21,10 @@ export const useGoodsReceiptStore = defineStore("goodsReceipt", () => { const allGoodsReceipts = computed(() => goodsReceipts.value); const draftReceipts = computed(() => - goodsReceipts.value.filter((receipt) => receipt.status === "brouillon") + goodsReceipts.value.filter((receipt) => receipt.status === "draft") ); - const validatedReceipts = computed(() => - goodsReceipts.value.filter((receipt) => receipt.status === "valide") - ); - const cancelledReceipts = computed(() => - goodsReceipts.value.filter((receipt) => receipt.status === "annule") + const postedReceipts = computed(() => + goodsReceipts.value.filter((receipt) => receipt.status === "posted") ); const isLoading = computed(() => loading.value); const hasError = computed(() => error.value !== null); @@ -91,7 +86,7 @@ export const useGoodsReceiptStore = defineStore("goodsReceipt", () => { const errorMessage = err.response?.data?.message || err.message || - "Erreur lors de la récupération des réceptions"; + "Erreur lors de la récupération des réceptions de marchandises"; setError(errorMessage); throw err; } finally { @@ -111,7 +106,7 @@ export const useGoodsReceiptStore = defineStore("goodsReceipt", () => { const errorMessage = err.response?.data?.message || err.message || - "Erreur lors de la récupération de la réception"; + "Erreur lors de la récupération de la réception de marchandise"; setError(errorMessage); throw err; } finally { @@ -132,7 +127,7 @@ export const useGoodsReceiptStore = defineStore("goodsReceipt", () => { const errorMessage = err.response?.data?.message || err.message || - "Erreur lors de la création de la réception"; + "Erreur lors de la création de la réception de marchandise"; setError(errorMessage); throw err; } finally { @@ -167,7 +162,7 @@ export const useGoodsReceiptStore = defineStore("goodsReceipt", () => { const errorMessage = err.response?.data?.message || err.message || - "Erreur lors de la mise à jour de la réception"; + "Erreur lors de la mise à jour de la réception de marchandise"; setError(errorMessage); throw err; } finally { @@ -195,27 +190,7 @@ export const useGoodsReceiptStore = defineStore("goodsReceipt", () => { const errorMessage = err.response?.data?.message || err.message || - "Erreur lors de la suppression de la réception"; - setError(errorMessage); - throw err; - } finally { - setLoading(false); - } - }; - - const fetchByPurchaseOrder = async (purchaseOrderId: number) => { - setLoading(true); - setError(null); - - try { - const response = await GoodsReceiptService.getByPurchaseOrder(purchaseOrderId); - setGoodsReceipts(response.data); - return response.data; - } catch (err: any) { - const errorMessage = - err.response?.data?.message || - err.message || - "Erreur lors de la récupération des réceptions de la commande"; + "Erreur lors de la suppression de la réception de marchandise"; setError(errorMessage); throw err; } finally { @@ -247,8 +222,7 @@ export const useGoodsReceiptStore = defineStore("goodsReceipt", () => { allGoodsReceipts, draftReceipts, - validatedReceipts, - cancelledReceipts, + postedReceipts, isLoading, hasError, getError, @@ -260,7 +234,6 @@ export const useGoodsReceiptStore = defineStore("goodsReceipt", () => { createGoodsReceipt, updateGoodsReceipt, deleteGoodsReceipt, - fetchByPurchaseOrder, clearCurrentGoodsReceipt, clearStore, clearError, diff --git a/thanasoft-front/src/views/pages/Stock/EditReception.vue b/thanasoft-front/src/views/pages/Stock/EditReception.vue new file mode 100644 index 0000000..d1efc32 --- /dev/null +++ b/thanasoft-front/src/views/pages/Stock/EditReception.vue @@ -0,0 +1,210 @@ + + + diff --git a/thanasoft-front/src/views/pages/Stock/NewReception.vue b/thanasoft-front/src/views/pages/Stock/NewReception.vue new file mode 100644 index 0000000..c74c1d1 --- /dev/null +++ b/thanasoft-front/src/views/pages/Stock/NewReception.vue @@ -0,0 +1,7 @@ + + + diff --git a/thanasoft-front/src/views/pages/Stock/Reception.vue b/thanasoft-front/src/views/pages/Stock/Reception.vue index 7c3a429..2ee38fe 100644 --- a/thanasoft-front/src/views/pages/Stock/Reception.vue +++ b/thanasoft-front/src/views/pages/Stock/Reception.vue @@ -1,11 +1,7 @@ - diff --git a/thanasoft-front/src/views/pages/Stock/ReceptionDetail.vue b/thanasoft-front/src/views/pages/Stock/ReceptionDetail.vue new file mode 100644 index 0000000..99e3905 --- /dev/null +++ b/thanasoft-front/src/views/pages/Stock/ReceptionDetail.vue @@ -0,0 +1,7 @@ + + +