Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化

# Conflicts:
#	sf_manufacturing/security/ir.model.access.csv
This commit is contained in:
mgw
2025-01-09 16:14:23 +08:00
11 changed files with 89 additions and 125 deletions

View File

@@ -16,6 +16,7 @@
'data/stock_data.xml',
'data/empty_racks_data.xml',
'data/panel_data.xml',
'data/sf_work_individuation_page.xml',
'data/agv_scheduling_data.xml',
'security/group_security.xml',
'security/ir.model.access.csv',

View File

@@ -0,0 +1,8 @@
<odoo>
<data noupdate="1">
<record model="sf.work.individuation.page" id="sf_work_individuation_page_1">
<field name="code">PTD</field>
<field name="name">后置三元检测</field>
</record>
</data>
</odoo>

View File

@@ -459,7 +459,7 @@ class MrpProduction(models.Model):
process_parameters.append(special.process_parameters_id.display_name)
if account_moves:
raise UserError(_("请联系工厂生产经理对采购订单%s生成的账单进行取消", ", ".join(account_moves)))
raise UserError(_("请联系工厂生产经理对会计凭证%s生成的账单进行取消", ", ".join([move.name for move in account_moves])))
if parameters_not:
raise UserError(_("【工艺设计】-【工序】为%s未选择参数,请选择", ", ".join(parameters_not)))
if process_parameters:
@@ -1189,7 +1189,8 @@ class MrpProduction(models.Model):
'res_model': 'sf.production.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_mrp_production_id': self.id,
'is_remanufacture_flag': True,
'default_reprogramming_num': cloud_programming['reprogramming_num'],
'default_programming_states': cloud_programming['programming_state'],
'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False
@@ -1348,10 +1349,6 @@ class MrpProduction(models.Model):
if self.is_scrap is True:
procurement_requests = []
sale_order = self.env['sale.order'].sudo().search([('name', '=', self.origin)])
values = self.env['mrp.production'].create_production1_values(self)
# productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(
# self.company_id).create(
# values)
# 查询出库移动记录
out_picking = self.env['stock.picking'].search(
[('origin', '=', sale_order.name), ('name', 'ilike', 'WH/OUT/')])
@@ -1375,31 +1372,14 @@ class MrpProduction(models.Model):
productions = self.env['mrp.production'].sudo().search(
[('origin', '=', self.origin)], order='id desc', limit=1)
productions.write({'programming_no': self.programming_no, 'is_remanufacture': True})
move = self.env['stock.move'].search([('origin', '=', productions.name)], order='id desc')
for mo in move:
domain = []
if mo.location_id.barcode == 'WH-POSTPRODUCTION' and mo.rule_id.picking_type_id.barcode == 'PC':
domain = [('barcode', '=', 'WH-PC'), ('sequence_code', '=', 'PC')]
elif mo.location_id.barcode == 'PL' and mo.rule_id.picking_type_id.barcode == 'INT':
domain = [('barcode', '=', 'WH-INTERNAL'), ('sequence_code', '=', 'INT')]
if domain:
picking_type = self.env['stock.picking.type'].search(domain)
mo.write({'picking_type_id': picking_type.id})
mo._assign_picking()
else:
if mo.reference != productions.name:
mo.reference = productions.name
if mo.production_id:
if mo.production_id != productions:
mo.production_id = False
mo_move = self.env['stock.move'].search(
[('origin', '=', sale_order.name), ('reference', 'ilike', 'WH/MO/')])
if mo_move:
sfp_move = self.env['stock.move'].search(
[('origin', '=', sale_order.name), ('reference', 'ilike', 'WH/SFP/')], limit=1)
mo_move.write({'reference': sfp_move.reference, 'partner_id': sfp_move.partner_id.id,
'picking_id': sfp_move.picking_id.id, 'picking_type_id': sfp_move.picking_type_id.id,
'production_id': False})
# mo_move = self.env['stock.move'].search(
# [('origin', '=', sale_order.name), ('reference', 'ilike', 'WH/MO/')])
# if mo_move:
# sfp_move = self.env['stock.move'].search(
# [('origin', '=', sale_order.name), ('reference', 'ilike', 'WH/SFP/')], limit=1)
# mo_move.write({'reference': sfp_move.reference, 'partner_id': sfp_move.partner_id.id,
# 'picking_id': sfp_move.picking_id.id, 'picking_type_id': sfp_move.picking_type_id.id,
# 'production_id': False})
# productions.procurement_group_id.mrp_production_ids.move_dest_ids.write(
# {'group_id': self.env['procurement.group'].search([('name', '=', sale_order.name)])})
stock_picking_remanufacture = self.env['stock.picking'].search([('origin', '=', productions.name)])
@@ -1500,7 +1480,7 @@ class MrpProduction(models.Model):
"""
重载创建制造订单的方法,单个制造订单,同一成品只创建一个采购组,用于后续单据的创建
"""
product_group_id = {}
group_id = self.env["procurement.group"].create({'name':vals_list[0].get('origin')}).id
for vals in vals_list:
if not vals.get('name', False) or vals['name'] == _('New'):
picking_type_id = vals.get('picking_type_id')
@@ -1509,15 +1489,7 @@ class MrpProduction(models.Model):
vals['picking_type_id'] = picking_type_id
vals['name'] = self.env['stock.picking.type'].browse(picking_type_id).sequence_id.next_by_id()
if not vals.get('procurement_group_id'):
product_id = self.env['product.product'].browse(vals['product_id'])
if product_id.product_tmpl_id.single_manufacturing:
if product_id.id not in product_group_id.keys():
procurement_group_vals = self._prepare_procurement_group_vals(vals)
group_id = self.env["procurement.group"].create(procurement_group_vals).id
vals['procurement_group_id'] = group_id
product_group_id[product_id.id] = group_id
else:
vals['procurement_group_id'] = product_group_id[product_id.id]
vals['procurement_group_id'] = group_id
return super(MrpProduction, self).create(vals_list)
@api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id',
@@ -1540,7 +1512,7 @@ class MrpProduction(models.Model):
@api.depends('procurement_group_id', 'procurement_group_id.stock_move_ids.group_id')
def _compute_picking_ids(self):
for order in self:
if order.product_id.product_tmpl_id.single_manufacturing == True:
if order.product_id.product_tmpl_id.single_manufacturing == True and not order.is_remanufacture:
first_order = self.env['mrp.production'].search(
[('origin', '=', order.origin), ('product_id', '=', order.product_id.id)], limit=1, order='id asc')
order.picking_ids = self.env['stock.picking'].search([

View File

@@ -25,6 +25,8 @@ class ResMrpRoutingWorkcenter(models.Model):
bom_id = fields.Many2one('mrp.bom', required=False)
surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺")
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
is_outsource = fields.Boolean('外协', default=False)
individuation_page = fields.Many2many('sf.work.individuation.page', string='个性化记录')
def get_no(self):
international_standards = self.search(
@@ -101,3 +103,10 @@ class ResMrpRoutingWorkcenter(models.Model):
domain = args + [('id', 'not in', route_workcenter_ids)]
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
return super()._name_search(name, args, operator, limit, name_get_uid)
class WorkIndividuationPage(models.Model):
_name = 'sf.work.individuation.page'
code = fields.Char('编号')
name = fields.Char('名称')

View File

@@ -130,7 +130,7 @@ class ResMrpWorkOrder(models.Model):
Y10_axis = fields.Float(default=0)
Z10_axis = fields.Float(default=0)
X_deviation_angle = fields.Integer(string="X轴偏差度", default=0)
test_results = fields.Selection([("合格", "合格"), ("返工", "返工")], default='合格',
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], default='合格',
string="检测结果", tracking=True)
cnc_ids = fields.One2many("sf.cnc.processing", 'workorder_id', string="CNC加工程序")
cmm_ids = fields.One2many("sf.cmm.program", 'workorder_id', string="CMM程序")
@@ -275,31 +275,18 @@ class ResMrpWorkOrder(models.Model):
def _compute_surface_technics_purchase_ids(self):
for order in self:
if order.routing_type == '表面工艺' and order.state not in ['cancel']:
# if order.production_id.production_type == '自动化产线加工':
# domain = [('programming_no', '=', order.production_id.programming_no)]
# else:buzhdiao
# domain = [('origin', '=', order.production_id.origin)]
# production_programming = self.env['mrp.production'].search(domain, order='name asc')
# production_list = [production.name for production in production_programming]
# production_no_remanufacture = production_programming.filtered(lambda a: a.is_remanufacture is False)
# technology_design = self.env['sf.technology.design'].search(
# [('process_parameters_id', '=', order.surface_technics_parameters_id.id),
# ('production_id', '=', order.production_id.id)])
# if technology_design.is_auto is False:
# domain = [('origin', '=', order.production_id.name)]
# else:
domain = [('purchase_type', '=', 'consignment'), ('origin', '=', order.production_id.name),
domain = [('purchase_type', '=', 'consignment'),
('origin', 'like', '%' + self.production_id.name + '%'),
('state', '!=', 'cancel')]
purchase = self.env['purchase.order'].search(domain)
purchase_num = 0
order.surface_technics_purchase_count = 0
if not purchase:
order.surface_technics_purchase_count = 0
for po in purchase:
for line in po.order_line:
if line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id:
if line.product_qty == 1:
purchase_num += 1
order.surface_technics_purchase_count = purchase_num
if any(
line.product_id and line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id
for line in po.order_line):
order.surface_technics_purchase_count = 1
else:
order.surface_technics_purchase_count = 0
@@ -1297,7 +1284,7 @@ class ResMrpWorkOrder(models.Model):
raise UserError('请先完成该工单的工艺外协再进行操作')
# 表面工艺外协,最后一张工单
workorders = self.production_id.workorder_ids
subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True).sorted('sequence')
subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
if self == subcontract_workorders[-1]:
# 给下一个库存移动就绪
self.move_subcontract_workorder_ids[0].move_dest_ids._action_done()
@@ -1321,8 +1308,10 @@ class ResMrpWorkOrder(models.Model):
is_production_id = False
rework_workorder = record.production_id.workorder_ids.filtered(lambda p: p.state == 'rework')
done_workorder = record.production_id.workorder_ids.filtered(lambda p1: p1.state in ['done'])
if (len(rework_workorder) + len(done_workorder) == len(record.production_id.workorder_ids)) or (
len(done_workorder) == len(record.production_id.workorder_ids)):
if (len(rework_workorder) + len(done_workorder) == len(
record.production_id.workorder_ids.filtered(lambda wo: wo.state != 'cancel'))) or (
len(done_workorder) == len(
record.production_id.workorder_ids.filtered(lambda wo: wo.state != 'cancel'))):
is_production_id = True
if record.routing_type in ['解除装夹'] or (
record.is_rework is True and record.routing_type in ['装夹预调']):

View File

@@ -72,35 +72,6 @@ class StockRule(models.Model):
moves_values_by_company = defaultdict(list)
mtso_products_by_locations = defaultdict(list)
# To handle the `mts_else_mto` procure method, we do a preliminary loop to
# isolate the products we would need to read the forecasted quantity,
# in order to to batch the read. We also make a sanitary check on the
# `location_src_id` field.
# list1 = []
# for item in procurements:
# num = int(item[0].product_qty)
# if num > 1:
# for no in range(1, num+1):
#
# Procurement = namedtuple('Procurement', ['product_id', 'product_qty',
# 'product_uom', 'location_id', 'name', 'origin',
# 'company_id',
# 'values'])
# s = Procurement(product_id=item[0].product_id,product_qty=1.0,product_uom=item[0].product_uom,
# location_id=item[0].location_id,
# name=item[0].name,
# origin=item[0].origin,
# company_id=item[0].company_id,
# values=item[0].values,
# )
# item1 = list(item)
# item1[0]=s
#
# list1.append(tuple(item1))
# else:
# list1.append(item)
for procurement, rule in procurements:
if not rule.location_src_id:
msg = _('No source location defined on stock rule: %s!') % (rule.name,)
@@ -618,15 +589,21 @@ class StockPicking(models.Model):
item.address_of_delivery = sale_info.address_of_delivery
# 设置外协出入单的名称
def _get_name_Res(self, rescode):
last_picking = self.sudo().search([('name', 'ilike', rescode)], order='create_date desc,id desc', limit=1)
if not last_picking:
num = "%04d" % 1
def _get_name_Res(self, rescode,sequence):
last_picking = self.sudo().search([('name', 'ilike', rescode)], order='name desc', limit=1)
sequence_id = sequence.next_by_id()
name_without_prefix = last_picking.name.removeprefix(rescode)
try:
name_value = int(name_without_prefix) # 假设 name 是一个数字字符串
except ValueError:
name_value = 0
if name_value >= int(sequence_id.removeprefix(rescode)):
sequence.write({
'number_next': name_value + 1,
})
return sequence.next_by_id()
else:
logging.info('编号:' + last_picking.name)
m = int(last_picking.name[-3:]) + 1
num = "%04d" % m
return '%s%s' % (rescode, num)
return sequence_id
def button_validate(self):
res = super().button_validate()
@@ -787,12 +764,15 @@ class ReStockMove(models.Model):
def _get_new_picking_values_Res(self, item, sorted_workorders, rescode):
picking_type_id = self.mapped('picking_type_id').id
sequence = False
if rescode == 'WH/OCOUT/':
picking_type_id = self.env.ref('sf_manufacturing.outcontract_picking_out').id
sequence = self.env.ref('sf_manufacturing.sequence_stock_picking_out')
elif rescode == 'WH/OCIN/':
picking_type_id = self.env.ref('sf_manufacturing.outcontract_picking_in').id
sequence = self.env.ref('sf_manufacturing.sequence_stock_picking_in')
return {
'name': self.env['stock.picking']._get_name_Res(rescode),
'name': self.env['stock.picking']._get_name_Res(rescode,sequence),
'origin': item.name,
'surface_technics_parameters_id': sorted_workorders.surface_technics_parameters_id.id,
'company_id': self.mapped('company_id').id,
@@ -1031,12 +1011,7 @@ class ReStockMove(models.Model):
"""
res = super(ReStockMove, self)._get_new_picking_values()
## 制造订单报废生成的新制造订单不走合并
production_remanufacture = None
if 'origin' in res:
if self.picking_type_id.name in ['生产发料', '内部调拨']:
production_remanufacture = self.env['mrp.production'].search(
[('name', '=', res['origin']), ('is_remanufacture', '=', True)])
if not production_remanufacture:
if not self.env.context.get('is_remanufacture_flag'):
if self[0].origin and self.picking_type_id.name in ['生产发料', '内部调拨']:
production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc')
productions = self.env['mrp.production'].search(

View File

@@ -189,4 +189,6 @@ access_mrp_workorder_group_purchase_director,mrp_workorder,model_mrp_workorder,s
access_mrp_workorder_group_purchase,mrp_workorder,model_mrp_workorder,sf_base.group_purchase,1,1,0,0
access_sf_programming_reason,sf_programming_reason,model_sf_programming_reason,base.group_user,1,1,1,0
access_sf_programming_record,sf_programming_record,model_sf_programming_record,base.group_user,1,1,1,0
access_sf_programming_record,sf_programming_record,model_sf_programming_record,base.group_user,1,1,1,0
access_sf_work_individuation_page,sf_work_individuation_page,model_sf_work_individuation_page,sf_base.group_sf_mrp_user,1,1,1,0
access_sf_work_individuation_page_group_plan_dispatch,sf_work_individuation_page_group_plan_dispatch,model_sf_work_individuation_page,sf_base.group_plan_dispatch,1,1,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
189
190
191
192
193
194

View File

@@ -17,6 +17,8 @@
<field name="bom_product_template_attribute_value_ids" position="after">
<field name="routing_type" required="1"/>
<field name="routing_tag" required="1" string="工序标签"/>
<field name="is_outsource"/>
<field name="individuation_page" widget="many2many_tags" options="{'create': False}"/>
<field name="is_repeat"/>
<field name="reserved_duration"/>
</field>

View File

@@ -12,7 +12,7 @@ class ProductionWizard(models.TransientModel):
_name = 'sf.production.wizard'
_description = '制造订单向导'
production_id = fields.Many2one('mrp.production', string='制造订单号')
mrp_production_id = fields.Many2one('mrp.production', string='制造订单号')
reprogramming_num = fields.Integer('重新编程次数', default=0)
is_reprogramming = fields.Boolean(string='申请重新编程', default=False)
is_remanufacture = fields.Boolean(string='重新生成制造订单', default=True)
@@ -27,24 +27,30 @@ class ProductionWizard(models.TransientModel):
self.is_reprogramming = False
def confirm(self):
self.production_id.detection_result_ids.write({'handle_result': '已处理'})
self.production_id.write({'state': 'cancel', 'scrap_ids': [(0, 0, {
self.mrp_production_id.detection_result_ids.write({'handle_result': '已处理'})
self.mrp_production_id.write({'state': 'cancel', 'scrap_ids': [(0, 0, {
'name': self.env['ir.sequence'].next_by_code('stock.scrap') or _('New'),
'product_id': self.production_id.product_id.id,
'product_id': self.mrp_production_id.product_id.id,
'scrap_qty': 1,
'origin': self.production_id.origin,
'origin': self.mrp_production_id.origin,
'date_done': fields.datetime.now(),
'lot_id': self.env['stock.move.line'].search(
[('move_id', '=', self.production_id.move_raw_ids[0].id)]).lot_id.id,
'location_id': self.production_id.move_raw_ids.filtered(lambda x: x.state not in (
[('move_id', '=', self.mrp_production_id.move_raw_ids[0].id)]).lot_id.id,
'location_id': self.mrp_production_id.move_raw_ids.filtered(lambda x: x.state not in (
'done',
'cancel')) and self.production_id.location_src_id.id or self.production_id.location_dest_id.id,
'cancel')) and self.mrp_production_id.location_src_id.id or self.mrp_production_id.location_dest_id.id,
'scrap_location_id': self.env['stock.scrap']._get_default_scrap_location_id(),
'state': 'done'})]})
self.production_id.action_cancel()
self.mrp_production_id.action_cancel()
# 解绑rfid_code
mrp_workorder_list = self.mrp_production_id.workorder_ids.filtered(lambda kw: kw.rfid_code)
for workorder in mrp_workorder_list:
rfid_code = workorder.rfid_code
workorder.write({'rfid_code_old': rfid_code,
'rfid_code': False})
if self.is_remanufacture is True:
ret = {'programming_list': [], 'is_reprogramming': self.is_reprogramming}
if self.is_reprogramming is True:
self.production_id.update_programming_state()
new_production = self.production_id.recreateManufacturing(ret)
self.production_id.write({'remanufacture_production_id': new_production.id})
self.mrp_production_id.update_programming_state()
new_production = self.mrp_production_id.recreateManufacturing(ret)
self.mrp_production_id.write({'remanufacture_production_id': new_production.id})

View File

@@ -6,7 +6,7 @@
<field name="arch" type="xml">
<form>
<sheet>
<field name="production_id" invisible="1"/>
<field name="mrp_production_id" invisible="1"/>
<field name="programming_states" invisible="1"/>
<div>
重新生成制造订单

View File

@@ -25,7 +25,7 @@ class SfQualityCncTest(models.Model):
('pass', '合格'),
('fail', '不合格')], string='判定结果')
number = fields.Integer('数量', default=1)
test_results = fields.Selection([("合格", "合格"), ("返工", "返工")], string="检测结果")
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], string="检测结果")
reason = fields.Selection(
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"),
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因")