stock-backend / app /api /materials.py
fromozuzhouzzz
Initial commit
fed8648
Raw
History Blame Contribute Delete
11 kB
from flask import jsonify, request, current_app
from flask_jwt_extended import jwt_required, get_jwt_identity
from app.models.material import Material
from app.api import bp, ns_materials, api
from app import db
from app.utils.validators import validate_json, validate_decimal
from flask_restx import Resource, fields
from sqlalchemy import distinct
# API 模型定义
material_model = api.model('Material', {
'name': fields.String(required=True, description='Material name'),
'unit': fields.String(required=True, description='Unit of measurement'),
'category': fields.String(required=False, description='Material category'),
'warning_threshold': fields.Float(required=True, description='Warning threshold quantity'),
'processed_name': fields.String(required=False, description='Processed material name'),
'processed_unit': fields.String(required=False, description='Processed material unit'),
'processed_warning_threshold': fields.Float(required=False, description='Processed material warning threshold'),
'conversion_ratio': fields.Float(required=False, description='Conversion ratio from raw to processed material')
})
@ns_materials.route('/categories/')
class MaterialCategoriesResource(Resource):
@api.doc('list_material_categories')
@jwt_required()
def get(self):
"""获取所有物料分类列表"""
try:
# 从数据库中查询所有不同的分类
categories_query = db.session.query(distinct(Material.category)).all()
# 提取分类名称,排除空值和None
db_categories = [category[0] for category in categories_query if category[0]]
# 排序
db_categories.sort() # 按字母顺序排序
# 注释掉强制添加"其他"分类的代码,只显示数据库中存在的分类
# if '其他' not in db_categories:
# db_categories.append('其他')
# else:
# # 如果"其他"分类已存在,将其移到最后
# db_categories.remove('其他')
# db_categories.append('其他')
current_app.logger.debug(f"Found {len(db_categories)} material categories")
return {'data': db_categories}
except Exception as e:
current_app.logger.error(f'Error fetching material categories: {str(e)}')
return {'message': 'Error fetching material categories'}, 500
@ns_materials.route('/')
class MaterialResource(Resource):
@api.doc('list_materials')
@jwt_required()
def get(self):
"""获取所有物料列表"""
try:
materials = Material.query.all()
result = [{
'id': m.id,
'name': m.name,
'unit': m.unit,
'category': m.category,
'warning_threshold': float(m.warning_threshold),
'processed_name': m.processed_name,
'processed_unit': m.processed_unit,
'processed_warning_threshold': float(m.processed_warning_threshold) if m.processed_warning_threshold else None,
'conversion_ratio': float(m.conversion_ratio) if m.conversion_ratio else None,
'created_at': m.created_at.isoformat()
} for m in materials]
# 打印调试信息
current_app.logger.debug(f"Found {len(result)} materials")
return {'data': result}
except Exception as e:
current_app.logger.error(f'Error fetching materials: {str(e)}')
return {'message': 'Error fetching materials'}, 500
@api.doc('create_material')
@api.expect(material_model)
@jwt_required()
def post(self):
"""创建新物料"""
try:
data = request.get_json()
# 验证数据
warning_threshold = validate_decimal(data['warning_threshold'])
if warning_threshold is None or warning_threshold <= 0:
return {'message': 'Invalid warning threshold value'}, 400
# 检查名称是否已存在
if Material.query.filter_by(name=data['name']).first():
return {'message': 'Material name already exists'}, 400
# 处理加工物料相关数据
processed_warning_threshold = None
if 'processed_warning_threshold' in data and data['processed_warning_threshold']:
processed_warning_threshold = validate_decimal(data['processed_warning_threshold'])
if processed_warning_threshold is not None and processed_warning_threshold <= 0:
return {'message': 'Invalid processed warning threshold value'}, 400
conversion_ratio = None
if 'conversion_ratio' in data and data['conversion_ratio']:
conversion_ratio = validate_decimal(data['conversion_ratio'])
if conversion_ratio is not None and conversion_ratio <= 0:
return {'message': 'Invalid conversion ratio value'}, 400
material = Material(
name=data['name'],
unit=data['unit'],
category=data.get('category', '其他'),
warning_threshold=warning_threshold,
processed_name=data.get('processed_name'),
processed_unit=data.get('processed_unit'),
processed_warning_threshold=processed_warning_threshold,
conversion_ratio=conversion_ratio
)
db.session.add(material)
db.session.commit()
return jsonify({
'id': material.id,
'name': material.name,
'unit': material.unit,
'category': material.category,
'warning_threshold': float(material.warning_threshold),
'processed_name': material.processed_name,
'processed_unit': material.processed_unit,
'processed_warning_threshold': float(material.processed_warning_threshold) if material.processed_warning_threshold else None,
'conversion_ratio': float(material.conversion_ratio) if material.conversion_ratio else None,
'created_at': material.created_at.isoformat()
})
except Exception as e:
current_app.logger.error(f'Error creating material: {str(e)}')
db.session.rollback()
return {'message': 'Error creating material'}, 500
@ns_materials.route('/<int:id>')
class MaterialDetail(Resource):
@api.doc('update_material')
@jwt_required()
def put(self, id):
"""更新物料信息"""
try:
material = Material.query.get_or_404(id)
data = request.get_json()
if 'name' in data and data['name'] != material.name:
if Material.query.filter_by(name=data['name']).first():
return {'message': 'Material name already exists'}, 400
material.name = data['name']
if 'unit' in data:
material.unit = data['unit']
if 'category' in data:
material.category = data['category']
if 'warning_threshold' in data:
warning_threshold = validate_decimal(data['warning_threshold'])
if warning_threshold is None or warning_threshold <= 0:
return {'message': 'Invalid warning threshold value'}, 400
material.warning_threshold = warning_threshold
# 更新物料加工相关字段
if 'processed_name' in data:
material.processed_name = data['processed_name']
if 'processed_unit' in data:
material.processed_unit = data['processed_unit']
if 'processed_warning_threshold' in data:
if data['processed_warning_threshold']:
processed_warning_threshold = validate_decimal(data['processed_warning_threshold'])
if processed_warning_threshold is not None and processed_warning_threshold <= 0:
return {'message': 'Invalid processed warning threshold value'}, 400
material.processed_warning_threshold = processed_warning_threshold
else:
material.processed_warning_threshold = None
if 'conversion_ratio' in data:
if data['conversion_ratio']:
conversion_ratio = validate_decimal(data['conversion_ratio'])
if conversion_ratio is not None and conversion_ratio <= 0:
return {'message': 'Invalid conversion ratio value'}, 400
material.conversion_ratio = conversion_ratio
else:
material.conversion_ratio = None
db.session.commit()
return jsonify({
'id': material.id,
'name': material.name,
'unit': material.unit,
'category': material.category,
'warning_threshold': float(material.warning_threshold),
'processed_name': material.processed_name,
'processed_unit': material.processed_unit,
'processed_warning_threshold': float(material.processed_warning_threshold) if material.processed_warning_threshold else None,
'conversion_ratio': float(material.conversion_ratio) if material.conversion_ratio else None,
'created_at': material.created_at.isoformat()
})
except Exception as e:
current_app.logger.error(f'Error updating material: {str(e)}')
db.session.rollback()
return {'message': 'Error updating material'}, 500
@api.doc('delete_material')
@jwt_required()
def delete(self, id):
"""删除物料"""
try:
material = Material.query.get_or_404(id)
# 检查是否有关联的库存记录,但不阻止删除
has_inventory = len(material.inventories) > 0
if has_inventory:
# 如果有库存记录,先删除所有相关的库存记录
for inventory in material.inventories:
db.session.delete(inventory)
# 删除物料
db.session.delete(material)
db.session.commit()
return '', 204
except Exception as e:
current_app.logger.error(f'Error deleting material: {str(e)}')
db.session.rollback()
return {'message': 'Error deleting material'}, 500