Merge pull request !9052 from 李鹏宇/dev_wang
This commit is contained in:
李鹏宇
2026-02-27 10:19:18 +00:00
committed by Gitee
5 changed files with 182 additions and 43 deletions
@@ -100,19 +100,49 @@ class InheritMaintenanceEquipment(models.Model):
workbook = load_workbook(template_path)
sheet = workbook.active
# 统计导出数据中最常见的年月(用于标题)
year_month_list = []
for record in equipment_records:
if record.purchase_date:
year_month_list.append((record.purchase_date.year, record.purchase_date.month))
if year_month_list:
# 取出现次数最多的年月
from collections import Counter
most_common_ym = Counter(year_month_list).most_common(1)[0][0]
title_year, title_month = most_common_ym
else:
# 如果没有购入日期,使用当前年月
now = datetime.datetime.now()
title_year, title_month = now.year, now.month
# 修改A1单元格:生成动态标题
title_text = f'{title_year}年{title_month}月固定资产盘点表'
sheet['A1'] = title_text
sheet['A1'].alignment = Alignment(horizontal='left', vertical='center')
# 修改K1单元格:生成制表人信息(直接替换,不追加)
current_date = datetime.datetime.now().strftime('%Y-%m-%d')
k1_text = f'制表人:叶华锋 {current_date}'
sheet['K1'] = k1_text
sheet['K1'].alignment = Alignment(horizontal='left', vertical='center')
# 从第3行开始写入数据(第1-2行是表头)
row = 3
# 设置列宽(A列序号列设置为较窄,C列型号列也设置较窄)
sheet.column_dimensions['A'].width = 6 # 序号列宽度
sheet.column_dimensions['C'].width = 12 # 型号列宽度
# 设置列宽
sheet.column_dimensions['A'].width = 6 # 序号列
sheet.column_dimensions['B'].width = 20 # 资产编号列(加宽)
sheet.column_dimensions['C'].width = 30 # 设备名称列(加宽)
sheet.column_dimensions['D'].width = 12 # 型号列
sheet.column_dimensions['K'].width = 25 # 制表人列(确保内容完整显示)
# 获取模板样式行(使用第3行作为参考)
template_row_idx = 3
if sheet.max_row >= template_row_idx:
# 先保存第3行的样式
template_styles = {}
for col in range(1, 11): # A到J列(10列)
for col in range(1, 12): # A到K列(11列)
cell = sheet.cell(row=template_row_idx, column=col)
template_styles[col] = {
'font': copy(cell.font),
@@ -132,54 +162,57 @@ class InheritMaintenanceEquipment(models.Model):
for merged_range_str in merged_cells_to_unmerge:
sheet.unmerge_cells(merged_range_str)
# 先为所有列应用模板样式
for col in range(1, 11): # A到J列
# 先为所有列应用模板样式并统一居中对齐
for col in range(1, 12): # A到K列
cell = sheet.cell(row=row, column=col)
if col in template_styles:
cell.font = copy(template_styles[col]['font'])
cell.fill = copy(template_styles[col]['fill'])
cell.border = copy(template_styles[col]['border'])
# 统一设置居中对齐
cell.alignment = Alignment(horizontal='center', vertical='center')
# 统一设置居中对齐
cell.alignment = Alignment(horizontal='center', vertical='center')
# A列:序号(从1开始)
sheet.cell(row=row, column=1, value=index)
# B列:设备名称
sheet.cell(row=row, column=2, value=record.name or '')
# B列:资产编号
sheet.cell(row=row, column=2, value=record.serial_no or '')
# C列:型号
sheet.cell(row=row, column=3, value=record.model or '')
# C列:设备名称
sheet.cell(row=row, column=3, value=record.name or '')
# D列:原值
sheet.cell(row=row, column=4, value=record.cost or 0)
# D列:型号
sheet.cell(row=row, column=4, value=record.model or '')
# E列:寿命
# E列:原值
sheet.cell(row=row, column=5, value=record.cost or 0)
# F列:寿命
warranty_date = ''
if record.warranty_date:
warranty_date = record.warranty_date.strftime('%Y-%m-%d')
sheet.cell(row=row, column=5, value=warranty_date)
sheet.cell(row=row, column=6, value=warranty_date)
# F列:设备状态
# G列:设备状态
mainten_state = ''
if record.mainten_state:
mainten_state = dict(record._fields['mainten_state'].selection).get(record.mainten_state, '')
sheet.cell(row=row, column=6, value=mainten_state)
sheet.cell(row=row, column=7, value=mainten_state)
# G列:员工
sheet.cell(row=row, column=7, value=record.employee_id.name if record.employee_id else '')
# H列:员工
sheet.cell(row=row, column=8, value=record.employee_id.name if record.employee_id else '')
# H列:部门
sheet.cell(row=row, column=8, value=record.department_id1.name if record.department_id1 else '')
# I列:部门
sheet.cell(row=row, column=9, value=record.department_id1.name if record.department_id1 else '')
# I列:设备类别
sheet.cell(row=row, column=9, value=record.category_id.name if record.category_id else '')
# J列:设备类别
sheet.cell(row=row, column=10, value=record.category_id.name if record.category_id else '')
# J列:公司(company 字段)
# K列:公司(company 字段)
company = ''
if record.company:
company = dict(record._fields['company'].selection).get(record.company, '')
sheet.cell(row=row, column=10, value=company)
sheet.cell(row=row, column=11, value=company)
# 设置最小行高,确保内容显示完整
if not sheet.row_dimensions[row].height or sheet.row_dimensions[row].height < 30:
@@ -193,8 +226,8 @@ class InheritMaintenanceEquipment(models.Model):
output.seek(0)
excel_data = base64.b64encode(output.read())
# 创建附件
filename = f'固定资产盘点表_{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}.xlsx'
# 创建附件(文件名只包含日期,不含时分秒)
filename = f'固定资产盘点表_{datetime.datetime.now().strftime("%Y%m%d")}.xlsx'
attachment = self.env['ir.attachment'].create({
'name': filename,
'type': 'binary',
@@ -26,3 +26,5 @@ class ProductProduct(models.Model):
class StockMove(models.Model):
_inherit = 'stock.move'
+101 -6
View File
@@ -38,9 +38,20 @@ class StockPicking(models.Model):
)
available_product_ids = fields.Many2many(
'product.product',
'stock_picking_product_available_rel',
'picking_id',
'product_id',
compute='_compute_available_product_ids',
string="可用产品"
)
available_location_ids = fields.Many2many(
'stock.location',
'stock_picking_location_available_rel',
'picking_id',
'location_id',
compute='_compute_available_location_ids',
string="可用仓库"
)
can_certify = fields.Boolean(
string="可以证明",
compute='_compute_can_certify',
@@ -81,19 +92,39 @@ class StockPicking(models.Model):
})
return result
@api.depends('location_id')
@api.depends('location_id', 'picking_type_code')
def _compute_available_product_ids(self):
"""根据仓库计算可用产品列表"""
"""根据仓库和作业类型计算可用产品列表"""
for record in self:
if record.location_id:
# 查询该仓库中有库存的产品
# 如果是入库单,返回所有产品(不限制)
if record.picking_type_code == 'incoming':
record.available_product_ids = self.env['product.product'].search([])
# 如果是借用/领用等,只返回该仓库中有库存的产品
elif record.location_id:
quants = self.env['stock.quant'].search([
('location_id', '=', record.location_id.id)
('location_id', '=', record.location_id.id),
('quantity', '>', 0) # 只查询有库存的
])
record.available_product_ids = quants.mapped('product_id')
else:
record.available_product_ids = False
@api.depends('picking_type_code')
def _compute_available_location_ids(self):
"""根据作业类型计算可用仓库列表"""
for record in self:
# 如果是入库单,不限制仓库选择
if record.picking_type_code == 'incoming':
record.available_location_ids = self.env['stock.location'].search([])
# 如果是借用/领用等,只显示有库存的仓库
else:
quants = self.env['stock.quant'].search([
('quantity', '>', 0)
])
record.available_location_ids = quants.mapped('location_id')
@api.onchange('user_id')
def _onchange_user_id(self):
if self.user_id:
@@ -173,9 +204,73 @@ class StockPicking(models.Model):
record._compute_json_display()
return True
def action_update_name_by_products(self):
"""批量更新入库单名称(根据产品生成)"""
updated_count = 0
for picking in self:
# 只处理入库单
if picking.picking_type_code == 'incoming' and picking.move_ids_without_package:
product_names = picking.move_ids_without_package.mapped('product_id.name')
if product_names:
products_str = '/'.join(product_names)
# 添加入库单ID确保唯一性
new_name = f"{products_str}的入库单-{picking.id}"
picking.write({'name': new_name})
updated_count += 1
# 返回提示消息
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': '更新完成',
'message': f'已成功更新 {updated_count} 条入库单的名称',
'type': 'success',
'sticky': False,
}
}
def action_acceptance(self):
"""验收按钮"""
self.ensure_one()
self.acceptance_date = fields.Datetime.now()
self.receipt_status = 'certification' # 状态变为证明
return True
return True
@api.model_create_multi
def create(self, vals_list):
"""重写create方法,根据产品生成入库单名称"""
pickings = super(StockPicking, self).create(vals_list)
for picking in pickings:
# 只处理入库单(picking_type_code为incoming)
if picking.picking_type_code == 'incoming' and picking.move_ids_without_package:
# 获取所有产品名称
product_names = picking.move_ids_without_package.mapped('product_id.name')
if product_names:
# 用/连接产品名称
products_str = '/'.join(product_names)
# 生成新的name格式:产品/产品/产品的入库单-ID(确保唯一性)
new_name = f"{products_str}的入库单-{picking.id}"
# 更新name字段
picking.write({'name': new_name})
return pickings
def write(self, vals):
"""重写write方法,当产品变化时更新入库单名称"""
result = super(StockPicking, self).write(vals)
# 如果修改了move_ids相关字段,更新name
if 'move_ids_without_package' in vals or 'move_ids' in vals:
for picking in self:
if picking.picking_type_code == 'incoming' and picking.move_ids_without_package:
product_names = picking.move_ids_without_package.mapped('product_id.name')
if product_names:
products_str = '/'.join(product_names)
new_name = f"{products_str}的入库单-{picking.id}"
# 避免递归调用,使用super().write
if picking.name != new_name:
super(StockPicking, picking).write({'name': new_name})
return result
+18 -9
View File
@@ -8,8 +8,11 @@
<xpath expr="//field[@name='picking_type_id']" position="after">
<field name="department_id" options="{'no_create': True}" invisible="1"/>
</xpath>
<xpath expr="//field[@name='location_id'][@groups='stock.group_stock_multi_locations']" position="attributes">
<attribute name="domain">[('id', 'in', available_location_ids)]</attribute>
</xpath>
<xpath expr="//field[@name='picking_type_id']" position="attributes">
<attribute name="readonly">receipt_status in ['acceptance', 'certification']</attribute>
<attribute name="readonly">state != 'draft' and receipt_status in ['acceptance', 'certification']</attribute>
</xpath>
<xpath expr="//field[@name='origin']" position="attributes">
<attribute name="invisible">1</attribute>
@@ -18,6 +21,7 @@
<field name="purchase_id" string="源单据" readonly="1" options="{'no_create': True, 'no_open': False}"/>
<field name="user_id" string="采购人员"/>
<field name="available_product_ids" invisible="1"/>
<field name="available_location_ids" invisible="1"/>
<field name="state" invisible="1"/>
<field name="can_certify" invisible="1"/>
</xpath>
@@ -31,14 +35,14 @@
<xpath expr="//page[@name='operations']/field[@name='move_ids_without_package']" position="after">
<group>
<group>
<field name="employee_id" string="验收人" required="1" options="{'no_create': True}" readonly="receipt_status == 'certification'"/>
<field name="document_ids" widget="preview_many2many" readonly="receipt_status == 'certification'"/>
<field name="acceptance_date" readonly="receipt_status == 'certification'"/>
<field name="employee_id" string="验收人" required="1" options="{'no_create': True}" readonly="state != 'draft' and receipt_status == 'certification'"/>
<field name="document_ids" widget="preview_many2many" readonly="state != 'draft' and receipt_status == 'certification'"/>
<field name="acceptance_date" readonly="state != 'draft' and receipt_status == 'certification'"/>
</group>
<group>
<field name="certifier_id" options="{'no_create': True}" readonly="receipt_status == 'acceptance'"/>
<field name="certifier_document_ids" widget="preview_many2many" readonly="receipt_status == 'acceptance'"/>
<field name="certification_date" readonly="receipt_status == 'acceptance'"/>
<field name="certifier_id" options="{'no_create': True}" readonly="state != 'draft' and receipt_status == 'acceptance'"/>
<field name="certifier_document_ids" widget="preview_many2many" readonly="state != 'draft' and receipt_status == 'acceptance'"/>
<field name="certification_date" readonly="state != 'draft' and receipt_status == 'acceptance'"/>
</group>
</group>
</xpath>
@@ -66,10 +70,10 @@
</xpath>
<xpath expr="//field[@name='partner_id']" position="attributes">
<attribute name="string">供应商</attribute>
<attribute name="readonly">receipt_status in ['acceptance', 'certification']</attribute>
<attribute name="readonly">state != 'draft' and receipt_status in ['acceptance', 'certification']</attribute>
</xpath>
<xpath expr="//field[@name='scheduled_date']" position="attributes">
<attribute name="readonly">receipt_status in ['acceptance', 'certification']</attribute>
<attribute name="readonly">state != 'draft' and receipt_status in ['acceptance', 'certification']</attribute>
</xpath>
<xpath expr="(//field[@name='state'][@widget='statusbar'])[1]" position="replace">
<field name="receipt_status" widget="statusbar" statusbar_visible="acceptance,certification,receipt" invisible="picking_type_code != 'incoming'"/>
@@ -84,6 +88,11 @@
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.vpicktree"/>
<field name="arch" type="xml">
<xpath expr="//tree/header" position="inside">
<button name="action_update_name_by_products" type="object" string="根据产品更新名称"
class="btn-secondary"
help="将选中的入库单名称更新为:产品1/产品2/产品3的入库单"/>
</xpath>
<xpath expr="//field[@name='name']" position="attributes">
<attribute name="string">编号</attribute>
</xpath>