113 lines
4.3 KiB
Python
113 lines
4.3 KiB
Python
"""
|
|
Parts Routes - GMAO Collège
|
|
Gestion des pièces détachées
|
|
"""
|
|
from flask import Blueprint, render_template, request, flash, redirect, url_for
|
|
from flask_login import login_required, current_user
|
|
from app_new.extensions import db
|
|
from ..core.models.company import Part, PartStockMovement
|
|
|
|
|
|
def _record_movement(part, new_quantity, movement_type, reason):
|
|
before = int(part.quantity or 0)
|
|
after = int(new_quantity)
|
|
if after < 0:
|
|
raise ValueError('Le stock ne peut pas devenir négatif.')
|
|
if after == before:
|
|
return None
|
|
movement = PartStockMovement(
|
|
part=part, user_id=current_user.id, movement_type=movement_type,
|
|
quantity_delta=after - before, quantity_before=before,
|
|
quantity_after=after, reason=(reason or '').strip() or 'Ajustement manuel',
|
|
)
|
|
part.quantity = after
|
|
db.session.add(movement)
|
|
return movement
|
|
|
|
parts_bp = Blueprint('parts', __name__)
|
|
|
|
@parts_bp.route('/')
|
|
@login_required
|
|
def index():
|
|
"""Liste des pièces."""
|
|
page = request.args.get('page', 1, type=int)
|
|
parts = Part.query.order_by(Part.name).paginate(page=page, per_page=20)
|
|
return render_template('parts/index.html', parts=parts)
|
|
|
|
@parts_bp.route('/<int:id>')
|
|
@login_required
|
|
def detail(id):
|
|
"""Détail d'une pièce."""
|
|
part = Part.query.get_or_404(id)
|
|
movements = part.stock_movements.order_by(PartStockMovement.created_at.desc()).limit(100).all()
|
|
return render_template('parts/detail.html', part=part, movements=movements)
|
|
|
|
|
|
@parts_bp.route('/<int:id>/edit', methods=['GET', 'POST'])
|
|
@login_required
|
|
def edit(id):
|
|
"""Modifier une pièce."""
|
|
part = Part.query.get_or_404(id)
|
|
if request.method == 'POST':
|
|
part.name = request.form.get('name')
|
|
part.reference = request.form.get('reference')
|
|
try:
|
|
_record_movement(part, request.form.get('quantity', type=int, default=0),
|
|
'correction', request.form.get('stock_reason'))
|
|
except ValueError as exc:
|
|
flash(str(exc), 'danger')
|
|
return render_template('parts/form.html', part=part), 400
|
|
part.unit = request.form.get('unit')
|
|
part.unit_price = request.form.get('unit_price', type=float)
|
|
part.supplier = request.form.get('supplier')
|
|
db.session.commit()
|
|
flash('Pièce modifiée avec succès.', 'success')
|
|
return redirect(url_for('parts.detail', id=part.id))
|
|
return render_template('parts/form.html', part=part)
|
|
|
|
|
|
@parts_bp.route('/new', methods=['GET', 'POST'])
|
|
@login_required
|
|
def create():
|
|
"""Créer une pièce."""
|
|
if request.method == 'POST':
|
|
quantity = max(0, request.form.get('quantity', type=int, default=0))
|
|
part = Part(
|
|
name=request.form.get('name'),
|
|
reference=request.form.get('reference'),
|
|
quantity=0,
|
|
unit=request.form.get('unit')
|
|
)
|
|
part.unit_price = request.form.get('unit_price', type=float)
|
|
part.supplier = request.form.get('supplier')
|
|
db.session.add(part)
|
|
db.session.flush()
|
|
if quantity:
|
|
_record_movement(part, quantity, 'entree', 'Stock initial')
|
|
db.session.commit()
|
|
flash('Pièce créée avec succès.', 'success')
|
|
return redirect(url_for('parts.index'))
|
|
|
|
return render_template('parts/new.html')
|
|
|
|
|
|
@parts_bp.route('/<int:id>/movement', methods=['POST'])
|
|
@login_required
|
|
def movement(id):
|
|
"""Enregistre une entrée, une sortie ou un inventaire sans écraser l'historique."""
|
|
part = Part.query.get_or_404(id)
|
|
movement_type = request.form.get('movement_type')
|
|
amount = request.form.get('quantity', type=int)
|
|
reason = (request.form.get('reason') or '').strip()
|
|
if movement_type not in {'entree', 'sortie', 'inventaire'} or amount is None or amount < 0 or not reason:
|
|
flash('Type, quantité positive et motif sont obligatoires.', 'danger')
|
|
return redirect(url_for('parts.detail', id=id))
|
|
new_quantity = amount if movement_type == 'inventaire' else int(part.quantity or 0) + (amount if movement_type == 'entree' else -amount)
|
|
try:
|
|
_record_movement(part, new_quantity, movement_type, reason)
|
|
db.session.commit()
|
|
flash('Mouvement de stock enregistré.', 'success')
|
|
except ValueError as exc:
|
|
db.session.rollback()
|
|
flash(str(exc), 'danger')
|
|
return redirect(url_for('parts.detail', id=id))
|