Compare commits

..

42 Commits

Author SHA1 Message Date
胡尧
61baa49d85 Merge branch 'feature/6694' into feature/merge-6694 2025-05-15 09:22:44 +08:00
胡尧
063c7a94c5 Accept Merge Request #2121: (feature/rollback-develop1 -> develop)
Merge Request: 修复误回退的代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2121?initial=true
2025-05-15 09:15:54 +08:00
胡尧
b783af6ef3 修复误回退的代码 2025-05-15 09:15:27 +08:00
胡尧
22cbe1bd9d 回退代码 2025-05-15 09:13:51 +08:00
胡尧
cc196e25da Accept Merge Request #2120: (feature/rollback-develop1 -> develop)
Merge Request: 退回至未合并工艺外协代码

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2120?initial=true
2025-05-15 09:03:49 +08:00
胡尧
961754428b 退回至未合并工艺外协代码 2025-05-15 09:02:31 +08:00
廖丹龙
7e47a85167 Accept Merge Request #2119: (feature/tool_standard_library_process -> develop)
Merge Request: sf-制造-工艺外协的采购订单确认后,外协的调拨单的作业详情的预留数量跟完成数量不对

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2119
2025-05-14 17:53:14 +08:00
liaodanlong
2644df4fd5 sf-制造-工艺外协的采购订单确认后,外协的调拨单的作业详情的预留数量跟完成数量不对 2025-05-14 17:45:42 +08:00
廖丹龙
e14aa35d90 Accept Merge Request #2118: (feature/tool_standard_library_process -> develop)
Merge Request: sf-制造-工艺外协的采购订单确认后,外协的调拨单的作业详情的预留数量跟完成数量不对

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2118
2025-05-14 17:00:42 +08:00
liaodanlong
9392819375 sf-制造-工艺外协的采购订单确认后,外协的调拨单的作业详情的预留数量跟完成数量不对 2025-05-14 16:58:05 +08:00
liaodanlong
ea3d9e5375 sf-制造-工艺外协的采购订单确认后,外协的调拨单的作业详情的预留数量跟完成数量不对 2025-05-14 16:46:29 +08:00
胡尧
7db0a44420 Accept Merge Request #2117: (feature/6694 -> develop)
Merge Request: 修改中控日志接口授权为none

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2117?initial=true
2025-05-14 16:09:30 +08:00
liaodanlong
238840b647 sf-制造-工艺外协的采购订单确认后,外协的调拨单的作业详情的预留数量跟完成数量不对 2025-05-14 14:54:36 +08:00
廖丹龙
51e25d8847 Accept Merge Request #2116: (feature/tool_standard_library_process -> 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/2116
2025-05-14 14:14:02 +08:00
liaodanlong
c6f06a4c32 外协调拨单就绪条件修改 2025-05-14 14:12:06 +08:00
liaodanlong
5b61a801c5 空值判断 2025-05-14 13:57:57 +08:00
廖丹龙
2893524902 Accept Merge Request #2115: (feature/tool_standard_library_process -> develop)
Merge Request: sf-制造-产品-表单详情中-工艺参数显示内容=未归档,未绑定产品的工艺参数

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2115?initial=true
2025-05-14 12:22:10 +08:00
liaodanlong
ed55e36f45 sf-制造-产品-表单详情中-工艺参数显示内容=未归档,未绑定产品的工艺参数 2025-05-14 11:49:14 +08:00
廖丹龙
c811f26104 Accept Merge Request #2114: (feature/tool_standard_library_process -> develop)
Merge Request: sf-制造-产品-表单详情中-工艺参数显示内容=未归档,未绑定产品的工艺参数

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2114
2025-05-14 11:11:29 +08:00
liaodanlong
3763b60758 sf-制造-产品-表单详情中-工艺参数显示内容=未归档,未绑定产品的工艺参数 2025-05-14 10:48:01 +08:00
廖丹龙
976907bd92 Accept Merge Request #2112: (feature/tool_standard_library_process -> 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/2112
2025-05-14 10:35:47 +08:00
liaodanlong
50c3b31ece 外协采购单使用采购申请创建出来的采购单 2025-05-14 10:24:21 +08:00
liaodanlong
8afc8bb3a6 外协调拨单只有当制造订单状态变为已编程才能就绪 2025-05-14 10:03:26 +08:00
廖丹龙
a0427d424f Accept Merge Request #2111: (feature/tool_standard_library_process -> develop)
Merge Request: sf-制造-工艺外协的采购订单确认后,外协的调拨单的作业详情的预留数量跟完成数量不对

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2111
2025-05-13 16:35:45 +08:00
liaodanlong
3148ce5a15 外协调拨单不同产品的调拨单混合到一起了 2025-05-13 15:49:02 +08:00
liaodanlong
8e726e1bf6 sf-制造-工艺外协的采购订单确认后,外协的调拨单的作业详情的预留数量跟完成数量不对 2025-05-13 15:01:54 +08:00
liaodanlong
4c0f9cfb39 外协工单零件图号零件名称 2025-05-13 11:04:45 +08:00
廖丹龙
86d3bd38c5 Accept Merge Request #2110: (feature/tool_standard_library_process -> 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/2110
2025-05-13 10:25:20 +08:00
liaodanlong
a8e4c7d9a0 调拨单报错问题处理 2025-05-13 10:24:09 +08:00
liaodanlong
7501832637 调拨单就绪问题处理 2025-05-13 10:01:58 +08:00
liaodanlong
ce0ead4da6 sf-工序-手动创建工艺可选参数及产品问题 2025-05-12 17:13:52 +08:00
廖丹龙
343e2f9f24 Accept Merge Request #2109: (feature/tool_standard_library_process -> 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/2109
2025-05-12 15:42:10 +08:00
liaodanlong
6d3793ee30 调拨单就绪问题处理 2025-05-12 15:21:10 +08:00
廖丹龙
43f7e97c28 Accept Merge Request #2108: (feature/tool_standard_library_process -> 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/2108
2025-05-12 15:12:06 +08:00
liaodanlong
9e6f3fa864 Merge branch 'refs/heads/develop' into feature/tool_standard_library_process
# Conflicts:
#	sf_manufacturing/models/purchase_order.py
2025-05-12 15:03:25 +08:00
liaodanlong
ed50d7c9a7 调拨单就绪问题处理 2025-05-12 14:55:19 +08:00
liaodanlong
89e23050e6 打印错误日志堆栈信息 2025-05-12 14:54:43 +08:00
胡尧
b7f912453f Accept Merge Request #2107: (feature/6694 -> develop)
Merge Request: 解决代码问题

Created By: @胡尧
Accepted By: @胡尧
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/2107?initial=true
2025-05-12 14:48:07 +08:00
liaodanlong
1040844b0a 采购申请跳转处理 2025-05-12 11:01:51 +08:00
liaodanlong
36a13c04de 采购申请数量与产品数量不一致问题 2025-05-12 10:55:21 +08:00
liaodanlong
f4318bd997 Merge remote-tracking branch 'origin/feature/tool_standard_library_process' into feature/tool_standard_library_process 2025-05-12 10:25:09 +08:00
liaodanlong
d47a30977c 已归档的表面工艺可选参数不进行产品创建 2025-05-12 10:24:22 +08:00
25 changed files with 240 additions and 476 deletions

View File

@@ -13,7 +13,6 @@
'views/purchase_request_view.xml', 'views/purchase_request_view.xml',
'wizard/purchase_request_line_make_purchase_order_view.xml', 'wizard/purchase_request_line_make_purchase_order_view.xml',
'views/purchase_request_line_view.xml', 'views/purchase_request_line_view.xml',
'views/stock_picking_views.xml',
], ],
'assets': { 'assets': {
'web.assets_backend': [ 'web.assets_backend': [

View File

@@ -1043,7 +1043,7 @@ msgstr "询价单"
#. module: purchase_request #. module: purchase_request
#: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__purchased_qty #: model:ir.model.fields,field_description:purchase_request.field_purchase_request_line__purchased_qty
msgid "RFQ/PO Qty" msgid "RFQ/PO Qty"
msgstr "已订购数" msgstr ""
#. module: purchase_request #. module: purchase_request
#. odoo-python #. odoo-python

View File

@@ -5,4 +5,3 @@ from . import sale_order
from . import mrp_production from . import mrp_production
from . import purchase_order from . import purchase_order
from . import stock_rule from . import stock_rule
from . import stock_picking

View File

@@ -19,8 +19,9 @@ class MrpProduction(models.Model):
# item.pr_mp_count = len(pr_ids) # item.pr_mp_count = len(pr_ids)
# 由于采购申请合并了所有销售订单行的采购,所以不区分产品 # 由于采购申请合并了所有销售订单行的采购,所以不区分产品
first_mp = self.env['mrp.production'].search( first_mp = self.env['mrp.production'].search(
[('origin', '=', item.origin)], limit=1, order='id asc') [('origin', '=', item.origin)], limit=1, order='id asc')
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_mp.name)]) pr_ids = self.env['purchase.request'].sudo().search(
[('origin', 'like', first_mp.name), ('is_subcontract', '!=', 'True')])
item.pr_mp_count = len(pr_ids) item.pr_mp_count = len(pr_ids)
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')]) # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')])
@@ -29,18 +30,10 @@ class MrpProduction(models.Model):
采购请求 采购请求
""" """
self.ensure_one() self.ensure_one()
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name),('is_subcontract', '!=', True)])
# if self.product_id.product_tmpl_id.single_manufacturing == True and not self.is_remanufacture:
# first_order = self.env['mrp.production'].search(
# [('origin', '=', self.origin), ('product_id', '=', self.product_id.id)], limit=1, order='id asc')
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_order.name)])
# else:
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name)])
# 由于采购申请合并了所有销售订单行的采购,所以不区分产品
first_mp = self.env['mrp.production'].search( first_mp = self.env['mrp.production'].search(
[('origin', '=', self.origin)], limit=1, order='id asc') [('origin', '=', self.origin)], limit=1, order='id asc')
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', first_mp.name)]) pr_ids = self.env['purchase.request'].sudo().search(
[('origin', 'like', first_mp.name), ('is_subcontract', '!=', 'True')])
action = { action = {
'res_model': 'purchase.request', 'res_model': 'purchase.request',
@@ -54,7 +47,7 @@ class MrpProduction(models.Model):
else: else:
action.update({ action.update({
'name': _("%s生成采购请求单", self.name), 'name': _("%s生成采购请求单", self.name),
'domain': [('id', 'in', pr_ids)], 'domain': [('id', 'in', pr_ids.ids)],
'view_mode': 'tree,form', 'view_mode': 'tree,form',
}) })
return action return action

View File

@@ -116,7 +116,7 @@ class PurchaseRequestLine(models.Model):
for pr in self: for pr in self:
qty_to_buy = sum(pr.mapped("product_qty")) qty_to_buy = sum(pr.mapped("product_qty"))
if pr.purchase_count > 0: if pr.purchase_count > 0:
qty_to_buy -= sum(pr.mapped("purchase_lines").filtered(lambda po: po.state != 'cancel').mapped( qty_to_buy -= sum(pr.mapped("purchase_lines").filtered(lambda po: po.state != 'cancel').mapped(
"product_qty")) "product_qty"))
pr.qty_to_buy = qty_to_buy > 0.0 pr.qty_to_buy = qty_to_buy > 0.0
pr.pending_qty_to_receive = qty_to_buy pr.pending_qty_to_receive = qty_to_buy

View File

@@ -1,35 +0,0 @@
from odoo import fields, api, models, _
class StockPicking(models.Model):
_inherit = "stock.picking"
purchase_request_count = fields.Integer('采购订单数量', compute='_compute_purchase_request')
@api.depends('name')
def _compute_purchase_request(self):
for record in self:
purchase_request_ids = self.env['purchase.request'].search([('origin', '=', record.name)])
record.purchase_request_count = len(purchase_request_ids)
def action_view_purchase_request(self):
self.ensure_one()
purchase_request_ids = self.env['purchase.request'].search([('origin', '=', self.name)])
action = {
'res_model': 'purchase.request',
'type': 'ir.actions.act_window',
}
if len(purchase_request_ids) == 1:
action.update({
'view_mode': 'form',
'res_id': purchase_request_ids[0].id,
})
else:
action.update({
'name': _("%s生成采购请求单", self.name),
'domain': [('id', 'in', purchase_request_ids.ids)],
'view_mode': 'tree,form',
})
return action

View File

@@ -48,8 +48,8 @@ class StockRule(models.Model):
# 如果补货组相同,并且产品相同,则合并 # 如果补货组相同,并且产品相同,则合并
procurements_dict = defaultdict() procurements_dict = defaultdict()
for procurement, rule in procurements: for procurement, rule in procurements:
if (procurement.product_id.id, procurement.values['group_id'], rule.id) not in procurements_dict: if (procurement.product_id, procurement.values['group_id'], rule) not in procurements_dict:
procurements_dict[(procurement.product_id.id, procurement.values['group_id'], rule.id)] = { procurements_dict[(procurement.product_id, procurement.values['group_id'], rule)] = {
'product_id': procurement.product_id, 'product_id': procurement.product_id,
'product_qty': procurement.product_qty, 'product_qty': procurement.product_qty,
'product_uom': procurement.product_uom, 'product_uom': procurement.product_uom,
@@ -61,8 +61,7 @@ class StockRule(models.Model):
'rule': rule 'rule': rule
} }
else: else:
procurements_dict[(procurement.product_id.id, procurement.values['group_id'], rule.id)]['product_qty'] += procurement.product_qty procurements_dict[(procurement.product_id, procurement.values['group_id'], rule)]['product_qty'] += procurement.product_qty
procurements_dict[(procurement.product_id.id, procurement.values['group_id'], rule.id)]['values']['move_dest_ids'] |= procurement.values['move_dest_ids']
new_procurements = [] new_procurements = []
for k, p in procurements_dict.items(): for k, p in procurements_dict.items():
new_procurements.append(( new_procurements.append((

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="stock_pikcing_inherited_form_jikimo_purchase_request" model="ir.ui.view">
<field name="name">stock.pikcing.inherited.form.jikimo.purchase.request</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']/button" position="before">
<button class="oe_stat_button" name="action_view_purchase_request" type="object" icon="fa-credit-card"
attrs="{'invisible': [('purchase_request_count', '=', 0)]}">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="purchase_request_count"/>
</span>
<span class="o_stat_text">采购申请</span>
</div>
</button>
</xpath>
</field>
</record>
</odoo>

View File

@@ -119,11 +119,3 @@ class PurchaseRequestLineMakePurchaseOrderItem(models.TransientModel):
_inherit = "purchase.request.line.make.purchase.order.item" _inherit = "purchase.request.line.make.purchase.order.item"
supply_method = fields.Selection(related='line_id.supply_method', string='供货方式') supply_method = fields.Selection(related='line_id.supply_method', string='供货方式')
wiz_id = fields.Many2one(
comodel_name="purchase.request.line.make.purchase.order",
string="Wizard",
required=False,
ondelete="cascade",
readonly=True,
)

View File

@@ -141,7 +141,7 @@ class QualityCheck(models.Model):
# # 出厂检验报告编号 # # 出厂检验报告编号
# report_number = fields.Char('出厂检验报告编号', compute='_compute_report_number', readonly=True) # report_number = fields.Char('出厂检验报告编号', compute='_compute_report_number', readonly=True)
# 总数量值为调拨单_产品明细_数量 # 总数量值为调拨单_产品明细_数量
total_qty = fields.Char('总数量', compute='_compute_total_qty', store=True) total_qty = fields.Char('总数量', compute='_compute_total_qty')
column_nums = fields.Integer('测量值列数', default=1) column_nums = fields.Integer('测量值列数', default=1)
@@ -153,9 +153,9 @@ class QualityCheck(models.Model):
for move in record.picking_id.move_ids_without_package: for move in record.picking_id.move_ids_without_package:
if move.product_id == record.product_id: if move.product_id == record.product_id:
total_qty = int(move.product_uom_qty) total_qty = int(move.product_uom_qty)
record.total_qty = total_qty if total_qty > 0 else 0 record.total_qty = total_qty if total_qty > 0 else ''
else: else:
record.total_qty = 0 record.total_qty = ''
# 检验数 # 检验数
check_qty = fields.Integer('检验数', default=lambda self: self._get_default_check_qty()) check_qty = fields.Integer('检验数', default=lambda self: self._get_default_check_qty())
@@ -735,9 +735,8 @@ class QualityCheck(models.Model):
def _compute_qty_to_test(self): def _compute_qty_to_test(self):
for qc in self: for qc in self:
if qc.is_lot_tested_fractionally: if qc.is_lot_tested_fractionally:
rounding = qc.product_id.uom_id.rounding if qc.product_id.uom_id else 0.01
qc.qty_to_test = float_round(qc.qty_line * qc.testing_percentage_within_lot / 100, qc.qty_to_test = float_round(qc.qty_line * qc.testing_percentage_within_lot / 100,
precision_rounding=rounding, rounding_method="UP") precision_rounding=self.product_id.uom_id.rounding, rounding_method="UP")
else: else:
qc.qty_to_test = qc.qty_line qc.qty_to_test = qc.qty_line

View File

@@ -23,8 +23,8 @@ class QualityCheckWizard(models.TransientModel):
lot_name = fields.Char(related='current_check_id.lot_name') lot_name = fields.Char(related='current_check_id.lot_name')
lot_line_id = fields.Many2one(related='current_check_id.lot_line_id') lot_line_id = fields.Many2one(related='current_check_id.lot_line_id')
qty_line = fields.Float(related='current_check_id.qty_line') qty_line = fields.Float(related='current_check_id.qty_line')
qty_to_test = fields.Float(related='current_check_id.qty_to_test', string='待检') qty_to_test = fields.Float(related='current_check_id.qty_to_test')
qty_tested = fields.Float(related='current_check_id.qty_tested', string='已检', readonly=False) qty_tested = fields.Float(related='current_check_id.qty_tested', readonly=False)
measure = fields.Float(related='current_check_id.measure', readonly=False) measure = fields.Float(related='current_check_id.measure', readonly=False)
measure_on = fields.Selection(related='current_check_id.measure_on') measure_on = fields.Selection(related='current_check_id.measure_on')
quality_state = fields.Selection(related='current_check_id.quality_state') quality_state = fields.Selection(related='current_check_id.quality_state')

View File

@@ -24,6 +24,8 @@ class SfProductionProcessParameter(models.Model):
obj = super(SfProductionProcessParameter, self).create(vals) obj = super(SfProductionProcessParameter, self).create(vals)
return obj return obj
def create_service_product(self): def create_service_product(self):
if not self.active:
return
service_categ = self.env.ref( service_categ = self.env.ref(
'sf_dlm.product_category_surface_technics_sf').sudo() 'sf_dlm.product_category_surface_technics_sf').sudo()

View File

@@ -41,7 +41,9 @@
attrs="{'invisible': [('categ_type', 'not in', ['成品','坯料', '原材料'])],'readonly': [('id', '!=', False)]}"/> attrs="{'invisible': [('categ_type', 'not in', ['成品','坯料', '原材料'])],'readonly': [('id', '!=', False)]}"/>
<field name="server_product_process_parameters_id" string="工艺参数" <field name="server_product_process_parameters_id" string="工艺参数"
options="{'no_create': True}" options="{'no_create': True}"
attrs="{'invisible': ['|',('detailed_type', '!=', 'service'),('detailed_type', '=', False)]}"/> attrs="{'invisible': ['|',('detailed_type', '!=', 'service'),('detailed_type', '=', False)]}"
domain="[('active', '=', 'True'),('outsourced_service_products', '!=', 'True')]"
/>
<field name="cutting_tool_material_id" class="custom_required" <field name="cutting_tool_material_id" class="custom_required"
options="{'no_create': True}" options="{'no_create': True}"
attrs="{'invisible': [('categ_type', '!=', '刀具')],'required': [('categ_type', '=', '刀具')],'readonly': [('id', '!=', False)]}" attrs="{'invisible': [('categ_type', '!=', '刀具')],'required': [('categ_type', '=', '刀具')],'readonly': [('id', '!=', False)]}"

View File

@@ -20,7 +20,6 @@
'data/sf_work_individuation_page.xml', 'data/sf_work_individuation_page.xml',
'data/agv_scheduling_data.xml', 'data/agv_scheduling_data.xml',
'data/product_data.xml', 'data/product_data.xml',
'data/automation_folder_data.xml',
'security/group_security.xml', 'security/group_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'wizard/workpiece_delivery_views.xml', 'wizard/workpiece_delivery_views.xml',

View File

@@ -2,18 +2,16 @@
import logging import logging
import json import json
from datetime import datetime from datetime import datetime
import base64
from odoo.addons.sf_manufacturing.models.agv_scheduling import RepeatTaskException from odoo.addons.sf_manufacturing.models.agv_scheduling import RepeatTaskException
from odoo import http from odoo import http
from odoo.http import request from odoo.http import request
from odoo.exceptions import MissingError
from odoo.addons.sf_base.decorators.api_log import api_log from odoo.addons.sf_base.decorators.api_log import api_log
class Manufacturing_Connect(http.Controller): class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('获取工单', requester='中控系统') @api_log('获取工单', requester='中控系统')
def get_Work_Info(self, **kw): def get_Work_Info(self, **kw):
@@ -56,7 +54,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('get_Work_Info error:%s' % e) logging.info('get_Work_Info error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/GetShiftPlan', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/GetShiftPlan', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('获取日计划', requester='中控系统') @api_log('获取日计划', requester='中控系统')
def get_ShiftPlan(self, **kw): def get_ShiftPlan(self, **kw):
@@ -110,7 +108,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('get_ShiftPlan error:%s' % e) logging.info('get_ShiftPlan error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/QcCheck', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/QcCheck', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
@api_log('工件预调(前置三元检测)', requester='中控系统') @api_log('工件预调(前置三元检测)', requester='中控系统')
def get_qcCheck(self, **kw): def get_qcCheck(self, **kw):
@@ -706,59 +704,3 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)} res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
logging.info('AGVDownProduct error:%s' % e) logging.info('AGVDownProduct error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/api/upload_three_check_data', type='http', auth='public', methods=['POST'], csrf=False, cros='*')
def upload_three_check_data(self):
res = {'Succeed': True, 'ErrorCode': 200, 'Messages': '上传成功'}
uploaded_files = request.httprequest.files.getlist('file')
if uploaded_files:
try:
for uploaded_file in uploaded_files:
file_content = uploaded_file.read()
file_name = uploaded_file.filename
production_name = '/'.join(file_name.split('_')[:-1])
processing_panel = file_name.split('_')[-1].split('.')[0]
# 找到对应的工单
production_id = request.env['mrp.production'].sudo().search([('name', '=', production_name)])
wo = production_id.workorder_ids.filtered(lambda wo: wo.processing_panel == processing_panel and wo.routing_type == '装夹预调')
if not wo:
raise MissingError('工单不存在')
folder_id = request.env.ref('sf_manufacturing.documents_pre_three_element_detection_folder')
document = request.env['documents.document'].sudo().search([('res_model', '=', 'mrp.workorder'), ('res_id', '=', wo.id)])
if document and document.attachment_id:
attachment = request.env['ir.attachment'].sudo().create({
'name': file_name,
'type': 'binary',
'datas': base64.b64encode(file_content),
'res_model': 'mrp.workorder',
'res_id': wo.id,
})
document.write({'attachment_id': attachment.id})
else:
# Create ir.attachment record
attachment = request.env['ir.attachment'].sudo().create({
'name': file_name,
'type': 'binary',
'datas': base64.b64encode(file_content),
'res_model': 'mrp.workorder',
'res_id': wo.id,
})
# 创建 documents.document 记录
request.env['documents.document'].sudo().create({
'name': file_name,
'attachment_id': attachment.id,
'folder_id': folder_id.id,
'res_model': 'mrp.workorder',
'res_id': wo.id,
})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
return json.JSONEncoder().encode(res)

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- 创建自动化文件夹 -->
<record id="documents_automation_folder" model="documents.folder">
<field name="name">自动化</field>
<field name="description">存放自动化生产流程相关文件</field>
<field name="sequence">20</field>
</record>
<record id="documents_pre_three_element_detection_folder" model="documents.folder">
<field name="name">前置三元检测报告</field>
<field name="parent_folder_id" ref="documents_automation_folder"/>
<field name="sequence">1</field>
</record>
</data>
</odoo>

View File

@@ -928,6 +928,8 @@ class MrpProduction(models.Model):
'sf_stock.stock_route_process_outsourcing').id)] 'sf_stock.stock_route_process_outsourcing').id)]
for product_id, request_line_list in grouped_purchase_request_line_sorted_list.items(): for product_id, request_line_list in grouped_purchase_request_line_sorted_list.items():
cur_request_line = request_line_list[0] cur_request_line = request_line_list[0]
if cur_request_line['product_qty'] == 1:
cur_request_line['product_qty'] = len(request_line_list)
# cur_request_line['product_qty'] = cur_request_line['product_qty'] # cur_request_line['product_qty'] = cur_request_line['product_qty']
cur_request_line['request_id'] = pr.id cur_request_line['request_id'] = pr.id
cur_request_line['origin'] = ", ".join({item['production_name'] for item in request_line_list if item.get('production_name')}) cur_request_line['origin'] = ", ".join({item['production_name'] for item in request_line_list if item.get('production_name')})

View File

@@ -6,7 +6,6 @@ import urllib.parse
from datetime import date from datetime import date
from datetime import datetime, timedelta from datetime import datetime, timedelta
import requests import requests
import tempfile
import os import os
import math import math
from lxml import etree from lxml import etree
@@ -444,21 +443,11 @@ class ResMrpWorkOrder(models.Model):
@api.depends('state', 'production_id.name') @api.depends('state', 'production_id.name')
def _compute_surface_technics_purchase_ids(self): def _compute_surface_technics_purchase_ids(self):
for order in self: for order in self:
if order.routing_type == '表面工艺' and order.state not in ['cancel']: pr_ids = self.env['purchase.request'].sudo().search(
# domain = [('group_id', '=', self.production_id.procurement_group_id.id), [('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'),
# ('purchase_type', '=', 'consignment'), ('state', '!=', 'cancel')] ('state', '!=', 'rejected')])
domain = [('purchase_type', '=', 'consignment'), if pr_ids.purchase_count:
('origin', 'like', '%' + self.production_id.name + '%'), order.surface_technics_purchase_count = pr_ids.purchase_count
('state', '!=', 'cancel')]
purchase = self.env['purchase.order'].search(domain)
order.surface_technics_purchase_count = 0
if not purchase:
order.surface_technics_purchase_count = 0
for po in purchase:
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: else:
order.surface_technics_purchase_count = 0 order.surface_technics_purchase_count = 0
@@ -503,16 +492,19 @@ class ResMrpWorkOrder(models.Model):
# if technology_design.is_auto is False: # if technology_design.is_auto is False:
# domain = [('origin', '=', self.production_id.name)] # domain = [('origin', '=', self.production_id.name)]
# else: # else:
purchase_orders_id = self._get_surface_technics_purchase_ids() pr_ids = self.env['purchase.request'].sudo().search(
result = { [('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'),
"type": "ir.actions.act_window", ('state', '!=', 'rejected')])
"res_model": "purchase.order", # purchase_orders_id = self._get_surface_technics_purchase_ids()
"res_id": purchase_orders_id.id, # result = {
# "domain": [['id', 'in', self.purchase_id]], # "type": "ir.actions.act_window",
"name": _("Purchase Orders"), # "res_model": "purchase.order",
'view_mode': 'form', # "res_id": purchase_orders_id.id,
} # # "domain": [['id', 'in', self.purchase_id]],
return result # "name": _("Purchase Orders"),
# 'view_mode': 'form',
# }
return pr_ids.action_view_purchase_order()
def _get_surface_technics_purchase_ids(self): def _get_surface_technics_purchase_ids(self):
domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment'), domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment'),
@@ -772,170 +764,146 @@ class ResMrpWorkOrder(models.Model):
# 获取三次元检测点数据 # 获取三次元检测点数据
def get_three_check_datas(self): def get_three_check_datas(self):
ftp_resconfig = self.env['res.config.settings'].get_values() ftp_resconfig = self.env['res.config.settings'].get_values()
# ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']), ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']),
# ftp_resconfig['ftp_user'], ftp_resconfig['ftp_user'],
# ftp_resconfig['ftp_password']) ftp_resconfig['ftp_password'])
# local_dir_path = '/ftp/before' local_dir_path = '/ftp/before'
# os.makedirs(local_dir_path, exist_ok=True) os.makedirs(local_dir_path, exist_ok=True)
local_filename = self.save_name + '.xls' local_filename = self.save_name + '.xls'
# local_file_path = os.path.join(local_dir_path, local_filename) local_file_path = os.path.join(local_dir_path, local_filename)
# logging.info('local_file_path:%s' % local_file_path) logging.info('local_file_path:%s' % local_file_path)
# # remote_path = '/home/ftp/ftp_root/ThreeTest/XT/Before/' + local_filename # remote_path = '/home/ftp/ftp_root/ThreeTest/XT/Before/' + local_filename
# remote_path = '/ThreeTest/XT/Before/' + local_filename remote_path = '/ThreeTest/XT/Before/' + local_filename
# logging.info('remote_path:%s' % remote_path) logging.info('remote_path:%s' % remote_path)
# is_get_detection_file = self.env['ir.config_parameter'].sudo().get_param('is_get_detection_file') is_get_detection_file = self.env['ir.config_parameter'].sudo().get_param('is_get_detection_file')
# if not is_get_detection_file: if not is_get_detection_file:
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') paload_data = {
paload_data = { "filename": local_filename
"filename": local_filename, }
"sf_host": base_url if not ftp_resconfig['get_check_file_path']:
} raise UserError('请先配置获取检测报告地址')
if not ftp_resconfig['get_check_file_path']: url = ftp_resconfig['get_check_file_path'] + '/get/check/report'
raise UserError('请先配置获取检测报告地址') response = requests.post(url, json=paload_data)
url = ftp_resconfig['get_check_file_path'] + '/get/check/report' logging.info('response:%s' % response.json())
response = requests.post(url, json=paload_data) if response.json().get('detail'):
# logging.info('response:%s' % response.json()) raise UserError(response.json().get('detail'))
# if response.json().get('detail'):
# raise UserError(response.json().get('detail'))
# if not ftp.file_exists(remote_path): if not ftp.file_exists(remote_path):
# raise UserError(f"文件不存在: {remote_path}") raise UserError(f"文件不存在: {remote_path}")
# with open(local_file_path, 'wb') as local_file: with open(local_file_path, 'wb') as local_file:
# ftp.ftp.retrbinary('RETR ' + remote_path, local_file.write) ftp.ftp.retrbinary('RETR ' + remote_path, local_file.write)
# logging.info('下载文件成功') logging.info('下载文件成功')
# 解析本地文件
# file_path = 'WH_MO_00099.xls' # 使用下载的实际文件路径
parser = etree.XMLParser(recover=True) # Using recover to handle errors
tree = etree.parse(local_file_path, parser)
logging.info('tree:%s' % tree)
root = tree.getroot()
logging.info('root:%s' % root)
document = self.env['documents.document'].sudo().search([('res_model', '=', 'mrp.workorder'), ('res_id', '=', self.id)]) # 准备一个外部字典来存储以PT为键的坐标字典
if not document: pt_coordinates = {}
raise UserError(f"未获取到检测数据") # 遍历每个工作表和行
binary_data = base64.b64decode(document.attachment_id.datas) for worksheet in root.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Worksheet'):
sheet_name = worksheet.attrib.get('{urn:schemas-microsoft-com:office:spreadsheet}Name')
logging.info('sheet_name:%s' % sheet_name)
if sheet_name == "Sheet1": # 确保我们只查看包含数据的工作表
current_pt = None
for row in worksheet.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Row'):
cells = list(row.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Cell'))
for i, cell in enumerate(cells):
data_cell = cell.find('.//{urn:schemas-microsoft-com:office:spreadsheet}Data')
if data_cell is not None and data_cell.text is not None: # 添加检查以确保data_cell.text不为空
# 检查是否是PT标识
logging.info(f"Data in cell: {data_cell.text}") # 输出单元格数据
if "PT" in data_cell.text:
current_pt = data_cell.text
pt_coordinates[current_pt] = []
elif data_cell.text in ["X", "Y", "Z"] and current_pt is not None:
# 确保当前单元格后面还有单元格存在,以获取理论值
if i + 1 < len(cells):
next_cell = cells[i + 1]
theory_value = next_cell.find(
'.//{urn:schemas-microsoft-com:office:spreadsheet}Data')
if theory_value is not None:
# 为当前PT键添加坐标数据
pt_coordinates[current_pt].append({
data_cell.text: float(theory_value.text)
})
logging.info(f"PT: {current_pt} - {data_cell.text}: {theory_value.text}")
logging.info('pt_coordinates=====%s' % pt_coordinates)
# pt_coordinates:{'PT1': [{'X': 38.9221}, {'Y': -18.7304}, {'Z': 128.0783}],
# 'PT2': [{'X': 39.2456}, {'Y': -76.9169}, {'Z': 123.7541}]}
# 创建临时文件保存响应内容 # 检查是否存在PT1等键
with tempfile.NamedTemporaryFile(delete=False, suffix='.xls') as temp_file: if 'PT1' in pt_coordinates and pt_coordinates['PT1']:
temp_file.write(binary_data) self.X1_axis = pt_coordinates['PT3'][0]['X']
temp_file_path = temp_file.name self.Y1_axis = pt_coordinates['PT3'][1]['Y']
self.Z1_axis = pt_coordinates['PT3'][2]['Z']
else:
raise UserError('PT1点未测或数据错误')
if 'PT2' in pt_coordinates and pt_coordinates['PT2']:
self.X2_axis = pt_coordinates['PT4'][0]['X']
self.Y2_axis = pt_coordinates['PT4'][1]['Y']
self.Z2_axis = pt_coordinates['PT4'][2]['Z']
else:
raise UserError('PT2点未测或数据错误')
if 'PT3' in pt_coordinates and pt_coordinates['PT3']:
self.X3_axis = pt_coordinates['PT5'][0]['X']
self.Y3_axis = pt_coordinates['PT5'][1]['Y']
self.Z3_axis = pt_coordinates['PT5'][2]['Z']
else:
raise UserError('PT3点未测或数据错误')
if 'PT4' in pt_coordinates and pt_coordinates['PT4']:
self.X4_axis = pt_coordinates['PT6'][0]['X']
self.Y4_axis = pt_coordinates['PT6'][1]['Y']
self.Z4_axis = pt_coordinates['PT6'][2]['Z']
else:
raise UserError('PT4点未测或数据错误')
if 'PT5' in pt_coordinates and pt_coordinates['PT5']:
self.X5_axis = pt_coordinates['PT7'][0]['X']
self.Y5_axis = pt_coordinates['PT7'][1]['Y']
self.Z5_axis = pt_coordinates['PT7'][2]['Z']
else:
raise UserError('PT5点未测或数据错误')
if 'PT6' in pt_coordinates and pt_coordinates['PT6']:
self.X6_axis = pt_coordinates['PT8'][0]['X']
self.Y6_axis = pt_coordinates['PT8'][1]['Y']
self.Z6_axis = pt_coordinates['PT8'][2]['Z']
else:
raise UserError('PT6点未测或数据错误')
if 'PT7' in pt_coordinates and pt_coordinates['PT7']:
self.X7_axis = pt_coordinates['PT9'][0]['X']
self.Y7_axis = pt_coordinates['PT9'][1]['Y']
self.Z7_axis = pt_coordinates['PT9'][2]['Z']
else:
raise UserError('PT7点未测或数据错误')
if 'PT8' in pt_coordinates and pt_coordinates['PT8']:
self.X8_axis = pt_coordinates['PT10'][0]['X']
self.Y8_axis = pt_coordinates['PT10'][1]['Y']
self.Z8_axis = pt_coordinates['PT10'][2]['Z']
else:
raise UserError('PT8点未测或数据错误')
if 'PT9' in pt_coordinates and pt_coordinates['PT9']:
self.X9_axis = pt_coordinates['PT1'][0]['X']
self.Y9_axis = pt_coordinates['PT1'][1]['Y']
self.Z9_axis = pt_coordinates['PT1'][2]['Z']
else:
raise UserError('PT9点未测或数据错误')
if 'PT10' in pt_coordinates and pt_coordinates['PT10']:
self.X10_axis = pt_coordinates['PT2'][0]['X']
self.Y10_axis = pt_coordinates['PT2'][1]['Y']
self.Z10_axis = pt_coordinates['PT2'][2]['Z']
else:
raise UserError('PT10点未测或数据错误')
try: self.data_state = True
# 使用临时文件进行解析 self.getcenter()
parser = etree.XMLParser(recover=True)
tree = etree.parse(temp_file_path, parser)
logging.info('tree:%s' % tree)
root = tree.getroot()
logging.info('root:%s' % root)
return True
# 准备一个外部字典来存储以PT为键的坐标字典
pt_coordinates = {}
# 遍历每个工作表和行
for worksheet in root.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Worksheet'):
sheet_name = worksheet.attrib.get('{urn:schemas-microsoft-com:office:spreadsheet}Name')
logging.info('sheet_name:%s' % sheet_name)
if sheet_name == "Sheet1": # 确保我们只查看包含数据的工作表
current_pt = None
for row in worksheet.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Row'):
cells = list(row.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Cell'))
for i, cell in enumerate(cells):
data_cell = cell.find('.//{urn:schemas-microsoft-com:office:spreadsheet}Data')
if data_cell is not None and data_cell.text is not None: # 添加检查以确保data_cell.text不为空
# 检查是否是PT标识
logging.info(f"Data in cell: {data_cell.text}") # 输出单元格数据
if "PT" in data_cell.text:
current_pt = data_cell.text
pt_coordinates[current_pt] = []
elif data_cell.text in ["X", "Y", "Z"] and current_pt is not None:
# 确保当前单元格后面还有单元格存在,以获取理论值
if i + 1 < len(cells):
next_cell = cells[i + 1]
theory_value = next_cell.find(
'.//{urn:schemas-microsoft-com:office:spreadsheet}Data')
if theory_value is not None:
# 为当前PT键添加坐标数据
pt_coordinates[current_pt].append({
data_cell.text: float(theory_value.text)
})
logging.info(f"PT: {current_pt} - {data_cell.text}: {theory_value.text}")
logging.info('pt_coordinates=====%s' % pt_coordinates)
# pt_coordinates:{'PT1': [{'X': 38.9221}, {'Y': -18.7304}, {'Z': 128.0783}],
# 'PT2': [{'X': 39.2456}, {'Y': -76.9169}, {'Z': 123.7541}]}
# 检查是否存在PT1等键
if 'PT1' in pt_coordinates and pt_coordinates['PT1']:
self.X1_axis = pt_coordinates['PT3'][0]['X']
self.Y1_axis = pt_coordinates['PT3'][1]['Y']
self.Z1_axis = pt_coordinates['PT3'][2]['Z']
else:
raise UserError('PT1点未测或数据错误')
if 'PT2' in pt_coordinates and pt_coordinates['PT2']:
self.X2_axis = pt_coordinates['PT4'][0]['X']
self.Y2_axis = pt_coordinates['PT4'][1]['Y']
self.Z2_axis = pt_coordinates['PT4'][2]['Z']
else:
raise UserError('PT2点未测或数据错误')
if 'PT3' in pt_coordinates and pt_coordinates['PT3']:
self.X3_axis = pt_coordinates['PT5'][0]['X']
self.Y3_axis = pt_coordinates['PT5'][1]['Y']
self.Z3_axis = pt_coordinates['PT5'][2]['Z']
else:
raise UserError('PT3点未测或数据错误')
if 'PT4' in pt_coordinates and pt_coordinates['PT4']:
self.X4_axis = pt_coordinates['PT6'][0]['X']
self.Y4_axis = pt_coordinates['PT6'][1]['Y']
self.Z4_axis = pt_coordinates['PT6'][2]['Z']
else:
raise UserError('PT4点未测或数据错误')
if 'PT5' in pt_coordinates and pt_coordinates['PT5']:
self.X5_axis = pt_coordinates['PT7'][0]['X']
self.Y5_axis = pt_coordinates['PT7'][1]['Y']
self.Z5_axis = pt_coordinates['PT7'][2]['Z']
else:
raise UserError('PT5点未测或数据错误')
if 'PT6' in pt_coordinates and pt_coordinates['PT6']:
self.X6_axis = pt_coordinates['PT8'][0]['X']
self.Y6_axis = pt_coordinates['PT8'][1]['Y']
self.Z6_axis = pt_coordinates['PT8'][2]['Z']
else:
raise UserError('PT6点未测或数据错误')
if 'PT7' in pt_coordinates and pt_coordinates['PT7']:
self.X7_axis = pt_coordinates['PT9'][0]['X']
self.Y7_axis = pt_coordinates['PT9'][1]['Y']
self.Z7_axis = pt_coordinates['PT9'][2]['Z']
else:
raise UserError('PT7点未测或数据错误')
if 'PT8' in pt_coordinates and pt_coordinates['PT8']:
self.X8_axis = pt_coordinates['PT10'][0]['X']
self.Y8_axis = pt_coordinates['PT10'][1]['Y']
self.Z8_axis = pt_coordinates['PT10'][2]['Z']
else:
raise UserError('PT8点未测或数据错误')
if 'PT9' in pt_coordinates and pt_coordinates['PT9']:
self.X9_axis = pt_coordinates['PT1'][0]['X']
self.Y9_axis = pt_coordinates['PT1'][1]['Y']
self.Z9_axis = pt_coordinates['PT1'][2]['Z']
else:
raise UserError('PT9点未测或数据错误')
if 'PT10' in pt_coordinates and pt_coordinates['PT10']:
self.X10_axis = pt_coordinates['PT2'][0]['X']
self.Y10_axis = pt_coordinates['PT2'][1]['Y']
self.Z10_axis = pt_coordinates['PT2'][2]['Z']
else:
raise UserError('PT10点未测或数据错误')
self.data_state = True
self.getcenter()
return True
except Exception as e:
logging.error('解析文件失败: %s' % str(e))
raise UserError(f'解析文件失败: {str(e)}')
finally:
# 清理临时文件
try:
os.unlink(temp_file_path)
except Exception as e:
logging.warning(f'清理临时文件失败: {str(e)}')
# ftp.download_file('three_check_datas.xls', '/home/ftpuser/three_check_datas.xls') # ftp.download_file('three_check_datas.xls', '/home/ftpuser/three_check_datas.xls')
# ftp.close() # ftp.close()
# data = xlrd.open_workbook('/home/ftpuser/three_check_datas.xls') # data = xlrd.open_workbook('/home/ftpuser/three_check_datas.xls')
@@ -1308,20 +1276,27 @@ class ResMrpWorkOrder(models.Model):
# for move_line in move.move_line_ids # for move_line in move.move_line_ids
# ) # )
if (workorder.production_id.production_type == '人工线下加工' if (workorder.production_id.production_type == '人工线下加工'
and workorder.production_id.schedule_state == ''): and workorder.production_id.programming_state == '编程'):
# and workorder.production_id.programming_state == '已编程' # and workorder.production_id.programming_state == '已编程'
if workorder.is_subcontract is True: if workorder.is_subcontract is True:
if workorder.production_id.state == 'rework': if workorder.production_id.state == 'rework':
workorder.state = 'waiting' workorder.state = 'waiting'
continue continue
purchase_orders_id = self._get_surface_technics_purchase_ids() purchase_orders_id = self._get_surface_technics_purchase_ids()
if purchase_orders_id.state == 'purchase': purchase_count = 0
for purchase_order in purchase_orders_id:
for purchase_order_line in purchase_order.order_line:
if purchase_order_line.product_id.server_product_process_parameters_id.id == workorder.surface_technics_parameters_id.id:
purchase_count = purchase_order_line.product_qty
if purchase_orders_id.state == 'purchase' and purchase_count>=workorder.production_id.product_qty:
workorder.state = 'ready' workorder.state = 'ready'
picking_id = workorder.production_id.picking_ids.filtered( picking_id = workorder.production_id.picking_ids.filtered(
lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区') lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
move_out = picking_id.move_ids move_out = picking_id.move_ids
# move_out = workorder.move_subcontract_workorder_ids[1] # move_out = workorder.move_subcontract_workorder_ids[1]
for mo in move_out: for mo in move_out:
if workorder.production_id.bom_id.bom_line_ids.product_id.id != mo.product_id.id:
continue
if mo.state != 'done': if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False}) mo.write({'state': 'assigned', 'production_id': False})
if not mo.move_line_ids: if not mo.move_line_ids:
@@ -1337,6 +1312,7 @@ class ResMrpWorkOrder(models.Model):
else: else:
workorder.state = 'ready' workorder.state = 'ready'
continue continue
continue
# ================= 如果制造订单刀具状态为[无效刀、缺刀] 或者 制造订单状态为[返工]========================== # ================= 如果制造订单刀具状态为[无效刀、缺刀] 或者 制造订单状态为[返工]==========================
if (workorder.production_id.tool_state in ['1', '2'] or workorder.production_id.state == 'rework' if (workorder.production_id.tool_state in ['1', '2'] or workorder.production_id.state == 'rework'
or workorder.production_id.schedule_state != '已排' or workorder.production_id.schedule_state != '已排'
@@ -1352,9 +1328,7 @@ class ResMrpWorkOrder(models.Model):
if workorder.is_subcontract is False: if workorder.is_subcontract is False:
workorder.state = 'ready' workorder.state = 'ready'
else: else:
if len(workorder.production_id.picking_ids.filtered( if workorder.production_id.programming_state == '已编程':
lambda w: w.state not in ['done',
'cancel'])) == 0 and workorder.production_id.programming_state == '已编程':
purchase_orders_id = self._get_surface_technics_purchase_ids() purchase_orders_id = self._get_surface_technics_purchase_ids()
if purchase_orders_id: if purchase_orders_id:
if purchase_orders_id.state == 'purchase': if purchase_orders_id.state == 'purchase':
@@ -1364,6 +1338,8 @@ class ResMrpWorkOrder(models.Model):
wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区') wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
move_out = picking_id.move_ids move_out = picking_id.move_ids
for mo in move_out: for mo in move_out:
if workorder.production_id.bom_id.bom_line_ids.product_id.id != mo.product_id.id:
continue
if mo.state != 'done': if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False}) mo.write({'state': 'assigned', 'production_id': False})
if not mo.move_line_ids: if not mo.move_line_ids:
@@ -1412,7 +1388,16 @@ class ResMrpWorkOrder(models.Model):
boolean = True boolean = True
if not boolean: if not boolean:
raise UserError('制造订单【%s】缺少组件的序列号信息!' % self.production_id.name) raise UserError('制造订单【%s】缺少组件的序列号信息!' % self.production_id.name)
self.pro_code = self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name self.pro_code = False # 默认值
if (
self.production_id
and self.production_id.move_raw_ids
and len(self.production_id.move_raw_ids) > 0
and self.production_id.move_raw_ids[0].move_line_ids
and len(self.production_id.move_raw_ids[0].move_line_ids) > 0
and self.production_id.move_raw_ids[0].move_line_ids[0].lot_id
):
self.pro_code = self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name
# cnc校验 # cnc校验
if self.production_id.production_type == '自动化产线加工': if self.production_id.production_type == '自动化产线加工':
cnc_workorder = self.search( cnc_workorder = self.search(
@@ -1447,6 +1432,8 @@ class ResMrpWorkOrder(models.Model):
# [('barcode', 'ilike', 'VL-SPOC')]).id), # [('barcode', 'ilike', 'VL-SPOC')]).id),
# ('origin', '=', self.production_id.name), ('state', 'not in', ['cancel', 'done'])]) # ('origin', '=', self.production_id.name), ('state', 'not in', ['cancel', 'done'])])
for mo in move_out: for mo in move_out:
if self.production_id.bom_id.bom_line_ids.product_id.id != mo.product_id.id:
continue
if mo.state != 'done': if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False}) mo.write({'state': 'assigned', 'production_id': False})
if not mo.move_line_ids: if not mo.move_line_ids:
@@ -1579,7 +1566,8 @@ class ResMrpWorkOrder(models.Model):
lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence') lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence')
if self == subcontract_workorders[-1]: if self == subcontract_workorders[-1]:
# 给下一个库存移动就绪 # 给下一个库存移动就绪
self.move_subcontract_workorder_ids[0].move_dest_ids._action_done() if self.move_subcontract_workorder_ids:
self.move_subcontract_workorder_ids[0].move_dest_ids._action_done()
# self.production_id.button_mark_done() # self.production_id.button_mark_done()
tem_date_planned_finished = record.date_planned_finished tem_date_planned_finished = record.date_planned_finished
tem_date_finished = record.date_finished tem_date_finished = record.date_finished

View File

@@ -126,11 +126,13 @@ class PurchaseOrder(models.Model):
if not work_ids: if not work_ids:
continue continue
min_sequence_wk = min(work_ids, key=lambda wk: wk.sequence) min_sequence_wk = min(work_ids, key=lambda wk: wk.sequence)
if min_sequence_wk.is_subcontract: if min_sequence_wk.is_subcontract and min_sequence_wk.state == 'ready':
picking_id = production_id.picking_ids.filtered( picking_id = production_id.picking_ids.filtered(
lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区') lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区')
move_out = picking_id.move_ids move_out = picking_id.move_ids
for mo in move_out: for mo in move_out:
if production_id.bom_id.bom_line_ids.product_id.id != mo.product_id.id:
continue
if mo.state != 'done': if mo.state != 'done':
mo.write({'state': 'assigned', 'production_id': False}) mo.write({'state': 'assigned', 'production_id': False})
if not mo.move_line_ids: if not mo.move_line_ids:
@@ -152,6 +154,8 @@ class PurchaseOrder(models.Model):
for line in self.order_line: for line in self.order_line:
# 将产品不追踪序列号的行项目设置qty_done # 将产品不追踪序列号的行项目设置qty_done
if not line.move_ids:
continue
if line.move_ids and line.move_ids[0].product_id.tracking == 'none': if line.move_ids and line.move_ids[0].product_id.tracking == 'none':
line.move_ids[0].quantity_done = line.move_ids[0].product_qty line.move_ids[0].quantity_done = line.move_ids[0].product_qty
return res return res
@@ -224,6 +228,24 @@ class PurchaseOrderLine(models.Model):
) )
record.part_number = filtered_order_line.product_id.part_number record.part_number = filtered_order_line.product_id.part_number
record.part_name = filtered_order_line.product_id.part_name record.part_name = filtered_order_line.product_id.part_name
elif record.order_id.purchase_type == 'consignment':
product_name = ''
match = re.search(r'(S\d{5}-\d)', record.related_product.name)
# 如果匹配成功,提取结果
if match:
product_name = match.group(0)
sale_order_name = ''
match_sale = re.search(r'S(\d+)', record.related_product.name)
if match_sale:
sale_order_name = match_sale.group(0)
sale_order = self.env['sale.order'].sudo().search(
[('name', '=', sale_order_name)])
if sale_order:
filtered_order_line = sale_order.order_line.filtered(
lambda order_line: re.search(f'{product_name}$', order_line.product_id.name)
)
record.part_number = filtered_order_line.product_id.part_number
record.part_name = filtered_order_line.product_id.part_name
else: else:
record.part_number = record.product_id.part_number record.part_number = record.product_id.part_number
record.part_name = record.product_id.part_name record.part_name = record.product_id.part_name

View File

@@ -20,13 +20,6 @@ class SfProductionProcessParameter(models.Model):
is_product_button = fields.Boolean(compute='_compute_is_product_button',default=False) is_product_button = fields.Boolean(compute='_compute_is_product_button',default=False)
is_delete_button = fields.Boolean(compute='_compute_is_delete_button', default=False) is_delete_button = fields.Boolean(compute='_compute_is_delete_button', default=False)
routing_id = fields.Many2one('mrp.routing.workcenter', string="工序") routing_id = fields.Many2one('mrp.routing.workcenter', string="工序")
@api.depends('outsourced_service_products')
def _compute_service_products(self):
for record in self:
# 假设取第一条作为主明细
record.service_products = record.outsourced_service_products.ids if record.outsourced_service_products else False
def _inverse_service_products(self): def _inverse_service_products(self):
for record in self: for record in self:
if record.service_products: if record.service_products:
@@ -34,6 +27,12 @@ class SfProductionProcessParameter(models.Model):
record.outsourced_service_products = record.service_products.ids if record.service_products else False record.outsourced_service_products = record.service_products.ids if record.service_products else False
else: else:
record.outsourced_service_products = False record.outsourced_service_products = False
@api.depends('outsourced_service_products')
def _compute_service_products(self):
for record in self:
# 假设取第一条作为主明细
record.service_products = record.outsourced_service_products[0].id if record.outsourced_service_products else False
def name_get(self): def name_get(self):
result = [] result = []
for record in self: for record in self:

View File

@@ -930,6 +930,8 @@ class ReStockMove(models.Model):
} }
def get_move_line(self, production_id, sorted_workorders): def get_move_line(self, production_id, sorted_workorders):
# if not self.move_ids[0].product_id.single_manufacturing and self.move_ids[0].product_id.tracking == 'none':
qty = production_id.product_qty
return { return {
'move_id': self.id, 'move_id': self.id,
'product_id': self.product_id.id, 'product_id': self.product_id.id,
@@ -937,7 +939,7 @@ class ReStockMove(models.Model):
'location_id': self.picking_id.location_id.id, 'location_id': self.picking_id.location_id.id,
'location_dest_id': self.picking_id.location_dest_id.id, 'location_dest_id': self.picking_id.location_dest_id.id,
'picking_id': self.picking_id.id, 'picking_id': self.picking_id.id,
'reserved_uom_qty': 1.0, 'reserved_uom_qty': qty,
'lot_id': production_id.move_line_raw_ids.lot_id.id, 'lot_id': production_id.move_line_raw_ids.lot_id.id,
'company_id': self.env.company.id, 'company_id': self.env.company.id,
# 'workorder_id': '' if not sorted_workorders else sorted_workorders.id, # 'workorder_id': '' if not sorted_workorders else sorted_workorders.id,

View File

@@ -268,8 +268,6 @@ class ReworkWizard(models.TransientModel):
'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program( 'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(
cnc_work.processing_panel, ret), cnc_work.processing_panel, ret),
'cnc_worksheet': old_cnc_rework.cnc_worksheet}) 'cnc_worksheet': old_cnc_rework.cnc_worksheet})
# 复制装夹图纸
new_cnc_workorder.processing_drawing = old_cnc_rework.processing_drawing
# ========== 处理装夹预调 【装夹图纸】 数据 ================ # ========== 处理装夹预调 【装夹图纸】 数据 ================
for new_pre_work in new_pre_workorder_ids: for new_pre_work in new_pre_workorder_ids:
pre_rework = max(self.production_id.workorder_ids.filtered( pre_rework = max(self.production_id.workorder_ids.filtered(

View File

@@ -3,6 +3,9 @@ import logging
import os import os
import json import json
import base64 import base64
import traceback
from odoo import http, fields, models from odoo import http, fields, models
from odoo.http import request from odoo.http import request
from odoo.addons.sf_base.controllers.controllers import MultiInheritController from odoo.addons.sf_base.controllers.controllers import MultiInheritController
@@ -273,7 +276,8 @@ class Sf_Mrs_Connect(http.Controller, MultiInheritController):
except Exception as e: except Exception as e:
res = {'status': -1, 'message': '系统解析失败'} res = {'status': -1, 'message': '系统解析失败'}
request.cr.rollback() request.cr.rollback()
logging.info('get_cnc_processing_create error:%s' % e) traceback_error = traceback.format_exc()
logging.error("get_cnc_processing_create error:%s" % traceback_error)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)

View File

@@ -5,7 +5,6 @@ from odoo import fields, models, api
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from datetime import datetime from datetime import datetime
from odoo.addons.sf_base.commons.common import Common from odoo.addons.sf_base.commons.common import Common
from odoo.tools import float_round
class QualityCheck(models.Model): class QualityCheck(models.Model):
@@ -141,71 +140,3 @@ class QualityCheck(models.Model):
return "零件特采发送成功" return "零件特采发送成功"
else: else:
raise ValidationError("零件特采发送失败") raise ValidationError("零件特采发送失败")
@api.model_create_multi
def create(self, vals_list):
for val in vals_list:
if 'point_id' in val and 'measure_on' not in val:
# 如果没有控制方式字段,则从检查点读取质量方式
point_id = self.env['quality.point'].browse(val['point_id'])
val.update({'measure_on': point_id.measure_on})
return super(QualityCheck, self).create(vals_list)
@api.depends('total_qty','testing_percentage_within_lot', 'is_lot_tested_fractionally')
def _compute_workorder_qty_to_test(self):
for qc in self:
if qc.is_lot_tested_fractionally:
rounding = qc.product_id.uom_id.rounding if qc.product_id.uom_id else 0.01
qc.workorder_qty_to_test = float_round(float(qc.total_qty) * qc.testing_percentage_within_lot / 100,
precision_rounding=rounding, rounding_method="UP")
else:
qc.workorder_qty_to_test = qc.total_qty
@api.depends('picking_id', 'workorder_id')
def _compute_total_qty(self):
super(QualityCheck, self)._compute_total_qty()
for qc in self:
if not qc.picking_id and qc.workorder_id:
qc.total_qty = qc.workorder_id.production_id.product_qty
@api.depends('workorder_qty_to_test')
def _compute_workorder_qty_tested(self):
for qc in self:
qc.workorder_qty_tested = qc.workorder_qty_to_test
workorder_qty_to_test = fields.Float('应检', compute='_compute_workorder_qty_to_test', store=True)
workorder_qty_tested = fields.Float('已检', compute='_compute_workorder_qty_tested', store=True)
workorder_qty_test_failed = fields.Float('不合格数')
@api.onchange('total_qty', 'workorder_qty_test_failed', 'workorder_qty_to_test', 'workorder_qty_tested')
def _onchage_qty(self):
for record in self:
if record.total_qty and record.workorder_qty_to_test and record.workorder_qty_to_test > float(record.total_qty):
record.workorder_qty_to_test = float(record.total_qty)
return {
'warning': {
'title': '警告',
'message': '待检数量不能超过总数量'
}
}
if record.workorder_qty_to_test and record.workorder_qty_tested and record.workorder_qty_tested > record.workorder_qty_to_test:
record.workorder_qty_tested = record.workorder_qty_to_test
return {
'warning': {
'title': '警告',
'message': '已检数量不能超过待检数量'
}
}
if record.workorder_qty_tested and record.workorder_qty_test_failed and record.workorder_qty_test_failed > record.workorder_qty_tested:
record.workorder_qty_test_failed = record.workorder_qty_tested
return {
'warning': {
'title': '警告',
'message': '不合格数量不能超过已检数量'
}
}

View File

@@ -88,42 +88,6 @@
<button name="do_cancel_publish" string="取消发布" type="object" class="btn-primary" confirm="确定取消发布吗?" attrs="{'invisible': ['|',('is_out_check', '=', False), ('publish_status', '!=', 'published')]}"/> <button name="do_cancel_publish" string="取消发布" type="object" class="btn-primary" confirm="确定取消发布吗?" attrs="{'invisible': ['|',('is_out_check', '=', False), ('publish_status', '!=', 'published')]}"/>
<button name="do_re_publish" string="重新发布" type="object" class="btn-primary" attrs="{'invisible': ['|', ('is_out_check', '=', False), ('publish_status', '!=', 'canceled')]}"/> <button name="do_re_publish" string="重新发布" type="object" class="btn-primary" attrs="{'invisible': ['|', ('is_out_check', '=', False), ('publish_status', '!=', 'canceled')]}"/>
</xpath> </xpath>
<xpath expr="//field[@name='total_qty']" position="attributes">
<attribute name="attrs">{
'invisible': ['&amp;', '|', ('measure_on', '!=', 'product'), ('is_out_check', '=', False), '|', ('measure_on', '!=', 'move_line'), ('workorder_id', '=', False)],
'readonly': ['|', ('measure_on', '!=', 'move_line'), ('workorder_id', '=', False)],
'on_change': ['|', ('measure_on', '!=', 'move_line'), ('workorder_id', '=', False)]
}</attribute>
</xpath>
<xpath expr="//field[@name='total_qty']" position="after">
<label for="workorder_qty_to_test"
attrs="{'invisible': ['|', '&amp;', ('measure_on', '!=', 'move_line'), ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}"/>
<div class="o_row"
attrs="{'invisible': ['|', '&amp;', ('measure_on', '!=', 'move_line'), ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}">
<field name="workorder_qty_to_test" attrs="{'readonly': 0, 'on_chnage': 1}"/>
<field name="uom_id"/>
</div>
<label for="workorder_qty_tested"
attrs="{'invisible': ['|', '&amp;', ('measure_on', '!=', 'move_line'), ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}"/>
<div class="o_row"
attrs="{'invisible': ['|', '&amp;', ('measure_on', '!=', 'move_line'), ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}">
<field name="workorder_qty_tested" attrs="{'readonly': [('quality_state', '!=', 'none')], 'on_chnage': 1}"/>
<field name="uom_id"/>
</div>
<label for="workorder_qty_test_failed"
attrs="{'invisible': ['|', '&amp;', ('measure_on', '!=', 'move_line'), ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}"/>
<div class="o_row"
attrs="{'invisible': ['|', '&amp;', ('measure_on', '!=', 'move_line'), ('workorder_id', '=', False), ('is_lot_tested_fractionally', '=', False)]}">
<field name="workorder_qty_test_failed" attrs="{'readonly': [('quality_state', '!=', 'none')], 'on_chnage': 1}"/>
<field name="uom_id"/>
</div>
</xpath>
<xpath expr="//label[@for='qty_tested']" position="attributes">
<attribute name="attrs">{'invisible': ['|', '|', ('measure_on', '!=', 'move_line'), ('is_lot_tested_fractionally', '=', False), '&amp;', ('measure_on', '=', 'move_line'), ('workorder_id', '!=', False)]}</attribute>
</xpath>
<xpath expr="//div[@class='o_row'][.//field[@name='qty_tested']]" position="attributes">
<attribute name="attrs">{'invisible': ['|', '|', ('measure_on', '!=', 'move_line'), ('is_lot_tested_fractionally', '=', False), '&amp;', ('measure_on', '=', 'move_line'), ('workorder_id', '!=', False)]}</attribute>
</xpath>
</field> </field>
</record> </record>