增加入库记录页面,同时修正三组入库的时间问题
This commit is contained in:
@ -40,7 +40,7 @@ class BuyInboundService:
|
||||
return []
|
||||
|
||||
# ============================================================
|
||||
# 2. 新增入库逻辑
|
||||
# 2. 新增入库逻辑 (修改:精确到时间)
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def handle_inbound(data):
|
||||
@ -51,14 +51,24 @@ class BuyInboundService:
|
||||
material = MaterialBase.query.get(base_id)
|
||||
if not material: raise ValueError("物料不存在")
|
||||
|
||||
in_date_val = datetime.utcnow().date()
|
||||
# [核心修改] 默认使用当前时间(含时分秒),不再截取 .date()
|
||||
current_time = datetime.now()
|
||||
in_date_val = current_time
|
||||
|
||||
if data.get('in_date'):
|
||||
try:
|
||||
date_str = str(data['in_date'])
|
||||
if len(date_str) > 10: date_str = date_str[:10]
|
||||
in_date_val = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||
# 如果前端传了时分秒,尝试直接解析
|
||||
if len(date_str) > 10:
|
||||
in_date_val = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
# 如果只传了日期,手动补上当前时间的时分秒,保证同日入库的排序正确
|
||||
d_temp = datetime.strptime(date_str, '%Y-%m-%d')
|
||||
in_date_val = datetime(d_temp.year, d_temp.month, d_temp.day,
|
||||
current_time.hour, current_time.minute, current_time.second)
|
||||
except:
|
||||
pass
|
||||
# 解析失败则使用当前时间作为兜底
|
||||
in_date_val = current_time
|
||||
|
||||
in_qty = float(data.get('in_quantity') or 0)
|
||||
u_price = float(data.get('unit_price') or 0)
|
||||
@ -80,10 +90,10 @@ class BuyInboundService:
|
||||
global_print_id=next_global_id,
|
||||
sku=generated_sku,
|
||||
barcode=final_barcode,
|
||||
in_date=in_date_val,
|
||||
in_date=in_date_val, # 存入 DateTime 对象
|
||||
serial_number=data.get('serial_number'),
|
||||
batch_number=data.get('batch_number'),
|
||||
status=data.get('status', '在库'), # 默认在库
|
||||
status=data.get('status', '在库'),
|
||||
in_quantity=in_qty,
|
||||
stock_quantity=in_qty,
|
||||
available_quantity=in_qty,
|
||||
@ -185,7 +195,7 @@ class BuyInboundService:
|
||||
return []
|
||||
|
||||
# ============================================================
|
||||
# 6. 获取列表 (核心逻辑修改)
|
||||
# 6. 获取列表 (修改:按时间倒序排序 + 展示只显示日期)
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def get_list(page, limit, keyword=None, statuses=None):
|
||||
@ -196,7 +206,7 @@ class BuyInboundService:
|
||||
try:
|
||||
query = db.session.query(StockBuy).outerjoin(MaterialBase, StockBuy.base_id == MaterialBase.id)
|
||||
|
||||
# 1. 关键词搜索 (覆盖所有关键字段)
|
||||
# 1. 关键词搜索
|
||||
if keyword:
|
||||
kw = f'%{keyword}%'
|
||||
query = query.filter(
|
||||
@ -210,24 +220,13 @@ class BuyInboundService:
|
||||
)
|
||||
)
|
||||
|
||||
# 2. 状态筛选与零库存隐藏逻辑
|
||||
# 用户要求:
|
||||
# - 默认显示:'在库', '借库'。
|
||||
# - 零库存规则:库存为0时,不在页面显示(除非筛选了'已出库')。
|
||||
|
||||
# 2. 状态筛选
|
||||
if not statuses:
|
||||
# 默认情况:只查 '在库' 和 '借库'
|
||||
statuses = ['在库', '借库']
|
||||
|
||||
# 构建筛选条件
|
||||
# 如果筛选条件中 包含 '已出库',则允许显示 stock_quantity >= 0 (即显示所有)
|
||||
# 如果筛选条件中 不包含 '已出库',则强制要求 stock_quantity > 0 (隐藏零库存)
|
||||
|
||||
if '已出库' in statuses:
|
||||
# 用户想看已出库的,直接按状态查,不做数量限制
|
||||
query = query.filter(StockBuy.status.in_(statuses))
|
||||
else:
|
||||
# 用户不想看已出库的,按状态查 AND 数量必须 > 0
|
||||
query = query.filter(
|
||||
and_(
|
||||
StockBuy.status.in_(statuses),
|
||||
@ -235,7 +234,8 @@ class BuyInboundService:
|
||||
)
|
||||
)
|
||||
|
||||
pagination = query.order_by(StockBuy.id.desc()).paginate(page=page, per_page=limit, error_out=False)
|
||||
# [核心修改] 按照入库时间倒序排序 (从近到远)
|
||||
pagination = query.order_by(StockBuy.in_date.desc()).paginate(page=page, per_page=limit, error_out=False)
|
||||
current_items = pagination.items
|
||||
|
||||
def parse_img(json_str):
|
||||
@ -247,10 +247,17 @@ class BuyInboundService:
|
||||
|
||||
items = []
|
||||
for item in current_items:
|
||||
# 获取单行数据,不再进行聚合计算
|
||||
qty_stock = float(item.stock_quantity or 0)
|
||||
qty_avail = float(item.available_quantity or 0)
|
||||
|
||||
# [核心修改] 格式化展示日期,去掉时分秒
|
||||
date_display = ''
|
||||
if item.in_date:
|
||||
try:
|
||||
date_display = item.in_date.strftime('%Y-%m-%d')
|
||||
except:
|
||||
date_display = str(item.in_date)[:10]
|
||||
|
||||
d = {
|
||||
'id': item.id,
|
||||
'base_id': item.base_id,
|
||||
@ -261,7 +268,7 @@ class BuyInboundService:
|
||||
'material_type': item.material.material_type if item.material else '',
|
||||
|
||||
'sku': item.sku,
|
||||
'inbound_date': str(item.in_date) if item.in_date else '',
|
||||
'inbound_date': date_display, # 前端展示用的日期字符串
|
||||
'barcode': item.barcode,
|
||||
'serial_number': item.serial_number,
|
||||
'batch_number': item.batch_number,
|
||||
@ -273,8 +280,6 @@ class BuyInboundService:
|
||||
'qty_stock': qty_stock,
|
||||
'qty_available': qty_avail,
|
||||
|
||||
# 解除挂钩:不再返回所有批次的总和,直接返回当前批次的数量
|
||||
# 为了兼容前端字段名,这里直接用当前行数量填充
|
||||
'sum_stock': qty_stock,
|
||||
'sum_available': qty_avail,
|
||||
|
||||
|
||||
@ -0,0 +1,190 @@
|
||||
from sqlalchemy import select, literal, union_all, desc, asc, func, or_, cast, String, Numeric, Date
|
||||
from app.extensions import db
|
||||
from app.models.inbound.buy import StockBuy
|
||||
from app.models.inbound.semi import StockSemi
|
||||
from app.models.inbound.product import StockProduct
|
||||
from app.models.base import MaterialBase
|
||||
import traceback
|
||||
|
||||
|
||||
class InboundSummaryService:
|
||||
|
||||
@staticmethod
|
||||
def get_list(page=1, per_page=10, keyword=None, start_date=None, end_date=None, source_type=None):
|
||||
"""
|
||||
聚合查询:
|
||||
1. 联合 StockBuy, StockSemi, StockProduct 三张表
|
||||
2. 关联 MaterialBase 获取名称规格
|
||||
3. 计算动态状态 (库存耗尽显示已出库)
|
||||
4. 排序:默认按入库日期倒序 (最近的在前)
|
||||
"""
|
||||
try:
|
||||
# =========================================================
|
||||
# 1. 构建三个子查询 (Subqueries)
|
||||
# =========================================================
|
||||
|
||||
# --- A. 采购件 (StockBuy) ---
|
||||
q_buy = db.session.query(
|
||||
StockBuy.id.label('id'),
|
||||
StockBuy.base_id.label('base_id'),
|
||||
StockBuy.sku.label('sku'),
|
||||
StockBuy.in_date.label('inbound_date'),
|
||||
StockBuy.in_quantity.label('in_qty'),
|
||||
StockBuy.stock_quantity.label('current_qty'),
|
||||
|
||||
cast(StockBuy.supplier_name, String).label('source_info'),
|
||||
StockBuy.status.label('orig_status'),
|
||||
cast(StockBuy.batch_number, String).label('batch_number'),
|
||||
cast(literal('buy'), String).label('source_type')
|
||||
)
|
||||
|
||||
# --- B. 半成品 (StockSemi) ---
|
||||
q_semi = db.session.query(
|
||||
StockSemi.id.label('id'),
|
||||
StockSemi.base_id.label('base_id'),
|
||||
StockSemi.sku.label('sku'),
|
||||
StockSemi.production_date.label('inbound_date'),
|
||||
StockSemi.in_quantity.label('in_qty'),
|
||||
StockSemi.stock_quantity.label('current_qty'),
|
||||
|
||||
cast(StockSemi.production_manager, String).label('source_info'),
|
||||
StockSemi.status.label('orig_status'),
|
||||
cast(StockSemi.batch_number, String).label('batch_number'),
|
||||
cast(literal('semi'), String).label('source_type')
|
||||
)
|
||||
|
||||
# --- C. 成品 (StockProduct) ---
|
||||
q_product = db.session.query(
|
||||
StockProduct.id.label('id'),
|
||||
StockProduct.base_id.label('base_id'),
|
||||
StockProduct.sku.label('sku'),
|
||||
StockProduct.production_date.label('inbound_date'),
|
||||
StockProduct.in_quantity.label('in_qty'),
|
||||
StockProduct.stock_quantity.label('current_qty'),
|
||||
|
||||
cast(StockProduct.production_manager, String).label('source_info'),
|
||||
StockProduct.status.label('orig_status'),
|
||||
cast(StockProduct.serial_number, String).label('batch_number'),
|
||||
cast(literal('product'), String).label('source_type')
|
||||
)
|
||||
|
||||
# =========================================================
|
||||
# 2. 组合查询 (UNION ALL)
|
||||
# =========================================================
|
||||
combined_query = union_all(q_buy, q_semi, q_product)
|
||||
cte = combined_query.subquery()
|
||||
|
||||
# =========================================================
|
||||
# 3. 主查询:关联 MaterialBase
|
||||
# =========================================================
|
||||
query = db.session.query(
|
||||
cte,
|
||||
MaterialBase.name.label('material_name'),
|
||||
MaterialBase.spec_model.label('spec_model'),
|
||||
MaterialBase.category.label('category'),
|
||||
MaterialBase.material_type.label('material_type')
|
||||
).outerjoin(
|
||||
MaterialBase, cte.c.base_id == MaterialBase.id
|
||||
)
|
||||
|
||||
# =========================================================
|
||||
# 4. 过滤条件
|
||||
# =========================================================
|
||||
if keyword:
|
||||
rule = or_(
|
||||
cte.c.sku.ilike(f'%{keyword}%'),
|
||||
cte.c.source_info.ilike(f'%{keyword}%'),
|
||||
cte.c.batch_number.ilike(f'%{keyword}%'),
|
||||
MaterialBase.name.ilike(f'%{keyword}%'),
|
||||
MaterialBase.spec_model.ilike(f'%{keyword}%')
|
||||
)
|
||||
query = query.filter(rule)
|
||||
|
||||
if start_date and end_date:
|
||||
query = query.filter(cte.c.inbound_date.between(start_date, end_date))
|
||||
|
||||
if source_type:
|
||||
query = query.filter(cte.c.source_type == source_type)
|
||||
|
||||
# =========================================================
|
||||
# 5. 获取总数
|
||||
# =========================================================
|
||||
count_query = db.session.query(func.count()) \
|
||||
.select_from(cte) \
|
||||
.outerjoin(MaterialBase, cte.c.base_id == MaterialBase.id)
|
||||
|
||||
if keyword:
|
||||
count_query = count_query.filter(rule)
|
||||
if start_date and end_date:
|
||||
count_query = count_query.filter(cte.c.inbound_date.between(start_date, end_date))
|
||||
if source_type:
|
||||
count_query = count_query.filter(cte.c.source_type == source_type)
|
||||
|
||||
total = count_query.scalar() or 0
|
||||
|
||||
# =========================================================
|
||||
# 6. 排序与分页
|
||||
# =========================================================
|
||||
# ★★★ 修改处:优先按入库日期倒序排列 (最近的在前) ★★★
|
||||
# 如果日期相同,再按 SKU 排序,保证分页稳定性
|
||||
query = query.order_by(desc(cte.c.inbound_date), asc(cte.c.sku))
|
||||
|
||||
pagination = query.limit(per_page).offset((page - 1) * per_page).all()
|
||||
|
||||
# =========================================================
|
||||
# 7. 数据格式化
|
||||
# =========================================================
|
||||
items = []
|
||||
type_map = {
|
||||
'buy': '采购入库',
|
||||
'semi': '半成品生产',
|
||||
'product': '成品完工'
|
||||
}
|
||||
|
||||
for row in pagination:
|
||||
date_str = ""
|
||||
if row.inbound_date:
|
||||
try:
|
||||
date_str = row.inbound_date.strftime('%Y-%m-%d')
|
||||
except Exception:
|
||||
date_str = str(row.inbound_date)
|
||||
|
||||
in_qty = float(row.in_qty) if row.in_qty is not None else 0.0
|
||||
current_qty = float(row.current_qty) if row.current_qty is not None else 0.0
|
||||
|
||||
# 状态逻辑
|
||||
final_status = row.orig_status
|
||||
if current_qty <= 0:
|
||||
final_status = "已出库"
|
||||
elif current_qty < in_qty:
|
||||
final_status = "部分出库"
|
||||
|
||||
items.append({
|
||||
'id': row.id,
|
||||
'sku': row.sku or "",
|
||||
'name': row.material_name or "未知物品",
|
||||
'spec_model': row.spec_model or "",
|
||||
'category': row.category or "",
|
||||
'material_type': row.material_type or "",
|
||||
|
||||
'inbound_date': date_str,
|
||||
'quantity': in_qty,
|
||||
'current_qty': current_qty,
|
||||
'source_info': row.source_info or "",
|
||||
'status': final_status,
|
||||
'source_type': row.source_type,
|
||||
'type_label': type_map.get(row.source_type, "未知类型"),
|
||||
'batch_number': row.batch_number or ""
|
||||
})
|
||||
|
||||
return {
|
||||
'items': items,
|
||||
'total': total,
|
||||
'pages': (total + per_page - 1) // per_page if per_page > 0 else 0,
|
||||
'current_page': page
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print("【InboundSummaryService Error】:", str(e))
|
||||
traceback.print_exc()
|
||||
raise e
|
||||
@ -32,6 +32,9 @@ class ProductInboundService:
|
||||
traceback.print_exc()
|
||||
return []
|
||||
|
||||
# ============================================================
|
||||
# 2. 新增入库逻辑 (修改:精确到时间)
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def handle_inbound(data):
|
||||
from app.models.inbound.product import StockProduct
|
||||
@ -42,14 +45,21 @@ class ProductInboundService:
|
||||
material = MaterialBase.query.get(base_id)
|
||||
if not material: raise ValueError("物料不存在")
|
||||
|
||||
in_date_val = datetime.utcnow().date()
|
||||
# [核心修改] 处理 production_date,包含时分秒
|
||||
current_time = datetime.now()
|
||||
in_date_val = current_time
|
||||
|
||||
if data.get('in_date'):
|
||||
try:
|
||||
date_str = str(data['in_date'])
|
||||
if len(date_str) > 10: date_str = date_str[:10]
|
||||
in_date_val = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||
if len(date_str) > 10:
|
||||
in_date_val = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
d_temp = datetime.strptime(date_str, '%Y-%m-%d')
|
||||
in_date_val = datetime(d_temp.year, d_temp.month, d_temp.day,
|
||||
current_time.hour, current_time.minute, current_time.second)
|
||||
except:
|
||||
pass
|
||||
in_date_val = current_time
|
||||
|
||||
in_qty = float(data.get('in_quantity') or 0)
|
||||
|
||||
@ -65,7 +75,6 @@ class ProductInboundService:
|
||||
generated_sku = str(next_global_id).zfill(10)
|
||||
final_barcode = data.get('barcode') or generated_sku
|
||||
|
||||
# 处理三个图片/链接列表
|
||||
photo_list = data.get('product_photo', [])
|
||||
quality_list = data.get('quality_report_link', [])
|
||||
inspection_list = data.get('inspection_report_link', [])
|
||||
@ -78,7 +87,7 @@ class ProductInboundService:
|
||||
base_id=material.id,
|
||||
global_print_id=next_global_id,
|
||||
sku=generated_sku,
|
||||
production_date=in_date_val,
|
||||
production_date=in_date_val, # 存入 DateTime
|
||||
barcode=final_barcode,
|
||||
serial_number=data.get('serial_number'),
|
||||
|
||||
@ -100,7 +109,6 @@ class ProductInboundService:
|
||||
|
||||
quality_status=data.get('quality_status', '合格'),
|
||||
|
||||
# 存为 JSON
|
||||
product_photo=json.dumps(photo_list),
|
||||
quality_report_link=json.dumps(quality_list),
|
||||
inspection_report_link=json.dumps(inspection_list),
|
||||
@ -136,7 +144,6 @@ class ProductInboundService:
|
||||
for f in fields:
|
||||
if f in data: setattr(stock, f, data[f])
|
||||
|
||||
# 更新 JSON 字段
|
||||
if 'product_photo' in data:
|
||||
imgs = data['product_photo']
|
||||
if isinstance(imgs, list): stock.product_photo = json.dumps(imgs)
|
||||
@ -188,9 +195,6 @@ class ProductInboundService:
|
||||
db.session.rollback()
|
||||
raise e
|
||||
|
||||
# ============================================================
|
||||
# 获取出库流转历史 (与 Buy 逻辑一致,关联 TransOutbound 表)
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def get_outbound_history(stock_id):
|
||||
"""获取出库历史"""
|
||||
@ -203,7 +207,7 @@ class ProductInboundService:
|
||||
return []
|
||||
|
||||
# ============================================================
|
||||
# 获取列表 (包含状态筛选与零库存隐藏逻辑)
|
||||
# 获取列表 (修改:按时间倒序排序 + 展示只显示日期)
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def get_list(page, limit, keyword=None, statuses=None):
|
||||
@ -211,7 +215,6 @@ class ProductInboundService:
|
||||
try:
|
||||
query = db.session.query(StockProduct).outerjoin(MaterialBase, StockProduct.base_id == MaterialBase.id)
|
||||
|
||||
# 1. 关键词搜索
|
||||
if keyword:
|
||||
query = query.filter(or_(
|
||||
MaterialBase.name.ilike(f'%{keyword}%'),
|
||||
@ -222,11 +225,9 @@ class ProductInboundService:
|
||||
StockProduct.sku.ilike(f'%{keyword}%')
|
||||
))
|
||||
|
||||
# 2. 状态筛选与零库存隐藏逻辑
|
||||
if not statuses:
|
||||
statuses = ['在库', '借库']
|
||||
|
||||
# 如果筛选包含'已出库',则显示所有数量;否则隐藏 stock_quantity <= 0 的记录
|
||||
if '已出库' in statuses:
|
||||
query = query.filter(StockProduct.status.in_(statuses))
|
||||
else:
|
||||
@ -237,7 +238,9 @@ class ProductInboundService:
|
||||
)
|
||||
)
|
||||
|
||||
pagination = query.order_by(StockProduct.id.desc()).paginate(page=page, per_page=limit, error_out=False)
|
||||
# [核心修改] 按照 production_date (入库日期) 倒序排序
|
||||
pagination = query.order_by(StockProduct.production_date.desc()).paginate(page=page, per_page=limit,
|
||||
error_out=False)
|
||||
|
||||
current_items = pagination.items
|
||||
|
||||
@ -252,20 +255,23 @@ class ProductInboundService:
|
||||
for item in current_items:
|
||||
d = item.to_dict()
|
||||
|
||||
# 直接使用当前行的库存,不再聚合
|
||||
# [核心修改] 格式化日期
|
||||
date_display = ''
|
||||
if item.production_date:
|
||||
try:
|
||||
date_display = item.production_date.strftime('%Y-%m-%d')
|
||||
except:
|
||||
date_display = str(item.production_date)[:10]
|
||||
d['inbound_date'] = date_display
|
||||
|
||||
d['qty_stock'] = float(item.stock_quantity or 0)
|
||||
d['qty_available'] = float(item.available_quantity or 0)
|
||||
|
||||
# 兼容前端字段 key
|
||||
d['sum_stock'] = d['qty_stock']
|
||||
d['sum_available'] = d['qty_available']
|
||||
|
||||
# 图片/链接解析
|
||||
d['product_photo'] = parse_img(item.product_photo)
|
||||
d['quality_report_link'] = parse_img(item.quality_report_link)
|
||||
d['inspection_report_link'] = parse_img(item.inspection_report_link)
|
||||
|
||||
# 打印ID
|
||||
d['global_print_id'] = item.global_print_id
|
||||
|
||||
items.append(d)
|
||||
|
||||
@ -41,7 +41,6 @@ class SemiInboundService:
|
||||
|
||||
@staticmethod
|
||||
def handle_inbound(data):
|
||||
# 局部导入 Model,解决循环引用
|
||||
from app.models.inbound.semi import StockSemi
|
||||
|
||||
try:
|
||||
@ -53,16 +52,21 @@ class SemiInboundService:
|
||||
if not material:
|
||||
raise ValueError(f"ID为 {base_id} 的基础物料不存在")
|
||||
|
||||
# 1. 处理入库日期
|
||||
in_date_val = datetime.utcnow().date()
|
||||
# [核心修改] 处理入库日期(production_date),包含时分秒
|
||||
current_time = datetime.now()
|
||||
in_date_val = current_time
|
||||
|
||||
if data.get('in_date'):
|
||||
try:
|
||||
date_str = str(data['in_date'])
|
||||
if len(date_str) > 10:
|
||||
date_str = date_str[:10]
|
||||
in_date_val = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||
in_date_val = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
d_temp = datetime.strptime(date_str, '%Y-%m-%d')
|
||||
in_date_val = datetime(d_temp.year, d_temp.month, d_temp.day,
|
||||
current_time.hour, current_time.minute, current_time.second)
|
||||
except ValueError:
|
||||
pass
|
||||
in_date_val = current_time
|
||||
|
||||
# 2. 处理生产时间
|
||||
p_start = None
|
||||
@ -102,18 +106,15 @@ class SemiInboundService:
|
||||
print("❌ 数据库序列 global_print_seq 不存在,请执行SQL创建!")
|
||||
raise e
|
||||
|
||||
# 5. 自动生成 SKU
|
||||
generated_sku = str(next_global_id).zfill(10)
|
||||
final_sku = data.get('sku')
|
||||
if not final_sku:
|
||||
final_sku = generated_sku
|
||||
|
||||
# 6. 条码逻辑处理
|
||||
final_barcode = data.get('barcode')
|
||||
if not final_barcode:
|
||||
final_barcode = final_sku
|
||||
|
||||
# 7. 图片列表转 JSON 字符串处理
|
||||
arrival_list = data.get('arrival_photo', [])
|
||||
quality_report_list = data.get('quality_report_link', [])
|
||||
|
||||
@ -125,7 +126,7 @@ class SemiInboundService:
|
||||
base_id=material.id,
|
||||
global_print_id=next_global_id,
|
||||
sku=final_sku,
|
||||
production_date=in_date_val,
|
||||
production_date=in_date_val, # 存入 DateTime
|
||||
|
||||
serial_number=data.get('serial_number'),
|
||||
batch_number=data.get('batch_number'),
|
||||
@ -151,7 +152,6 @@ class SemiInboundService:
|
||||
manual_cost=manual_cost,
|
||||
total_price=total_value,
|
||||
|
||||
# [核心修改] 将列表转为 JSON 字符串存储
|
||||
arrival_photo=json.dumps(arrival_list),
|
||||
quality_report_link=json.dumps(quality_report_list),
|
||||
|
||||
@ -174,8 +174,6 @@ class SemiInboundService:
|
||||
from app.models.inbound.semi import StockSemi
|
||||
|
||||
try:
|
||||
print(f"----- UPDATE SEMI DEBUG: ID={stock_id} -----")
|
||||
|
||||
stock = StockSemi.query.get(stock_id)
|
||||
if not stock:
|
||||
raise ValueError("记录不存在")
|
||||
@ -200,7 +198,6 @@ class SemiInboundService:
|
||||
if frontend_key in data:
|
||||
setattr(stock, db_attr, data[frontend_key])
|
||||
|
||||
# [核心修改] 图片字段更新 (List -> JSON String)
|
||||
if 'arrival_photo' in data:
|
||||
imgs = data['arrival_photo']
|
||||
if isinstance(imgs, list):
|
||||
@ -211,7 +208,6 @@ class SemiInboundService:
|
||||
if isinstance(imgs, list):
|
||||
stock.quality_report_link = json.dumps(imgs)
|
||||
|
||||
# 时间处理
|
||||
if 'production_start_time' in data:
|
||||
try:
|
||||
if data['production_start_time']:
|
||||
@ -232,7 +228,6 @@ class SemiInboundService:
|
||||
except:
|
||||
pass
|
||||
|
||||
# 更新 production_time_range 字符串
|
||||
if 'production_time_range' in data:
|
||||
raw_range = data['production_time_range']
|
||||
if isinstance(raw_range, list):
|
||||
@ -269,8 +264,6 @@ class SemiInboundService:
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
print(f"----- UPDATE SEMI FAILED: {str(e)} -----")
|
||||
traceback.print_exc()
|
||||
raise e
|
||||
|
||||
@staticmethod
|
||||
@ -287,9 +280,6 @@ class SemiInboundService:
|
||||
db.session.rollback()
|
||||
raise e
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# [核心修改] 获取关联出库历史 (跟 Buy 保持一致)
|
||||
# ------------------------------------------------------------------
|
||||
@staticmethod
|
||||
def get_outbound_history(stock_id):
|
||||
"""获取出库历史"""
|
||||
@ -301,9 +291,9 @@ class SemiInboundService:
|
||||
except:
|
||||
return []
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# [核心修改] 列表查询:支持状态筛选、默认隐藏0库存、去除聚合
|
||||
# ------------------------------------------------------------------
|
||||
# ============================================================
|
||||
# 6. 获取列表 (修改:按时间倒序排序 + 展示只显示日期)
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def get_list(page, limit, keyword=None, statuses=None):
|
||||
from app.models.inbound.semi import StockSemi
|
||||
@ -324,11 +314,9 @@ class SemiInboundService:
|
||||
)
|
||||
)
|
||||
|
||||
# [新增] 状态筛选与零库存隐藏逻辑
|
||||
if not statuses:
|
||||
statuses = ['在库', '借库']
|
||||
|
||||
# 如果筛选包含'已出库',则显示所有数量;否则隐藏 stock_quantity <= 0 的记录
|
||||
if '已出库' in statuses:
|
||||
query = query.filter(StockSemi.status.in_(statuses))
|
||||
else:
|
||||
@ -339,7 +327,9 @@ class SemiInboundService:
|
||||
)
|
||||
)
|
||||
|
||||
pagination = query.order_by(StockSemi.id.desc()).paginate(page=page, per_page=limit, error_out=False)
|
||||
# [核心修改] 按照 production_date (入库日期) 倒序排序
|
||||
pagination = query.order_by(StockSemi.production_date.desc()).paginate(page=page, per_page=limit,
|
||||
error_out=False)
|
||||
|
||||
current_items = pagination.items
|
||||
|
||||
@ -354,19 +344,22 @@ class SemiInboundService:
|
||||
for item in current_items:
|
||||
d = item.to_dict()
|
||||
|
||||
# 直接使用当前行的库存,不再聚合
|
||||
# [核心修改] 格式化展示日期,覆盖 to_dict 的默认行为
|
||||
date_display = ''
|
||||
if item.production_date:
|
||||
try:
|
||||
date_display = item.production_date.strftime('%Y-%m-%d')
|
||||
except:
|
||||
date_display = str(item.production_date)[:10]
|
||||
d['inbound_date'] = date_display
|
||||
|
||||
d['qty_stock'] = float(item.stock_quantity or 0)
|
||||
d['qty_available'] = float(item.available_quantity or 0)
|
||||
|
||||
# 兼容前端字段名
|
||||
d['sum_stock'] = d['qty_stock']
|
||||
d['sum_available'] = d['qty_available']
|
||||
|
||||
# 图片解析
|
||||
d['arrival_photo'] = parse_img(item.arrival_photo)
|
||||
d['quality_report_link'] = parse_img(item.quality_report_link)
|
||||
|
||||
# 打印ID
|
||||
d['global_print_id'] = item.global_print_id
|
||||
|
||||
items.append(d)
|
||||
|
||||
Reference in New Issue
Block a user