Compare commits

..

14 Commits

Author SHA1 Message Date
liaodanlong
9f67dbe9aa 工序外协工单没有自动触发工单开始、结束的优化需求处理 2024-12-12 16:06:55 +08:00
liaodanlong
ab32f881c2 制造订单退回调整删除表面工艺再次确认后-没被删除的表面工艺的外协出入库单变成了草稿状态问题处理 2024-12-11 17:09:51 +08:00
liaodanlong
f32ca13dff 修改日志的使用库 2024-12-11 11:05:25 +08:00
liaodanlong
d1ba2cc3d4 Merge branch 'refs/heads/develop' into feature/delivery_status
# Conflicts:
#	sf_manufacturing/models/mrp_production.py
2024-12-11 10:07:34 +08:00
liaodanlong
4738b03a45 销售订单与制造订单关联 2024-12-11 10:03:20 +08:00
禹翔辉
e14e750bd9 Accept Merge Request #1610: (feature/返工入口 -> develop)
Merge Request: 开放返工入口

Created By: @禹翔辉
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1610?initial=true
2024-12-09 16:08:52 +08:00
yuxianghui
1c4a6ca85e 开放返工入口 2024-12-09 16:07:34 +08:00
管欢
c922ae1571 Accept Merge Request #1609: (feature/manufactur_order -> develop)
Merge Request: 手动创建的采购单生成的内部调拨单没有源单据

Created By: @管欢
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @管欢
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1609
2024-12-09 09:48:12 +08:00
guanhuan
ed6189a963 Merge remote-tracking branch 'origin/feature/manufactur_order' into feature/manufactur_order 2024-12-09 09:36:08 +08:00
guanhuan
a6979b213b 手动创建的采购单生成的内部调拨单没有源单据 2024-12-09 09:35:41 +08:00
马广威
f873ebe079 Accept Merge Request #1608: (release/release_2.6 -> develop)
Merge Request: Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1608
2024-12-05 18:26:29 +08:00
马广威
e50b601ccf Accept Merge Request #1607: (feature/制造功能优化 -> develop)
Merge Request: 对合并后的采购单修改匹配逻辑

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1607?initial=true
2024-12-05 18:15:09 +08:00
mgw
37c47b0a54 对合并后的采购单修改匹配逻辑 2024-12-05 18:14:19 +08:00
liaodanlong
73ab43f16a 人工线下加工 2024-11-28 10:05:17 +08:00
8 changed files with 114 additions and 19 deletions

View File

@@ -15,3 +15,4 @@ from . import sf_technology_design
from . import sf_production_common from . import sf_production_common
from . import sale_order from . import sale_order
from . import quick_easy_order from . import quick_easy_order
from . import purchase_order

View File

@@ -8,6 +8,7 @@ import re
import requests import requests
from itertools import groupby from itertools import groupby
from collections import defaultdict, namedtuple from collections import defaultdict, namedtuple
from odoo import api, fields, models, SUPERUSER_ID, _ from odoo import api, fields, models, SUPERUSER_ID, _
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_base.commons.common import Common from odoo.addons.sf_base.commons.common import Common
@@ -18,6 +19,7 @@ class MrpProduction(models.Model):
_inherit = 'mrp.production' _inherit = 'mrp.production'
_description = "制造订单" _description = "制造订单"
_order = 'create_date desc' _order = 'create_date desc'
sale_order_id = fields.Many2one('sale.order', string='销售订单', compute='_compute_sale_order_id', store=True)
deadline_of_delivery = fields.Date('订单交期', tracking=True, compute='_compute_deadline_of_delivery') deadline_of_delivery = fields.Date('订单交期', tracking=True, compute='_compute_deadline_of_delivery')
# tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘") # tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests") maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
@@ -34,6 +36,29 @@ class MrpProduction(models.Model):
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True) tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True) tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
def _compute_sale_order_id(self):
for production in self:
# 初始化 sale_order_id 为 False
sale_order_id = False
# 使用正则表达式查找产品名称中的 'S' 开头的字母数字字符串
match = re.search(r'S\d+', production.product_id.with_context(lang='zh_CN').name) # 从字符串开始匹配
if match:
result = match.group(0)
try:
# 查找与匹配的字符串相符的销售订单
sale_order = self.env['sale.order'].search(
[('name', '=', result)], limit=1, order='id asc'
)
if sale_order:
production.sale_order_id = sale_order.id
else:
logging.warning("No sale order found for production {} with product {} (name match: {})".format(
production.id, production.product_id.name, result))
except Exception as e:
logging.error("Error while fetching sale order for production {}: {}".format(production.id, str(e)))
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id') @api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
def _compute_deadline_of_delivery(self): def _compute_deadline_of_delivery(self):
for production in self: for production in self:
@@ -762,7 +787,7 @@ class MrpProduction(models.Model):
# 立即创建外协出入库单和采购订单 # 立即创建外协出入库单和采购订单
# self.env['stock.picking'].create_outcontract_picking(workorder, production) # self.env['stock.picking'].create_outcontract_picking(workorder, production)
# self.env['purchase.order'].get_purchase_order(workorder, production, # self.env['purchase.order'].get_purchase_order(workorder, production,
# product_id_to_production_names) # product_id_to_production_names)
consecutive_workorders = [] consecutive_workorders = []
# 处理最后一个组,即使它可能只有一个工作订单 # 处理最后一个组,即使它可能只有一个工作订单
@@ -774,6 +799,7 @@ class MrpProduction(models.Model):
for workorders in reversed(proc_workorders): for workorders in reversed(proc_workorders):
self.env['stock.picking'].create_outcontract_picking(workorders, production, sorted_workorders) self.env['stock.picking'].create_outcontract_picking(workorders, production, sorted_workorders)
self.env['purchase.order'].get_purchase_order(workorders, production, product_id_to_production_names) self.env['purchase.order'].get_purchase_order(workorders, production, product_id_to_production_names)
# 工单排序 # 工单排序
def _reset_work_order_sequence1(self, k): def _reset_work_order_sequence1(self, k):
for rec in self: for rec in self:
@@ -1435,6 +1461,26 @@ class MrpProduction(models.Model):
for production in self: for production in self:
production.production_type = '自动化产线加工' if not production.product_id.is_manual_processing else '人工线下加工' production.production_type = '自动化产线加工' if not production.product_id.is_manual_processing else '人工线下加工'
@api.depends('procurement_group_id.mrp_production_ids.move_dest_ids.group_id.sale_id')
def _compute_sale_order_count(self):
for production in self:
if production.sale_order_id:
production.sale_order_count = 1
else:
production.sale_order_count = 0
def action_view_sale_orders(self):
if self.sale_order_id:
action = {
'res_model': 'sale.order',
'type': 'ir.actions.act_window',
}
action.update({
'view_mode': 'form',
'res_id': self.sale_order_id.id,
})
return action
@api.model_create_multi @api.model_create_multi
def create(self, vals_list): def create(self, vals_list):
""" """

View File

@@ -129,7 +129,7 @@ class ResMrpWorkOrder(models.Model):
Y10_axis = fields.Float(default=0) Y10_axis = fields.Float(default=0)
Z10_axis = fields.Float(default=0) Z10_axis = fields.Float(default=0)
X_deviation_angle = fields.Integer(string="X轴偏差度", default=0) X_deviation_angle = fields.Integer(string="X轴偏差度", default=0)
test_results = fields.Selection([("合格", "合格")], default='合格', test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], default='合格',
string="检测结果", tracking=True) string="检测结果", tracking=True)
cnc_ids = fields.One2many("sf.cnc.processing", 'workorder_id', string="CNC加工程序") cnc_ids = fields.One2many("sf.cnc.processing", 'workorder_id', string="CNC加工程序")
cmm_ids = fields.One2many("sf.cmm.program", 'workorder_id', string="CMM程序") cmm_ids = fields.One2many("sf.cmm.program", 'workorder_id', string="CMM程序")

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from odoo import api, fields, models, _
from odoo.tools import OrderedSet
# _get_surface_technics_purchase_ids
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
def button_confirm(self):
super().button_confirm()
workorders = self.env['mrp.workorder'].search([('purchase_id', '=', self.id)])
for workorder in workorders:
if workorder.routing_type == '表面工艺' and workorder.is_subcontract is True:
move_out = workorder.move_subcontract_workorder_ids[1]
# move_out = self.env['stock.move'].search(
# [('location_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
# ('location_dest_id', '=', self.env['stock.location'].search(
# [('barcode', 'ilike', 'VL-SPOC')]).id),
# ('origin', '=', self.production_id.name), ('state', 'not in', ['cancel', 'done'])])
for mo in move_out:
if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False})
if not mo.move_line_ids:
self.env['stock.move.line'].create(mo.get_move_line(workorder.production_id, workorder))
return True

View File

@@ -646,18 +646,25 @@ class StockPicking(models.Model):
subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True).sorted('sequence') subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True).sorted('sequence')
if workorder == subcontract_workorders[-1]: if workorder == subcontract_workorders[-1]:
self.env['stock.quant']._update_reserved_quantity( self.env['stock.quant']._update_reserved_quantity(
move_in.product_id, move_in.location_dest_id, move_in.product_uom_qty, lot_id=move_in.move_line_ids.lot_id, move_in.product_id, move_in.location_dest_id, move_in.product_uom_qty,
lot_id=move_in.move_line_ids.lot_id,
package_id=False, owner_id=False, strict=False package_id=False, owner_id=False, strict=False
) )
workorder.button_finish()
picking_type_out = self.env.ref('sf_manufacturing.outcontract_picking_out').id
if res and self.picking_type_id.id == picking_type_out:
move_out = self.move_ids
if move_out:
workorder = move_out.subcontract_workorder_id
workorder.button_start()
return res return res
# 创建 外协出库入单 # 创建 外协出库入单
def create_outcontract_picking(self, workorders, item, sorted_workorders): def create_outcontract_picking(self, workorders, item, sorted_workorders):
for workorder in workorders: for workorder in workorders:
if workorder.move_subcontract_workorder_ids: if workorder.move_subcontract_workorder_ids:
workorder.move_subcontract_workorder_ids.write({'state': 'draft'}) workorder.move_subcontract_workorder_ids.write({'state': 'waiting'})
workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'draft'}) workorder.move_subcontract_workorder_ids.picking_id.write({'state': 'waiting'})
else: else:
# 创建一个新的补货组 # 创建一个新的补货组
procurement_group_id = self.env['procurement.group'].create({ procurement_group_id = self.env['procurement.group'].create({
@@ -671,14 +678,16 @@ class StockPicking(models.Model):
else: else:
# 从sorted_workorders中找到上一工单的move # 从sorted_workorders中找到上一工单的move
if sorted_workorders.index(workorder) > 0: if sorted_workorders.index(workorder) > 0:
move_dest_id = sorted_workorders[sorted_workorders.index(workorder) - 1].move_subcontract_workorder_ids[1].id move_dest_id = \
sorted_workorders[sorted_workorders.index(workorder) - 1].move_subcontract_workorder_ids[1].id
new_picking = True new_picking = True
outcontract_picking_type_in = self.env.ref( outcontract_picking_type_in = self.env.ref(
'sf_manufacturing.outcontract_picking_in').id, 'sf_manufacturing.outcontract_picking_in').id,
outcontract_picking_type_out = self.env.ref( outcontract_picking_type_out = self.env.ref(
'sf_manufacturing.outcontract_picking_out').id, 'sf_manufacturing.outcontract_picking_out').id,
moves_in = self.env['stock.move'].sudo().create( moves_in = self.env['stock.move'].sudo().create(
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_in, procurement_group_id.id, move_dest_id)) self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_in,
procurement_group_id.id, move_dest_id))
picking_in = self.create( picking_in = self.create(
moves_in._get_new_picking_values_Res(item, workorder, 'WH/OCIN/')) moves_in._get_new_picking_values_Res(item, workorder, 'WH/OCIN/'))
# pick_ids.append(picking_in.id) # pick_ids.append(picking_in.id)
@@ -686,7 +695,8 @@ class StockPicking(models.Model):
{'picking_id': picking_in.id, 'state': 'waiting'}) {'picking_id': picking_in.id, 'state': 'waiting'})
moves_in._assign_picking_post_process(new=new_picking) moves_in._assign_picking_post_process(new=new_picking)
moves_out = self.env['stock.move'].sudo().create( moves_out = self.env['stock.move'].sudo().create(
self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_out, procurement_group_id.id, moves_in.id)) self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_out,
procurement_group_id.id, moves_in.id))
workorder.write({'move_subcontract_workorder_ids': [(6, 0, [moves_in.id, moves_out.id])]}) workorder.write({'move_subcontract_workorder_ids': [(6, 0, [moves_in.id, moves_out.id])]})
picking_out = self.create( picking_out = self.create(
moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/')) moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/'))
@@ -695,7 +705,6 @@ class StockPicking(models.Model):
{'picking_id': picking_out.id, 'state': 'waiting'}) {'picking_id': picking_out.id, 'state': 'waiting'})
moves_out._assign_picking_post_process(new=new_picking) moves_out._assign_picking_post_process(new=new_picking)
@api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id') @api.depends('move_type', 'immediate_transfer', 'move_ids.state', 'move_ids.picking_id')
def _compute_state(self): def _compute_state(self):
super(StockPicking, self)._compute_state() super(StockPicking, self)._compute_state()
@@ -706,7 +715,9 @@ class StockPicking(models.Model):
if picking.move_ids: if picking.move_ids:
workorder = picking.move_ids[0].subcontract_workorder_id workorder = picking.move_ids[0].subcontract_workorder_id
if picking.state == 'assigned': if picking.state == 'assigned':
if workorder.state in ['pending', 'waiting'] or workorder._get_surface_technics_purchase_ids().state in ['draft', 'sent']: if workorder.state in ['pending',
'waiting'] or workorder._get_surface_technics_purchase_ids().state in [
'draft', 'sent']:
picking.state = 'waiting' picking.state = 'waiting'
@@ -719,7 +730,8 @@ class ReStockMove(models.Model):
def _get_stock_move_values_Res(self, item, picking_type_id, group_id, move_dest_ids=False): def _get_stock_move_values_Res(self, item, picking_type_id, group_id, move_dest_ids=False):
route_id = self.env.ref('sf_manufacturing.route_surface_technology_outsourcing').id route_id = self.env.ref('sf_manufacturing.route_surface_technology_outsourcing').id
stock_rule = self.env['stock.rule'].sudo().search([('route_id', '=', route_id), ('picking_type_id', '=', picking_type_id)]) stock_rule = self.env['stock.rule'].sudo().search(
[('route_id', '=', route_id), ('picking_type_id', '=', picking_type_id)])
move_values = { move_values = {
'name': '', 'name': '',
'company_id': item.company_id.id, 'company_id': item.company_id.id,
@@ -993,11 +1005,13 @@ class ReStockMove(models.Model):
production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc') production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc')
productions = self.env['mrp.production'].search( productions = self.env['mrp.production'].search(
[('origin', '=', production.origin), ('product_id', '=', production.product_id.id)]) [('origin', '=', production.origin), ('product_id', '=', production.product_id.id)])
res['origin'] = ','.join(productions.mapped('name')) if productions.mapped('name'):
res['origin'] = ','.join(productions.mapped('name'))
res['retrospect_ref'] = production.product_id.name res['retrospect_ref'] = production.product_id.name
return res return res
subcontract_workorder_id = fields.Many2one('mrp.workorder', '外协工单组件', check_company=True, index='btree_not_null') subcontract_workorder_id = fields.Many2one('mrp.workorder', '外协工单组件', check_company=True,
index='btree_not_null')
class ReStockQuant(models.Model): class ReStockQuant(models.Model):

View File

@@ -43,6 +43,7 @@
<field name="activity_ids" string="下一个活动" widget="list_activity" optional="hide"/> <field name="activity_ids" string="下一个活动" widget="list_activity" optional="hide"/>
</xpath> </xpath>
<xpath expr="//field[@name='origin']" position="replace"> <xpath expr="//field[@name='origin']" position="replace">
<field name="sale_order_id" optional="show"/>
<field name="origin" optional="hide"/> <field name="origin" optional="hide"/>
</xpath> </xpath>
<xpath expr="//field[@name='components_availability']" position="replace"> <xpath expr="//field[@name='components_availability']" position="replace">
@@ -123,6 +124,7 @@
<field name="tool_state" <field name="tool_state"
attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/> attrs="{'invisible': [('production_type', 'not in', ['自动化产线加工'])]}"/>
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/> <field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
<field name="sale_order_id" readonly="1"/>
<field name="deadline_of_delivery" readonly="1"/> <field name="deadline_of_delivery" readonly="1"/>
<field name="tool_state_remark2" invisible="1"/> <field name="tool_state_remark2" invisible="1"/>
</xpath> </xpath>
@@ -584,6 +586,7 @@
<searchpanel> <searchpanel>
<field name="state" icon="fa-filter" enable_counters="1"/> <field name="state" icon="fa-filter" enable_counters="1"/>
<field name="delivery_status" icon="fa-filter" enable_counters="1"/> <field name="delivery_status" icon="fa-filter" enable_counters="1"/>
<field name="production_type" icon="fa-filter" enable_counters="1"/>
</searchpanel> </searchpanel>
</xpath> </xpath>
<filter name='todo' position="replace"/> <filter name='todo' position="replace"/>

View File

@@ -200,7 +200,7 @@
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> --> <!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
<!-- <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"--> <!-- <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"-->
<!-- attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&amp;',('rfid_code','in',['',False]),('state','=','done')]}"/>--> <!-- attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&amp;',('rfid_code','in',['',False]),('state','=','done')]}"/>-->
<button name="button_rework_pre" type="object" string="异常反馈" invisible="1" <button name="button_rework_pre" type="object" string="异常反馈"
class="btn-primary" class="btn-primary"
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/> attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/>
<button name="unbind_tray" type="object" string="解绑托盘" <button name="unbind_tray" type="object" string="解绑托盘"

View File

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